/** @file | |
This driver is used to manage Designware SD/MMC PCI host controllers. | |
It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. | |
Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "DwMmcHcDxe.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 DW_MMC_HC_SLOT_CAP *Capability | |
) | |
{ | |
// | |
// Dump Capability Data | |
// | |
DEBUG ((DEBUG_INFO, " == Slot [%d] Capability is 0x%x ==\n", Slot, Capability)); | |
DEBUG ((DEBUG_INFO, " Base Clk Freq %dKHz\n", Capability->BaseClkFreq)); | |
DEBUG ((DEBUG_INFO, " BusWidth %d\n", Capability->BusWidth)); | |
DEBUG ((DEBUG_INFO, " HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " Voltage 1.8 %a\n", Capability->Voltage18 ? "TRUE" : "FALSE")); | |
DEBUG ((DEBUG_INFO, " 64-bit Sys Bus %a\n", Capability->SysBus64 ? "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")); | |
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 | |
DwMmcHcGetSlotInfo ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
OUT UINT8 *FirstBar, | |
OUT UINT8 *SlotNum | |
) | |
{ | |
EFI_STATUS Status; | |
DW_MMC_HC_SLOT_INFO SlotInfo; | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
DW_MMC_HC_SLOT_OFFSET, | |
sizeof (SlotInfo), | |
&SlotInfo | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*FirstBar = SlotInfo.FirstBar; | |
*SlotNum = SlotInfo.SlotNum + 1; | |
ASSERT ((*FirstBar + *SlotNum) < DW_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 | |
DwMmcHcRwMmio ( | |
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; | |
if ((PciIo == NULL) || (Data == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Read) { | |
Status = PciIo->Mem.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
BarIndex, | |
(UINT64) Offset, | |
Count, | |
Data | |
); | |
} else { | |
Status = PciIo->Mem.Write ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
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 | |
DwMmcHcOrMmio ( | |
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 = DwMmcHcRwMmio (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 = DwMmcHcRwMmio (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 | |
DwMmcHcAndMmio ( | |
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 = DwMmcHcRwMmio (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 = DwMmcHcRwMmio (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 | |
DwMmcHcCheckMmioSet ( | |
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 = DwMmcHcRwMmio (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 | |
DwMmcHcWaitMmioSet ( | |
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 = DwMmcHcCheckMmioSet ( | |
PciIo, | |
BarIndex, | |
Offset, | |
Count, | |
MaskValue, | |
TestValue | |
); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} | |
/** | |
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 | |
DwMmcHcEnableInterrupt ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 IntStatus; | |
UINT32 IdIntEn; | |
UINT32 IdSts; | |
// | |
// Enable all bits in Interrupt Mask Register | |
// | |
IntStatus = 0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_INTMASK, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Clear status in Interrupt Status Register | |
// | |
IntStatus = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
IdIntEn = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_IDINTEN, FALSE, sizeof (IdIntEn), &IdIntEn); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcReset: init dma interrupts fail: %r\n", Status)); | |
return Status; | |
} | |
IdSts = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_IDSTS, FALSE, sizeof (IdSts), &IdSts); | |
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 | |
DwMmcHcGetCapability ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_HANDLE Controller, | |
IN UINT8 Slot, | |
OUT DW_MMC_HC_SLOT_CAP *Capacity | |
) | |
{ | |
PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; | |
EFI_STATUS Status; | |
if (Capacity == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = gBS->LocateProtocol ( | |
&gPlatformDwMmcProtocolGuid, | |
NULL, | |
(VOID **) &PlatformDwMmc | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = PlatformDwMmc->GetCapability (Controller, Slot, Capacity); | |
return Status; | |
} | |
/** | |
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 | |
DwMmcHcGetMaxCurrent ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
OUT UINT64 *MaxCurrent | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
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 | |
DwMmcHcCardDetect ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN EFI_HANDLE Controller, | |
IN UINT8 Slot, | |
OUT BOOLEAN *MediaPresent | |
) | |
{ | |
PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; | |
EFI_STATUS Status; | |
if (MediaPresent == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = gBS->LocateProtocol ( | |
&gPlatformDwMmcProtocolGuid, | |
NULL, | |
(VOID **) &PlatformDwMmc | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*MediaPresent = PlatformDwMmc->CardDetect (Controller, Slot); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
EFI_STATUS | |
DwMmcHcUpdateClock ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Cmd; | |
UINT32 IntStatus; | |
Cmd = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY | | |
BIT_CMD_START; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
while (1) { | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CMD, TRUE, sizeof (Cmd), &Cmd); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (!(Cmd & CMD_START_BIT)) { | |
break; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IntStatus & DW_MMC_INT_HLE) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcUpdateClock: failed to update mmc clock frequency\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Stop SD/MMC card clock. | |
@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 | |
DwMmcHcStopClock ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 ClkEna; | |
// Disable MMC clock first | |
ClkEna = 0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKENA, FALSE, sizeof (ClkEna), &ClkEna); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcUpdateClock (PciIo, Slot); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return Status; | |
} | |
/** | |
SD/MMC card clock supply. | |
@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] ClockFreq The max clock frequency to be set. The unit is KHz. | |
@param[in] Capability The capability of the slot. | |
@retval EFI_SUCCESS The clock is supplied successfully. | |
@retval Others The clock isn't supplied successfully. | |
**/ | |
EFI_STATUS | |
DwMmcHcClockSupply ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN UINT64 ClockFreq, | |
IN DW_MMC_HC_SLOT_CAP Capability | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 BaseClkFreq; | |
UINT32 SettingFreq; | |
UINT32 Divisor; | |
UINT32 Remainder; | |
UINT32 MmcStatus; | |
UINT32 ClkEna; | |
UINT32 ClkSrc; | |
// | |
// Calculate a divisor for SD clock frequency | |
// | |
ASSERT (Capability.BaseClkFreq != 0); | |
BaseClkFreq = Capability.BaseClkFreq; | |
if (ClockFreq == 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (ClockFreq > BaseClkFreq) { | |
ClockFreq = BaseClkFreq; | |
} | |
// | |
// Calculate the divisor of base frequency. | |
// | |
Divisor = 0; | |
SettingFreq = BaseClkFreq; | |
while (ClockFreq < SettingFreq) { | |
Divisor++; | |
SettingFreq = BaseClkFreq / (2 * Divisor); | |
Remainder = BaseClkFreq % (2 * Divisor); | |
if ((ClockFreq == SettingFreq) && (Remainder == 0)) { | |
break; | |
} | |
if ((ClockFreq == SettingFreq) && (Remainder != 0)) { | |
SettingFreq ++; | |
} | |
} | |
DEBUG ((DEBUG_INFO, "BaseClkFreq %dKHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); | |
// Wait until MMC is idle | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while (MmcStatus & DW_MMC_STS_DATA_BUSY); | |
do { | |
Status = DwMmcHcStopClock (PciIo, Slot); | |
} while (EFI_ERROR (Status)); | |
do { | |
ClkSrc = 0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKSRC, FALSE, sizeof (ClkSrc), &ClkSrc); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// Set clock divisor | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKDIV, FALSE, sizeof (Divisor), &Divisor); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// Enable MMC clock | |
ClkEna = 1; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CLKENA, FALSE, sizeof (ClkEna), &ClkEna); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
Status = DwMmcHcUpdateClock (PciIo, Slot); | |
} while (EFI_ERROR (Status)); | |
return EFI_SUCCESS; | |
} | |
/** | |
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 | |
DwMmcHcPowerControl ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN UINT8 PowerCtrl | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
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 | |
DwMmcHcSetBusWidth ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN BOOLEAN IsDdr, | |
IN UINT16 BusWidth | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Ctype; | |
UINT32 Uhs; | |
switch (BusWidth) { | |
case 1: | |
Ctype = MMC_1BIT_MODE; | |
break; | |
case 4: | |
Ctype = MMC_4BIT_MODE; | |
break; | |
case 8: | |
Ctype = MMC_8BIT_MODE; | |
break; | |
default: | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTYPE, FALSE, sizeof (Ctype), &Ctype); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_UHSREG, TRUE, sizeof (Uhs), &Uhs); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IsDdr) { | |
Uhs |= UHS_DDR_MODE; | |
} else { | |
Uhs &= ~(UHS_DDR_MODE); | |
} | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_UHSREG, FALSE, sizeof (Uhs), &Uhs); | |
return Status; | |
} | |
/** | |
Supply SD/MMC card with lowest clock frequency 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. | |
@retval EFI_SUCCESS The clock is supplied successfully. | |
@retval Others The clock isn't supplied successfully. | |
**/ | |
EFI_STATUS | |
DwMmcHcInitClockFreq ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN DW_MMC_HC_SLOT_CAP Capability | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 InitFreq; | |
// | |
// Calculate a divisor for SD clock frequency | |
// | |
if (Capability.BaseClkFreq == 0) { | |
// | |
// Don't support get Base Clock Frequency information via another method | |
// | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Supply 400KHz clock frequency at initialization phase. | |
// | |
InitFreq = DWMMC_INIT_CLOCK_FREQ; | |
Status = DwMmcHcClockSupply (PciIo, Slot, InitFreq, Capability); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
MicroSecondDelay (100); | |
return Status; | |
} | |
/** | |
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 | |
DwMmcHcInitPowerVoltage ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN DW_MMC_HC_SLOT_CAP Capability | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Data; | |
Data = 0x1; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_PWREN, FALSE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: enable power fails: %r\n", Status)); | |
return Status; | |
} | |
Data = DW_MMC_CTRL_RESET_ALL; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTRL, FALSE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: reset fails: %r\n", Status)); | |
return Status; | |
} | |
Status = DwMmcHcWaitMmioSet ( | |
PciIo, | |
Slot, | |
DW_MMC_CTRL, | |
sizeof (Data), | |
DW_MMC_CTRL_RESET_ALL, | |
0x00, | |
DW_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "DwMmcHcInitPowerVoltage: reset done with %r\n", Status)); | |
return Status; | |
} | |
Data = DW_MMC_CTRL_INT_EN; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_CTRL, FALSE, sizeof (Data), &Data); | |
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 | |
DwMmcHcInitTimeoutCtrl ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Data; | |
Data = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_TMOUT, FALSE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcInitTimeoutCtrl: set timeout fails: %r\n", Status)); | |
return Status; | |
} | |
Data = 0x00FFFFFF; | |
Status = DwMmcHcRwMmio (PciIo, Slot, DW_MMC_DEBNCE, FALSE, sizeof (Data), &Data); | |
return Status; | |
} | |
/** | |
Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value | |
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. | |
@retval EFI_SUCCESS The host controller is initialized successfully. | |
@retval Others The host controller isn't initialized successfully. | |
**/ | |
EFI_STATUS | |
DwMmcHcInitHost ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT8 Slot, | |
IN DW_MMC_HC_SLOT_CAP Capability | |
) | |
{ | |
EFI_STATUS Status; | |
Status = DwMmcHcInitPowerVoltage (PciIo, Slot, Capability); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return Status; | |
} | |
EFI_STATUS | |
DwMmcHcStartDma ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 Ctrl; | |
UINT32 Bmod; | |
PciIo = Trb->Private->PciIo; | |
// Reset DMA | |
Ctrl = DW_MMC_CTRL_DMA_RESET; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, FALSE, sizeof (Ctrl), &Ctrl); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: reset fails: %r\n", Status)); | |
return Status; | |
} | |
Status = DwMmcHcWaitMmioSet ( | |
PciIo, | |
Trb->Slot, | |
DW_MMC_CTRL, | |
sizeof (Ctrl), | |
DW_MMC_CTRL_DMA_RESET, | |
0x00, | |
DW_MMC_HC_GENERIC_TIMEOUT | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "DwMmcHcStartDma: reset done with %r\n", Status)); | |
return Status; | |
} | |
Bmod = DW_MMC_IDMAC_SWRESET; | |
Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_BMOD, sizeof (Bmod), &Bmod); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD fail: %r\n", Status)); | |
return Status; | |
} | |
// Select IDMAC | |
Ctrl = DW_MMC_CTRL_IDMAC_EN; | |
Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_CTRL, sizeof (Ctrl), &Ctrl); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: init IDMAC fail: %r\n", Status)); | |
return Status; | |
} | |
// Enable IDMAC | |
Bmod = DW_MMC_IDMAC_ENABLE | DW_MMC_IDMAC_FB; | |
Status = DwMmcHcOrMmio (PciIo, Trb->Slot, DW_MMC_BMOD, sizeof (Bmod), &Bmod); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set BMOD failure: %r\n", Status)); | |
return Status; | |
} | |
return Status; | |
} | |
EFI_STATUS | |
DwMmcHcStopDma ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 Ctrl; | |
UINT32 Bmod; | |
PciIo = Trb->Private->PciIo; | |
// Disable and reset IDMAC | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, TRUE, sizeof (Ctrl), &Ctrl); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Ctrl &= ~DW_MMC_CTRL_IDMAC_EN; | |
Ctrl |= DW_MMC_CTRL_DMA_RESET; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CTRL, FALSE, sizeof (Ctrl), &Ctrl); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// Stop IDMAC | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BMOD, TRUE, sizeof (Bmod), &Bmod); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Bmod &= ~(DW_MMC_BMOD_FB | DW_MMC_BMOD_DE); | |
Bmod |= DW_MMC_BMOD_SWR; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BMOD, FALSE, sizeof (Bmod), &Bmod); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return Status; | |
} | |
/** | |
Build DMA descriptor table for transfer. | |
@param[in] Trb The pointer to the DW_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The DMA descriptor table is created successfully. | |
@retval Others The DMA descriptor table isn't created successfully. | |
**/ | |
EFI_STATUS | |
BuildDmaDescTable ( | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS Data; | |
UINT64 DataLen; | |
UINT64 Entries; | |
UINT32 Index; | |
UINT64 Remaining; | |
UINTN TableSize; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
EFI_STATUS Status; | |
UINTN Bytes; | |
UINTN Blocks; | |
DW_MMC_HC_DMA_DESC_LINE *DmaDesc; | |
UINT32 DmaDescPhy; | |
UINT32 Idsts; | |
UINT32 BytCnt; | |
UINT32 BlkSize; | |
Data = Trb->DataPhy; | |
DataLen = Trb->DataLen; | |
PciIo = Trb->Private->PciIo; | |
// | |
// Only support 32bit DMA Descriptor Table | |
// | |
if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) | |
// for 32-bit address descriptor table. | |
// | |
if ((Data & (BIT0 | BIT1)) != 0) { | |
DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct DMA desc is not aligned to 4 bytes boundary!\n", Data)); | |
} | |
Entries = (DataLen + DWMMC_DMA_BUF_SIZE - 1) / DWMMC_DMA_BUF_SIZE; | |
TableSize = Entries * sizeof (DW_MMC_HC_DMA_DESC_LINE); | |
Blocks = (DataLen + DW_MMC_BLOCK_SIZE - 1) / DW_MMC_BLOCK_SIZE; | |
Trb->DmaDescPages = (UINT32)EFI_SIZE_TO_PAGES (Entries * DWMMC_DMA_BUF_SIZE); | |
Status = PciIo->AllocateBuffer ( | |
PciIo, | |
AllocateAnyPages, | |
EfiBootServicesData, | |
EFI_SIZE_TO_PAGES (TableSize), | |
(VOID **)&Trb->DmaDesc, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
ZeroMem (Trb->DmaDesc, TableSize); | |
Bytes = TableSize; | |
Status = PciIo->Map ( | |
PciIo, | |
EfiPciIoOperationBusMasterCommonBuffer, | |
Trb->DmaDesc, | |
&Bytes, | |
&Trb->DmaDescPhy, | |
&Trb->DmaMap | |
); | |
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), | |
Trb->DmaDesc | |
); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if ((UINT64)(UINTN)Trb->DmaDescPhy > 0x100000000ul) { | |
// | |
// The DMA doesn't support 64bit addressing. | |
// | |
PciIo->Unmap ( | |
PciIo, | |
Trb->DmaMap | |
); | |
#if 0 | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES (TableSize), | |
Trb->DmaDesc | |
); | |
#endif | |
return EFI_DEVICE_ERROR; | |
} | |
if (DataLen < DW_MMC_BLOCK_SIZE) { | |
BlkSize = DataLen; | |
BytCnt = DataLen; | |
Remaining = DataLen; | |
} else { | |
BlkSize = DW_MMC_BLOCK_SIZE; | |
BytCnt = DW_MMC_BLOCK_SIZE * Blocks; | |
Remaining = DW_MMC_BLOCK_SIZE * Blocks; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BLKSIZ, FALSE, sizeof (BlkSize), &BlkSize); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "BuildDmaDescTable: set block size fails: %r\n", Status)); | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BYTCNT, FALSE, sizeof (BytCnt), &BytCnt); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
DmaDesc = Trb->DmaDesc; | |
for (Index = 0; Index < Entries; Index++, DmaDesc++) { | |
DmaDesc->Des0 = DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_CH | | |
DW_MMC_IDMAC_DES0_DIC; | |
DmaDesc->Des1 = DW_MMC_IDMAC_DES1_BS1 (DWMMC_DMA_BUF_SIZE); | |
// Buffer Address | |
DmaDesc->Des2 = (UINT32)((UINTN)Trb->DataPhy + (DWMMC_DMA_BUF_SIZE * Index)); | |
// Next Descriptor Address | |
DmaDesc->Des3 = (UINT32)((UINTN)Trb->DmaDescPhy + sizeof (DW_MMC_HC_DMA_DESC_LINE) * (Index + 1)); | |
Remaining = Remaining - DWMMC_DMA_BUF_SIZE; | |
} | |
// First Descriptor | |
Trb->DmaDesc[0].Des0 |= DW_MMC_IDMAC_DES0_FS; | |
// Last Descriptor | |
Trb->DmaDesc[Entries - 1].Des0 &= ~(DW_MMC_IDMAC_DES0_CH | DW_MMC_IDMAC_DES0_DIC); | |
Trb->DmaDesc[Entries - 1].Des0 |= DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_LD; | |
Trb->DmaDesc[Entries - 1].Des1 = DW_MMC_IDMAC_DES1_BS1 (Remaining + DWMMC_DMA_BUF_SIZE); | |
// Set the next field of the Last Descriptor | |
Trb->DmaDesc[Entries - 1].Des3 = 0; | |
DmaDescPhy = (UINT32)Trb->DmaDescPhy; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_DBADDR, FALSE, sizeof (DmaDescPhy), &DmaDescPhy); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
// Clear interrupts | |
Idsts = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, FALSE, sizeof (Idsts), &Idsts); | |
return Status; | |
} | |
EFI_STATUS | |
ReadFifo ( | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 Data; | |
UINT32 Received; | |
UINT32 Count; | |
UINT32 Intsts; | |
UINT32 Sts; | |
UINT32 FifoCount; | |
UINT32 Index; // count with bytes | |
UINT32 Ascending; | |
UINT32 Descending; | |
PciIo = Trb->Private->PciIo; | |
Received = 0; | |
Count = 0; | |
Index = 0; | |
Ascending = 0; | |
Descending = ((Trb->DataLen + 3) & ~3) - 4; | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (Intsts), &Intsts); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read RINTSTS, Status:%r\n", Status)); | |
return Status; | |
} | |
if (Trb->DataLen && ((Intsts & DW_MMC_INT_RXDR) || (Intsts & DW_MMC_INT_DTO))) { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (Sts), &Sts); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read STATUS, Status:%r\n", Status)); | |
return Status; | |
} | |
// Convert to bytes | |
FifoCount = GET_STS_FIFO_COUNT (Sts) << 2; | |
if ((FifoCount == 0) && (Received < Trb->DataLen)) { | |
continue; | |
} | |
Index = 0; | |
Count = (MIN (FifoCount, Trb->DataLen) + 3) & ~3; | |
while (Index < Count) { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_FIFO_START, TRUE, sizeof (Data), &Data); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ReadFifo: failed to read FIFO, Status:%r\n", Status)); | |
return Status; | |
} | |
if (Trb->UseBE) { | |
*(UINT32 *)((UINTN)Trb->Data + Descending) = SwapBytes32 (Data); | |
Descending = Descending - 4; | |
} else { | |
*(UINT32 *)((UINTN)Trb->Data + Ascending) = Data; | |
Ascending += 4; | |
} | |
Index += 4; | |
Received += 4; | |
} // while | |
} // if | |
} while (((Intsts & DW_MMC_INT_CMD_DONE) == 0) || (Received < Trb->DataLen)); | |
// Clear RINTSTS | |
Intsts = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (Intsts), &Intsts); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "ReadFifo: failed to write RINTSTS, Status:%r\n", Status)); | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a new TRB for the SD/MMC cmd request. | |
@param[in] Private A pointer to the DW_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. | |
**/ | |
DW_MMC_HC_TRB * | |
DwMmcCreateTrb ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN UINT8 Slot, | |
IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, | |
IN EFI_EVENT Event | |
) | |
{ | |
DW_MMC_HC_TRB *Trb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
EFI_PCI_IO_PROTOCOL_OPERATION Flag; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINTN MapLength; | |
Trb = AllocateZeroPool (sizeof (DW_MMC_HC_TRB)); | |
if (Trb == NULL) { | |
return NULL; | |
} | |
Trb->Signature = DW_MMC_HC_TRB_SIG; | |
Trb->Slot = Slot; | |
Trb->BlockSize = 0x200; | |
Trb->Packet = Packet; | |
Trb->Event = Event; | |
Trb->Started = FALSE; | |
Trb->Timeout = Packet->Timeout; | |
Trb->Private = Private; | |
if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { | |
Trb->Data = Packet->InDataBuffer; | |
Trb->DataLen = Packet->InTransferLength; | |
Trb->Read = TRUE; | |
ZeroMem (Trb->Data, Trb->DataLen); | |
} 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 (((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->Read) { | |
Flag = EfiPciIoOperationBusMasterWrite; | |
} else { | |
Flag = EfiPciIoOperationBusMasterRead; | |
} | |
PciIo = Private->PciIo; | |
#if 0 | |
if ((Private->Slot[Trb->Slot].CardType == SdCardType) && | |
(Trb->DataLen != 0) && | |
(Trb->DataLen <= DWMMC_FIFO_THRESHOLD)) { | |
#else | |
if (Private->Slot[Trb->Slot].CardType == SdCardType) { | |
#endif | |
Trb->UseFifo = TRUE; | |
} else { | |
Trb->UseFifo = FALSE; | |
if (Trb->DataLen) { | |
MapLength = Trb->DataLen; | |
Status = PciIo->Map ( | |
PciIo, | |
Flag, | |
Trb->Data, | |
&MapLength, | |
&Trb->DataPhy, | |
&Trb->DataMap | |
); | |
if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { | |
Status = EFI_BAD_BUFFER_SIZE; | |
goto Error; | |
} | |
Status = BuildDmaDescTable (Trb); | |
if (EFI_ERROR (Status)) { | |
PciIo->Unmap (PciIo, Trb->DataMap); | |
goto Error; | |
} | |
Status = DwMmcHcStartDma (Private, Trb); | |
if (EFI_ERROR (Status)) { | |
PciIo->Unmap (PciIo, Trb->DataMap); | |
goto Error; | |
} | |
} | |
} | |
} // TuningBlock | |
if (Event != NULL) { | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
InsertTailList (&Private->Queue, &Trb->TrbList); | |
gBS->RestoreTPL (OldTpl); | |
} | |
return Trb; | |
Error: | |
//DwMmcFreeTrb (Trb); | |
return NULL; | |
} | |
/** | |
Free the resource used by the TRB. | |
@param[in] Trb The pointer to the DW_MMC_HC_TRB instance. | |
**/ | |
VOID | |
DwMmcFreeTrb ( | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PciIo = Trb->Private->PciIo; | |
if (Trb->DmaMap != NULL) { | |
PciIo->Unmap ( | |
PciIo, | |
Trb->DmaMap | |
); | |
} | |
#if 0 | |
// Free is handled in Unmap(). | |
if (Trb->DmaDesc != NULL) { | |
PciIo->FreeBuffer ( | |
PciIo, | |
Trb->DmaDescPages, | |
Trb->DmaDesc | |
); | |
} | |
#endif | |
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 DW_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the DW_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 | |
DwMmcCheckTrbEnv ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
/** | |
Wait for the env to be ready for execute specified TRB. | |
@param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the DW_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 | |
DwMmcWaitTrbEnv ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_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 = DwMmcCheckTrbEnv (Private, Trb); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} | |
EFI_STATUS | |
DwEmmcExecTrb ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 Cmd; | |
UINT32 MmcStatus; | |
UINT32 IntStatus; | |
UINT32 Argument; | |
UINT32 ErrMask; | |
UINT32 Timeout; | |
Packet = Trb->Packet; | |
PciIo = Trb->Private->PciIo; | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
// Wait until MMC is idle | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while (MmcStatus & DW_MMC_STS_DATA_BUSY); | |
IntStatus = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); | |
if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) || | |
(Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) { | |
switch (Packet->SdMmcCmdBlk->CommandIndex) { | |
case EMMC_SET_RELATIVE_ADDR: | |
Cmd |= BIT_CMD_SEND_INIT; | |
break; | |
case EMMC_SEND_STATUS: | |
Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE; | |
break; | |
case EMMC_STOP_TRANSMISSION: | |
Cmd |= BIT_CMD_STOP_ABORT_CMD; | |
break; | |
} | |
if (Packet->InTransferLength) { | |
Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_READ; | |
} else if (Packet->OutTransferLength) { | |
Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE; | |
} | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; | |
} else { | |
switch (Packet->SdMmcCmdBlk->CommandIndex) { | |
case EMMC_GO_IDLE_STATE: | |
Cmd |= BIT_CMD_SEND_INIT; | |
break; | |
case EMMC_SEND_OP_COND: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT; | |
break; | |
case EMMC_ALL_SEND_CID: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE | | |
BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT; | |
break; | |
} | |
} | |
switch (Packet->SdMmcCmdBlk->ResponseType) { | |
case SdMmcResponseTypeR2: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_LONG_RESPONSE; | |
break; | |
case SdMmcResponseTypeR3: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT; | |
break; | |
} | |
Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START; | |
Argument = Packet->SdMmcCmdBlk->CommandArgument; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMDARG, FALSE, sizeof (Argument), &Argument); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | | |
DW_MMC_INT_RCRC | DW_MMC_INT_RE; | |
ErrMask |= DW_MMC_INT_DCRC | DW_MMC_INT_DRT | DW_MMC_INT_SBE; | |
do { | |
Timeout = 10000; | |
if (--Timeout == 0) { | |
break; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IntStatus & ErrMask) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) == 0)) { | |
// Transfer Not Done | |
MicroSecondDelay (10); | |
continue; | |
} | |
MicroSecondDelay (10); | |
} while (!(IntStatus & DW_MMC_INT_CMD_DONE)); | |
switch (Packet->SdMmcCmdBlk->ResponseType) { | |
case SdMmcResponseTypeR1: | |
case SdMmcResponseTypeR1b: | |
case SdMmcResponseTypeR3: | |
case SdMmcResponseTypeR4: | |
case SdMmcResponseTypeR5: | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
break; | |
case SdMmcResponseTypeR2: | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP1, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp1), &Packet->SdMmcStatusBlk->Resp1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP2, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp2), &Packet->SdMmcStatusBlk->Resp2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP3, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp3), &Packet->SdMmcStatusBlk->Resp3); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
break; | |
} | |
// | |
// The workaround on EMMC_SEND_CSD is used to be compatible with SDHC. | |
// | |
if (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_CSD) { | |
{ | |
UINT32 Buf[4]; | |
ZeroMem (Buf, sizeof (Buf)); | |
CopyMem ((UINT8 *)Buf, (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, sizeof (Buf) - 1); | |
CopyMem ((UINT8 *)&Packet->SdMmcStatusBlk->Resp0, (UINT8 *)Buf, sizeof (Buf) - 1); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
DwSdExecTrb ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT32 Cmd; | |
UINT32 MmcStatus; | |
UINT32 IntStatus; | |
UINT32 Argument; | |
UINT32 ErrMask; | |
UINT32 Timeout; | |
UINT32 Idsts; | |
UINT32 BytCnt; | |
UINT32 BlkSize; | |
Packet = Trb->Packet; | |
PciIo = Trb->Private->PciIo; | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
// Wait until MMC is idle | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_STATUS, TRUE, sizeof (MmcStatus), &MmcStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while (MmcStatus & DW_MMC_STS_DATA_BUSY); | |
IntStatus = ~0; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, FALSE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); | |
if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) || | |
(Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) { | |
switch (Packet->SdMmcCmdBlk->CommandIndex) { | |
case SD_SET_RELATIVE_ADDR: | |
Cmd |= BIT_CMD_SEND_INIT; | |
break; | |
case SD_STOP_TRANSMISSION: | |
Cmd |= BIT_CMD_STOP_ABORT_CMD; | |
break; | |
case SD_SEND_SCR: | |
Trb->UseBE = TRUE; | |
break; | |
} | |
if (Packet->InTransferLength) { | |
Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_READ; | |
} else if (Packet->OutTransferLength) { | |
Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | BIT_CMD_WRITE; | |
} | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; | |
} else { | |
switch (Packet->SdMmcCmdBlk->CommandIndex) { | |
case SD_GO_IDLE_STATE: | |
Cmd |= BIT_CMD_SEND_INIT; | |
break; | |
} | |
} | |
switch (Packet->SdMmcCmdBlk->ResponseType) { | |
case SdMmcResponseTypeR2: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_LONG_RESPONSE; | |
break; | |
case SdMmcResponseTypeR3: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT; | |
break; | |
case SdMmcResponseTypeR1b: | |
case SdMmcResponseTypeR4: | |
case SdMmcResponseTypeR6: | |
case SdMmcResponseTypeR7: | |
Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; | |
break; | |
} | |
Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START; | |
if (Trb->UseFifo == TRUE) { | |
BytCnt = Packet->InTransferLength; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BYTCNT, FALSE, sizeof (BytCnt), &BytCnt); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Packet->InTransferLength > DW_MMC_BLOCK_SIZE) { | |
BlkSize = DW_MMC_BLOCK_SIZE; | |
} else { | |
BlkSize = Packet->InTransferLength; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_BLKSIZ, FALSE, sizeof (BlkSize), &BlkSize); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set block size fails: %r\n", Status)); | |
return Status; | |
} | |
} | |
Argument = Packet->SdMmcCmdBlk->CommandArgument; | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMDARG, FALSE, sizeof (Argument), &Argument); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ArmDataSynchronizationBarrier (); | |
ArmInstructionSynchronizationBarrier (); | |
ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | | |
DW_MMC_INT_RCRC | DW_MMC_INT_RE; | |
ErrMask |= DW_MMC_INT_DRT | DW_MMC_INT_SBE; | |
if (Packet->InTransferLength || Packet->OutTransferLength) { | |
ErrMask |= DW_MMC_INT_DCRC; | |
} | |
if (Trb->UseFifo == TRUE) { | |
Status = ReadFifo (Trb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else { | |
Timeout = 10000; | |
do { | |
if (--Timeout == 0) { | |
break; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RINTSTS, TRUE, sizeof (IntStatus), &IntStatus); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (IntStatus & ErrMask) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) == 0)) { | |
// Transfer not Done | |
MicroSecondDelay (10); | |
continue; | |
} | |
MicroSecondDelay (10); | |
} while (!(IntStatus & DW_MMC_INT_CMD_DONE)); | |
if (Packet->InTransferLength) { | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while ((Idsts & DW_MMC_IDSTS_RI) == 0); | |
Status = DwMmcHcStopDma (Private, Trb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else if (Packet->OutTransferLength) { | |
do { | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while ((Idsts & DW_MMC_IDSTS_TI) == 0); | |
Status = DwMmcHcStopDma (Private, Trb); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} // Packet->InTransferLength | |
} // UseFifo | |
switch (Packet->SdMmcCmdBlk->ResponseType) { | |
case SdMmcResponseTypeR1: | |
case SdMmcResponseTypeR1b: | |
case SdMmcResponseTypeR3: | |
case SdMmcResponseTypeR4: | |
case SdMmcResponseTypeR5: | |
case SdMmcResponseTypeR6: | |
case SdMmcResponseTypeR7: | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
break; | |
case SdMmcResponseTypeR2: | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP0, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp0), &Packet->SdMmcStatusBlk->Resp0); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP1, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp1), &Packet->SdMmcStatusBlk->Resp1); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP2, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp2), &Packet->SdMmcStatusBlk->Resp2); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = DwMmcHcRwMmio (PciIo, Trb->Slot, DW_MMC_RESP3, TRUE, sizeof (Packet->SdMmcStatusBlk->Resp3), &Packet->SdMmcStatusBlk->Resp3); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
break; | |
} | |
// | |
// The workaround on SD_SEND_CSD is used to be compatible with SDHC. | |
// | |
if (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_CSD) { | |
{ | |
UINT32 Buf[4]; | |
ZeroMem (Buf, sizeof (Buf)); | |
CopyMem ((UINT8 *)Buf, (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, sizeof (Buf) - 1); | |
CopyMem ((UINT8 *)&Packet->SdMmcStatusBlk->Resp0, (UINT8 *)Buf, sizeof (Buf) - 1); | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Execute the specified TRB. | |
@param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the DW_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 | |
DwMmcExecTrb ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status = EFI_SUCCESS; | |
UINT32 Slot; | |
Slot = Trb->Slot; | |
if (Private->Slot[Slot].CardType == EmmcCardType) { | |
Status = DwEmmcExecTrb (Private, Trb); | |
} else if (Private->Slot[Slot].CardType == SdCardType) { | |
Status = DwSdExecTrb (Private, Trb); | |
} else { | |
ASSERT (0); | |
} | |
return Status; | |
} | |
/** | |
Check the TRB execution result. | |
@param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the DW_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 | |
DwMmcCheckTrbResult ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_MMC_HC_TRB *Trb | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; | |
UINT32 Idsts; | |
Packet = Trb->Packet; | |
if (Trb->UseFifo == TRUE) { | |
return EFI_SUCCESS; | |
} | |
if (Packet->InTransferLength) { | |
do { | |
Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while ((Idsts & BIT1) == 0); | |
} else if (Packet->OutTransferLength) { | |
do { | |
Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, TRUE, sizeof (Idsts), &Idsts); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} while ((Idsts & BIT0) == 0); | |
} else { | |
return EFI_SUCCESS; | |
} | |
Idsts = ~0; | |
Status = DwMmcHcRwMmio (Private->PciIo, Trb->Slot, DW_MMC_IDSTS, FALSE, sizeof (Idsts), &Idsts); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Wait for the TRB execution result. | |
@param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance. | |
@param[in] Trb The pointer to the DW_MMC_HC_TRB instance. | |
@retval EFI_SUCCESS The TRB is executed successfully. | |
@retval Others Some erros happen when executing this request. | |
**/ | |
EFI_STATUS | |
DwMmcWaitTrbResult ( | |
IN DW_MMC_HC_PRIVATE_DATA *Private, | |
IN DW_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 = DwMmcCheckTrbResult (Private, Trb); | |
if (Status != EFI_NOT_READY) { | |
return Status; | |
} | |
// | |
// Stall for 1 microsecond. | |
// | |
gBS->Stall (1); | |
Timeout--; | |
} | |
return EFI_TIMEOUT; | |
} |