/**
 * @file
 *
 * Config Southbridge USB controller
 *
 * Init USB features.
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      CIMx-SB
 * @e sub-project:
 * @e \$Revision:$   @e \$Date:$
 *
 */
/*
 *****************************************************************************
 *
 * Copyright (c) 2011, Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***************************************************************************
 *
 */


#include "SBPLATFORM.h"
#include "cbtypes.h"

//
// Declaration of local functions
//

/**
 * EhciInitAfterPciInit - Config USB controller after PCI emulation
 *
 * @param[in] Value Controller PCI config address (bus# + device# + function#)
 * @param[in] pConfig Southbridge configuration structure pointer.
 */
VOID EhciInitAfterPciInit (IN UINT32 Value, IN AMDSBCFG* pConfig);
/**
 * OhciInitAfterPciInit - Config USB OHCI controller after PCI emulation
 *
 * @param[in] Value  Controller PCI config address (bus# + device# + function#)
 * @param[in] pConfig Southbridge configuration structure pointer.
 */
VOID OhciInitAfterPciInit (IN UINT32 Value, IN AMDSBCFG* pConfig);

/**
 * SetEhciP11Wr - FIXME
 *
 * @param[in] Value  Controller PCI config address (bus# + device# + function#)
 * @param[in] pConfig Southbridge configuration structure pointer.
 */
UINT32 SetEhciPllWr (IN UINT32 Value, IN AMDSBCFG* pConfig);


/**
 * usbInitBeforePciEnum - Config USB controller before PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usbInitBeforePciEnum (
  IN       AMDSBCFG* pConfig
  )
{
  // Disabled All USB controller
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGEF, AccWidthUint8, BIT7, 0);
  // Clear PM_IO 0x65[4] UsbResetByPciRstEnable, Set this bit so that usb gets reset whenever there is PCIRST.
  // Enable UsbResumeEnable (USB PME) * Default value
  // In SB700 USB SleepCtrl set as BIT10+BIT9, but SB800 default is BIT9+BIT8 (6 uframes)
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGF0, AccWidthUint16 | S3_SAVE, ~BIT2, BIT2 + BIT7 + BIT8 + BIT9);
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGEF, AccWidthUint8, 0, pConfig->USBMODE.UsbModeReg);
}

/**
 * usbInitAfterPciInit - Config USB controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usbInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGED, AccWidthUint8, ~BIT1, BIT1);

  usb1EhciInitAfterPciInit (pConfig);
  usb2EhciInitAfterPciInit (pConfig);
  usb3EhciInitAfterPciInit (pConfig);
  usb1OhciInitAfterPciInit (pConfig);
  usb2OhciInitAfterPciInit (pConfig);
  usb3OhciInitAfterPciInit (pConfig);
  usb4OhciInitAfterPciInit (pConfig);

  if ( pConfig->UsbPhyPowerDown ) {
    RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGF0, AccWidthUint8, ~BIT0, BIT0);
  } else
  {
    RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGF0, AccWidthUint8, ~BIT0, 0);
  }

}

/**
 * usb1EhciInitAfterPciInit - Config USB1 EHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb1EhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB1_EHCI_BUS_DEV_FUN << 16);
  EhciInitAfterPciInit (ddDeviceId, pConfig);
}

/**
 * usb2EhciInitAfterPciInit - Config USB2 EHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb2EhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB2_EHCI_BUS_DEV_FUN << 16);
  EhciInitAfterPciInit (ddDeviceId, pConfig);
}

/**
 * usb3EhciInitAfterPciInit - Config USB3 EHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb3EhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB3_EHCI_BUS_DEV_FUN << 16);
  EhciInitAfterPciInit (ddDeviceId, pConfig);
}

VOID
EhciInitAfterPciInit (
  IN       UINT32 Value,
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddBarAddress;
  UINT32  ddVar;
  //Get BAR address
  ReadPCI ((UINT32) Value + SB_EHCI_REG10, AccWidthUint32, &ddBarAddress);
  if ( (ddBarAddress != - 1) && (ddBarAddress != 0) ) {
    //Enable Memory access
    RWPCI ((UINT32) Value + SB_EHCI_REG04, AccWidthUint8, 0, BIT1);
    if (pConfig->BuildParameters.EhciSsid != 0 ) {
      RWPCI ((UINT32) Value + SB_EHCI_REG2C, AccWidthUint32 | S3_SAVE, 0x00, pConfig->BuildParameters.EhciSsid);
    }
    //USB Common PHY CAL & Control Register setting
    ddVar = 0x00020F00;
    WriteMEM (ddBarAddress + SB_EHCI_BAR_REGC0, AccWidthUint32, &ddVar);
    // RPR IN AND OUT DATA PACKET FIFO THRESHOLD
    // EHCI BAR 0xA4 //IN threshold bits[7:0]=0x40 //OUT threshold bits[23:16]=0x40
    RWMEM (ddBarAddress + SB_EHCI_BAR_REGA4, AccWidthUint32, 0xFF00FF00, 0x00400040);
    // RPR EHCI Dynamic Clock Gating Feature
    RWMEM (ddBarAddress + SB_EHCI_BAR_REGBC, AccWidthUint32, ~BIT12, 0);
    // RPR Enable adding extra flops to PHY rsync path
    // Step 1:
    //  EHCI_BAR 0xB4 [6] = 1
    //  EHCI_BAR 0xB4 [7] = 0
    //  EHCI_BAR 0xB4 [12] = 0 ("VLoad")
    //  All other bit field untouched
    // Step 2:
    //  EHCI_BAR 0xB4[12] = 1
    RWMEM (ddBarAddress + SB_EHCI_BAR_REGB4, AccWidthUint32, ~(BIT6 + BIT7 + BIT12), 0x00);
    RWMEM (ddBarAddress + SB_EHCI_BAR_REGB4, AccWidthUint32, ~BIT12, BIT12);
    //Set EHCI_pci_configx50[6]='1' to disable EHCI MSI support
    //RPR recommended setting "EHCI Async Park Mode"
    //Set EHCI_pci_configx50[23]='0' to enable "EHCI Async Park Mode support"
    //RPR Enabling EHCI Async Stop Enhancement
    //Set EHCI_pci_configx50[29]='1' to disableEnabling EHCI Async Stop Enhancement
    RWPCI ((UINT32) Value + SB_EHCI_REG50, AccWidthUint32 | S3_SAVE, ~(BIT23), BIT29 + BIT23 + BIT8 + BIT6);
    // RPR recommended setting "EHCI Advance PHY Power Savings"
    // Set EHCI_pci_configx50[31]='1'
    // Fix for EHCI controller driver  yellow sign issue under device manager
    // when used in conjunction with HSET tool driver. EHCI PCI config 0x50[20]=1
    RWPCI ((UINT32) Value + SB_EHCI_REG50 + 2, AccWidthUint16 | S3_SAVE, (UINT16)0xFFFF, BIT15);
    // RPR USB Delay A-Link Express L1 State
    // RPR PING Response Fix Enable EHCI_PCI_Config  x54[1] = 1
    // RPR Empty-list Detection Fix Enable EHCI_PCI_Config  x54[3] = 1
    RWPCI ((UINT32) Value + SB_EHCI_REG54, AccWidthUint32 | S3_SAVE, ~BIT0, BIT0);
    if ( pConfig->BuildParameters.UsbMsi) {
      RWPCI ((UINT32) Value + SB_EHCI_REG50, AccWidthUint32 | S3_SAVE, ~BIT6, 0x00);
    }
  }
}

/**
 * usb1OhciInitAfterPciInit - Config USB1 OHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb1OhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB1_OHCI_BUS_DEV_FUN << 16);
  OhciInitAfterPciInit (ddDeviceId, pConfig);
}

/**
 * usb2OhciInitAfterPciInit - Config USB2 OHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb2OhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB2_OHCI_BUS_DEV_FUN << 16);
  OhciInitAfterPciInit (ddDeviceId, pConfig);
}

/**
 * usb3OhciInitAfterPciInit - Config USB3 OHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb3OhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB3_OHCI_BUS_DEV_FUN << 16);
  OhciInitAfterPciInit (ddDeviceId, pConfig);
}

/**
 * usb4OhciInitAfterPciInit - Config USB4 OHCI controller after PCI emulation
 *
 *
 *
 * @param[in] pConfig Southbridge configuration structure pointer.
 *
 */
VOID
usb4OhciInitAfterPciInit (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddDeviceId;
  ddDeviceId = (USB4_OHCI_BUS_DEV_FUN << 16);
  OhciInitAfterPciInit (ddDeviceId, pConfig);
  if (pConfig->BuildParameters.Ohci4Ssid != 0 ) {
    RWPCI ((USB4_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG2C, AccWidthUint32 | S3_SAVE, 0x00, pConfig->BuildParameters.Ohci4Ssid);
  }
}

VOID
OhciInitAfterPciInit (
  IN       UINT32 Value,
  IN       AMDSBCFG* pConfig
  )
{
  // Disable the MSI capability of USB host controllers
  RWPCI ((UINT32) Value + SB_OHCI_REG40 + 1, AccWidthUint8 | S3_SAVE, 0xFF, BIT0);
  RWPCI ((UINT32) Value + SB_OHCI_REG50, AccWidthUint8 | S3_SAVE, ~(BIT5 + BIT12), 0x00);
  // RPR USB SMI Handshake
  RWPCI ((UINT32) Value + SB_OHCI_REG50 + 1, AccWidthUint8 | S3_SAVE, ~BIT4, 0x00);
  // SB02186
  RWPCI ((UINT32) Value + SB_OHCI_REG50 + 1, AccWidthUint8 | S3_SAVE, 0xFC, 0x00);
  if (Value != (USB4_OHCI_BUS_DEV_FUN << 16)) {
    if ( pConfig->BuildParameters.OhciSsid != 0 ) {
      RWPCI ((UINT32) Value + SB_OHCI_REG2C, AccWidthUint32 | S3_SAVE, 0x00, pConfig->BuildParameters.OhciSsid);
    }
  }
  //RPR recommended setting to, enable fix to cover the corner case S3 wake up issue from some USB 1.1 devices
  //OHCI 0_PCI_Config 0x50[30] = 1
  RWPCI ((UINT32) Value + SB_OHCI_REG50 + 3, AccWidthUint8 | S3_SAVE, ~BIT6, BIT6);
  if ( pConfig->BuildParameters.UsbMsi) {
    RWPCI ((UINT32) Value + SB_OHCI_REG40 + 1, AccWidthUint8 | S3_SAVE, ~BIT0, 0x00);
    RWPCI ((UINT32) Value + SB_OHCI_REG50, AccWidthUint8 | S3_SAVE, ~BIT5, BIT5);
  }
}


UINT32
SetEhciPllWr (
  IN       UINT32 Value,
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  ddRetureValue;
  UINT32  ddBarAddress;
  UINT16  dwVar;
  UINT16  dwData;
  UINT8  portSC;
  ddRetureValue = 0;
  dwData = 0;
  // Memory, and etc.
  //_asm { jmp $};
  RWPCI ((UINT32) Value + 0xC4, AccWidthUint8, 0xF0, 0x00);
  RWPCI ((UINT32) Value + 0x04, AccWidthUint8, 0xFF, 0x02);
  // Get Bar address
  ReadPCI ((UINT32) Value + 0x10, AccWidthUint32, &ddBarAddress);
  for (portSC = 0x64; portSC < 0x75; portSC += 0x04 ) {
    // Get OHCI command registers
    ReadMEM (ddBarAddress + portSC, AccWidthUint16, &dwVar);
    if ( dwVar & BIT6 ) {
      ddRetureValue = ddBarAddress + portSC;
      RWMEM (ddBarAddress + portSC, AccWidthUint16, ~BIT6, 0);
      for (;;) {
        SbStall (5);
        ReadMEM (ddBarAddress + portSC, AccWidthUint16, &dwData);
        if (dwData == 0x1005) break;
      }
      dwData = 0;
    }
  }
  return ddRetureValue;
}

VOID
usbSetPllDuringS3 (
  IN       AMDSBCFG* pConfig
  )
{
  UINT32  resumeEhciPortTmp;
  UINT32  resumeEhciPort;
  resumeEhciPortTmp = 0;
  resumeEhciPort = 0;
//  UINT32  ddDeviceId;
//if Force Port Resume == 1
// {
//     clear Force Port Resume;
//     while (!(PORTSC == 0x1005)){wait 5 us; read PORTSC;}
// }
  if (pConfig->USBMODE.UsbModeReg & BIT1) {
    resumeEhciPortTmp = SetEhciPllWr (USB1_EHCI_BUS_DEV_FUN << 16, pConfig);
    if (resumeEhciPortTmp > 0) resumeEhciPort = resumeEhciPortTmp;
  }
  if (pConfig->USBMODE.UsbModeReg & BIT3) {
    resumeEhciPortTmp = SetEhciPllWr (USB2_EHCI_BUS_DEV_FUN << 16, pConfig);
    if (resumeEhciPortTmp > 0) resumeEhciPort = resumeEhciPortTmp;
  }
  if (pConfig->USBMODE.UsbModeReg & BIT5) {
    resumeEhciPortTmp = SetEhciPllWr (USB3_EHCI_BUS_DEV_FUN << 16, pConfig);
    if (resumeEhciPortTmp > 0) resumeEhciPort = resumeEhciPortTmp;
  }

  RWPCI ((UINT32) (USB1_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, 0);
  RWPCI ((UINT32) (USB2_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, 0);
  RWPCI ((UINT32) (USB3_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, 0);
  RWPCI ((UINT32) (USB4_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, 0);
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGF3, AccWidthUint8, 0, 0x20);
  SbStall (10);
  RWMEM (ACPI_MMIO_BASE + PMIO_BASE + SB_PMIOA_REGF3, AccWidthUint8, 0, 0x00);
  RWPCI ((UINT32) (USB1_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, BIT29);
  RWPCI ((UINT32) (USB2_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, BIT29);
  RWPCI ((UINT32) (USB3_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, BIT29);
  RWPCI ((UINT32) (USB4_OHCI_BUS_DEV_FUN << 16) + SB_OHCI_REG50, AccWidthUint32, ~BIT29, BIT29);

  if (resumeEhciPort > 0) {
    RWMEM (resumeEhciPort, AccWidthUint8, ~BIT7, BIT7);
    SbStall (4000);
    RWMEM (resumeEhciPort, AccWidthUint8, ~BIT6, BIT6);
  }

  RWPCI ((UINT32) (USB1_EHCI_BUS_DEV_FUN << 16) + 0xC4, AccWidthUint8, 0xF0, 0x03);
  RWPCI ((UINT32) (USB2_EHCI_BUS_DEV_FUN << 16) + 0xC4, AccWidthUint8, 0xF0, 0x03);
  RWPCI ((UINT32) (USB3_EHCI_BUS_DEV_FUN << 16) + 0xC4, AccWidthUint8, 0xF0, 0x03);

}