/********************************************************************************
|
Copyright (C) 2016 Marvell International Ltd.
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
*******************************************************************************/
|
|
#include "UtmiPhyLib.h"
|
|
typedef struct {
|
EFI_PHYSICAL_ADDRESS UtmiPllAddr;
|
EFI_PHYSICAL_ADDRESS UtmiBaseAddr;
|
EFI_PHYSICAL_ADDRESS UsbCfgAddr;
|
EFI_PHYSICAL_ADDRESS UtmiCfgAddr;
|
UINT32 UtmiPhyPort;
|
UINT32 PhyId;
|
} UTMI_PHY_DATA;
|
|
STATIC
|
VOID
|
RegSetSilent (
|
IN EFI_PHYSICAL_ADDRESS Addr,
|
IN UINT32 Data,
|
IN UINT32 Mask
|
)
|
{
|
UINT32 RegData;
|
|
RegData = MmioRead32 (Addr);
|
RegData &= ~Mask;
|
RegData |= Data;
|
MmioWrite32 (Addr, RegData);
|
}
|
|
STATIC
|
VOID
|
RegSet (
|
IN EFI_PHYSICAL_ADDRESS Addr,
|
IN UINT32 Data,
|
IN UINT32 Mask
|
)
|
{
|
DEBUG((DEBUG_INFO, "Write to address = %10x, data = %10x (mask = %10x)-\n",
|
Addr, Data, Mask));
|
DEBUG((DEBUG_INFO, "old value = %10x ==>\n", MmioRead32 (Addr)));
|
RegSetSilent (Addr, Data, Mask);
|
DEBUG((DEBUG_INFO, "new value %10x\n", MmioRead32 (Addr)));
|
}
|
|
STATIC
|
VOID
|
UtmiPhyPowerDown (
|
IN UINT32 UtmiIndex,
|
IN EFI_PHYSICAL_ADDRESS UtmiBaseAddr,
|
IN EFI_PHYSICAL_ADDRESS UsbCfgAddr,
|
IN EFI_PHYSICAL_ADDRESS UtmiCfgAddr,
|
IN UINT32 UtmiPhyPort
|
)
|
{
|
UINT32 Mask, Data;
|
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: UTMI %d - Power down transceiver(power down Phy)\n",
|
UtmiIndex));
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: Power down PLL, and SuspendDM\n"));
|
/* Power down UTMI PHY */
|
RegSet (UtmiCfgAddr, 0x0 << UTMI_PHY_CFG_PU_OFFSET, UTMI_PHY_CFG_PU_MASK);
|
/* Config USB3 Device UTMI enable */
|
Mask = UTMI_USB_CFG_DEVICE_EN_MASK;
|
|
/*
|
* Prior to PHY init, configure mux for Device
|
* (Device can be connected to UTMI0 or to UTMI1)
|
*/
|
if (UtmiPhyPort == UTMI_PHY_TO_USB_DEVICE0) {
|
Data = 0x1 << UTMI_USB_CFG_DEVICE_EN_OFFSET;
|
/* Config USB3 Device UTMI MUX */
|
Mask |= UTMI_USB_CFG_DEVICE_MUX_MASK;
|
Data |= UtmiIndex << UTMI_USB_CFG_DEVICE_MUX_OFFSET;
|
} else {
|
Data = 0x0 << UTMI_USB_CFG_DEVICE_EN_OFFSET;
|
}
|
RegSet (UsbCfgAddr, Data, Mask);
|
|
/* Set Test suspendm mode */
|
Mask = UTMI_CTRL_STATUS0_SUSPENDM_MASK;
|
Data = 0x1 << UTMI_CTRL_STATUS0_SUSPENDM_OFFSET;
|
/* Enable Test UTMI select */
|
Mask |= UTMI_CTRL_STATUS0_TEST_SEL_MASK;
|
Data |= 0x1 << UTMI_CTRL_STATUS0_TEST_SEL_OFFSET;
|
RegSet (UtmiBaseAddr + UTMI_CTRL_STATUS0_REG, Data, Mask);
|
|
/* Wait for UTMI power down */
|
MicroSecondDelay (1000);
|
}
|
|
STATIC
|
VOID
|
UtmiPhyConfig (
|
IN UINT32 UtmiIndex,
|
IN EFI_PHYSICAL_ADDRESS UtmiPllAddr,
|
IN EFI_PHYSICAL_ADDRESS UtmiBaseAddr,
|
IN EFI_PHYSICAL_ADDRESS UsbCfgAddr,
|
IN EFI_PHYSICAL_ADDRESS UtmiCfgAddr,
|
IN UINT32 UtmiPhyPort
|
)
|
{
|
UINT32 Mask, Data;
|
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: Configure UTMI PHY %d registers\n",
|
UtmiIndex));
|
/* Reference Clock Divider Select */
|
Mask = UTMI_PLL_CTRL_REFDIV_MASK;
|
Data = 0x5 << UTMI_PLL_CTRL_REFDIV_OFFSET;
|
/* Feedback Clock Divider Select - 90 for 25Mhz */
|
Mask |= UTMI_PLL_CTRL_FBDIV_MASK;
|
Data |= 0x60 << UTMI_PLL_CTRL_FBDIV_OFFSET;
|
/* Select LPFR - 0x0 for 25Mhz/5=5Mhz */
|
Mask |= UTMI_PLL_CTRL_SEL_LPFR_MASK;
|
Data |= 0x0 << UTMI_PLL_CTRL_SEL_LPFR_OFFSET;
|
RegSet (UtmiPllAddr + UTMI_PLL_CTRL_REG, Data, Mask);
|
|
/* Impedance Calibration Threshold Setting */
|
RegSet (UtmiPllAddr + UTMI_CALIB_CTRL_REG,
|
0x7 << UTMI_CALIB_CTRL_IMPCAL_VTH_OFFSET,
|
UTMI_CALIB_CTRL_IMPCAL_VTH_MASK);
|
|
/* Start Impedance and PLL Calibration */
|
Mask = UTMI_CALIB_CTRL_PLLCAL_START_MASK;
|
Data = (0x1 << UTMI_CALIB_CTRL_PLLCAL_START_OFFSET);
|
Mask |= UTMI_CALIB_CTRL_IMPCAL_START_MASK;
|
Data |= (0x1 << UTMI_CALIB_CTRL_IMPCAL_START_OFFSET);
|
RegSet (UtmiPllAddr + UTMI_CALIB_CTRL_REG, Data, Mask);
|
|
/* Set LS TX driver strength coarse control */
|
Mask = UTMI_TX_CH_CTRL_DRV_EN_LS_MASK;
|
Data = 0x3 << UTMI_TX_CH_CTRL_DRV_EN_LS_OFFSET;
|
Mask |= UTMI_TX_CH_CTRL_AMP_MASK;
|
Data |= 0x4 << UTMI_TX_CH_CTRL_AMP_OFFSET;
|
Mask |= UTMI_TX_CH_CTRL_IMP_SEL_LS_MASK;
|
Data |= 0x3 << UTMI_TX_CH_CTRL_IMP_SEL_LS_OFFSET;
|
RegSet (UtmiBaseAddr + UTMI_TX_CH_CTRL_REG, Data, Mask);
|
|
/* Enable SQ */
|
Mask = UTMI_RX_CH_CTRL0_SQ_DET_MASK;
|
Data = 0x1 << UTMI_RX_CH_CTRL0_SQ_DET_OFFSET;
|
/* Enable analog squelch detect */
|
Mask |= UTMI_RX_CH_CTRL0_SQ_ANA_DTC_MASK;
|
Data |= 0x0 << UTMI_RX_CH_CTRL0_SQ_ANA_DTC_OFFSET;
|
Mask |= UTMI_RX_CH_CTRL0_DISCON_THRESH_MASK;
|
Data |= 0x0 << UTMI_RX_CH_CTRL0_DISCON_THRESH_OFFSET;
|
RegSet (UtmiBaseAddr + UTMI_RX_CH_CTRL0_REG, Data, Mask);
|
|
/* Set External squelch calibration number */
|
Mask = UTMI_RX_CH_CTRL1_SQ_AMP_CAL_MASK;
|
Data = 0x1 << UTMI_RX_CH_CTRL1_SQ_AMP_CAL_OFFSET;
|
/* Enable the External squelch calibration */
|
Mask |= UTMI_RX_CH_CTRL1_SQ_AMP_CAL_EN_MASK;
|
Data |= 0x1 << UTMI_RX_CH_CTRL1_SQ_AMP_CAL_EN_OFFSET;
|
RegSet (UtmiBaseAddr + UTMI_RX_CH_CTRL1_REG, Data, Mask);
|
|
/* Set Control VDAT Reference Voltage - 0.325V */
|
Mask = UTMI_CHGDTC_CTRL_VDAT_MASK;
|
Data = 0x1 << UTMI_CHGDTC_CTRL_VDAT_OFFSET;
|
/* Set Control VSRC Reference Voltage - 0.6V */
|
Mask |= UTMI_CHGDTC_CTRL_VSRC_MASK;
|
Data |= 0x1 << UTMI_CHGDTC_CTRL_VSRC_OFFSET;
|
RegSet (UtmiBaseAddr + UTMI_CHGDTC_CTRL_REG, Data, Mask);
|
}
|
|
STATIC
|
UINTN
|
UtmiPhyPowerUp (
|
IN UINT32 UtmiIndex,
|
IN EFI_PHYSICAL_ADDRESS UtmiPllAddr,
|
IN EFI_PHYSICAL_ADDRESS UtmiBaseAddr,
|
IN EFI_PHYSICAL_ADDRESS UsbCfgAddr,
|
IN EFI_PHYSICAL_ADDRESS UtmiCfgAddr,
|
IN UINT32 UtmiPhyPort
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 Data;
|
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: UTMI %d - Power up transceiver(Power up Phy)\n",
|
UtmiIndex));
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: exit SuspendDM\n"));
|
/* Power up UTMI PHY */
|
RegSet (UtmiCfgAddr, 0x1 << UTMI_PHY_CFG_PU_OFFSET, UTMI_PHY_CFG_PU_MASK);
|
/* Disable Test UTMI select */
|
RegSet (UtmiBaseAddr + UTMI_CTRL_STATUS0_REG,
|
0x0 << UTMI_CTRL_STATUS0_TEST_SEL_OFFSET,
|
UTMI_CTRL_STATUS0_TEST_SEL_MASK);
|
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: Wait for PLL and impedance calibration done, and PLL ready\n"));
|
|
/* Delay 10ms */
|
MicroSecondDelay (10000);
|
|
Data = MmioRead32 (UtmiPllAddr + UTMI_CALIB_CTRL_REG);
|
if ((Data & UTMI_CALIB_CTRL_IMPCAL_DONE_MASK) == 0) {
|
DEBUG((DEBUG_ERROR, "UtmiPhy: Impedance calibration is not done\n"));
|
Status = EFI_D_ERROR;
|
}
|
if ((Data & UTMI_CALIB_CTRL_PLLCAL_DONE_MASK) == 0) {
|
DEBUG((DEBUG_ERROR, "UtmiPhy: PLL calibration is not done\n"));
|
Status = EFI_D_ERROR;
|
}
|
Data = MmioRead32 (UtmiPllAddr + UTMI_PLL_CTRL_REG);
|
if ((Data & UTMI_PLL_CTRL_PLL_RDY_MASK) == 0) {
|
DEBUG((DEBUG_ERROR, "UtmiPhy: PLL is not ready\n"));
|
Status = EFI_D_ERROR;
|
}
|
|
return Status;
|
}
|
|
/*
|
* Cp110UtmiPhyInit initializes the UTMI PHY
|
* the init split in 3 parts:
|
* 1. Power down transceiver and PLL
|
* 2. UTMI PHY configure
|
* 3. Power up transceiver and PLL
|
*/
|
STATIC
|
VOID
|
Cp110UtmiPhyInit (
|
IN UTMI_PHY_DATA *UtmiData
|
)
|
{
|
EFI_STATUS Status;
|
|
UtmiPhyPowerDown (UtmiData->PhyId,
|
UtmiData->UtmiBaseAddr,
|
UtmiData->UsbCfgAddr,
|
UtmiData->UtmiCfgAddr,
|
UtmiData->UtmiPhyPort);
|
|
/* Power down PLL */
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: PHY power down PLL\n"));
|
MmioAnd32 (UtmiData->UsbCfgAddr, ~UTMI_USB_CFG_PLL_MASK);
|
|
UtmiPhyConfig (UtmiData->PhyId,
|
UtmiData->UtmiPllAddr,
|
UtmiData->UtmiBaseAddr,
|
UtmiData->UsbCfgAddr,
|
UtmiData->UtmiCfgAddr,
|
UtmiData->UtmiPhyPort);
|
|
Status = UtmiPhyPowerUp (UtmiData->PhyId,
|
UtmiData->UtmiPllAddr,
|
UtmiData->UtmiBaseAddr,
|
UtmiData->UsbCfgAddr,
|
UtmiData->UtmiCfgAddr,
|
UtmiData->UtmiPhyPort);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "UtmiPhy: Failed to initialize UTMI PHY %d\n", UtmiData->PhyId));
|
return;
|
}
|
|
DEBUG ((DEBUG_ERROR, "UTMI PHY %d initialized to ", UtmiData->PhyId));
|
if (UtmiData->UtmiPhyPort == UTMI_PHY_TO_USB_DEVICE0) {
|
DEBUG ((DEBUG_ERROR, "USB Device\n"));
|
} else {
|
DEBUG ((DEBUG_ERROR, "USB Host%d\n", UtmiData->UtmiPhyPort));
|
}
|
|
/* Power up PLL */
|
DEBUG((DEBUG_INFO, "UtmiPhy: stage: PHY power up PLL\n"));
|
MmioOr32 (UtmiData->UsbCfgAddr, UTMI_USB_CFG_PLL_MASK);
|
}
|
|
EFI_STATUS
|
UtmiPhyInit (
|
VOID
|
)
|
{
|
MARVELL_BOARD_DESC_PROTOCOL *BoardDescProtocol;
|
MV_BOARD_UTMI_DESC *BoardDesc;
|
UTMI_PHY_DATA UtmiData;
|
EFI_STATUS Status;
|
UINTN Index;
|
|
/* Obtain board description */
|
Status = gBS->LocateProtocol (&gMarvellBoardDescProtocolGuid,
|
NULL,
|
(VOID **)&BoardDescProtocol);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = BoardDescProtocol->BoardDescUtmiGet (BoardDescProtocol, &BoardDesc);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
/* Initialize enabled chips */
|
for (Index = 0; Index < BoardDesc->UtmiDevCount; Index++) {
|
/* Get base address of UTMI phy */
|
UtmiData.UtmiBaseAddr = BoardDesc[Index].SoC->UtmiBaseAddress;
|
|
/* Get base address of PLL registers */
|
UtmiData.UtmiPllAddr = BoardDesc[Index].SoC->UtmiPllAddress;
|
|
/* Get usb config address */
|
UtmiData.UsbCfgAddr = BoardDesc[Index].SoC->UsbConfigAddress;
|
|
/* Get UTMI config address */
|
UtmiData.UtmiCfgAddr = BoardDesc[Index].SoC->UtmiConfigAddress;
|
|
/* Get UTMI PHY ID */
|
UtmiData.PhyId = BoardDesc[Index].SoC->UtmiPhyId;
|
|
/* Get the usb port type */
|
UtmiData.UtmiPhyPort = BoardDesc[Index].UtmiPortType;
|
|
/* Currently only Cp110 is supported */
|
Cp110UtmiPhyInit (&UtmiData);
|
}
|
|
BoardDescProtocol->BoardDescFree (BoardDesc);
|
|
return EFI_SUCCESS;
|
}
|