/** @file
|
*
|
* Copyright (c) 2017, Andrei Warkentin <andrey.warkentin@gmail.com>
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include "ArasanMmcHostDxe.h"
|
|
#define DEBUG_MMCHOST_SD DEBUG_VERBOSE
|
|
STATIC BOOLEAN mCardIsPresent = FALSE;
|
STATIC CARD_DETECT_STATE mCardDetectState = CardDetectRequired;
|
UINT32 LastExecutedCommand = (UINT32) -1;
|
|
STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
|
STATIC UINTN mMmcHsBase;
|
|
STATIC
|
UINT32
|
EFIAPI
|
SdMmioWrite32 (
|
IN UINTN Address,
|
IN UINT32 Value
|
)
|
{
|
UINT32 ret;
|
ret = (UINT32)MmioWrite32 (Address, Value);
|
// There is a bug about clock domain crossing on writes, delay to avoid it
|
gBS->Stall (STALL_AFTER_REG_WRITE_US);
|
return ret;
|
}
|
|
STATIC
|
UINT32
|
EFIAPI
|
SdMmioOr32 (
|
IN UINTN Address,
|
IN UINT32 OrData
|
)
|
{
|
return SdMmioWrite32 (Address, MmioRead32 (Address) | OrData);
|
}
|
|
STATIC
|
UINT32
|
EFIAPI
|
SdMmioAnd32 (
|
IN UINTN Address,
|
IN UINT32 AndData
|
)
|
{
|
return SdMmioWrite32 (Address, MmioRead32 (Address) & AndData);
|
}
|
|
STATIC
|
UINT32
|
EFIAPI
|
SdMmioAndThenOr32 (
|
IN UINTN Address,
|
IN UINT32 AndData,
|
IN UINT32 OrData
|
)
|
{
|
return SdMmioWrite32 (Address, (MmioRead32 (Address) & AndData) | OrData);
|
}
|
|
|
/**
|
These SD commands are optional, according to the SD Spec
|
**/
|
BOOLEAN
|
IgnoreCommand (
|
UINT32 Command
|
)
|
{
|
switch (Command) {
|
case MMC_CMD20:
|
return TRUE;
|
default:
|
return FALSE;
|
}
|
}
|
|
/**
|
Translates a generic SD command into the format used by the Arasan SD Host Controller
|
**/
|
UINT32
|
TranslateCommand (
|
UINT32 Command,
|
UINT32 Argument
|
)
|
{
|
UINT32 Translation = 0xffffffff;
|
|
if (LastExecutedCommand == CMD55) {
|
switch (Command) {
|
case MMC_CMD6:
|
Translation = ACMD6;
|
DEBUG ((DEBUG_MMCHOST_SD, "ACMD6\n"));
|
break;
|
case MMC_ACMD22:
|
Translation = ACMD22;
|
DEBUG ((DEBUG_MMCHOST_SD, "ACMD22\n"));
|
break;
|
case MMC_ACMD41:
|
Translation = ACMD41;
|
DEBUG ((DEBUG_MMCHOST_SD, "ACMD41\n"));
|
break;
|
case MMC_ACMD51:
|
Translation = ACMD51;
|
DEBUG ((DEBUG_MMCHOST_SD, "ACMD51\n"));
|
break;
|
default:
|
DEBUG ((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized App command: %d\n", Command));
|
}
|
} else {
|
switch (Command) {
|
case MMC_CMD0:
|
Translation = CMD0;
|
break;
|
case MMC_CMD1:
|
Translation = CMD1;
|
break;
|
case MMC_CMD2:
|
Translation = CMD2;
|
break;
|
case MMC_CMD3:
|
Translation = CMD3;
|
break;
|
case MMC_CMD5:
|
Translation = CMD5;
|
break;
|
case MMC_CMD6:
|
Translation = CMD6;
|
break;
|
case MMC_CMD7:
|
Translation = CMD7;
|
break;
|
case MMC_CMD8: {
|
if (Argument == CMD8_SD_ARG) {
|
Translation = CMD8_SD;
|
DEBUG ((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n"));
|
} else {
|
ASSERT (Argument == CMD8_MMC_ARG);
|
Translation = CMD8_MMC;
|
DEBUG ((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n"));
|
}
|
break;
|
}
|
case MMC_CMD9:
|
Translation = CMD9;
|
break;
|
case MMC_CMD11:
|
Translation = CMD11;
|
break;
|
case MMC_CMD12:
|
Translation = CMD12;
|
break;
|
case MMC_CMD13:
|
Translation = CMD13;
|
break;
|
case MMC_CMD16:
|
Translation = CMD16;
|
break;
|
case MMC_CMD17:
|
Translation = CMD17;
|
break;
|
case MMC_CMD18:
|
Translation = CMD18;
|
break;
|
case MMC_CMD23:
|
Translation = CMD23;
|
break;
|
case MMC_CMD24:
|
Translation = CMD24;
|
break;
|
case MMC_CMD25:
|
Translation = CMD25;
|
break;
|
case MMC_CMD55:
|
Translation = CMD55;
|
break;
|
default:
|
DEBUG ((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized Command: %d\n", Command));
|
}
|
}
|
|
return Translation;
|
}
|
|
/**
|
Repeatedly polls a register until its value becomes correct, or until MAX_RETRY_COUNT polls is reached
|
**/
|
EFI_STATUS
|
PollRegisterWithMask (
|
IN UINTN Register,
|
IN UINTN Mask,
|
IN UINTN ExpectedValue
|
)
|
{
|
UINTN RetryCount = 0;
|
|
while (RetryCount < MAX_RETRY_COUNT) {
|
if ((MmioRead32 (Register) & Mask) != ExpectedValue) {
|
RetryCount++;
|
gBS->Stall (STALL_AFTER_RETRY_US);
|
} else {
|
break;
|
}
|
}
|
|
if (RetryCount == MAX_RETRY_COUNT) {
|
return EFI_TIMEOUT;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
EFI_STATUS
|
SoftReset (
|
IN UINT32 Mask
|
)
|
{
|
DEBUG ((DEBUG_MMCHOST_SD, "SoftReset with mask 0x%x\n", Mask));
|
|
SdMmioOr32 (MMCHS_SYSCTL, Mask);
|
if (PollRegisterWithMask (MMCHS_SYSCTL, Mask, 0) == EFI_TIMEOUT) {
|
DEBUG ((DEBUG_ERROR, "Failed to SoftReset with mask 0x%x\n", Mask));
|
return EFI_TIMEOUT;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Calculate the clock divisor
|
**/
|
EFI_STATUS
|
CalculateClockFrequencyDivisor (
|
IN UINTN TargetFrequency,
|
OUT UINT32 *DivisorValue,
|
OUT UINTN *ActualFrequency
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 Divisor;
|
UINT32 BaseFrequency = 0;
|
|
if (PcdGet32 (PcdSdIsArasan)) {
|
Status = mFwProtocol->GetClockRate (RPI_MBOX_CLOCK_RATE_EMMC, &BaseFrequency);
|
} else {
|
Status = mFwProtocol->GetClockRate (RPI_MBOX_CLOCK_RATE_EMMC2, &BaseFrequency);
|
}
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Couldn't get RPI_MBOX_CLOCK_RATE_EMMC\n"));
|
return Status;
|
}
|
|
ASSERT (BaseFrequency != 0);
|
Divisor = BaseFrequency / TargetFrequency;
|
|
// Arasan controller is based on 3.0 spec so the div is multiple of 2
|
// Actual Frequency = BaseFequency/(Div*2)
|
Divisor /= 2;
|
|
if ((TargetFrequency < BaseFrequency) &&
|
(TargetFrequency * 2 * Divisor != BaseFrequency)) {
|
Divisor += 1;
|
}
|
|
if (Divisor > MAX_DIVISOR_VALUE) {
|
Divisor = MAX_DIVISOR_VALUE;
|
}
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: BaseFrequency 0x%x Divisor 0x%x\n", BaseFrequency, Divisor));
|
|
*DivisorValue = (Divisor & 0xFF) << 8;
|
Divisor >>= 8;
|
*DivisorValue |= (Divisor & 0x03) << 6;
|
|
if (ActualFrequency) {
|
if (Divisor == 0) {
|
*ActualFrequency = BaseFrequency;
|
} else {
|
*ActualFrequency = BaseFrequency / Divisor;
|
*ActualFrequency >>= 1;
|
}
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: *ActualFrequency 0x%x\n", *ActualFrequency));
|
}
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: *DivisorValue 0x%x\n", *DivisorValue));
|
|
return EFI_SUCCESS;
|
}
|
|
BOOLEAN
|
MMCIsReadOnly (
|
IN EFI_MMC_HOST_PROTOCOL *This
|
)
|
{
|
BOOLEAN IsReadOnly = !((MmioRead32 (MMCHS_PRES_STATE) & WRITE_PROTECT_OFF) == WRITE_PROTECT_OFF);
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCIsReadOnly(): %d\n", IsReadOnly));
|
return IsReadOnly;
|
}
|
|
EFI_STATUS
|
MMCBuildDevicePath (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN EFI_DEVICE_PATH_PROTOCOL **DevicePath
|
)
|
{
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
|
EFI_GUID DevicePathGuid = EFI_CALLER_ID_GUID;
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCBuildDevicePath()\n"));
|
|
NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH));
|
CopyGuid (&((VENDOR_DEVICE_PATH*) NewDevicePathNode)->Guid, &DevicePathGuid);
|
*DevicePath = NewDevicePathNode;
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
MMCSendCommand (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN MMC_CMD MmcCmd,
|
IN UINT32 Argument
|
)
|
{
|
UINTN MmcStatus;
|
UINTN RetryCount = 0;
|
UINTN CmdSendOKMask;
|
EFI_STATUS Status = EFI_SUCCESS;
|
BOOLEAN IsAppCmd = (LastExecutedCommand == CMD55);
|
BOOLEAN IsDATCmd = FALSE;
|
BOOLEAN IsADTCCmd = FALSE;
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCSendCommand(MmcCmd: %08x, Argument: %08x)\n", MmcCmd, Argument));
|
|
if (IgnoreCommand (MmcCmd)) {
|
return EFI_SUCCESS;
|
}
|
|
MmcCmd = TranslateCommand (MmcCmd, Argument);
|
if (MmcCmd == 0xffffffff) {
|
return EFI_UNSUPPORTED;
|
}
|
|
if ((MmcCmd & CMD_R1_ADTC) == CMD_R1_ADTC) {
|
IsADTCCmd = TRUE;
|
}
|
if (((MmcCmd & CMD_R1B) == CMD_R1B &&
|
/*
|
* Abort commands don't get inhibited by DAT.
|
*/
|
(MmcCmd & TYPE (CMD_TYPE_ABORT)) != TYPE (CMD_TYPE_ABORT)) ||
|
IsADTCCmd ||
|
/*
|
* We want to detect BRR/BWR change.
|
*/
|
MmcCmd == CMD_SEND_STATUS) {
|
IsDATCmd = TRUE;
|
}
|
|
CmdSendOKMask = CMDI_MASK;
|
if (IsDATCmd) {
|
CmdSendOKMask |= DATI_MASK;
|
}
|
|
if (PollRegisterWithMask (MMCHS_PRES_STATE,
|
CmdSendOKMask, 0) == EFI_TIMEOUT) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): not ready for MMC_CMD%u PresState 0x%x MmcStatus 0x%x\n",
|
__FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd),
|
MmioRead32 (MMCHS_PRES_STATE), MmioRead32 (MMCHS_INT_STAT)));
|
Status = EFI_TIMEOUT;
|
goto Exit;
|
}
|
|
if (IsAppCmd && MmcCmd == ACMD22) {
|
SdMmioWrite32 (MMCHS_BLK, 4);
|
} else if (IsAppCmd && MmcCmd == ACMD51) {
|
SdMmioWrite32 (MMCHS_BLK, 8);
|
} else if (!IsAppCmd && MmcCmd == CMD6) {
|
SdMmioWrite32 (MMCHS_BLK, 64);
|
} else if (IsADTCCmd) {
|
SdMmioWrite32 (MMCHS_BLK, BLEN_512BYTES);
|
}
|
|
// Set Data timeout counter value to max value.
|
SdMmioAndThenOr32 (MMCHS_SYSCTL, (UINT32) ~DTO_MASK, DTO_VAL);
|
|
//
|
// Clear Interrupt Status Register, but not the Card Inserted bit
|
// to avoid messing with card detection logic.
|
//
|
SdMmioWrite32 (MMCHS_INT_STAT, ALL_EN & ~(CARD_INS));
|
|
// Set command argument register
|
SdMmioWrite32 (MMCHS_ARG, Argument);
|
|
// Send the command
|
SdMmioWrite32 (MMCHS_CMD, MmcCmd);
|
|
// Check for the command status.
|
while (RetryCount < MAX_RETRY_COUNT) {
|
MmcStatus = MmioRead32 (MMCHS_INT_STAT);
|
|
// Read status of command response
|
if ((MmcStatus & ERRI) != 0) {
|
//
|
// CMD5 (CMD_IO_SEND_OP_COND) is only valid for SDIO
|
// cards and thus expected to fail.
|
//
|
if (MmcCmd != CMD_IO_SEND_OP_COND) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): MMC_CMD%u ERRI MmcStatus 0x%x\n",
|
__FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd), MmcStatus));
|
}
|
|
SoftReset (SRC);
|
|
Status = EFI_DEVICE_ERROR;
|
goto Exit;
|
}
|
|
// Check if command is completed.
|
if ((MmcStatus & CC) == CC) {
|
SdMmioWrite32 (MMCHS_INT_STAT, CC);
|
break;
|
}
|
|
RetryCount++;
|
gBS->Stall (STALL_AFTER_RETRY_US);
|
}
|
|
gBS->Stall (STALL_AFTER_SEND_CMD_US);
|
|
if (RetryCount == MAX_RETRY_COUNT) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): MMC_CMD%u completion TIMEOUT PresState 0x%x MmcStatus 0x%x\n",
|
__FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd),
|
MmioRead32 (MMCHS_PRES_STATE), MmcStatus));
|
Status = EFI_TIMEOUT;
|
goto Exit;
|
}
|
|
Exit:
|
if (EFI_ERROR (Status)) {
|
LastExecutedCommand = (UINT32) -1;
|
} else {
|
LastExecutedCommand = MmcCmd;
|
}
|
return Status;
|
}
|
|
EFI_STATUS
|
MMCNotifyState (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN MMC_STATE State
|
)
|
{
|
EFI_STATUS Status;
|
UINTN ClockFrequency;
|
UINT32 Divisor;
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State));
|
|
// Stall all operations except init until card detection has occurred.
|
if (State != MmcHwInitializationState && mCardDetectState != CardDetectCompleted) {
|
return EFI_NOT_READY;
|
}
|
|
switch (State) {
|
case MmcHwInitializationState:
|
{
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: current divisor %x\n", MmioRead32(MMCHS_SYSCTL)));
|
|
Status = SoftReset (SRA);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: CAP %X CAPH %X\n", MmioRead32(MMCHS_CAPA),MmioRead32(MMCHS_CUR_CAPA)));
|
|
// Lets switch to card detect test mode.
|
SdMmioOr32 (MMCHS_HCTL, BIT7|BIT6);
|
|
// set card voltage
|
SdMmioAnd32 (MMCHS_HCTL, ~SDBP_ON);
|
SdMmioAndThenOr32 (MMCHS_HCTL, (UINT32) ~SDBP_MASK, SDVS_3_3_V);
|
SdMmioOr32 (MMCHS_HCTL, SDBP_ON);
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: AC12 %X HCTL %X\n", MmioRead32(MMCHS_AC12),MmioRead32(MMCHS_HCTL)));
|
|
// First turn off the clock
|
SdMmioAnd32 (MMCHS_SYSCTL, ~CEN);
|
|
// Attempt to set the clock to 400Khz which is the expected initialization speed
|
Status = CalculateClockFrequencyDivisor (400000, &Divisor, NULL);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Fail to initialize SD clock\n"));
|
return Status;
|
}
|
|
// Set Data Timeout Counter value, set clock frequency, enable internal clock
|
SdMmioOr32 (MMCHS_SYSCTL, DTO_VAL | Divisor | CEN | ICS | ICE);
|
SdMmioOr32 (MMCHS_HCTL, SDBP_ON);
|
// wait for ICS
|
while ((MmioRead32 (MMCHS_SYSCTL) & ICS_MASK) != ICS);
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: AC12 %X HCTL %X\n", MmioRead32(MMCHS_AC12),MmioRead32(MMCHS_HCTL)));
|
|
// Enable interrupts
|
SdMmioWrite32 (MMCHS_IE, ALL_EN);
|
}
|
break;
|
case MmcIdleState:
|
break;
|
case MmcReadyState:
|
break;
|
case MmcIdentificationState:
|
break;
|
case MmcStandByState:
|
ClockFrequency = 25000000;
|
|
// First turn off the clock
|
SdMmioAnd32 (MMCHS_SYSCTL, ~CEN);
|
|
Status = CalculateClockFrequencyDivisor (ClockFrequency, &Divisor, NULL);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MmcStandByState(): Fail to initialize SD clock to %u Hz\n",
|
ClockFrequency));
|
return Status;
|
}
|
|
// Setup new divisor
|
SdMmioAndThenOr32 (MMCHS_SYSCTL, (UINT32) ~CLKD_MASK, Divisor);
|
|
// Wait for the clock to stabilise
|
while ((MmioRead32 (MMCHS_SYSCTL) & ICS_MASK) != ICS);
|
|
// Set Data Timeout Counter value, set clock frequency, enable internal clock
|
SdMmioOr32 (MMCHS_SYSCTL, CEN);
|
break;
|
case MmcTransferState:
|
break;
|
case MmcSendingDataState:
|
break;
|
case MmcReceiveDataState:
|
break;
|
case MmcProgrammingState:
|
break;
|
case MmcDisconnectState:
|
case MmcInvalidState:
|
default:
|
DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Invalid State: %d\n", State));
|
ASSERT (0);
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
BOOLEAN
|
MMCIsCardPresent (
|
IN EFI_MMC_HOST_PROTOCOL *This
|
)
|
{
|
EFI_STATUS Status;
|
|
//
|
// If we are already in progress (we may get concurrent calls)
|
// or completed the detection, just return the current value.
|
//
|
if (mCardDetectState != CardDetectRequired) {
|
return mCardIsPresent;
|
}
|
|
mCardDetectState = CardDetectInProgress;
|
mCardIsPresent = FALSE;
|
|
//
|
// The two following commands should succeed even if no card is present.
|
//
|
Status = MMCNotifyState (This, MmcHwInitializationState);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
|
// If we failed init, go back to requiring card detection
|
mCardDetectState = CardDetectRequired;
|
return FALSE;
|
}
|
|
Status = MMCSendCommand (This, MMC_CMD0, 0);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "MMCIsCardPresent: CMD0 Error, Status=%r.\n", Status));
|
goto out;
|
}
|
|
//
|
// CMD8 should tell us if an SD card is present.
|
//
|
Status = MMCSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
|
if (!EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Maybe SD card detected.\n"));
|
mCardIsPresent = TRUE;
|
goto out;
|
}
|
|
//
|
// MMC/eMMC won't accept CMD8, but we can try CMD1.
|
//
|
Status = MMCSendCommand (This, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);
|
if (!EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Maybe MMC card detected.\n"));
|
mCardIsPresent = TRUE;
|
goto out;
|
}
|
|
//
|
// SDIO?
|
//
|
Status = MMCSendCommand (This, MMC_CMD5, 0);
|
if (!EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Maybe SDIO card detected.\n"));
|
mCardIsPresent = TRUE;
|
goto out;
|
}
|
|
DEBUG ((DEBUG_INFO, "MMCIsCardPresent: Not detected, Status=%r.\n", Status));
|
|
out:
|
mCardDetectState = CardDetectCompleted;
|
return mCardIsPresent;
|
}
|
|
EFI_STATUS
|
MMCReceiveResponse (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN MMC_RESPONSE_TYPE Type,
|
IN UINT32* Buffer
|
)
|
{
|
ASSERT (Buffer != NULL);
|
|
if (Type == MMC_RESPONSE_TYPE_R2) {
|
|
// 16-byte response
|
Buffer[0] = MmioRead32 (MMCHS_RSP10);
|
Buffer[1] = MmioRead32 (MMCHS_RSP32);
|
Buffer[2] = MmioRead32 (MMCHS_RSP54);
|
Buffer[3] = MmioRead32 (MMCHS_RSP76);
|
|
Buffer[3] <<= 8;
|
Buffer[3] |= (Buffer[2] >> 24) & 0xFF;
|
Buffer[2] <<= 8;
|
Buffer[2] |= (Buffer[1] >> 24) & 0xFF;
|
Buffer[1] <<= 8;
|
Buffer[1] |= (Buffer[0] >> 24) & 0xFF;
|
Buffer[0] <<= 8;
|
|
DEBUG ((DEBUG_MMCHOST_SD,
|
"ArasanMMCHost: MMCReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n",
|
Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3]));
|
} else {
|
// 4-byte response
|
Buffer[0] = MmioRead32 (MMCHS_RSP10);
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCReceiveResponse(Type: %08x), Buffer[0]: %08x\n", Type, Buffer[0]));
|
}
|
|
gBS->Stall (STALL_AFTER_REC_RESP_US);
|
if (LastExecutedCommand == CMD_STOP_TRANSMISSION) {
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: soft-resetting after CMD12\n"));
|
return SoftReset (SRC | SRD);
|
}
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
MMCReadBlockData (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN EFI_LBA Lba,
|
IN UINTN Length,
|
IN UINT32* Buffer
|
)
|
{
|
UINTN MmcStatus;
|
UINTN RemLength;
|
UINTN Count;
|
|
DEBUG ((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
|
__FUNCTION__, __LINE__, Lba, Length, Buffer));
|
|
if (Buffer == NULL) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): NULL Buffer\n", __FUNCTION__, __LINE__));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (Length % sizeof (UINT32) != 0) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): bad Length %u\n", __FUNCTION__, __LINE__, Length));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
RemLength = Length;
|
while (RemLength != 0) {
|
UINTN RetryCount = 0;
|
UINT32 BlockLen = MIN (RemLength, BLEN_512BYTES);
|
|
while (RetryCount < MAX_RETRY_COUNT) {
|
MmcStatus = MmioRead32 (MMCHS_INT_STAT);
|
if ((MmcStatus & BRR) != 0) {
|
SdMmioWrite32 (MMCHS_INT_STAT, BRR);
|
/*
|
* Data is ready.
|
*/
|
mFwProtocol->SetLed (TRUE);
|
for (Count = 0; Count < BlockLen; Count += 4, Buffer++) {
|
*Buffer = MmioRead32 (MMCHS_DATA);
|
}
|
|
mFwProtocol->SetLed (FALSE);
|
break;
|
}
|
|
gBS->Stall (STALL_AFTER_RETRY_US);
|
RetryCount++;
|
}
|
|
if (RetryCount == MAX_RETRY_COUNT) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n",
|
__FUNCTION__, __LINE__, Length - RemLength, Length, MmcStatus));
|
return EFI_TIMEOUT;
|
}
|
|
RemLength -= BlockLen;
|
gBS->Stall (STALL_AFTER_READ_US);
|
}
|
|
SdMmioWrite32 (MMCHS_INT_STAT, BRR);
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
MMCWriteBlockData (
|
IN EFI_MMC_HOST_PROTOCOL *This,
|
IN EFI_LBA Lba,
|
IN UINTN Length,
|
IN UINT32* Buffer
|
)
|
{
|
UINTN MmcStatus;
|
UINTN RemLength;
|
UINTN Count;
|
|
DEBUG ((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n",
|
__FUNCTION__, __LINE__, Lba, Length, Buffer));
|
|
if (Buffer == NULL) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): NULL Buffer\n", __FUNCTION__, __LINE__));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (Length % sizeof (UINT32) != 0) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): bad Length %u\n", __FUNCTION__, __LINE__, Length));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
RemLength = Length;
|
while (RemLength != 0) {
|
UINTN RetryCount = 0;
|
UINT32 BlockLen = MIN (RemLength, BLEN_512BYTES);
|
|
while (RetryCount < MAX_RETRY_COUNT) {
|
MmcStatus = MmioRead32 (MMCHS_INT_STAT);
|
if ((MmcStatus & BWR) != 0) {
|
SdMmioWrite32 (MMCHS_INT_STAT, BWR);
|
/*
|
* Can write data.
|
*/
|
mFwProtocol->SetLed (TRUE);
|
for (Count = 0; Count < BlockLen; Count += 4, Buffer++) {
|
SdMmioWrite32 (MMCHS_DATA, *Buffer);
|
}
|
|
mFwProtocol->SetLed (FALSE);
|
break;
|
}
|
|
gBS->Stall (STALL_AFTER_RETRY_US);
|
RetryCount++;
|
}
|
|
if (RetryCount == MAX_RETRY_COUNT) {
|
DEBUG ((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n",
|
__FUNCTION__, __LINE__, Length - RemLength, Length, MmcStatus));
|
return EFI_TIMEOUT;
|
}
|
|
RemLength -= BlockLen;
|
gBS->Stall (STALL_AFTER_WRITE_US);
|
}
|
|
SdMmioWrite32 (MMCHS_INT_STAT, BWR);
|
return EFI_SUCCESS;
|
}
|
|
BOOLEAN
|
MMCIsMultiBlock (
|
IN EFI_MMC_HOST_PROTOCOL *This
|
)
|
{
|
return TRUE;
|
}
|
|
EFI_MMC_HOST_PROTOCOL gMMCHost =
|
{
|
MMC_HOST_PROTOCOL_REVISION,
|
MMCIsCardPresent,
|
MMCIsReadOnly,
|
MMCBuildDevicePath,
|
MMCNotifyState,
|
MMCSendCommand,
|
MMCReceiveResponse,
|
MMCReadBlockData,
|
MMCWriteBlockData,
|
NULL,
|
MMCIsMultiBlock
|
};
|
|
EFI_STATUS
|
MMCInitialize (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_STATUS Status;
|
EFI_HANDLE Handle = NULL;
|
|
DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCInitialize()\n"));
|
|
if (PcdGet32 (PcdSdIsArasan)) {
|
DEBUG ((DEBUG_INFO, "SD is routed to Arasan\n"));
|
mMmcHsBase = MMCHS1_BASE;
|
} else if (RPI_MODEL == 4) {
|
DEBUG ((DEBUG_INFO, "SD is routed to emmc2\n"));
|
mMmcHsBase = MMCHS2_BASE;
|
} else {
|
DEBUG ((DEBUG_INFO, "SD is not routed to Arasan\n"));
|
return EFI_REQUEST_UNLOAD_IMAGE;
|
}
|
|
Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
|
(VOID**)&mFwProtocol);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
&Handle,
|
&gRaspberryPiMmcHostProtocolGuid,
|
&gMMCHost,
|
NULL
|
);
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
}
|