/** @file

Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

External(\_SB.PC00.XHCI.PS0X, MethodObj)
External(\_SB.PC00.XHCI.PS3X, MethodObj)
External(\_SB.PC00.XHCI.RHUB.PS0X, MethodObj)
External(\_SB.PC00.XHCI.RHUB.PS2X, MethodObj)
External(\_SB.PC00.XHCI.RHUB.PS3X, MethodObj)
External(\_SB.PC00.XHCI.RHUB.INIR, MethodObj)

Scope(\_SB_.PC00.XHCI) {

    OperationRegion(XPRT,PCI_Config,0x00,0x100)
    Field(XPRT,AnyAcc,NoLock,Preserve)
    {
      DVID, 16,
      Offset(0x74),
      D0D3,  2,  // 0x74 BIT[1:0]
          ,  6,
      PMEE,  1,  // PME Enable
          ,  6,
      PMES,  1,  // PME Status
      Offset(0xA8), // SSCFG Reg for WPTLP
          , 13,
      MW13,  1,  // 0xA8 BIT[13]
      MW14,  1,  // 0xA8 BIT[14]
          , 17,
      Offset(0xB0), // SSCFG Reg for LPTLP
          , 13,
      MB13,  1,  // 0xB0 BIT[13]
      MB14,  1,  // 0xB0 BIT[14]
          , 17,
      Offset(0xD0),
      PR2,  32,  // XUSB2PR: xHC USB 2.0 Port Routing Register.
      PR2M, 32,  // XUSB2PRM: xHC USB 2.0 Port Routing Mask Register.
      PR3,  32,  // USB3_PSSEN: USB3.0 Port SuperSpeed Enable Register.
      PR3M, 32   // USB3PRM: USB3.0 Port Routing Mask Register
    }

    //
    // Variable to store the maximum D state supported in S0.
    //
    Name (XFLT, 0)
    //
    // XHCI controller won't go into D3Hot during S0 until _DSM method is evaluated by filter driver.
    //
    Method(_DSM,4,serialized){
      If(PCIC(Arg0)) { return(PCID(Arg0,Arg1,Arg2,Arg3)) }
      //
      // Check GUID ac340cb7-e901-45bf-b7e6-2b34ec931e23
      //
      If(LEqual(Arg0, Buffer(0x10) { 0xb7, 0x0c, 0x34, 0xac, 0x01, 0xe9, 0xbf, 0x45, 0xb7, 0xe6, 0x2b, 0x34, 0xec, 0x93, 0x1e, 0x23 }))
      {
        //
        // Only Supported value is "0x3"
        //
        If(LEqual(Arg1, 0x3))
        {
          Store(Arg1,XFLT)
        }
      }
      return(0)
    }

    Method(_S3D, 0, NotSerialized)
    {
      Return(3)
    }
    Method(_S4D, 0, NotSerialized)
    {
      Return(3)
    }
    Method(_S3W, 0, NotSerialized)
    {
      Return(3)
    }
    Method(_S4W, 0, NotSerialized)
    {
      Return(3)
    }

    //
    // Once the filter driver is installed, D3 is allowed.
    //
    Method(_S0W, 0x0, NotSerialized)
    {
      If(LEqual(XFLT, Zero))
      {
        Return(0x0)
      }
      Else
      {
        Return(0x3)
      }
    }

    Method(_PRW, 0)
    {
      Return(GPRW(0x6D, 4)) // can wakeup from S4 state
    }

    Method(_DSW, 3)
    {
      Store(Arg0, PMEE)
    }

    Method(_INI) {
      // _INI for RTD3 run conditionally if implemented in platform specific code
      If(CondRefOf(\_SB.PC00.XHCI.RHUB.INIR)) {      // _INI for RTD3
        \_SB.PC00.XHCI.RHUB.INIR()
      }
    }

    //
    // GPE handler for XHCI, this is part of _Lxx handler for bus 0 PME
    //
    Method(GPEH)
    {
      If(LEqual(DVID, 0xFFFF)) {
        Return()
      }
      Store(PMES, Local0)
      Store(1, PMES) // clear PME Status
      If(LAnd(PMEE, Local0)) {
        Notify(XHCI, 0x02)
      }
    }

    OperationRegion(XHCP, SystemMemory, Add(\_SB.PC00.GPCB(), 0xA0000), 0x100)
    Field(XHCP,AnyAcc,Lock,Preserve)
    {
      Offset(0x4),
      PDBM, 16,
      Offset(0x10),
      MEMB, 64
    }

    //
    // USRA (USbR port Address), method for calculating address of first USBR port in XHCI controller
    //
    Method(USRA,0,Serialized) {
      If(LEqual(PCHV, SPTH)) { // SPT-H
        Return (15)
      } Else {                 // SPT-LP
        Return (11)
      }
    }

    //
    // SSPA (SuperSpeed Port Address), method for calculating address of first SS port in XHCI controller
    //
    Method(SSPA,0,Serialized) {
      If(LEqual(PCHV, SPTH)) { // SPT-H
        Return (17)
      } Else {                 // SPT-LP
        Return (13)
      }
    }

    Name(XRST, Zero)

    Method(_PS0,0,Serialized)
    {

      If(LEqual(^DVID,0xFFFF))
      {
        Return()
      }

      Store(^MEMB,Local2)         // Save MBAR
      Store(^PDBM,Local1)         // Save CMD

      And(^PDBM,Not(0x06),^PDBM)  // Clear MSE/BME

      //
      // Switch to D0
      //
      Store(0,^D0D3)

      Store(\XWMB,^MEMB)          // Set MBAR
      Or(Local1,0x0002,^PDBM)     // Set MSE

      //
      //
      //

      And(^PDBM,Not(0x02),^PDBM)  // Clear MSE

      Store(Local2,^MEMB)         // Restore MBAR
      Store(Local1,^PDBM)         // Restore CMD

      //
      // Call platform XHC PS0 method if present
      //
      If(CondRefOf(\_SB.PC00.XHCI.PS0X))
      {
        \_SB.PC00.XHCI.PS0X()
      }
        //
        //
        If(LAnd(UWAB,LEqual(D0D3,0))) {

          //
          // If the USB WA Bit is set and any XHCI or XDCI controller is in D0
          //
          //
          // USB2 PHPY Power Gating - SW WA
          //
          // 1. BIOS writes a new command to the MTPMC register to cause the PMC to disable power gating
          Store(1,MPMC)
          
          // 2. BIOS waits for PCH_PM_STS.MSG_FULL_STS to be 0
          //      In parallel and in response to the previous command from BIOS, PMC FW will:
          //         * Make the USB2 force common lane PG match the BIOS policy (common lane PG disallowed)
          //         * Disable SUS power gating in XHCI
          //         * Wait for USB2 PHY side_pok to be 1
          //         * Disable core power gating in USB2 PHY
          //         * Disable common lane power gating in USB2 PHY (probably not necessary, consider removal)
          //         * Wait for USB2 SUS restoration status to be set, and do USB2 SUS power gating restoration
          //         * Get the PHY in the correct state before allowing transition to D0.
          //        * Clear MSG_FULL_STS
          //    BIOS sees MSG_FULL_STS clear and exits the method
          //
          While(PMFS) {
            Sleep(10)
          }
        } // End If(UWAB)

    }

    Method(_PS3,0,Serialized)
    {

      If(LEqual(^DVID,0xFFFF))
      {
        Return()
      }

      Store(^MEMB,Local2)         // Save MBAR
      Store(^PDBM,Local1)         // Save CMD

      And(^PDBM,Not(0x06),^PDBM)  // Clear MSE/BME

      //
      // Switch back to D0
      //
      Store(0,^D0D3)

      Store(\XWMB,^MEMB)          // Set MBAR
      Or(Local1,0x0002,^PDBM)     // Set MSE

      //
      //
      //

      And(^PDBM,Not(0x02),^PDBM)  // Clear MSE

      //
      // Switch back to D3
      //
      Store(3,^D0D3)

      Store(Local2,^MEMB)         // Restore MBAR
      Store(Local1,^PDBM)         // Restore CMD

      //
      // Call platform XHC PS3 method if present
      //
      If(CondRefOf(\_SB.PC00.XHCI.PS3X))
      {
        \_SB.PC00.XHCI.PS3X()
      }
        If(LAnd(UWAB,LEqual(D0D3,3))) {
          //
          // If the USB WA Bit is set and XHCI is in D3
          //

          // 1. BIOS writes a new command to the MTPMC register to enable power gating
          Store(3,MPMC)
          
          // 2. BIOS waits for PCH_PM_STS.MSG_FULL_STS to be 0
          //      In parallel and in response to the previous command from BIOS, PMC FW will:
          //        * Make the USB2 force common lane PG match the BIOS policy (common lane PG allowed)
          //        * Enable SUS power gating in XHCI
          //        * Enable core power gating in USB2 PHY
          //        * Enable common lane power gating in the USB2 PHY
          //        * Clear MSG_FULL_STS
          //    BIOS sees MSG_FULL_STS clear and exits the method
          //
          While(PMFS) {
            Sleep(10)
          }
        } // End If(UWAB)
    }


    // Apply S3 workaround. 
    // Arguments : 
    //  None
    // Changes 8090 Bit 10 before S3. 
    // 
    Method(XHCS,0, Serialized) {
      Store(^MEMB,Local2)         // Save MBAR
      Store(^PDBM,Local1)         // Save CMD

      And(^PDBM,Not(0x06),^PDBM)  // Clear MSE/BME

      Store(\XWMB,^MEMB)          // Set MBAR
      Or(Local1,0x0002,^PDBM)     // Set MSE

      OperationRegion(MC11,SystemMemory,\XWMB,0x9000)
      Field(MC11,DWordAcc,Lock,Preserve)
      {
        Offset(0x8090),   //  HC Transfer Manager - TRM 
            ,  10,
        UCLI,  1,         // CLEAR IN EP
      }

      Store(0x1,UCLI)

      And(^PDBM,Not(0x02),^PDBM)  // Clear MSE

      Store(Local2,^MEMB)         // Restore MBAR
      Store(Local1,^PDBM)         // Restore CMD
    
    }

    //
    //
    // Check for XHCI switch UUID
    //
    // Arguments:
    //  Arg0 (Buffer) : UUID
    //
    // Returns:
    //  1: It's valid UUID
    //  0: Invalid UUID
    //
    Method(CUID,1,Serialized) {
      If(LEqual(Arg0,ToUUID("7c9512a9-1705-4cb4-af7d-506a2423ab71"))) {
        Return(1)
      }
      Return(0)
    }

    Device(RHUB)
    {
      Name(_ADR, Zero)

      // PS0 Method for xHCI Root Hub
      Method(_PS0,0,Serialized)
      {
        If(LEqual(\_SB.PC00.XHCI.DVID,0xFFFF))
        {
          Return()
        }
        //
        // Call platform XHC.RHUB PS0 method if present.
        //
        If(CondRefOf(\_SB.PC00.XHCI.RHUB.PS0X))
        { 
          \_SB.PC00.XHCI.RHUB.PS0X()
        }
      }

      // PS2 Method for xHCI Root Hub
      Method(_PS2,0,Serialized)
      {
        If(LEqual(\_SB.PC00.XHCI.DVID,0xFFFF))
        {
          Return()
        }
        //
        // Call platform XHC.RHUB PS2 method if present.
        //
        If(CondRefOf(\_SB.PC00.XHCI.RHUB.PS2X))
        {
          \_SB.PC00.XHCI.RHUB.PS2X()
        }
      }

      // PS3 Method for xHCI Root Hub
      Method(_PS3,0,Serialized)
      {
        If(LEqual(\_SB.PC00.XHCI.DVID,0xFFFF))
        {
          Return()
        }
        //
        // Call platform XHC.RHUB PS3 method if present.
        //
        If(CondRefOf(\_SB.PC00.XHCI.RHUB.PS3X))
        {
          \_SB.PC00.XHCI.RHUB.PS3X()
        }
      }

      //
      // High Speed Ports (without USBR)
      //
      Device(HS01)
      {
        Name(_ADR, 0x01)
      }

      Device(HS02)
      {
        Name(_ADR, 0x02)
      }

      Device(HS03)
      {
        Name(_ADR, 0x03)
      }

      Device(HS04)
      {
        Name(_ADR, 0x04)
      }

      Device(HS05)
      {
        Name(_ADR, 0x05)
      }

      Device(HS06)
      {
        Name(_ADR, 0x06)
      }

      Device(HS07)
      {
        Name(_ADR, 0x07)
      }

      Device(HS08)
      {
        Name(_ADR, 0x08)
      }

      Device(HS09)
      {
        Name(_ADR, 0x09)
      }

      Device(HS10)
      {
        Name(_ADR, 0x0A)
      }

      //
      // USBR port will be known as USBR instead of HS09 / HS15
      //
      Device(USR1)
      {
        Method(_ADR) { Return (Add(USRA(),0)) }
      }

      //
      // USBR port 2 will be known as USBR instead of xxxx
      //
      Device(USR2)
      {
        Method(_ADR) { Return (Add(USRA(),1)) }
      }

      //
      // Super Speed Ports
      //
      Device(SS01)
      {
        Method(_ADR) { Return (Add(SSPA(),0)) }
      }

      Device(SS02)
      {
        Method(_ADR) { Return (Add(SSPA(),1)) }
      }

      Device(SS03)
      {
        Method(_ADR) { Return (Add(SSPA(),2)) }
      }

      Device(SS04)
      {
        Method(_ADR) { Return (Add(SSPA(),3)) }
      }

      Device(SS05)
      {
        Method(_ADR) { Return (Add(SSPA(),4)) }
      }

      Device(SS06)
      {
        Method(_ADR) { Return (Add(SSPA(),5)) }
      }

    } // device rhub
} //scope

//
// SPT-H
//

  Scope(\_SB_.PC00.XHCI.RHUB) {



    Device(HS11)
    {
      Name(_ADR, 0xB)
    }


    Device(HS12)
    {
      Name(_ADR, 0xC)
    }

    Device(HS13)
    {
      Name(_ADR, 0xD)
    }

    Device(HS14)
    {
      Name(_ADR, 0xE)
    }

    Device(SS07)
    {
      Method(_ADR) { Return (Add(SSPA(),6)) }
    }

    Device(SS08)
    {
      Method(_ADR) { Return (Add(SSPA(),7)) }
    }

    Device(SS09)
    {
      Method(_ADR) { Return (Add(SSPA(),8)) }
    }

    Device(SS10)
    {
      Method(_ADR) { Return (Add(SSPA(),9)) }
    }

  } //scope

