Skip to content
Permalink
Browse files
Platform/RaspberryPi/Drivers: Add SD/(e)MMC card detection
The Raspberry Pi 3 and Pi 4 platforms (with latest EEPROM) can boot
straight from USB, without the need for an SD card being present.
However, the IsCardPresent () calls from the ArasanMmcHost and SdHost
drivers are currently hardwired to return TRUE, which results in
straight to USB boot failing due to the SD drivers looping on
errors while trying to poke at a non-existent card...

Ideally, we would use the Card Detect signal from the uSD slot, to
report on the presence or absence of a card, but the Raspberry Pi
Foundation did not wire those signals in the Pi 2 and subsequent
models, leaving us with only potentially interfering SD commands
as means to perform card detection.

As a result of this, we are left with no other choice but limit
detection to occurring only once, prior to formal SD card init,
and then return the detected value for subsequent calls. This,
however, is more than good enough for the intended purpose, which
is to allow straight to USB boot. The sequence is a simplified
variant of the identification code in MmcDxe.

Tested on Raspberry Pi 2B, 3B and CM3 (for both SD controllers)
and Pi 4 (for Arasan, as that's the only controller available today)

Addresses pftf/RPi3#13, pftf/RPi3#14, pftf/RPi4#37.

Co-authored-by: Andrei Warkentin <andrey.warkentin@gmail.com>
Signed-off-by: Pete Batard <pete@akeo.ie>
Reviewed-by: Andrei Warkentin <andrey.warkentin@gmail.com>
Tested-by: Andrei Warkentin <andrey.warkentin@gmail.com>
Reviewed-by: Leif Lindholm <leif@nuviainc.com>
  • Loading branch information
2 people authored and leiflindholm committed Jul 14, 2020
1 parent 5d2d48e commit 225426271bb03bc83037b70ede0673585db01324
Showing 3 changed files with 154 additions and 17 deletions.
@@ -11,7 +11,8 @@

#define DEBUG_MMCHOST_SD DEBUG_VERBOSE

BOOLEAN PreviousIsCardPresent = FALSE;
STATIC BOOLEAN mCardIsPresent = FALSE;
STATIC CARD_DETECT_STATE mCardDetectState = CardDetectRequired;
UINT32 LastExecutedCommand = (UINT32) -1;

STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
@@ -239,14 +240,6 @@ CalculateClockFrequencyDivisor (
return EFI_SUCCESS;
}

BOOLEAN
MMCIsCardPresent (
IN EFI_MMC_HOST_PROTOCOL *This
)
{
return TRUE;
}

BOOLEAN
MMCIsReadOnly (
IN EFI_MMC_HOST_PROTOCOL *This
@@ -418,6 +411,11 @@ MMCNotifyState (

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:
{
@@ -489,6 +487,78 @@ MMCNotifyState (
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,
@@ -64,6 +64,8 @@ STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata",
"genpulses", "writewait2", "?",
"startpowdown" };
#endif /* NDEBUG */
STATIC BOOLEAN mCardIsPresent = FALSE;
STATIC CARD_DETECT_STATE mCardDetectState = CardDetectRequired;
STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0);

STATIC inline BOOLEAN
@@ -264,14 +266,6 @@ SdHostSetClockFrequency (
return Status;
}

STATIC BOOLEAN
SdIsCardPresent (
IN EFI_MMC_HOST_PROTOCOL *This
)
{
return TRUE;
}

STATIC BOOLEAN
SdIsReadOnly (
IN EFI_MMC_HOST_PROTOCOL *This
@@ -639,6 +633,11 @@ SdNotifyState (
{
DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", 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, "MmcHwInitializationState\n", State));
@@ -718,6 +717,68 @@ SdNotifyState (
return EFI_SUCCESS;
}

STATIC BOOLEAN
SdIsCardPresent (
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 = SdNotifyState (This, MmcHwInitializationState);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "SdIsCardPresent: Error MmcHwInitializationState, Status=%r.\n", Status));
// If we failed init, go back to requiring card detection
mCardDetectState = CardDetectRequired;
return FALSE;
}

Status = SdSendCommand (This, MMC_CMD0, 0);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "SdIsCardPresent: CMD0 Error, Status=%r.\n", Status));
goto out;
}

//
// CMD8 should tell us if an SD card is present.
//
Status = SdSendCommand (This, MMC_CMD8, CMD8_SD_ARG);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "SdIsCardPresent: Maybe SD card detected.\n"));
mCardIsPresent = TRUE;
goto out;
}

//
// MMC/eMMC won't accept CMD8, but we can try CMD1.
//
Status = SdSendCommand (This, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "SdIsCardPresent: Maybe MMC card detected.\n"));
mCardIsPresent = TRUE;
goto out;
}

DEBUG ((DEBUG_INFO, "SdIsCardPresent: Not detected, Status=%r.\n", Status));

out:
mCardDetectState = CardDetectCompleted;
return mCardIsPresent;
}

BOOLEAN
SdIsMultiBlock (
IN EFI_MMC_HOST_PROTOCOL *This
@@ -82,6 +82,12 @@ typedef enum _MMC_STATE {
MmcDisconnectState,
} MMC_STATE;

typedef enum _CARD_DETECT_STATE {
CardDetectRequired = 0,
CardDetectInProgress,
CardDetectCompleted
} CARD_DETECT_STATE;

#define EMMCBACKWARD (0)
#define EMMCHS26 (1 << 0) // High-Speed @26MHz at rated device voltages
#define EMMCHS52 (1 << 1) // High-Speed @52MHz at rated device voltages

0 comments on commit 2254262

Please sign in to comment.