/*******************************************************************************
|
Copyright (C) 2016 Marvell International Ltd.
|
Copyright (c) 2020, Arm Limited. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
*******************************************************************************/
|
|
#include "XenonSdhci.h"
|
|
STATIC
|
VOID
|
XenonReadVersion (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
OUT UINT32 *ControllerVersion
|
)
|
{
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_CTRL_VER, TRUE, SDHC_REG_SIZE_2B, ControllerVersion);
|
}
|
|
// Auto Clock Gating
|
STATIC
|
VOID
|
XenonSetAcg (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN BOOLEAN Enable
|
)
|
{
|
UINT32 Var;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
|
if (Enable) {
|
Var &= ~AUTO_CLKGATE_DISABLE_MASK;
|
} else {
|
Var |= AUTO_CLKGATE_DISABLE_MASK;
|
}
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
STATIC
|
VOID
|
XenonSetSlot (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 Slot,
|
IN BOOLEAN Enable
|
)
|
{
|
UINT32 Var;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
if (Enable) {
|
Var |= ((0x1 << Slot) << SLOT_ENABLE_SHIFT);
|
} else {
|
Var &= ~((0x1 << Slot) << SLOT_ENABLE_SHIFT);
|
}
|
|
// Enable SDCLK off while idle
|
Var |= SDCLK_IDLEOFF_ENABLE_MASK;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_SYS_OP_CTRL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
//
|
// Stub function, which will in future be responsible for
|
// setting SDIO controller in either HIGH (if Voltage parameter
|
// is equal 1) or LOW (if Voltage is equal 0)
|
//
|
STATIC
|
VOID
|
XenonSetSdio (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINTN Voltage
|
)
|
{
|
// Currently SDIO isn't supported
|
return;
|
}
|
|
STATIC
|
VOID
|
XenonSetPower (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT32 Vcc,
|
IN UINT32 Vccq,
|
IN UINT8 Mode
|
)
|
{
|
UINT8 Pwr = 0;
|
|
// Below statement calls routine to set voltage for SDIO devices in either HIGH (1) or LOW (0) mode
|
switch (Vcc) {
|
case MMC_VDD_165_195:
|
Pwr = SDHCI_POWER_180;
|
if (Mode == XENON_MMC_MODE_SD_SDIO) {
|
XenonSetSdio (PciIo, 0);
|
}
|
break;
|
case MMC_VDD_29_30:
|
case MMC_VDD_30_31:
|
Pwr = SDHCI_POWER_300;
|
if (Mode == XENON_MMC_MODE_SD_SDIO) {
|
XenonSetSdio (PciIo, 1);
|
}
|
break;
|
case MMC_VDD_32_33:
|
case MMC_VDD_33_34:
|
Pwr = SDHCI_POWER_330;
|
if (Mode == XENON_MMC_MODE_SD_SDIO) {
|
XenonSetSdio (PciIo, 1);
|
}
|
break;
|
default:
|
DEBUG((DEBUG_ERROR, "SD/MMC: Does not support power mode(0x%X)\n", Vcc));
|
break;
|
}
|
|
if (Pwr == 0) {
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_POWER_CTRL, FALSE, SDHC_REG_SIZE_1B, &Pwr);
|
return;
|
}
|
|
Pwr |= SDHCI_POWER_ON;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_POWER_CTRL, FALSE, SDHC_REG_SIZE_1B, &Pwr);
|
}
|
|
UINTN
|
XenonSetClk (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT32 Clock
|
)
|
{
|
UINT32 Div;
|
UINT32 Clk;
|
UINT32 Retry;
|
UINT32 ControllerVersion;
|
UINT16 Value = 0;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, FALSE, SDHC_REG_SIZE_2B, &Value);
|
|
if (Clock == 0) {
|
return 0;
|
}
|
|
XenonReadVersion (PciIo, &ControllerVersion);
|
|
if (ControllerVersion >= SDHCI_SPEC_300) {
|
// Version 3.00 Divisors must be a multiple of 2
|
if (XENON_MMC_MAX_CLK <= Clock) {
|
Div = 1;
|
} else {
|
for (Div = 2; Div < SDHCI_MAX_DIV_SPEC_300; Div += 2) {
|
if ((XENON_MMC_MAX_CLK / Div) <= Clock)
|
break;
|
}
|
}
|
} else {
|
// Version 2.00 Divisors must be a power of 2
|
for (Div = 1; Div < SDHCI_MAX_DIV_SPEC_200; Div *= 2) {
|
if ((XENON_MMC_MAX_CLK / Div) <= Clock)
|
break;
|
}
|
}
|
Div >>= 1;
|
|
Clk = (Div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
|
Clk |= ((Div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT;
|
Clk |= SDHCI_CLOCK_INT_EN;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, FALSE, SDHC_REG_SIZE_2B, &Clk);
|
|
//
|
// Poll for internal controller clock to be stabilised
|
// Wait up to 200us for this to occur
|
//
|
Retry = 200;
|
|
do {
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, TRUE, SDHC_REG_SIZE_2B, &Clk);
|
if (Retry == 0) {
|
DEBUG((DEBUG_ERROR, "SD/MMC: Internal Clock never stabilised\n"));
|
return -1;
|
}
|
|
Retry--;
|
|
// Wait for internal clock to be stabilised
|
gBS->Stall (1);
|
|
} while (!(Clk & SDHCI_CLOCK_INT_STABLE));
|
|
Clk |= SDHCI_CLOCK_CARD_EN;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, FALSE, SDHC_REG_SIZE_2B, &Clk);
|
|
return 0;
|
}
|
|
VOID
|
XenonPhyInit (
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
)
|
{
|
UINT32 Var, Wait, Time;
|
UINT32 Clock = XENON_MMC_MAX_CLK;
|
|
// Init PHY
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var |= PHY_INITIALIZAION;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Add duration of FC_SYNC_RST
|
Wait = ((Var >> FC_SYNC_RST_DURATION_SHIFT) & FC_SYNC_RST_DURATION_MASK);
|
|
// Add interval between FC_SYNC_EN and FC_SYNC_RST
|
Wait += ((Var >> FC_SYNC_RST_EN_DURATION_SHIFT) & FC_SYNC_RST_EN_DURATION_MASK);
|
|
// Add duration of asserting FC_SYNC_EN
|
Wait += ((Var >> FC_SYNC_EN_DURATION_SHIFT) & FC_SYNC_EN_DURATION_MASK);
|
|
// Add duration of Waiting for PHY
|
Wait += ((Var >> WAIT_CYCLE_BEFORE_USING_SHIFT) & WAIT_CYCLE_BEFORE_USING_MASK);
|
|
// 4 addtional bus clock and 4 AXI bus clock are required left shift 20 bits
|
Wait += 8;
|
Wait <<= 20;
|
|
// Use the possibly slowest bus frequency value
|
if (Clock == 0) {
|
Clock = XENON_MMC_MIN_CLK;
|
}
|
|
// Get the Wait Time in unit of ms
|
Wait = Wait / Clock;
|
Wait++;
|
|
// Poll for host eMMC PHY init to complete, wait up to 100us
|
Time = 100;
|
while (Time--) {
|
Var = XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var &= PHY_INITIALIZAION;
|
if (!Var) {
|
break;
|
}
|
|
// Wait for host eMMC PHY init to complete
|
gBS->Stall (1);
|
}
|
|
if (Time <= 0) {
|
DEBUG((DEBUG_ERROR, "SD/MMC: Failed to init MMC PHY in Time\n"));
|
return;
|
}
|
|
return;
|
}
|
|
//
|
// Enable eMMC PHY HW DLL
|
// DLL should be enabled and stable before HS200/SDR104 tuning,
|
// and before HS400 data strobe setting.
|
//
|
STATIC
|
EFI_STATUS
|
EmmcPhyEnableDll (
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
)
|
{
|
UINT32 Var;
|
UINT16 SlotState;
|
UINT8 Retry;
|
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_DLL_CONTROL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
if (Var & DLL_ENABLE) {
|
return EFI_SUCCESS;
|
}
|
|
// Enable DLL
|
Var |= (DLL_ENABLE | DLL_FAST_LOCK);
|
|
//
|
// Set Phase as 90 degree, which is most common value.
|
//
|
Var &= ~((DLL_PHASE_MASK << DLL_PHSEL0_SHIFT) |
|
(DLL_PHASE_MASK << DLL_PHSEL1_SHIFT));
|
Var |= ((DLL_PHASE_90_DEGREE << DLL_PHSEL0_SHIFT) |
|
(DLL_PHASE_90_DEGREE << DLL_PHSEL1_SHIFT));
|
|
Var &= ~(DLL_BYPASS_EN | DLL_REFCLK_SEL);
|
Var |= DLL_UPDATE;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_DLL_CONTROL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Wait max 32 ms for the DLL to lock
|
Retry = 32;
|
do {
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, XENON_SLOT_EXT_PRESENT_STATE, TRUE, SDHC_REG_SIZE_2B, &SlotState);
|
|
if (Retry == 0) {
|
DEBUG ((DEBUG_ERROR, "SD/MMC: Fail to lock DLL\n"));
|
return EFI_TIMEOUT;
|
}
|
|
gBS->Stall (1000);
|
Retry--;
|
|
} while (!(SlotState & DLL_LOCK_STATE));
|
|
return EFI_SUCCESS;
|
}
|
|
//
|
// Config to eMMC PHY to prepare for tuning.
|
// Enable HW DLL and set the TUNING_STEP
|
//
|
STATIC
|
EFI_STATUS
|
EmmcPhyConfigTuning (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 TuningStepDivisor
|
)
|
{
|
UINT32 Var, TuningStep;
|
EFI_STATUS Status;
|
|
Status = EmmcPhyEnableDll (PciIo);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
// Achieve TUNING_STEP with HW DLL help
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, XENON_SLOT_DLL_CUR_DLY_VAL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
TuningStep = Var / TuningStepDivisor;
|
if (TuningStep > TUNING_STEP_MASK) {
|
DEBUG ((DEBUG_ERROR, "HS200 TUNING_STEP %d is larger than MAX value\n", TuningStep));
|
TuningStep = TUNING_STEP_MASK;
|
}
|
|
// Set TUNING_STEP for later tuning
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, XENON_SLOT_OP_STATUS_CTRL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var &= ~(TUN_CONSECUTIVE_TIMES_MASK << TUN_CONSECUTIVE_TIMES_SHIFT);
|
Var |= (TUN_CONSECUTIVE_TIMES << TUN_CONSECUTIVE_TIMES_SHIFT);
|
Var &= ~(TUNING_STEP_MASK << TUNING_STEP_SHIFT);
|
Var |= (TuningStep << TUNING_STEP_SHIFT);
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, XENON_SLOT_OP_STATUS_CTRL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
BOOLEAN
|
XenonPhySlowMode (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN SD_MMC_BUS_MODE Timing,
|
IN BOOLEAN SlowMode
|
)
|
{
|
UINT32 Var = 0;
|
|
// Check if Slow Mode is required in lower speed mode in SDR mode
|
if (((Timing == SdMmcUhsSdr50) ||
|
(Timing == SdMmcUhsSdr25) ||
|
(Timing == SdMmcUhsSdr12) ||
|
(Timing == SdMmcSdDs) ||
|
(Timing == SdMmcSdHs) ||
|
(Timing == SdMmcMmcHsDdr) ||
|
(Timing == SdMmcMmcHsSdr) ||
|
(Timing == SdMmcMmcLegacy)) && SlowMode) {
|
Var = QSN_PHASE_SLOW_MODE_BIT;
|
XenonHcOrMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, SDHC_REG_SIZE_4B, &Var);
|
return TRUE;
|
}
|
|
Var = ~QSN_PHASE_SLOW_MODE_BIT;
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, SDHC_REG_SIZE_4B, &Var);
|
return FALSE;
|
}
|
|
EFI_STATUS
|
XenonSetPhy (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN BOOLEAN SlowMode,
|
IN UINT8 TuningStepDivisor,
|
IN SD_MMC_BUS_MODE Timing
|
)
|
{
|
UINT32 Var = 0;
|
UINT16 ClkCtrl;
|
|
// Setup pad, bit[28] and bits[26:24]
|
Var = OEN_QSN | FC_QSP_RECEN | FC_CMD_RECEN | FC_DQ_RECEN;
|
// All FC_XX_RECEIVCE should be set as CMOS Type
|
Var |= FC_ALL_CMOS_RECEIVER;
|
XenonHcOrMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL, SDHC_REG_SIZE_4B, &Var);
|
|
// Set CMD and DQ Pull Up
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL1, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU);
|
Var &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD);
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL1, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
if (Timing == SdMmcUhsSdr12 || Timing == SdMmcSdDs) {
|
if (SlowMode) {
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var |= QSN_PHASE_SLOW_MODE_BIT;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
goto PhyInit;
|
}
|
|
//
|
// If Timing belongs to high speed, set bit[17] of
|
// EMMC_PHY_TIMING_ADJUST register
|
//
|
if ((Timing == SdMmcMmcHs400) ||
|
(Timing == SdMmcMmcHs200) ||
|
(Timing == SdMmcUhsDdr50) ||
|
(Timing == SdMmcUhsSdr50) ||
|
(Timing == SdMmcUhsSdr104) ||
|
(Timing == SdMmcUhsSdr25)) {
|
Var = ~OUTPUT_QSN_PHASE_SELECT;
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_TIMING_ADJUST, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
if (XenonPhySlowMode (PciIo, Timing, SlowMode)) {
|
goto PhyInit;
|
}
|
|
// Set default ZNR and ZPR value
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL2, TRUE, SDHC_REG_SIZE_4B, &Var);
|
Var &= ~((ZNR_MASK << ZNR_SHIFT) | ZPR_MASK);
|
Var |= ((ZNR_DEF_VALUE << ZNR_SHIFT) | ZPR_DEF_VALUE);
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_PAD_CONTROL2, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Need to disable the clock to set EMMC_PHY_FUNC_CONTROL register
|
ClkCtrl = ~SDHCI_CLOCK_CARD_EN;
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, SDHC_REG_SIZE_2B, &ClkCtrl);
|
|
if ((Timing == SdMmcMmcHs400) ||
|
(Timing == SdMmcUhsDdr50)) {
|
Var = (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
|
XenonHcOrMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, SDHC_REG_SIZE_4B, &Var);
|
} else {
|
Var = ~((DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE);
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
if (Timing == SdMmcMmcHs400) {
|
Var = ~DQ_ASYNC_MODE;
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, SDHC_REG_SIZE_4B, &Var);
|
} else {
|
Var = DQ_ASYNC_MODE;
|
XenonHcOrMmio (PciIo, SD_BAR_INDEX, EMMC_PHY_FUNC_CONTROL, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
// Enable bus clock
|
ClkCtrl = SDHCI_CLOCK_CARD_EN;
|
XenonHcOrMmio (PciIo, SD_BAR_INDEX, SDHC_CLOCK_CTRL, SDHC_REG_SIZE_2B, &ClkCtrl);
|
|
// Delay 200us to wait for the completion of bus clock
|
gBS->Stall (200);
|
|
if (Timing == SdMmcMmcHs400) {
|
Var = LOGIC_TIMING_VALUE;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, EMMC_LOGIC_TIMING_ADJUST, FALSE, SDHC_REG_SIZE_4B, &Var);
|
} else {
|
// Disable data strobe
|
Var = ~ENABLE_DATA_STROBE;
|
XenonHcAndMmio (PciIo, SD_BAR_INDEX, XENON_SLOT_EMMC_CTRL, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
PhyInit:
|
XenonPhyInit (PciIo);
|
|
if ((Timing == SdMmcMmcHs200) ||
|
(Timing == SdMmcUhsSdr104)) {
|
return EmmcPhyConfigTuning (PciIo, TuningStepDivisor);
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
VOID
|
XenonConfigureInterrupts (
|
IN EFI_PCI_IO_PROTOCOL *PciIo
|
)
|
{
|
UINT32 Var;
|
|
// Clear interrupt status
|
Var = SDHC_CLR_ALL_IRQ_MASK;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_NOR_INT_STS, FALSE, SDHC_REG_SIZE_4B, &Var);
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_NOR_INT_STS, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Enable only interrupts served by the SD controller
|
Var = SDHC_CLR_ALL_IRQ_MASK & ~(NOR_INT_STS_CARD_INS | NOR_INT_STS_CARD_INT);
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_NOR_INT_STS_EN, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Mask all sdhci interrupt sources
|
Var = SDHC_CLR_ALL_IRQ_MASK & ~NOR_INT_SIG_EN_CARD_INT;
|
XenonHcRwMmio (PciIo, SD_BAR_INDEX, SDHC_NOR_INT_SIG_EN, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
// Enable Parallel Transfer Mode
|
STATIC
|
VOID
|
XenonSetParallelTransfer (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 Slot,
|
IN BOOLEAN Enable
|
)
|
{
|
UINT32 Var;
|
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
|
if (Enable) {
|
Var |= (0x1 << Slot);
|
} else {
|
Var &= ~(0x1 << Slot);
|
}
|
|
// Mask command conflict error
|
Var |= MASK_CMD_CONFLICT_ERR;
|
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SYS_EXT_OP_CTRL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
STATIC
|
VOID
|
XenonSetTuning (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 Slot,
|
IN BOOLEAN Enable
|
)
|
{
|
UINT32 Var;
|
|
// Set the Re-Tuning Request functionality
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SLOT_RETUNING_REQ_CTRL, TRUE, SDHC_REG_SIZE_4B, &Var);
|
|
if (Enable) {
|
Var |= RETUNING_COMPATIBLE;
|
} else {
|
Var &= ~RETUNING_COMPATIBLE;
|
}
|
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHC_SLOT_RETUNING_REQ_CTRL, FALSE, SDHC_REG_SIZE_4B, &Var);
|
|
// Set the Re-tuning Event Signal Enable
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHCI_SIGNAL_ENABLE, TRUE, SDHC_REG_SIZE_4B, &Var);
|
|
if (Enable) {
|
Var |= SDHCI_RETUNE_EVT_INTSIG;
|
} else {
|
Var &= ~SDHCI_RETUNE_EVT_INTSIG;
|
}
|
|
XenonHcRwMmio(PciIo, SD_BAR_INDEX, SDHCI_SIGNAL_ENABLE, FALSE, SDHC_REG_SIZE_4B, &Var);
|
}
|
|
VOID
|
XenonReset (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 Slot,
|
IN UINT8 Mask
|
)
|
{
|
UINT32 Retry = 1000;
|
UINT8 SwReset;
|
|
SwReset = Mask;
|
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_SW_RST,
|
FALSE,
|
sizeof (SwReset),
|
&SwReset
|
);
|
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_SW_RST,
|
TRUE,
|
sizeof (SwReset),
|
&SwReset
|
);
|
|
while (SwReset & Mask) {
|
if (Retry == 0) {
|
DEBUG((DEBUG_ERROR, "SD/MMC: Reset never completed\n"));
|
return;
|
}
|
|
Retry--;
|
|
// Poll interval for SwReset is 100us according to SDHCI spec
|
gBS-> Stall (100);
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_SW_RST,
|
TRUE,
|
sizeof (SwReset),
|
&SwReset
|
);
|
}
|
}
|
|
STATIC
|
VOID
|
XenonTransferPio (
|
IN UINT8 Slot,
|
IN OUT VOID *Buffer,
|
IN UINT16 BlockSize,
|
IN BOOLEAN Read
|
)
|
{
|
UINTN Index;
|
UINT8 *Offs;
|
|
//
|
// SD stack's intrinsic functions cannot perform properly reading/writing from
|
// buffer register, that is why MmioRead/MmioWrite are used. It is temporary
|
// solution.
|
//
|
for (Index = 0; Index < BlockSize; Index += 4) {
|
Offs = (UINT8*)((UINTN)Buffer + Index);
|
if (Read) {
|
*(UINT32 *)Offs = MmioRead32 (SDHC_DAT_BUF_PORT_ADDR);
|
} else {
|
MmioWrite32 (SDHC_DAT_BUF_PORT_ADDR, *(UINT32 *)Offs);
|
}
|
}
|
}
|
|
EFI_STATUS
|
XenonTransferData (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN UINT8 Slot,
|
IN OUT VOID *Buffer,
|
IN UINT32 DataLen,
|
IN UINT16 BlockSize,
|
IN UINT16 Blocks,
|
IN BOOLEAN Read
|
)
|
{
|
UINT32 IntStatus, PresentState, Rdy, Mask, Retry, Block = 0;
|
|
if (Buffer == NULL) {
|
return EFI_DEVICE_ERROR;
|
}
|
|
Retry = SDHC_INT_STATUS_POLL_RETRY_DATA_TRAN;
|
Rdy = NOR_INT_STS_TX_RDY | NOR_INT_STS_RX_RDY;
|
Mask = PRESENT_STATE_BUFFER_RD_EN | PRESENT_STATE_BUFFER_WR_EN;
|
|
do {
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_NOR_INT_STS,
|
TRUE,
|
sizeof (IntStatus),
|
&IntStatus
|
);
|
|
if (IntStatus & NOR_INT_STS_ERR_INT) {
|
DEBUG((DEBUG_INFO, "SD/MMC: Error detected in status %0x\n", IntStatus));
|
return EFI_DEVICE_ERROR;
|
}
|
|
if (IntStatus & Rdy) {
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_PRESENT_STATE,
|
TRUE,
|
sizeof (PresentState),
|
&PresentState
|
);
|
|
if (!(PresentState & Mask)) {
|
continue;
|
}
|
|
XenonHcRwMmio (
|
PciIo,
|
Slot,
|
SDHC_NOR_INT_STS,
|
FALSE,
|
sizeof (Rdy),
|
&Rdy
|
);
|
|
XenonTransferPio (Slot, Buffer, BlockSize, Read);
|
|
Buffer = (VOID*)((UINTN)Buffer + BlockSize);
|
if (++Block >= Blocks) {
|
break;
|
}
|
}
|
|
if (Retry-- > 0) {
|
|
// Poll interval for data transfer complete bit in NOR_INT_STS register is 10us
|
gBS->Stall (10);
|
} else {
|
DEBUG((DEBUG_INFO, "SD/MMC: Transfer data timeout\n"));
|
return EFI_TIMEOUT;
|
}
|
} while (!(IntStatus & NOR_INT_STS_XFER_COMPLETE));
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
XenonInit (
|
IN EFI_PCI_IO_PROTOCOL *PciIo,
|
IN BOOLEAN Support1v8,
|
IN BOOLEAN SlowMode,
|
IN UINT8 TuningStepDivisor
|
)
|
{
|
EFI_STATUS Status;
|
|
// Disable auto clock generator
|
XenonSetAcg (PciIo, FALSE);
|
|
// XENON has only one port
|
XenonSetSlot (PciIo, XENON_MMC_SLOT_ID, TRUE);
|
|
if (Support1v8) {
|
XenonSetPower (PciIo, MMC_VDD_165_195, eMMC_VCCQ_1_8V, XENON_MMC_MODE_SD_SDIO);
|
} else {
|
XenonSetPower (PciIo, MMC_VDD_32_33, eMMC_VCCQ_3_3V, XENON_MMC_MODE_SD_SDIO);
|
}
|
|
XenonConfigureInterrupts (PciIo);
|
|
// Enable parallel transfer
|
XenonSetParallelTransfer (PciIo, XENON_MMC_SLOT_ID, TRUE);
|
XenonSetTuning (PciIo, XENON_MMC_SLOT_ID, FALSE);
|
|
// Enable auto clock generator
|
XenonSetAcg (PciIo, TRUE);
|
|
// Set lowest clock and the PHY for the initialization phase
|
XenonSetClk (PciIo, XENON_MMC_BASE_CLK);
|
Status = XenonSetPhy (PciIo, SlowMode, TuningStepDivisor, SdMmcSdDs);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
return EFI_SUCCESS;
|
}
|