/** @file | |
This driver is used to manage SD/MMC PCI host controllers which are compliance | |
with SD Host Controller Simplified Specification version 3.00 plus the 64-bit | |
System Addressing support in SD Host Controller Simplified Specification version | |
4.20. | |
It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. | |
Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. | |
Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "SdMmcPciHcDxe.h" | |
/** | |
Dump the content of SD/MMC host controller's Capability Register. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Capability The buffer to store the capability data. | |
**/ | |
VOID | |
DumpCapabilityReg ( | |
IN UINT8 Slot, | |
IN SD_MMC_HC_SLOT_CAP *Capability | |
) | |
{ | |
// | |
// Dump Capability Data | |
// | |
DEBUG ((DEBUG_INFO, " == Slot [%d] Capability is 0x%llx ==\n", Slot, *(UINT64 *)Capability)); | |
DEBUG ((DEBUG_INFO, " Timeout Clk Freq %d%a\n", Capability->TimeoutFreq, (Capability->TimeoutUnit) ? "MHz" : "KHz")); | |
DEBUG ((DEBUG_INFO, " Base Clk Freq %dMHz\n", Capability->BaseClkFreq)); | |
DEBUG ((DEBUG_INFO, " Max Blk Len %dbytes\n", 512 * (1 << Capability->MaxBlkLen))); | |
DEBUG ((DEBUG_INFO, " 8-bit Support %a\n", Capability->BusWidth8 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " ADMA2 Support %a\n", Capability->Adma2 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " SDMA Support %a\n", Capability->Sdma ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Suspend/Resume %a\n", Capability->SuspRes ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Voltage 3.3 %a\n", Capability->Voltage33 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Voltage 3.0 %a\n", Capability->Voltage30 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Voltage 1.8 %a\n", Capability->Voltage18 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " V4 64-bit Sys Bus %a\n", Capability->SysBus64V4 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " V3 64-bit Sys Bus %a\n", Capability->SysBus64V3 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Async Interrupt %a\n", Capability->AsyncInt ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " SlotType ")); | |
if (Capability->SlotType == 0x00) { | |
DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot")); | |
} else if (Capability->SlotType == 0x01) { | |
DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot")); | |
} else if (Capability->SlotType == 0x02) { | |
DEBUG ((DEBUG_INFO, "%a\n", "Shared Bus Slot")); | |
} else { | |
DEBUG ((DEBUG_INFO, "%a\n", "Reserved")); | |
} | |
DEBUG ((DEBUG_INFO, " SDR50 Support %a\n", Capability->Sdr50 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " SDR104 Support %a\n", Capability->Sdr104 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " DDR50 Support %a\n", Capability->Ddr50 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Driver Type A %a\n", Capability->DriverTypeA ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Driver Type C %a\n", Capability->DriverTypeC ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Driver Type D %a\n", Capability->DriverTypeD ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Driver Type 4 %a\n", Capability->DriverType4 ? "TRUE" : "FALSE")); | |
if (Capability->TimerCount == 0) { | |
DEBUG ((DEBUG_INFO, " Retuning TimerCnt Disabled\n")); | |
} else { | |
DEBUG ((DEBUG_INFO, " Retuning TimerCnt %dseconds\n", 2 * (Capability->TimerCount - 1))); | |
} | |
DEBUG ((DEBUG_INFO, " SDR50 Tuning %a\n", Capability->TuningSDR50 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Retuning Mode Mode %d\n", Capability->RetuningMod + 1)); | |
DEBUG ((DEBUG_INFO, " Clock Multiplier M = %d\n", Capability->ClkMultiplier + 1)); | |
DEBUG ((DEBUG_INFO, " HS 400 %a\n", Capability->Hs400 ? "TRUE" : "FALSE")); | |
return; | |
} | |
/** | |
Read SlotInfo register from SD/MMC host controller pci config space. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[out] FirstBar The buffer to store the first BAR value. | |
@param[out] SlotNum The buffer to store the supported slot number. | |
@retval EFI_SUCCESS The operation succeeds. | |
@retval Others The operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcGetSlotInfo ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
OUT UINT8 *FirstBar, | |
OUT UINT8 *SlotNum | |
) | |
{ | |
EFI_STATUS Status; | |
SD_MMC_HC_SLOT_INFO SlotInfo; | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
SD_MMC_HC_SLOT_OFFSET, | |
sizeof (SlotInfo), | |
&SlotInfo | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*FirstBar = SlotInfo.FirstBar; | |
*SlotNum = SlotInfo.SlotNum + 1; | |
ASSERT ((*FirstBar + *SlotNum) < SD_MMC_HC_MAX_SLOT); | |
return EFI_SUCCESS; | |
} | |
/** | |
Read/Write specified SD/MMC host controller mmio register. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] BarIndex The BAR index of the standard PCI Configuration | |
header to use as the base address for the memory | |
operation to perform. | |
@param[in] Offset The offset within the selected BAR to start the | |
memory operation. | |
@param[in] Read A boolean to indicate it's read or write operation. | |
@param[in] Count The width of the mmio register in bytes. | |
Must be 1, 2 , 4 or 8 bytes. | |
@param[in, out] Data For read operations, the destination buffer to store | |
the results. For write operations, the source buffer | |
to write data from. The caller is responsible for | |
having ownership of the data buffer and ensuring its | |
size not less than Count bytes. | |
@retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid. | |
@retval EFI_SUCCESS The read/write operation succeeds. | |
@retval Others The read/write operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcRwMmio ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 BarIndex, | |
IN UINT32 Offset, | |
IN BOOLEAN Read, | |
IN UINT8 Count, | |
IN OUT VOID *Data | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL_WIDTH Width; | |
if ((PciIo == NULL) || (Data == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
switch (Count) { | |
case 1: | |
Width = EfiPciIoWidthUint8; | |
break; | |
case 2: | |
Width = EfiPciIoWidthUint16; | |
Count = 1; | |
break; | |
case 4: | |
Width = EfiPciIoWidthUint32; | |
Count = 1; | |
break; | |
case 8: | |
Width = EfiPciIoWidthUint32; | |
Count = 2; | |
break; | |
default: | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Read) { | |
Status = PciIo->Mem.Read ( | |
PciIo, | |
Width, | |
BarIndex, | |
(UINT64)Offset, | |
Count, | |
Data | |
); | |
} else { | |
Status = PciIo->Mem.Write ( | |
PciIo, | |
Width, | |
BarIndex, | |
(UINT64)Offset, | |
Count, | |
Data | |
); | |
} | |
return Status; | |
} | |
/** | |
Do OR operation with the value of the specified SD/MMC host controller mmio register. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] BarIndex The BAR index of the standard PCI Configuration | |
header to use as the base address for the memory | |
operation to perform. | |
@param[in] Offset The offset within the selected BAR to start the | |
memory operation. | |
@param[in] Count The width of the mmio register in bytes. | |
Must be 1, 2 , 4 or 8 bytes. | |
@param[in] OrData The pointer to the data used to do OR operation. | |
The caller is responsible for having ownership of | |
the data buffer and ensuring its size not less than | |
Count bytes. | |
@retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid. | |
@retval EFI_SUCCESS The OR operation succeeds. | |
@retval Others The OR operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcOrMmio ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 BarIndex, | |
IN UINT32 Offset, | |
IN UINT8 Count, | |
IN VOID *OrData | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Data; | |
UINT64 Or; | |
Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Count == 1) { | |
Or = *(UINT8 *)OrData; | |
} else if (Count == 2) { | |
Or = *(UINT16 *)OrData; | |
} else if (Count == 4) { | |
Or = *(UINT32 *)OrData; | |
} else if (Count == 8) { | |
Or = *(UINT64 *)OrData; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
Data |= Or; | |
Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data); | |
return Status; | |
} | |
/** | |
Do AND operation with the value of the specified SD/MMC host controller mmio register. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] BarIndex The BAR index of the standard PCI Configuration | |
header to use as the base address for the memory | |
operation to perform. | |
@param[in] Offset The offset within the selected BAR to start the | |
memory operation. | |
@param[in] Count The width of the mmio register in bytes. | |
Must be 1, 2 , 4 or 8 bytes. | |
@param[in] AndData The pointer to the data used to do AND operation. | |
The caller is responsible for having ownership of | |
the data buffer and ensuring its size not less than | |
Count bytes. | |
@retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid. | |
@retval EFI_SUCCESS The AND operation succeeds. | |
@retval Others The AND operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcAndMmio ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 BarIndex, | |
IN UINT32 Offset, | |
IN UINT8 Count, | |
IN VOID *AndData | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Data; | |
UINT64 And; | |
Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Count == 1) { | |
And = *(UINT8 *)AndData; | |
} else if (Count == 2) { | |
And = *(UINT16 *)AndData; | |
} else if (Count == 4) { | |
And = *(UINT32 *)AndData; | |
} else if (Count == 8) { | |
And = *(UINT64 *)AndData; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
Data &= And; | |
Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data); | |
return Status; | |
} | |
/** | |
Wait for the value of the specified MMIO register set to the test value. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] BarIndex The BAR index of the standard PCI Configuration | |
header to use as the base address for the memory | |
operation to perform. | |
@param[in] Offset The offset within the selected BAR to start the | |
memory operation. | |
@param[in] Count The width of the mmio register in bytes. | |
Must be 1, 2, 4 or 8 bytes. | |
@param[in] MaskValue The mask value of memory. | |
@param[in] TestValue The test value of memory. | |
@retval EFI_NOT_READY The MMIO register hasn't set to the expected value. | |
@retval EFI_SUCCESS The MMIO register has expected value. | |
@retval Others The MMIO operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcCheckMmioSet ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 BarIndex, | |
IN UINT32 Offset, | |
IN UINT8 Count, | |
IN UINT64 MaskValue, | |
IN UINT64 TestValue | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Value; | |
// | |
// Access PCI MMIO space to see if the value is the tested one. | |
// | |
Value = 0; | |
Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Value); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Value &= MaskValue; | |
if (Value == TestValue) { | |
return EFI_SUCCESS; | |
} | |
return EFI_NOT_READY; | |
} | |
/** | |
Wait for the value of the specified MMIO register set to the test value. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] BarIndex The BAR index of the standard PCI Configuration | |
header to use as the base address for the memory | |
operation to perform. | |
@param[in] Offset The offset within the selected BAR to start the | |
memory operation. | |
@param[in] Count The width of the mmio register in bytes. | |
Must be 1, 2, 4 or 8 bytes. | |
@param[in] MaskValue The mask value of memory. | |
@param[in] TestValue The test value of memory. | |
@param[in] Timeout The time out value for wait memory set, uses 1 | |
microsecond as a unit. | |
@retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout | |
range. | |
@retval EFI_SUCCESS The MMIO register has expected value. | |
@retval Others The MMIO operation fails. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SdMmcHcWaitMmioSet ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 BarIndex, | |
IN UINT32 Offset, | |
IN UINT8 Count, | |
IN UINT64 MaskValue, | |
IN UINT64 TestValue, | |
IN UINT64 Timeout | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN InfiniteWait; | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
while (InfiniteWait || (Timeout > 0)) { | |
Status = SdMmcHcCheckMmioSet ( | |
PciIo, | |
BarIndex, | |
Offset, | |
Count, | |
MaskValue, | |
TestValue | |
); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} | |
/** | |
Get the controller version information from the specified slot. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[out] Version The buffer to store the version information. | |
@retval EFI_SUCCESS The operation executes successfully. | |
@retval Others The operation fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcGetControllerVersion ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
OUT UINT16 *Version | |
) | |
{ | |
EFI_STATUS Status; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CTRL_VER, TRUE, sizeof (UINT16), Version); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*Version &= 0xFF; | |
return EFI_SUCCESS; | |
} | |
/** | |
Software reset the specified SD/MMC host controller and enable all interrupts. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@retval EFI_SUCCESS The software reset executes successfully. | |
@retval Others The software reset fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcReset ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 SwReset; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
// | |
// Notify the SD/MMC override protocol that we are about to reset | |
// the SD/MMC host controller. | |
// | |
if ((mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
Private->ControllerHandle, | |
Slot, | |
EdkiiSdMmcResetPre, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: SD/MMC pre reset notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
return Status; | |
} | |
} | |
PciIo = Private->PciIo; | |
SwReset = BIT0; | |
Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_SW_RST, sizeof (SwReset), &SwReset); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "SdMmcHcReset: write SW Reset for All fails: %r\n", Status)); | |
return Status; | |
} | |
Status = SdMmcHcWaitMmioSet ( | |
PciIo, | |
Slot, | |
SD_MMC_HC_SW_RST, | |
sizeof (SwReset), | |
BIT0, | |
0x00, | |
SD_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "SdMmcHcReset: reset done with %r\n", Status)); | |
return Status; | |
} | |
// | |
// Enable all interrupt after reset all. | |
// | |
Status = SdMmcHcEnableInterrupt (PciIo, Slot); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_INFO, | |
"SdMmcHcReset: SdMmcHcEnableInterrupt done with %r\n", | |
Status | |
)); | |
return Status; | |
} | |
// | |
// Notify the SD/MMC override protocol that we have just reset | |
// the SD/MMC host controller. | |
// | |
if ((mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
Private->ControllerHandle, | |
Slot, | |
EdkiiSdMmcResetPost, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: SD/MMC post reset notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
} | |
} | |
return Status; | |
} | |
/** | |
Set all interrupt status bits in Normal and Error Interrupt Status Enable | |
register. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@retval EFI_SUCCESS The operation executes successfully. | |
@retval Others The operation fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcEnableInterrupt ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 IntStatus; | |
// | |
// Enable all bits in Error Interrupt Status Enable Register | |
// | |
IntStatus = 0xFFFF; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_ERR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Enable all bits in Normal Interrupt Status Enable Register | |
// | |
IntStatus = 0xFFFF; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); | |
return Status; | |
} | |
/** | |
Get the capability data from the specified slot. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[out] Capability The buffer to store the capability data. | |
@retval EFI_SUCCESS The operation executes successfully. | |
@retval Others The operation fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcGetCapability ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
OUT SD_MMC_HC_SLOT_CAP *Capability | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Cap; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CAP, TRUE, sizeof (Cap), &Cap); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
CopyMem (Capability, &Cap, sizeof (Cap)); | |
return EFI_SUCCESS; | |
} | |
/** | |
Get the maximum current capability data from the specified slot. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[out] MaxCurrent The buffer to store the maximum current capability data. | |
@retval EFI_SUCCESS The operation executes successfully. | |
@retval Others The operation fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcGetMaxCurrent ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
OUT UINT64 *MaxCurrent | |
) | |
{ | |
EFI_STATUS Status; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_MAX_CURRENT_CAP, TRUE, sizeof (UINT64), MaxCurrent); | |
return Status; | |
} | |
/** | |
Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller | |
slot. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[out] MediaPresent The pointer to the media present boolean value. | |
@retval EFI_SUCCESS There is no media change happened. | |
@retval EFI_MEDIA_CHANGED There is media change happened. | |
@retval Others The detection fails. | |
**/ | |
EFI_STATUS | |
SdMmcHcCardDetect ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
OUT BOOLEAN *MediaPresent | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 Data; | |
UINT32 PresentState; | |
// | |
// Check Present State Register to see if there is a card presented. | |
// | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((PresentState & BIT16) != 0) { | |
*MediaPresent = TRUE; | |
} else { | |
*MediaPresent = FALSE; | |
} | |
// | |
// Check Normal Interrupt Status Register | |
// | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS, TRUE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((Data & (BIT6 | BIT7)) != 0) { | |
// | |
// Clear BIT6 and BIT7 by writing 1 to these two bits if set. | |
// | |
Data &= BIT6 | BIT7; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return EFI_MEDIA_CHANGED; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Stop SD/MMC card clock. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@retval EFI_SUCCESS Succeed to stop SD/MMC clock. | |
@retval Others Fail to stop SD/MMC clock. | |
**/ | |
EFI_STATUS | |
SdMmcHcStopClock ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 PresentState; | |
UINT16 ClockCtrl; | |
// | |
// Ensure no SD transactions are occurring on the SD Bus by | |
// waiting for Command Inhibit (DAT) and Command Inhibit (CMD) | |
// in the Present State register to be 0. | |
// | |
Status = SdMmcHcWaitMmioSet ( | |
PciIo, | |
Slot, | |
SD_MMC_HC_PRESENT_STATE, | |
sizeof (PresentState), | |
BIT0 | BIT1, | |
0, | |
SD_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Set SD Clock Enable in the Clock Control register to 0 | |
// | |
ClockCtrl = (UINT16) ~BIT2; | |
Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); | |
return Status; | |
} | |
/** | |
Start the SD clock. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number. | |
@retval EFI_SUCCESS Succeeded to start the SD clock. | |
@retval Others Failed to start the SD clock. | |
**/ | |
EFI_STATUS | |
SdMmcHcStartSdClock ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
UINT16 ClockCtrl; | |
// | |
// Set SD Clock Enable in the Clock Control register to 1 | |
// | |
ClockCtrl = BIT2; | |
return SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); | |
} | |
/** | |
SD/MMC card clock supply. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] BusTiming BusTiming at which the frequency change is done. | |
@param[in] FirstTimeSetup Flag to indicate whether the clock is being setup for the first time. | |
@param[in] ClockFreq The max clock frequency to be set. The unit is KHz. | |
@retval EFI_SUCCESS The clock is supplied successfully. | |
@retval Others The clock isn't supplied successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcClockSupply ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN SD_MMC_BUS_MODE BusTiming, | |
IN BOOLEAN FirstTimeSetup, | |
IN UINT64 ClockFreq | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 SettingFreq; | |
UINT32 Divisor; | |
UINT32 Remainder; | |
UINT16 ClockCtrl; | |
UINT32 BaseClkFreq; | |
UINT16 ControllerVer; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PciIo = Private->PciIo; | |
BaseClkFreq = Private->BaseClkFreq[Slot]; | |
ControllerVer = Private->ControllerVersion[Slot]; | |
if ((BaseClkFreq == 0) || (ClockFreq == 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (ClockFreq > (BaseClkFreq * 1000)) { | |
ClockFreq = BaseClkFreq * 1000; | |
} | |
// | |
// Calculate the divisor of base frequency. | |
// | |
Divisor = 0; | |
SettingFreq = BaseClkFreq * 1000; | |
while (ClockFreq < SettingFreq) { | |
Divisor++; | |
SettingFreq = (BaseClkFreq * 1000) / (2 * Divisor); | |
Remainder = (BaseClkFreq * 1000) % (2 * Divisor); | |
if ((ClockFreq == SettingFreq) && (Remainder == 0)) { | |
break; | |
} | |
if ((ClockFreq == SettingFreq) && (Remainder != 0)) { | |
SettingFreq++; | |
} | |
} | |
DEBUG ((DEBUG_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); | |
// | |
// Set SDCLK Frequency Select and Internal Clock Enable fields in Clock Control register. | |
// | |
if ((ControllerVer >= SD_MMC_HC_CTRL_VER_300) && | |
(ControllerVer <= SD_MMC_HC_CTRL_VER_420)) | |
{ | |
ASSERT (Divisor <= 0x3FF); | |
ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2); | |
} else if ((ControllerVer == SD_MMC_HC_CTRL_VER_100) || | |
(ControllerVer == SD_MMC_HC_CTRL_VER_200)) | |
{ | |
// | |
// Only the most significant bit can be used as divisor. | |
// | |
if (((Divisor - 1) & Divisor) != 0) { | |
Divisor = 1 << (HighBitSet32 (Divisor) + 1); | |
} | |
ASSERT (Divisor <= 0x80); | |
ClockCtrl = (Divisor & 0xFF) << 8; | |
} else { | |
DEBUG ((DEBUG_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Stop bus clock at first | |
// | |
Status = SdMmcHcStopClock (PciIo, Slot); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Supply clock frequency with specified divisor | |
// | |
ClockCtrl |= BIT0; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); | |
return Status; | |
} | |
// | |
// Wait Internal Clock Stable in the Clock Control register to be 1 | |
// | |
Status = SdMmcHcWaitMmioSet ( | |
PciIo, | |
Slot, | |
SD_MMC_HC_CLOCK_CTRL, | |
sizeof (ClockCtrl), | |
BIT1, | |
BIT1, | |
SD_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcHcStartSdClock (PciIo, Slot); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// We don't notify the platform on first time setup to avoid changing | |
// legacy behavior. During first time setup we also don't know what type | |
// of the card slot it is and which enum value of BusTiming applies. | |
// | |
if (!FirstTimeSetup && (mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
Private->ControllerHandle, | |
Slot, | |
EdkiiSdMmcSwitchClockFreqPost, | |
&BusTiming | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: SD/MMC switch clock freq post notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
return Status; | |
} | |
} | |
Private->Slot[Slot].CurrentFreq = ClockFreq; | |
return Status; | |
} | |
/** | |
SD/MMC bus power control. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] PowerCtrl The value setting to the power control register. | |
@retval TRUE There is a SD/MMC card attached. | |
@retval FALSE There is no a SD/MMC card attached. | |
**/ | |
EFI_STATUS | |
SdMmcHcPowerControl ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN UINT8 PowerCtrl | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Clr SD Bus Power | |
// | |
PowerCtrl &= (UINT8) ~BIT0; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register | |
// | |
PowerCtrl |= BIT0; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); | |
return Status; | |
} | |
/** | |
Set the SD/MMC bus width. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8. | |
@retval EFI_SUCCESS The bus width is set successfully. | |
@retval Others The bus width isn't set successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcSetBusWidth ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN UINT16 BusWidth | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 HostCtrl1; | |
if (BusWidth == 1) { | |
HostCtrl1 = (UINT8) ~(BIT5 | BIT1); | |
Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); | |
} else if (BusWidth == 4) { | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
HostCtrl1 |= BIT1; | |
HostCtrl1 &= (UINT8) ~BIT5; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); | |
} else if (BusWidth == 8) { | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
HostCtrl1 &= (UINT8) ~BIT1; | |
HostCtrl1 |= BIT5; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); | |
} else { | |
ASSERT (FALSE); | |
return EFI_INVALID_PARAMETER; | |
} | |
return Status; | |
} | |
/** | |
Configure V4 controller enhancements at initialization. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Capability The capability of the slot. | |
@param[in] ControllerVer The version of host controller. | |
@retval EFI_SUCCESS The clock is supplied successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcInitV4Enhancements ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN SD_MMC_HC_SLOT_CAP Capability, | |
IN UINT16 ControllerVer | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 HostCtrl2; | |
// | |
// Check if controller version V4 or higher | |
// | |
if (ControllerVer >= SD_MMC_HC_CTRL_VER_400) { | |
HostCtrl2 = SD_MMC_HC_V4_EN; | |
// | |
// Check if controller version V4.0 | |
// | |
if (ControllerVer == SD_MMC_HC_CTRL_VER_400) { | |
// | |
// Check if 64bit support is available | |
// | |
if (Capability.SysBus64V3 != 0) { | |
HostCtrl2 |= SD_MMC_HC_64_ADDR_EN; | |
DEBUG ((DEBUG_INFO, "Enabled V4 64 bit system bus support\n")); | |
} | |
} | |
// | |
// Check if controller version V4.10 or higher | |
// | |
else if (ControllerVer >= SD_MMC_HC_CTRL_VER_410) { | |
// | |
// Check if 64bit support is available | |
// | |
if (Capability.SysBus64V4 != 0) { | |
HostCtrl2 |= SD_MMC_HC_64_ADDR_EN; | |
DEBUG ((DEBUG_INFO, "Enabled V4 64 bit system bus support\n")); | |
} | |
HostCtrl2 |= SD_MMC_HC_26_DATA_LEN_ADMA_EN; | |
DEBUG ((DEBUG_INFO, "Enabled V4 26 bit data length ADMA support\n")); | |
} | |
Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Supply SD/MMC card with maximum voltage at initialization. | |
Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Capability The capability of the slot. | |
@retval EFI_SUCCESS The voltage is supplied successfully. | |
@retval Others The voltage isn't supplied successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcInitPowerVoltage ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN SD_MMC_HC_SLOT_CAP Capability | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 MaxVoltage; | |
UINT8 HostCtrl2; | |
// | |
// Calculate supported maximum voltage according to SD Bus Voltage Select | |
// | |
if (Capability.Voltage33 != 0) { | |
// | |
// Support 3.3V | |
// | |
MaxVoltage = 0x0E; | |
} else if (Capability.Voltage30 != 0) { | |
// | |
// Support 3.0V | |
// | |
MaxVoltage = 0x0C; | |
} else if (Capability.Voltage18 != 0) { | |
// | |
// Support 1.8V | |
// | |
MaxVoltage = 0x0A; | |
HostCtrl2 = BIT3; | |
Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
gBS->Stall (5000); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else { | |
ASSERT (FALSE); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register | |
// | |
Status = SdMmcHcPowerControl (PciIo, Slot, MaxVoltage); | |
return Status; | |
} | |
/** | |
Initialize the Timeout Control register with most conservative value at initialization. | |
Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@retval EFI_SUCCESS The timeout control register is configured successfully. | |
@retval Others The timeout control register isn't configured successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcInitTimeoutCtrl ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 Timeout; | |
Timeout = 0x0E; | |
Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_TIMEOUT_CTRL, FALSE, sizeof (Timeout), &Timeout); | |
return Status; | |
} | |
/** | |
Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value | |
at initialization. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@retval EFI_SUCCESS The host controller is initialized successfully. | |
@retval Others The host controller isn't initialized successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcInitHost ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
SD_MMC_HC_SLOT_CAP Capability; | |
// | |
// Notify the SD/MMC override protocol that we are about to initialize | |
// the SD/MMC host controller. | |
// | |
if ((mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
Private->ControllerHandle, | |
Slot, | |
EdkiiSdMmcInitHostPre, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: SD/MMC pre init notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
return Status; | |
} | |
} | |
PciIo = Private->PciIo; | |
Capability = Private->Capability[Slot]; | |
Status = SdMmcHcInitV4Enhancements (PciIo, Slot, Capability, Private->ControllerVersion[Slot]); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Perform first time clock setup with 400 KHz frequency. | |
// We send the 0 as the BusTiming value because at this time | |
// we still do not know the slot type and which enum value will apply. | |
// Since it is a first time setup SdMmcHcClockSupply won't notify | |
// the platofrm driver anyway so it doesn't matter. | |
// | |
Status = SdMmcHcClockSupply (Private, Slot, 0, TRUE, 400); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcHcInitPowerVoltage (PciIo, Slot, Capability); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcHcInitTimeoutCtrl (PciIo, Slot); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Notify the SD/MMC override protocol that we are have just initialized | |
// the SD/MMC host controller. | |
// | |
if ((mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
Private->ControllerHandle, | |
Slot, | |
EdkiiSdMmcInitHostPost, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_WARN, | |
"%a: SD/MMC post init notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
} | |
} | |
return Status; | |
} | |
/** | |
Set SD Host Controler control 2 registry according to selected speed. | |
@param[in] ControllerHandle The handle of the controller. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Timing The timing to select. | |
@retval EFI_SUCCESS The timing is set successfully. | |
@retval Others The timing isn't set successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcUhsSignaling ( | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN SD_MMC_BUS_MODE Timing | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 HostCtrl2; | |
HostCtrl2 = (UINT8) ~SD_MMC_HC_CTRL_UHS_MASK; | |
Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
switch (Timing) { | |
case SdMmcUhsSdr12: | |
HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR12; | |
break; | |
case SdMmcUhsSdr25: | |
HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR25; | |
break; | |
case SdMmcUhsSdr50: | |
HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR50; | |
break; | |
case SdMmcUhsSdr104: | |
HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR104; | |
break; | |
case SdMmcUhsDdr50: | |
HostCtrl2 = SD_MMC_HC_CTRL_UHS_DDR50; | |
break; | |
case SdMmcMmcLegacy: | |
HostCtrl2 = SD_MMC_HC_CTRL_MMC_LEGACY; | |
break; | |
case SdMmcMmcHsSdr: | |
HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS_SDR; | |
break; | |
case SdMmcMmcHsDdr: | |
HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS_DDR; | |
break; | |
case SdMmcMmcHs200: | |
HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS200; | |
break; | |
case SdMmcMmcHs400: | |
HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS400; | |
break; | |
default: | |
HostCtrl2 = 0; | |
break; | |
} | |
Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((mOverride != NULL) && (mOverride->NotifyPhase != NULL)) { | |
Status = mOverride->NotifyPhase ( | |
ControllerHandle, | |
Slot, | |
EdkiiSdMmcUhsSignaling, | |
&Timing | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a: SD/MMC uhs signaling notifier callback failed - %r\n", | |
__func__, | |
Status | |
)); | |
return Status; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Set driver strength in host controller. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] SlotIndex The slot index of the card. | |
@param[in] DriverStrength DriverStrength to set in the controller. | |
@retval EFI_SUCCESS Driver strength programmed successfully. | |
@retval Others Failed to set driver strength. | |
**/ | |
EFI_STATUS | |
SdMmcSetDriverStrength ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 SlotIndex, | |
IN SD_DRIVER_STRENGTH_TYPE DriverStrength | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 HostCtrl2; | |
if (DriverStrength == SdDriverStrengthIgnore) { | |
return EFI_SUCCESS; | |
} | |
HostCtrl2 = (UINT16) ~SD_MMC_HC_CTRL_DRIVER_STRENGTH_MASK; | |
Status = SdMmcHcAndMmio (PciIo, SlotIndex, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
HostCtrl2 = (DriverStrength << 4) & SD_MMC_HC_CTRL_DRIVER_STRENGTH_MASK; | |
return SdMmcHcOrMmio (PciIo, SlotIndex, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); | |
} | |
/** | |
Turn on/off LED. | |
@param[in] PciIo The PCI IO protocol instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] On The boolean to turn on/off LED. | |
@retval EFI_SUCCESS The LED is turned on/off successfully. | |
@retval Others The LED isn't turned on/off successfully. | |
**/ | |
EFI_STATUS | |
SdMmcHcLedOnOff ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN BOOLEAN On | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 HostCtrl1; | |
if (On) { | |
HostCtrl1 = BIT0; | |
Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); | |
} else { | |
HostCtrl1 = (UINT8) ~BIT0; | |
Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); | |
} | |
return Status; | |
} | |
/** | |
Build ADMA descriptor table for transfer. | |
Refer to SD Host Controller Simplified spec 4.2 Section 1.13 for details. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@param[in] ControllerVer The version of host controller. | |
@retval EFI_SUCCESS The ADMA descriptor table is created successfully. | |
@retval Others The ADMA descriptor table isn't created successfully. | |
**/ | |
EFI_STATUS | |
BuildAdmaDescTable ( | |
IN SD_MMC_HC_TRB *Trb, | |
IN UINT16 ControllerVer | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS Data; | |
UINT64 DataLen; | |
UINT64 Entries; | |
UINT32 Index; | |
UINT64 Remaining; | |
UINT64 Address; | |
UINTN TableSize; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
EFI_STATUS Status; | |
UINTN Bytes; | |
UINT32 AdmaMaxDataPerLine; | |
UINT32 DescSize; | |
VOID *AdmaDesc; | |
AdmaMaxDataPerLine = ADMA_MAX_DATA_PER_LINE_16B; | |
DescSize = sizeof (SD_MMC_HC_ADMA_32_DESC_LINE); | |
AdmaDesc = NULL; | |
Data = Trb->DataPhy; | |
DataLen = Trb->DataLen; | |
PciIo = Trb->Private->PciIo; | |
// | |
// Check for valid ranges in 32bit ADMA Descriptor Table | |
// | |
if ((Trb->Mode == SdMmcAdma32bMode) && | |
((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul))) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check address field alignment | |
// | |
if (Trb->Mode != SdMmcAdma32bMode) { | |
// | |
// Address field shall be set on 64-bit boundary (Lower 3-bit is always set to 0) | |
// | |
if ((Data & (BIT0 | BIT1 | BIT2)) != 0) { | |
DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 8 bytes boundary!\n", Data)); | |
} | |
} else { | |
// | |
// Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) | |
// | |
if ((Data & (BIT0 | BIT1)) != 0) { | |
DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data)); | |
} | |
} | |
// | |
// Configure 64b ADMA. | |
// | |
if (Trb->Mode == SdMmcAdma64bV3Mode) { | |
DescSize = sizeof (SD_MMC_HC_ADMA_64_V3_DESC_LINE); | |
} else if (Trb->Mode == SdMmcAdma64bV4Mode) { | |
DescSize = sizeof (SD_MMC_HC_ADMA_64_V4_DESC_LINE); | |
} | |
// | |
// Configure 26b data length. | |
// | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
AdmaMaxDataPerLine = ADMA_MAX_DATA_PER_LINE_26B; | |
} | |
Entries = DivU64x32 ((DataLen + AdmaMaxDataPerLine - 1), AdmaMaxDataPerLine); | |
TableSize = (UINTN)MultU64x32 (Entries, DescSize); | |
Trb->AdmaPages = (UINT32)EFI_SIZE_TO_PAGES (TableSize); | |
Status = PciIo->AllocateBuffer ( | |
PciIo, | |
AllocateAnyPages, | |
EfiBootServicesData, | |
EFI_SIZE_TO_PAGES (TableSize), | |
(VOID **)&AdmaDesc, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ZeroMem (AdmaDesc, TableSize); | |
Bytes = TableSize; | |
Status = PciIo->Map ( | |
PciIo, | |
EfiPciIoOperationBusMasterCommonBuffer, | |
AdmaDesc, | |
&Bytes, | |
&Trb->AdmaDescPhy, | |
&Trb->AdmaMap | |
); | |
if (EFI_ERROR (Status) || (Bytes != TableSize)) { | |
// | |
// Map error or unable to map the whole RFis buffer into a contiguous region. | |
// | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES (TableSize), | |
AdmaDesc | |
); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if ((Trb->Mode == SdMmcAdma32bMode) && | |
((UINT64)(UINTN)Trb->AdmaDescPhy > 0x100000000ul)) | |
{ | |
// | |
// The ADMA doesn't support 64bit addressing. | |
// | |
PciIo->Unmap ( | |
PciIo, | |
Trb->AdmaMap | |
); | |
Trb->AdmaMap = NULL; | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES (TableSize), | |
AdmaDesc | |
); | |
return EFI_DEVICE_ERROR; | |
} | |
Remaining = DataLen; | |
Address = Data; | |
if (Trb->Mode == SdMmcAdma32bMode) { | |
Trb->Adma32Desc = AdmaDesc; | |
} else if (Trb->Mode == SdMmcAdma64bV3Mode) { | |
Trb->Adma64V3Desc = AdmaDesc; | |
} else { | |
Trb->Adma64V4Desc = AdmaDesc; | |
} | |
for (Index = 0; Index < Entries; Index++) { | |
if (Trb->Mode == SdMmcAdma32bMode) { | |
if (Remaining <= AdmaMaxDataPerLine) { | |
Trb->Adma32Desc[Index].Valid = 1; | |
Trb->Adma32Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma32Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); | |
} | |
Trb->Adma32Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); | |
Trb->Adma32Desc[Index].Address = (UINT32)Address; | |
break; | |
} else { | |
Trb->Adma32Desc[Index].Valid = 1; | |
Trb->Adma32Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma32Desc[Index].UpperLength = 0; | |
} | |
Trb->Adma32Desc[Index].LowerLength = 0; | |
Trb->Adma32Desc[Index].Address = (UINT32)Address; | |
} | |
} else if (Trb->Mode == SdMmcAdma64bV3Mode) { | |
if (Remaining <= AdmaMaxDataPerLine) { | |
Trb->Adma64V3Desc[Index].Valid = 1; | |
Trb->Adma64V3Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma64V3Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); | |
} | |
Trb->Adma64V3Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); | |
Trb->Adma64V3Desc[Index].LowerAddress = (UINT32)Address; | |
Trb->Adma64V3Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); | |
break; | |
} else { | |
Trb->Adma64V3Desc[Index].Valid = 1; | |
Trb->Adma64V3Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma64V3Desc[Index].UpperLength = 0; | |
} | |
Trb->Adma64V3Desc[Index].LowerLength = 0; | |
Trb->Adma64V3Desc[Index].LowerAddress = (UINT32)Address; | |
Trb->Adma64V3Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); | |
} | |
} else { | |
if (Remaining <= AdmaMaxDataPerLine) { | |
Trb->Adma64V4Desc[Index].Valid = 1; | |
Trb->Adma64V4Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma64V4Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); | |
} | |
Trb->Adma64V4Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); | |
Trb->Adma64V4Desc[Index].LowerAddress = (UINT32)Address; | |
Trb->Adma64V4Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); | |
break; | |
} else { | |
Trb->Adma64V4Desc[Index].Valid = 1; | |
Trb->Adma64V4Desc[Index].Act = 2; | |
if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { | |
Trb->Adma64V4Desc[Index].UpperLength = 0; | |
} | |
Trb->Adma64V4Desc[Index].LowerLength = 0; | |
Trb->Adma64V4Desc[Index].LowerAddress = (UINT32)Address; | |
Trb->Adma64V4Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); | |
} | |
} | |
Remaining -= AdmaMaxDataPerLine; | |
Address += AdmaMaxDataPerLine; | |
} | |
// | |
// Set the last descriptor line as end of descriptor table | |
// | |
if (Trb->Mode == SdMmcAdma32bMode) { | |
Trb->Adma32Desc[Index].End = 1; | |
} else if (Trb->Mode == SdMmcAdma64bV3Mode) { | |
Trb->Adma64V3Desc[Index].End = 1; | |
} else { | |
Trb->Adma64V4Desc[Index].End = 1; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Prints the contents of the command packet to the debug port. | |
@param[in] DebugLevel Debug level at which the packet should be printed. | |
@param[in] Packet Pointer to packet to print. | |
**/ | |
VOID | |
SdMmcPrintPacket ( | |
IN UINT32 DebugLevel, | |
IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet | |
) | |
{ | |
if (Packet == NULL) { | |
return; | |
} | |
DEBUG ((DebugLevel, "Printing EFI_SD_MMC_PASS_THRU_COMMAND_PACKET\n")); | |
if (Packet->SdMmcCmdBlk != NULL) { | |
DEBUG ((DebugLevel, "Command index: %d, argument: %X\n", Packet->SdMmcCmdBlk->CommandIndex, Packet->SdMmcCmdBlk->CommandArgument)); | |
DEBUG ((DebugLevel, "Command type: %d, response type: %d\n", Packet->SdMmcCmdBlk->CommandType, Packet->SdMmcCmdBlk->ResponseType)); | |
} | |
if (Packet->SdMmcStatusBlk != NULL) { | |
DEBUG (( | |
DebugLevel, | |
"Response 0: %X, 1: %X, 2: %X, 3: %X\n", | |
Packet->SdMmcStatusBlk->Resp0, | |
Packet->SdMmcStatusBlk->Resp1, | |
Packet->SdMmcStatusBlk->Resp2, | |
Packet->SdMmcStatusBlk->Resp3 | |
)); | |
} | |
DEBUG ((DebugLevel, "Timeout: %ld\n", Packet->Timeout)); | |
DEBUG ((DebugLevel, "InDataBuffer: %p\n", Packet->InDataBuffer)); | |
DEBUG ((DebugLevel, "OutDataBuffer: %p\n", Packet->OutDataBuffer)); | |
DEBUG ((DebugLevel, "InTransferLength: %d\n", Packet->InTransferLength)); | |
DEBUG ((DebugLevel, "OutTransferLength: %d\n", Packet->OutTransferLength)); | |
DEBUG ((DebugLevel, "TransactionStatus: %r\n", Packet->TransactionStatus)); | |
} | |
/** | |
Prints the contents of the TRB to the debug port. | |
@param[in] DebugLevel Debug level at which the TRB should be printed. | |
@param[in] Trb Pointer to the TRB structure. | |
**/ | |
VOID | |
SdMmcPrintTrb ( | |
IN UINT32 DebugLevel, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
if (Trb == NULL) { | |
return; | |
} | |
DEBUG ((DebugLevel, "Printing SD_MMC_HC_TRB\n")); | |
DEBUG ((DebugLevel, "Slot: %d\n", Trb->Slot)); | |
DEBUG ((DebugLevel, "BlockSize: %d\n", Trb->BlockSize)); | |
DEBUG ((DebugLevel, "Data: %p\n", Trb->Data)); | |
DEBUG ((DebugLevel, "DataLen: %d\n", Trb->DataLen)); | |
DEBUG ((DebugLevel, "Read: %d\n", Trb->Read)); | |
DEBUG ((DebugLevel, "DataPhy: %lX\n", Trb->DataPhy)); | |
DEBUG ((DebugLevel, "DataMap: %p\n", Trb->DataMap)); | |
DEBUG ((DebugLevel, "Mode: %d\n", Trb->Mode)); | |
DEBUG ((DebugLevel, "AdmaLengthMode: %d\n", Trb->AdmaLengthMode)); | |
DEBUG ((DebugLevel, "Event: %p\n", Trb->Event)); | |
DEBUG ((DebugLevel, "Started: %d\n", Trb->Started)); | |
DEBUG ((DebugLevel, "CommandComplete: %d\n", Trb->CommandComplete)); | |
DEBUG ((DebugLevel, "Timeout: %ld\n", Trb->Timeout)); | |
DEBUG ((DebugLevel, "Retries: %d\n", Trb->Retries)); | |
DEBUG ((DebugLevel, "PioModeTransferCompleted: %d\n", Trb->PioModeTransferCompleted)); | |
DEBUG ((DebugLevel, "PioBlockIndex: %d\n", Trb->PioBlockIndex)); | |
DEBUG ((DebugLevel, "Adma32Desc: %p\n", Trb->Adma32Desc)); | |
DEBUG ((DebugLevel, "Adma64V3Desc: %p\n", Trb->Adma64V3Desc)); | |
DEBUG ((DebugLevel, "Adma64V4Desc: %p\n", Trb->Adma64V4Desc)); | |
DEBUG ((DebugLevel, "AdmaMap: %p\n", Trb->AdmaMap)); | |
DEBUG ((DebugLevel, "AdmaPages: %X\n", Trb->AdmaPages)); | |
SdMmcPrintPacket (DebugLevel, Trb->Packet); | |
} | |
/** | |
Sets up host memory to allow DMA transfer. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Packet A pointer to the SD command data structure. | |
@retval EFI_SUCCESS Memory has been mapped for DMA transfer. | |
@retval Others Memory has not been mapped. | |
**/ | |
EFI_STATUS | |
SdMmcSetupMemoryForDmaTransfer ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_PCI_IO_PROTOCOL_OPERATION Flag; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINTN MapLength; | |
EFI_STATUS Status; | |
if (Trb->Read) { | |
Flag = EfiPciIoOperationBusMasterWrite; | |
} else { | |
Flag = EfiPciIoOperationBusMasterRead; | |
} | |
PciIo = Private->PciIo; | |
if ((Trb->Data != NULL) && (Trb->DataLen != 0)) { | |
MapLength = Trb->DataLen; | |
Status = PciIo->Map ( | |
PciIo, | |
Flag, | |
Trb->Data, | |
&MapLength, | |
&Trb->DataPhy, | |
&Trb->DataMap | |
); | |
if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
} | |
if ((Trb->Mode == SdMmcAdma32bMode) || | |
(Trb->Mode == SdMmcAdma64bV3Mode) || | |
(Trb->Mode == SdMmcAdma64bV4Mode)) | |
{ | |
Status = BuildAdmaDescTable (Trb, Private->ControllerVersion[Slot]); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a new TRB for the SD/MMC cmd request. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Slot The slot number of the SD card to send the command to. | |
@param[in] Packet A pointer to the SD command data structure. | |
@param[in] Event If Event is NULL, blocking I/O is performed. If Event is | |
not NULL, then nonblocking I/O is performed, and Event | |
will be signaled when the Packet completes. | |
@return Created Trb or NULL. | |
**/ | |
SD_MMC_HC_TRB * | |
SdMmcCreateTrb ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, | |
IN EFI_EVENT Event | |
) | |
{ | |
SD_MMC_HC_TRB *Trb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
Trb = AllocateZeroPool (sizeof (SD_MMC_HC_TRB)); | |
if (Trb == NULL) { | |
return NULL; | |
} | |
Trb->Signature = SD_MMC_HC_TRB_SIG; | |
Trb->Slot = Slot; | |
Trb->BlockSize = 0x200; | |
Trb->Packet = Packet; | |
Trb->Event = Event; | |
Trb->Started = FALSE; | |
Trb->CommandComplete = FALSE; | |
Trb->Timeout = Packet->Timeout; | |
Trb->Retries = SD_MMC_TRB_RETRIES; | |
Trb->PioModeTransferCompleted = FALSE; | |
Trb->PioBlockIndex = 0; | |
Trb->Private = Private; | |
if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { | |
Trb->Data = Packet->InDataBuffer; | |
Trb->DataLen = Packet->InTransferLength; | |
Trb->Read = TRUE; | |
} else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) { | |
Trb->Data = Packet->OutDataBuffer; | |
Trb->DataLen = Packet->OutTransferLength; | |
Trb->Read = FALSE; | |
} else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) { | |
Trb->Data = NULL; | |
Trb->DataLen = 0; | |
} else { | |
goto Error; | |
} | |
if ((Trb->DataLen != 0) && (Trb->DataLen < Trb->BlockSize)) { | |
Trb->BlockSize = (UINT16)Trb->DataLen; | |
} | |
if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && | |
(Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || | |
((Private->Slot[Trb->Slot].CardType == SdCardType) && | |
(Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) | |
{ | |
Trb->Mode = SdMmcPioMode; | |
} else { | |
if (Trb->DataLen == 0) { | |
Trb->Mode = SdMmcNoData; | |
} else if (Private->Capability[Slot].Adma2 != 0) { | |
Trb->Mode = SdMmcAdma32bMode; | |
Trb->AdmaLengthMode = SdMmcAdmaLen16b; | |
if ((Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_300) && | |
(Private->Capability[Slot].SysBus64V3 == 1)) | |
{ | |
Trb->Mode = SdMmcAdma64bV3Mode; | |
} else if (((Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_400) && | |
(Private->Capability[Slot].SysBus64V3 == 1)) || | |
((Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410) && | |
(Private->Capability[Slot].SysBus64V4 == 1))) | |
{ | |
Trb->Mode = SdMmcAdma64bV4Mode; | |
} | |
if (Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410) { | |
Trb->AdmaLengthMode = SdMmcAdmaLen26b; | |
} | |
Status = SdMmcSetupMemoryForDmaTransfer (Private, Slot, Trb); | |
if (EFI_ERROR (Status)) { | |
goto Error; | |
} | |
} else if (Private->Capability[Slot].Sdma != 0) { | |
Trb->Mode = SdMmcSdmaMode; | |
Status = SdMmcSetupMemoryForDmaTransfer (Private, Slot, Trb); | |
if (EFI_ERROR (Status)) { | |
goto Error; | |
} | |
} else { | |
Trb->Mode = SdMmcPioMode; | |
} | |
} | |
if (Event != NULL) { | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
InsertTailList (&Private->Queue, &Trb->TrbList); | |
gBS->RestoreTPL (OldTpl); | |
} | |
return Trb; | |
Error: | |
SdMmcFreeTrb (Trb); | |
return NULL; | |
} | |
/** | |
Free the resource used by the TRB. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
**/ | |
VOID | |
SdMmcFreeTrb ( | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PciIo = Trb->Private->PciIo; | |
if (Trb->AdmaMap != NULL) { | |
PciIo->Unmap ( | |
PciIo, | |
Trb->AdmaMap | |
); | |
} | |
if (Trb->Adma32Desc != NULL) { | |
PciIo->FreeBuffer ( | |
PciIo, | |
Trb->AdmaPages, | |
Trb->Adma32Desc | |
); | |
} | |
if (Trb->Adma64V3Desc != NULL) { | |
PciIo->FreeBuffer ( | |
PciIo, | |
Trb->AdmaPages, | |
Trb->Adma64V3Desc | |
); | |
} | |
if (Trb->Adma64V4Desc != NULL) { | |
PciIo->FreeBuffer ( | |
PciIo, | |
Trb->AdmaPages, | |
Trb->Adma64V4Desc | |
); | |
} | |
if (Trb->DataMap != NULL) { | |
PciIo->Unmap ( | |
PciIo, | |
Trb->DataMap | |
); | |
} | |
FreePool (Trb); | |
return; | |
} | |
/** | |
Check if the env is ready for execute specified TRB. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The env is ready for TRB execution. | |
@retval EFI_NOT_READY The env is not ready for TRB execution. | |
@retval Others Some erros happen. | |
**/ | |
EFI_STATUS | |
SdMmcCheckTrbEnv ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 PresentState; | |
Packet = Trb->Packet; | |
if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc) || | |
(Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR1b) || | |
(Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR5b)) | |
{ | |
// | |
// Wait Command Inhibit (CMD) and Command Inhibit (DAT) in | |
// the Present State register to be 0 | |
// | |
PresentState = BIT0 | BIT1; | |
} else { | |
// | |
// Wait Command Inhibit (CMD) in the Present State register | |
// to be 0 | |
// | |
PresentState = BIT0; | |
} | |
PciIo = Private->PciIo; | |
Status = SdMmcHcCheckMmioSet ( | |
PciIo, | |
Trb->Slot, | |
SD_MMC_HC_PRESENT_STATE, | |
sizeof (PresentState), | |
PresentState, | |
0 | |
); | |
return Status; | |
} | |
/** | |
Wait for the env to be ready for execute specified TRB. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The env is ready for TRB execution. | |
@retval EFI_TIMEOUT The env is not ready for TRB execution in time. | |
@retval Others Some erros happen. | |
**/ | |
EFI_STATUS | |
SdMmcWaitTrbEnv ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
UINT64 Timeout; | |
BOOLEAN InfiniteWait; | |
// | |
// Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register | |
// | |
Packet = Trb->Packet; | |
Timeout = Packet->Timeout; | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
while (InfiniteWait || (Timeout > 0)) { | |
// | |
// Check Trb execution result by reading Normal Interrupt Status register. | |
// | |
Status = SdMmcCheckTrbEnv (Private, Trb); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} | |
/** | |
Execute the specified TRB. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The TRB is sent to host controller successfully. | |
@retval Others Some erros happen when sending this request to the host controller. | |
**/ | |
EFI_STATUS | |
SdMmcExecTrb ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT16 Cmd; | |
UINT16 IntStatus; | |
UINT32 Argument; | |
UINT32 BlkCount; | |
UINT16 BlkSize; | |
UINT16 TransMode; | |
UINT8 HostCtrl1; | |
UINT64 SdmaAddr; | |
UINT64 AdmaAddr; | |
BOOLEAN AddressingMode64; | |
AddressingMode64 = FALSE; | |
Packet = Trb->Packet; | |
PciIo = Trb->Private->PciIo; | |
// | |
// Clear all bits in Error Interrupt Status Register | |
// | |
IntStatus = 0xFFFF; | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ERR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Clear all bits in Normal Interrupt Status Register excepts for Card Removal & Card Insertion bits. | |
// | |
IntStatus = 0xFF3F; | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { | |
Status = SdMmcHcCheckMmioSet ( | |
PciIo, | |
Trb->Slot, | |
SD_MMC_HC_HOST_CTRL2, | |
sizeof (UINT16), | |
SD_MMC_HC_64_ADDR_EN, | |
SD_MMC_HC_64_ADDR_EN | |
); | |
if (!EFI_ERROR (Status)) { | |
AddressingMode64 = TRUE; | |
} | |
} | |
// | |
// Set Host Control 1 register DMA Select field | |
// | |
if ((Trb->Mode == SdMmcAdma32bMode) || | |
(Trb->Mode == SdMmcAdma64bV4Mode)) | |
{ | |
HostCtrl1 = BIT4; | |
Status = SdMmcHcOrMmio (PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else if (Trb->Mode == SdMmcAdma64bV3Mode) { | |
HostCtrl1 = BIT4|BIT3; | |
Status = SdMmcHcOrMmio (PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
SdMmcHcLedOnOff (PciIo, Trb->Slot, TRUE); | |
if (Trb->Mode == SdMmcSdmaMode) { | |
if ((!AddressingMode64) && | |
((UINT64)(UINTN)Trb->DataPhy >= 0x100000000ul)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
SdmaAddr = (UINT64)(UINTN)Trb->DataPhy; | |
if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (UINT64), &SdmaAddr); | |
} else { | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (UINT32), &SdmaAddr); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else if ((Trb->Mode == SdMmcAdma32bMode) || | |
(Trb->Mode == SdMmcAdma64bV3Mode) || | |
(Trb->Mode == SdMmcAdma64bV4Mode)) | |
{ | |
AdmaAddr = (UINT64)(UINTN)Trb->AdmaDescPhy; | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
BlkSize = Trb->BlockSize; | |
if (Trb->Mode == SdMmcSdmaMode) { | |
// | |
// Set SDMA boundary to be 512K bytes. | |
// | |
BlkSize |= 0x7000; | |
} | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_SIZE, FALSE, sizeof (BlkSize), &BlkSize); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
BlkCount = 0; | |
if (Trb->Mode != SdMmcNoData) { | |
// | |
// Calcuate Block Count. | |
// | |
BlkCount = (Trb->DataLen / Trb->BlockSize); | |
} | |
if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_410) { | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (UINT32), &BlkCount); | |
} else { | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_COUNT, FALSE, sizeof (UINT16), &BlkCount); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Argument = Packet->SdMmcCmdBlk->CommandArgument; | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ARG1, FALSE, sizeof (Argument), &Argument); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
TransMode = 0; | |
if (Trb->Mode != SdMmcNoData) { | |
if (Trb->Mode != SdMmcPioMode) { | |
TransMode |= BIT0; | |
} | |
if (Trb->Read) { | |
TransMode |= BIT4; | |
} | |
if (BlkCount > 1) { | |
TransMode |= BIT5 | BIT1; | |
} | |
// | |
// Only SD memory card needs to use AUTO CMD12 feature. | |
// | |
if (Private->Slot[Trb->Slot].CardType == SdCardType) { | |
if (BlkCount > 1) { | |
TransMode |= BIT2; | |
} | |
} | |
} | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_TRANS_MOD, FALSE, sizeof (TransMode), &TransMode); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Cmd = (UINT16)LShiftU64 (Packet->SdMmcCmdBlk->CommandIndex, 8); | |
if (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc) { | |
Cmd |= BIT5; | |
} | |
// | |
// Convert ResponseType to value | |
// | |
if (Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeBc) { | |
switch (Packet->SdMmcCmdBlk->ResponseType) { | |
case SdMmcResponseTypeR1: | |
case SdMmcResponseTypeR5: | |
case SdMmcResponseTypeR6: | |
case SdMmcResponseTypeR7: | |
Cmd |= (BIT1 | BIT3 | BIT4); | |
break; | |
case SdMmcResponseTypeR2: | |
Cmd |= (BIT0 | BIT3); | |
break; | |
case SdMmcResponseTypeR3: | |
case SdMmcResponseTypeR4: | |
Cmd |= BIT1; | |
break; | |
case SdMmcResponseTypeR1b: | |
case SdMmcResponseTypeR5b: | |
Cmd |= (BIT0 | BIT1 | BIT3 | BIT4); | |
break; | |
default: | |
ASSERT (FALSE); | |
break; | |
} | |
} | |
// | |
// Execute cmd | |
// | |
Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_COMMAND, FALSE, sizeof (Cmd), &Cmd); | |
return Status; | |
} | |
/** | |
Performs SW reset based on passed error status mask. | |
@param[in] Private Pointer to driver private data. | |
@param[in] Slot Index of the slot to reset. | |
@param[in] ErrIntStatus Error interrupt status mask. | |
@retval EFI_SUCCESS Software reset performed successfully. | |
@retval Other Software reset failed. | |
**/ | |
EFI_STATUS | |
SdMmcSoftwareReset ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN UINT16 ErrIntStatus | |
) | |
{ | |
UINT8 SwReset; | |
EFI_STATUS Status; | |
SwReset = 0; | |
if ((ErrIntStatus & 0x0F) != 0) { | |
SwReset |= BIT1; | |
} | |
if ((ErrIntStatus & 0x70) != 0) { | |
SwReset |= BIT2; | |
} | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Slot, | |
SD_MMC_HC_SW_RST, | |
FALSE, | |
sizeof (SwReset), | |
&SwReset | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcHcWaitMmioSet ( | |
Private->PciIo, | |
Slot, | |
SD_MMC_HC_SW_RST, | |
sizeof (SwReset), | |
0xFF, | |
0, | |
SD_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Checks the error status in error status register | |
and issues appropriate software reset as described in | |
SD specification section 3.10. | |
@param[in] Private Pointer to driver private data. | |
@param[in] Slot Index of the slot for device. | |
@param[in] IntStatus Normal interrupt status mask. | |
@retval EFI_CRC_ERROR CRC error happened during CMD execution. | |
@retval EFI_SUCCESS No error reported. | |
@retval Others Some other error happened. | |
**/ | |
EFI_STATUS | |
SdMmcCheckAndRecoverErrors ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN UINT16 IntStatus | |
) | |
{ | |
UINT16 ErrIntStatus; | |
EFI_STATUS Status; | |
EFI_STATUS ErrorStatus; | |
if ((IntStatus & BIT15) == 0) { | |
return EFI_SUCCESS; | |
} | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Slot, | |
SD_MMC_HC_ERR_INT_STS, | |
TRUE, | |
sizeof (ErrIntStatus), | |
&ErrIntStatus | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
DEBUG ((DEBUG_ERROR, "Error reported by SDHCI\n")); | |
DEBUG ((DEBUG_ERROR, "Interrupt status = %X\n", IntStatus)); | |
DEBUG ((DEBUG_ERROR, "Error interrupt status = %X\n", ErrIntStatus)); | |
// | |
// If the data timeout error is reported | |
// but data transfer is signaled as completed we | |
// have to ignore data timeout. We also assume that no | |
// other error is present on the link since data transfer | |
// completed successfully. Error interrupt status | |
// register is going to be reset when the next command | |
// is started. | |
// | |
if (((ErrIntStatus & BIT4) != 0) && ((IntStatus & BIT1) != 0)) { | |
return EFI_SUCCESS; | |
} | |
// | |
// We treat both CMD and DAT CRC errors and | |
// end bits errors as EFI_CRC_ERROR. This will | |
// let higher layer know that the error possibly | |
// happened due to random bus condition and the | |
// command can be retried. | |
// | |
if ((ErrIntStatus & (BIT1 | BIT2 | BIT5 | BIT6)) != 0) { | |
ErrorStatus = EFI_CRC_ERROR; | |
} else { | |
ErrorStatus = EFI_DEVICE_ERROR; | |
} | |
Status = SdMmcSoftwareReset (Private, Slot, ErrIntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return ErrorStatus; | |
} | |
/** | |
Reads the response data into the TRB buffer. | |
This function assumes that caller made sure that | |
command has completed. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS Response read successfully. | |
@retval Others Failed to get response. | |
**/ | |
EFI_STATUS | |
SdMmcGetResponse ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
UINT8 Index; | |
UINT32 Response[4]; | |
EFI_STATUS Status; | |
Packet = Trb->Packet; | |
if (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeBc) { | |
return EFI_SUCCESS; | |
} | |
for (Index = 0; Index < 4; Index++) { | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_RESPONSE + Index * 4, | |
TRUE, | |
sizeof (UINT32), | |
&Response[Index] | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
CopyMem (Packet->SdMmcStatusBlk, Response, sizeof (Response)); | |
return EFI_SUCCESS; | |
} | |
/** | |
Checks if the command completed. If the command | |
completed it gets the response and records the | |
command completion in the TRB. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@param[in] IntStatus Snapshot of the normal interrupt status register. | |
@retval EFI_SUCCESS Command completed successfully. | |
@retval EFI_NOT_READY Command completion still pending. | |
@retval Others Command failed to complete. | |
**/ | |
EFI_STATUS | |
SdMmcCheckCommandComplete ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb, | |
IN UINT16 IntStatus | |
) | |
{ | |
UINT16 Data16; | |
EFI_STATUS Status; | |
if ((IntStatus & BIT0) != 0) { | |
Data16 = BIT0; | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_NOR_INT_STS, | |
FALSE, | |
sizeof (Data16), | |
&Data16 | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcGetResponse (Private, Trb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Trb->CommandComplete = TRUE; | |
return EFI_SUCCESS; | |
} | |
return EFI_NOT_READY; | |
} | |
/** | |
Transfers data from card using PIO method. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@param[in] IntStatus Snapshot of the normal interrupt status register. | |
@retval EFI_SUCCESS PIO transfer completed successfully. | |
@retval EFI_NOT_READY PIO transfer completion still pending. | |
@retval Others PIO transfer failed to complete. | |
**/ | |
EFI_STATUS | |
SdMmcTransferDataWithPio ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb, | |
IN UINT16 IntStatus | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 Data16; | |
UINT32 BlockCount; | |
EFI_PCI_IO_PROTOCOL_WIDTH Width; | |
UINTN Count; | |
BlockCount = (Trb->DataLen / Trb->BlockSize); | |
if (Trb->DataLen % Trb->BlockSize != 0) { | |
BlockCount += 1; | |
} | |
if (Trb->PioBlockIndex >= BlockCount) { | |
return EFI_SUCCESS; | |
} | |
switch (Trb->BlockSize % sizeof (UINT32)) { | |
case 0: | |
Width = EfiPciIoWidthFifoUint32; | |
Count = Trb->BlockSize / sizeof (UINT32); | |
break; | |
case 2: | |
Width = EfiPciIoWidthFifoUint16; | |
Count = Trb->BlockSize / sizeof (UINT16); | |
break; | |
case 1: | |
case 3: | |
default: | |
Width = EfiPciIoWidthFifoUint8; | |
Count = Trb->BlockSize; | |
break; | |
} | |
if (Trb->Read) { | |
if ((IntStatus & BIT5) == 0) { | |
return EFI_NOT_READY; | |
} | |
Data16 = BIT5; | |
SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data16), &Data16); | |
Status = Private->PciIo->Mem.Read ( | |
Private->PciIo, | |
Width, | |
Trb->Slot, | |
SD_MMC_HC_BUF_DAT_PORT, | |
Count, | |
(VOID *)((UINT8 *)Trb->Data + (Trb->BlockSize * Trb->PioBlockIndex)) | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Trb->PioBlockIndex++; | |
} else { | |
if ((IntStatus & BIT4) == 0) { | |
return EFI_NOT_READY; | |
} | |
Data16 = BIT4; | |
SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data16), &Data16); | |
Status = Private->PciIo->Mem.Write ( | |
Private->PciIo, | |
Width, | |
Trb->Slot, | |
SD_MMC_HC_BUF_DAT_PORT, | |
Count, | |
(VOID *)((UINT8 *)Trb->Data + (Trb->BlockSize * Trb->PioBlockIndex)) | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Trb->PioBlockIndex++; | |
} | |
if (Trb->PioBlockIndex >= BlockCount) { | |
Trb->PioModeTransferCompleted = TRUE; | |
return EFI_SUCCESS; | |
} else { | |
return EFI_NOT_READY; | |
} | |
} | |
/** | |
Update the SDMA address on the SDMA buffer boundary interrupt. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS Updated SDMA buffer address. | |
@retval Others Failed to update SDMA buffer address. | |
**/ | |
EFI_STATUS | |
SdMmcUpdateSdmaAddress ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
UINT64 SdmaAddr; | |
EFI_STATUS Status; | |
SdmaAddr = SD_MMC_SDMA_ROUND_UP ((UINTN)Trb->DataPhy, SD_MMC_SDMA_BOUNDARY); | |
if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_ADMA_SYS_ADDR, | |
FALSE, | |
sizeof (UINT64), | |
&SdmaAddr | |
); | |
} else { | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_SDMA_ADDR, | |
FALSE, | |
sizeof (UINT32), | |
&SdmaAddr | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Trb->DataPhy = (UINT64)(UINTN)SdmaAddr; | |
return EFI_SUCCESS; | |
} | |
/** | |
Checks if the data transfer completed and performs any actions | |
neccessary to continue the data transfer such as SDMA system | |
address fixup or PIO data transfer. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@param[in] IntStatus Snapshot of the normal interrupt status register. | |
@retval EFI_SUCCESS Data transfer completed successfully. | |
@retval EFI_NOT_READY Data transfer completion still pending. | |
@retval Others Data transfer failed to complete. | |
**/ | |
EFI_STATUS | |
SdMmcCheckDataTransfer ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb, | |
IN UINT16 IntStatus | |
) | |
{ | |
UINT16 Data16; | |
EFI_STATUS Status; | |
if ((IntStatus & BIT1) != 0) { | |
Data16 = BIT1; | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_NOR_INT_STS, | |
FALSE, | |
sizeof (Data16), | |
&Data16 | |
); | |
return Status; | |
} | |
if ((Trb->Mode == SdMmcPioMode) && !Trb->PioModeTransferCompleted) { | |
Status = SdMmcTransferDataWithPio (Private, Trb, IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
if ((Trb->Mode == SdMmcSdmaMode) && ((IntStatus & BIT3) != 0)) { | |
Data16 = BIT3; | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_NOR_INT_STS, | |
FALSE, | |
sizeof (Data16), | |
&Data16 | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = SdMmcUpdateSdmaAddress (Private, Trb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
return EFI_NOT_READY; | |
} | |
/** | |
Check the TRB execution result. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The TRB is executed successfully. | |
@retval EFI_NOT_READY The TRB is not completed for execution. | |
@retval Others Some erros happen when executing this request. | |
**/ | |
EFI_STATUS | |
SdMmcCheckTrbResult ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
UINT16 IntStatus; | |
Packet = Trb->Packet; | |
// | |
// Check Trb execution result by reading Normal Interrupt Status register. | |
// | |
Status = SdMmcHcRwMmio ( | |
Private->PciIo, | |
Trb->Slot, | |
SD_MMC_HC_NOR_INT_STS, | |
TRUE, | |
sizeof (IntStatus), | |
&IntStatus | |
); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
// | |
// Check if there are any errors reported by host controller | |
// and if neccessary recover the controller before next command is executed. | |
// | |
Status = SdMmcCheckAndRecoverErrors (Private, Trb->Slot, IntStatus); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
// | |
// Tuning commands are the only ones that do not generate command | |
// complete interrupt. Process them here before entering the code | |
// that waits for command completion. | |
// | |
if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && | |
(Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || | |
((Private->Slot[Trb->Slot].CardType == SdCardType) && | |
(Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) | |
{ | |
Status = SdMmcTransferDataWithPio (Private, Trb, IntStatus); | |
goto Done; | |
} | |
if (!Trb->CommandComplete) { | |
Status = SdMmcCheckCommandComplete (Private, Trb, IntStatus); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
} | |
if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc) || | |
(Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR1b) || | |
(Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR5b)) | |
{ | |
Status = SdMmcCheckDataTransfer (Private, Trb, IntStatus); | |
} else { | |
Status = EFI_SUCCESS; | |
} | |
Done: | |
if (Status != EFI_NOT_READY) { | |
SdMmcHcLedOnOff (Private->PciIo, Trb->Slot, FALSE); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "TRB failed with %r\n", Status)); | |
SdMmcPrintTrb (DEBUG_ERROR, Trb); | |
} else { | |
DEBUG ((DEBUG_VERBOSE, "TRB success\n")); | |
SdMmcPrintTrb (DEBUG_VERBOSE, Trb); | |
} | |
} | |
return Status; | |
} | |
/** | |
Wait for the TRB execution result. | |
@param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the SD_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The TRB is executed successfully. | |
@retval Others Some erros happen when executing this request. | |
**/ | |
EFI_STATUS | |
SdMmcWaitTrbResult ( | |
IN SD_MMC_HC_PRIVATE_DATA *Private, | |
IN SD_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
UINT64 Timeout; | |
BOOLEAN InfiniteWait; | |
Packet = Trb->Packet; | |
// | |
// Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register | |
// | |
Timeout = Packet->Timeout; | |
if (Timeout == 0) { | |
InfiniteWait = TRUE; | |
} else { | |
InfiniteWait = FALSE; | |
} | |
while (InfiniteWait || (Timeout > 0)) { | |
// | |
// Check Trb execution result by reading Normal Interrupt Status register. | |
// | |
Status = SdMmcCheckTrbResult (Private, Trb); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} |