/** @file * * Copyright (c) 2017, Andrei Warkentin * Copyright (c) Microsoft Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-2-Clause-Patent * **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SDHOST_BLOCK_BYTE_LENGTH 512 // Driver Timing Parameters #define CMD_STALL_AFTER_POLL_US 1 #define CMD_MIN_POLL_TOTAL_TIME_US 100000 // 100ms #define CMD_MAX_POLL_COUNT (CMD_MIN_POLL_TOTAL_TIME_US / CMD_STALL_AFTER_POLL_US) #define CMD_MAX_RETRY_COUNT 3 #define CMD_STALL_AFTER_RETRY_US 20 // 20us #define FIFO_MAX_POLL_COUNT 1000000 #define STALL_TO_STABILIZE_US 10000 // 10ms #define IDENT_MODE_SD_CLOCK_FREQ_HZ 400000 // 400KHz // Macros adopted from MmcDxe internal header #define SDHOST_R0_READY_FOR_DATA BIT8 #define SDHOST_R0_CURRENTSTATE(Response) ((Response >> 9) & 0xF) #define DEBUG_MMCHOST_SD DEBUG_VERBOSE #define DEBUG_MMCHOST_SD_INFO DEBUG_INFO #define DEBUG_MMCHOST_SD_ERROR DEBUG_ERROR STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol; // Per Physical Layer Simplified Specs #ifndef NDEBUG STATIC CONST CHAR8* mStrSdState[] = { "idle", "ready", "ident", "stby", "tran", "data", "rcv", "prg", "dis", "ina" }; STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata", "writedata", "readwait", "readcrc", "writecrc", "writewait1", "powerdown", "powerup", "writestart1", "writestart2", "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 IsAppCmd ( VOID ) { return mLastGoodCmd == MMC_CMD55; } STATIC BOOLEAN IsBusyCmd ( IN UINT32 MmcCmd ) { if (IsAppCmd ()) { return FALSE; } return MmcCmd == MMC_CMD7 || MmcCmd == MMC_CMD12; } STATIC BOOLEAN IsWriteCmd ( IN UINT32 MmcCmd ) { if (IsAppCmd ()) { return FALSE; } return MmcCmd == MMC_CMD24 || MmcCmd == MMC_CMD25; } STATIC BOOLEAN IsReadCmd ( IN UINT32 MmcCmd, IN UINT32 Argument ) { if (MmcCmd == MMC_CMD8 && !IsAppCmd ()) { if (Argument == CMD8_MMC_ARG) { DEBUG ((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n")); return TRUE; } else { ASSERT (Argument == CMD8_SD_ARG); DEBUG ((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n")); return FALSE; } } return (MmcCmd == MMC_CMD6 && !IsAppCmd ()) || (MmcCmd == MMC_CMD17 && !IsAppCmd ()) || (MmcCmd == MMC_CMD18 && !IsAppCmd ()) || (MmcCmd == MMC_CMD13 && IsAppCmd ()) || (MmcCmd == MMC_ACMD22 && IsAppCmd ()) || (MmcCmd == MMC_ACMD51 && IsAppCmd ()); } STATIC VOID SdHostDumpRegisters ( VOID ) { DEBUG ((DEBUG_MMCHOST_SD, "SdHost: Registers Dump:\n")); DEBUG ((DEBUG_MMCHOST_SD, " CMD: 0x%8.8X\n", MmioRead32 (SDHOST_CMD))); DEBUG ((DEBUG_MMCHOST_SD, " ARG: 0x%8.8X\n", MmioRead32 (SDHOST_ARG))); DEBUG ((DEBUG_MMCHOST_SD, " TOUT: 0x%8.8X\n", MmioRead32 (SDHOST_TOUT))); DEBUG ((DEBUG_MMCHOST_SD, " CDIV: 0x%8.8X\n", MmioRead32 (SDHOST_CDIV))); DEBUG ((DEBUG_MMCHOST_SD, " RSP0: 0x%8.8X\n", MmioRead32 (SDHOST_RSP0))); DEBUG ((DEBUG_MMCHOST_SD, " RSP1: 0x%8.8X\n", MmioRead32 (SDHOST_RSP1))); DEBUG ((DEBUG_MMCHOST_SD, " RSP2: 0x%8.8X\n", MmioRead32 (SDHOST_RSP2))); DEBUG ((DEBUG_MMCHOST_SD, " RSP3: 0x%8.8X\n", MmioRead32 (SDHOST_RSP3))); DEBUG ((DEBUG_MMCHOST_SD, " HSTS: 0x%8.8X\n", MmioRead32 (SDHOST_HSTS))); DEBUG ((DEBUG_MMCHOST_SD, " VDD: 0x%8.8X\n", MmioRead32 (SDHOST_VDD))); DEBUG ((DEBUG_MMCHOST_SD, " EDM: 0x%8.8X\n", MmioRead32 (SDHOST_EDM))); DEBUG ((DEBUG_MMCHOST_SD, " HCFG: 0x%8.8X\n", MmioRead32 (SDHOST_HCFG))); DEBUG ((DEBUG_MMCHOST_SD, " HBCT: 0x%8.8X\n", MmioRead32 (SDHOST_HBCT))); DEBUG ((DEBUG_MMCHOST_SD, " HBLC: 0x%8.8X\n\n", MmioRead32 (SDHOST_HBLC))); } #ifndef NDEBUG STATIC EFI_STATUS SdHostGetSdStatus ( UINT32* SdStatus ) { ASSERT (SdStatus != NULL); // On command completion with R1 or R1b response type // the SDCard status will be in RSP0 UINT32 Rsp0 = MmioRead32 (SDHOST_RSP0); if (Rsp0 != 0xFFFFFFFF) { *SdStatus = Rsp0; return EFI_SUCCESS; } return EFI_NO_RESPONSE; } #endif /* NDEBUG */ STATIC VOID SdHostDumpSdCardStatus ( VOID ) { #ifndef NDEBUG UINT32 SdCardStatus; EFI_STATUS Status = SdHostGetSdStatus (&SdCardStatus); if (!EFI_ERROR (Status)) { UINT32 CurrState = SDHOST_R0_CURRENTSTATE (SdCardStatus); DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdCardStatus 0x%8.8X: ReadyForData?%d, State[%d]: %a\n", SdCardStatus, ((SdCardStatus & SDHOST_R0_READY_FOR_DATA) ? 1 : 0), CurrState, ((CurrState < (sizeof (mStrSdState) / sizeof (*mStrSdState))) ? mStrSdState[CurrState] : "UNDEF"))); } #endif /* NDEBUG */ } STATIC VOID SdHostDumpStatus ( VOID ) { SdHostDumpRegisters (); #ifndef NDEBUG UINT32 Hsts = MmioRead32 (SDHOST_HSTS); if (Hsts & SDHOST_HSTS_ERROR) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: Diagnose HSTS: 0x%8.8X\n", Hsts)); DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: Last Good CMD = %u\n", MMC_GET_INDX (mLastGoodCmd))); if (Hsts & SDHOST_HSTS_FIFO_ERROR) DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - Fifo Error\n")); if (Hsts & SDHOST_HSTS_CRC7_ERROR) DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CRC7 Error\n")); if (Hsts & SDHOST_HSTS_CRC16_ERROR) DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CRC16 Error\n")); if (Hsts & SDHOST_HSTS_CMD_TIME_OUT) DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CMD Timeout (TOUT %x)\n", MmioRead32 (SDHOST_TOUT))); if (Hsts & SDHOST_HSTS_REW_TIME_OUT) DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - Read/Erase/Write Transfer Timeout\n")); } UINT32 Edm = MmioRead32 (SDHOST_EDM); DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, "SdHost: Diagnose EDM: 0x%8.8X\n", Edm)); DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, " - FSM: 0x%x (%a)\n", (Edm & 0xF), mFsmState[Edm & 0xF])); DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, " - Fifo Count: %d\n", ((Edm >> 4) & 0x1F))); DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, " - Fifo Write Threshold: %d\n", ((Edm >> SDHOST_EDM_WRITE_THRESHOLD_SHIFT) & SDHOST_EDM_THRESHOLD_MASK))); DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, " - Fifo Read Threshold: %d\n", ((Edm >> SDHOST_EDM_READ_THRESHOLD_SHIFT) & SDHOST_EDM_THRESHOLD_MASK))); #endif SdHostDumpSdCardStatus (); } STATIC EFI_STATUS SdHostSetClockFrequency ( IN UINTN TargetSdFreqHz ) { EFI_STATUS Status; UINT32 CoreClockFreqHz = 0; // First figure out the core clock Status = mFwProtocol->GetClockRate (RPI_MBOX_CLOCK_RATE_CORE, &CoreClockFreqHz); if (EFI_ERROR (Status)) { return Status; } ASSERT (CoreClockFreqHz != 0); // fSDCLK = fcore_pclk/(ClockDiv+2) UINT32 ClockDiv = (CoreClockFreqHz - (2 * TargetSdFreqHz)) / TargetSdFreqHz; UINT32 ActualSdFreqHz = CoreClockFreqHz / (ClockDiv + 2); DEBUG ((DEBUG_MMCHOST_SD_INFO, "SdHost: CoreClock=%dHz, CDIV=%d, Requested SdClock=%dHz, Actual SdClock=%dHz\n", CoreClockFreqHz, ClockDiv, TargetSdFreqHz, ActualSdFreqHz)); MmioWrite32 (SDHOST_CDIV, ClockDiv); // Set timeout after 1 second, i.e ActualSdFreqHz SD clock cycles MmioWrite32 (SDHOST_TOUT, ActualSdFreqHz); gBS->Stall (STALL_TO_STABILIZE_US); return Status; } STATIC BOOLEAN SdIsReadOnly ( IN EFI_MMC_HOST_PROTOCOL *This ) { return FALSE; } STATIC EFI_STATUS SdBuildDevicePath ( 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, "SdHost: SdBuildDevicePath()\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; } STATIC EFI_STATUS SdSendCommand ( IN EFI_MMC_HOST_PROTOCOL *This, IN MMC_CMD MmcCmd, IN UINT32 Argument ) { UINT32 Hsts; // // Fail fast, CMD5 (CMD_IO_SEND_OP_COND) // is only valid for SDIO cards and thus // expected to always fail. // if (MmcCmd == MMC_CMD5) { DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdSendCommand(CMD%d, Argument: %08x) ignored\n", MMC_GET_INDX (MmcCmd), Argument)); return EFI_UNSUPPORTED; } if (MmioRead32 (SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdSendCommand(): Failed to execute CMD%d, a CMD is already being executed.\n", MMC_GET_INDX (MmcCmd))); SdHostDumpStatus (); return EFI_DEVICE_ERROR; } // Write command argument MmioWrite32 (SDHOST_ARG, Argument); UINT32 SdCmd = 0; { // Set response type if (MmcCmd & MMC_CMD_WAIT_RESPONSE) { if (MmcCmd & MMC_CMD_LONG_RESPONSE) { SdCmd |= SDHOST_CMD_RESPONSE_CMD_LONG_RESP; } } else { SdCmd |= SDHOST_CMD_RESPONSE_CMD_NO_RESP; } if (IsBusyCmd (MmcCmd)) { SdCmd |= SDHOST_CMD_BUSY_CMD; } if (IsReadCmd (MmcCmd, Argument)) { SdCmd |= SDHOST_CMD_READ_CMD; } if (IsWriteCmd (MmcCmd)) { SdCmd |= SDHOST_CMD_WRITE_CMD; } SdCmd |= MMC_GET_INDX (MmcCmd); } if (IsReadCmd (MmcCmd, Argument) || IsWriteCmd (MmcCmd)) { if (IsAppCmd () && MmcCmd == MMC_ACMD22) { MmioWrite32 (SDHOST_HBCT, 0x4); } else if (IsAppCmd () && MmcCmd == MMC_ACMD51) { MmioWrite32 (SDHOST_HBCT, 0x8); } else if (!IsAppCmd () && MmcCmd == MMC_CMD6) { MmioWrite32 (SDHOST_HBCT, 0x40); } else { MmioWrite32 (SDHOST_HBCT, SDHOST_BLOCK_BYTE_LENGTH); } } DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdSendCommand(CMD%d, Argument: %08x): BUSY=%d, RESP=%d, WRITE=%d, READ=%d\n", MMC_GET_INDX (MmcCmd), Argument, ((SdCmd & SDHOST_CMD_BUSY_CMD) ? 1 : 0), ((SdCmd & (SDHOST_CMD_RESPONSE_CMD_LONG_RESP | SDHOST_CMD_RESPONSE_CMD_NO_RESP)) >> 9), ((SdCmd & SDHOST_CMD_WRITE_CMD) ? 1 : 0), ((SdCmd & SDHOST_CMD_READ_CMD) ? 1 : 0))); UINT32 PollCount = 0; UINT32 RetryCount = 0; BOOLEAN IsCmdExecuted = FALSE; EFI_STATUS Status = EFI_SUCCESS; // Keep retrying the command until it succeeds. while ((RetryCount < CMD_MAX_RETRY_COUNT) && !IsCmdExecuted) { Status = EFI_SUCCESS; // Clear prev cmd status MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); if (IsReadCmd (MmcCmd, Argument) || IsWriteCmd (MmcCmd)) { // Flush Fifo if this cmd will start a new transfer in case // there is stale bytes in the Fifo MmioOr32 (SDHOST_EDM, SDHOST_EDM_FIFO_CLEAR); } if (MmioRead32 (SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "%a(%u): CMD%d is still being executed after %d trial(s)\n", __FUNCTION__, __LINE__, MMC_GET_INDX (MmcCmd), RetryCount)); } // Write command and set it to start execution MmioWrite32 (SDHOST_CMD, SDHOST_CMD_NEW_FLAG | SdCmd); // Poll for the command status until it finishes execution while (PollCount < CMD_MAX_POLL_COUNT) { UINT32 CmdReg = MmioRead32 (SDHOST_CMD); // Read status of command response if (CmdReg & SDHOST_CMD_FAIL_FLAG) { Status = EFI_DEVICE_ERROR; /* * Must fall-through and wait for the command completion! */ } // Check if command is completed. if (!(CmdReg & SDHOST_CMD_NEW_FLAG)) { IsCmdExecuted = TRUE; break; } ++PollCount; gBS->Stall (CMD_STALL_AFTER_POLL_US); } if (!IsCmdExecuted) { ++RetryCount; gBS->Stall (CMD_STALL_AFTER_RETRY_US); } } if (RetryCount == CMD_MAX_RETRY_COUNT) { Status = EFI_TIMEOUT; } Hsts = MmioRead32 (SDHOST_HSTS); if (EFI_ERROR (Status) || (Hsts & SDHOST_HSTS_ERROR) != 0) { if (MmcCmd == MMC_CMD1 && (Hsts & SDHOST_HSTS_CRC7_ERROR) != 0) { /* * SdHost seems to have no way to specify * R3 as a transfer type. */ IsCmdExecuted = TRUE; Status = EFI_SUCCESS; MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); } else if (MmcCmd == MMC_CMD7 && Argument == 0) { /* * Deselecting the SDCard with CMD7 and RCA=0x0 * always timeout on SDHost. */ Status = EFI_SUCCESS; } else { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "%a(%u): CMD%d execution failed after %d trial(s)\n", __FUNCTION__, __LINE__, MMC_GET_INDX (MmcCmd), RetryCount)); SdHostDumpStatus (); } MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); } if (IsCmdExecuted && !EFI_ERROR (Status)) { ASSERT (!(MmioRead32 (SDHOST_HSTS) & SDHOST_HSTS_ERROR)); mLastGoodCmd = MmcCmd; } return Status; } STATIC EFI_STATUS SdReceiveResponse ( IN EFI_MMC_HOST_PROTOCOL *This, IN MMC_RESPONSE_TYPE Type, IN UINT32* Buffer ) { if (Buffer == NULL) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdReceiveResponse(): Input Buffer is NULL\n")); return EFI_INVALID_PARAMETER; } if ((Type == MMC_RESPONSE_TYPE_R1) || (Type == MMC_RESPONSE_TYPE_R1b) || (Type == MMC_RESPONSE_TYPE_R3) || (Type == MMC_RESPONSE_TYPE_R6) || (Type == MMC_RESPONSE_TYPE_R7)) { Buffer[0] = MmioRead32 (SDHOST_RSP0); DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdReceiveResponse(Type: %x), Buffer[0]: %08x\n", Type, Buffer[0])); } else if (Type == MMC_RESPONSE_TYPE_R2) { Buffer[0] = MmioRead32 (SDHOST_RSP0); Buffer[1] = MmioRead32 (SDHOST_RSP1); Buffer[2] = MmioRead32 (SDHOST_RSP2); Buffer[3] = MmioRead32 (SDHOST_RSP3); DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n", Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3])); } return EFI_SUCCESS; } STATIC EFI_STATUS SdReadBlockData ( IN EFI_MMC_HOST_PROTOCOL *This, IN EFI_LBA Lba, IN UINTN Length, IN UINT32* Buffer ) { DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdReadBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", (UINT32)Lba, Length, Buffer)); ASSERT (Buffer != NULL); ASSERT (Length % 4 == 0); EFI_STATUS Status = EFI_SUCCESS; mFwProtocol->SetLed (TRUE); { UINT32 NumWords = Length / 4; UINT32 WordIdx; for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) { UINT32 PollCount = 0; while (PollCount < FIFO_MAX_POLL_COUNT) { UINT32 Hsts = MmioRead32 (SDHOST_HSTS); if ((Hsts & SDHOST_HSTS_DATA_FLAG) != 0) { MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG); Buffer[WordIdx] = MmioRead32 (SDHOST_DATA); break; } ++PollCount; gBS->Stall (CMD_STALL_AFTER_RETRY_US); } if (PollCount == FIFO_MAX_POLL_COUNT) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdReadBlockData(): Block Word%d read poll timed-out\n", WordIdx)); SdHostDumpStatus (); MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); Status = EFI_TIMEOUT; break; } } } mFwProtocol->SetLed (FALSE); return Status; } STATIC EFI_STATUS SdWriteBlockData ( IN EFI_MMC_HOST_PROTOCOL *This, IN EFI_LBA Lba, IN UINTN Length, IN UINT32* Buffer ) { DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdWriteBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", (UINT32)Lba, Length, Buffer)); ASSERT (Buffer != NULL); ASSERT (Length % SDHOST_BLOCK_BYTE_LENGTH == 0); EFI_STATUS Status = EFI_SUCCESS; mFwProtocol->SetLed (TRUE); { UINT32 NumWords = Length / 4; UINT32 WordIdx; for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) { UINT32 PollCount = 0; while (PollCount < FIFO_MAX_POLL_COUNT) { if (MmioRead32 (SDHOST_HSTS) & SDHOST_HSTS_DATA_FLAG) { MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG); MmioWrite32 (SDHOST_DATA, Buffer[WordIdx]); break; } ++PollCount; gBS->Stall (CMD_STALL_AFTER_RETRY_US); } if (PollCount == FIFO_MAX_POLL_COUNT) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdWriteBlockData(): Block Word%d write poll timed-out\n", WordIdx)); SdHostDumpStatus (); MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); Status = EFI_TIMEOUT; break; } } } mFwProtocol->SetLed (FALSE); return Status; } STATIC EFI_STATUS SdSetIos ( IN EFI_MMC_HOST_PROTOCOL *This, IN UINT32 BusClockFreq, IN UINT32 BusWidth, IN UINT32 TimingMode ) { if (BusWidth != 0) { UINT32 Hcfg = MmioRead32 (SDHOST_HCFG); DEBUG ((DEBUG_MMCHOST_SD_INFO, "Setting BusWidth %u\n", BusWidth)); if (BusWidth == 4) { Hcfg |= SDHOST_HCFG_WIDE_EXT_BUS; } else { Hcfg &= ~SDHOST_HCFG_WIDE_EXT_BUS; } Hcfg |= SDHOST_HCFG_WIDE_INT_BUS | SDHOST_HCFG_SLOW_CARD; MmioWrite32 (SDHOST_HCFG, Hcfg); } if (BusClockFreq != 0) { DEBUG ((DEBUG_MMCHOST_SD_INFO, "Setting Freq %u Hz\n", BusClockFreq)); SdHostSetClockFrequency (BusClockFreq); } return EFI_SUCCESS; } STATIC EFI_STATUS SdNotifyState ( IN EFI_MMC_HOST_PROTOCOL *This, IN MMC_STATE State ) { 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)); // Turn-off SD Card power MmioWrite32 (SDHOST_VDD, 0); // Reset command and arg MmioWrite32 (SDHOST_CMD, 0); MmioWrite32 (SDHOST_ARG, 0); // Reset clock divider MmioWrite32 (SDHOST_CDIV, 0); // Default timeout MmioWrite32 (SDHOST_TOUT, 0xffffffff); // Clear status flags MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR);; // Reset controller configs MmioWrite32 (SDHOST_HCFG, 0); MmioWrite32 (SDHOST_HBCT, 0); MmioWrite32 (SDHOST_HBLC, 0); gBS->Stall (STALL_TO_STABILIZE_US); // Turn-on SD Card power MmioWrite32 (SDHOST_VDD, 1); gBS->Stall (STALL_TO_STABILIZE_US); // Write controller configs UINT32 Hcfg = 0; Hcfg |= SDHOST_HCFG_WIDE_INT_BUS; Hcfg |= SDHOST_HCFG_SLOW_CARD; // Use all bits of CDIV in DataMode MmioWrite32 (SDHOST_HCFG, Hcfg); // Set default clock frequency EFI_STATUS Status = SdHostSetClockFrequency (IDENT_MODE_SD_CLOCK_FREQ_HZ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdNotifyState(): Fail to initialize SD clock to %dHz\n", IDENT_MODE_SD_CLOCK_FREQ_HZ)); SdHostDumpStatus (); return Status; } break; case MmcIdleState: DEBUG ((DEBUG_MMCHOST_SD, "MmcIdleState\n", State)); break; case MmcReadyState: DEBUG ((DEBUG_MMCHOST_SD, "MmcReadyState\n", State)); break; case MmcIdentificationState: DEBUG ((DEBUG_MMCHOST_SD, "MmcIdentificationState\n", State)); break; case MmcStandByState: DEBUG ((DEBUG_MMCHOST_SD, "MmcStandByState\n", State)); break; case MmcTransferState: DEBUG ((DEBUG_MMCHOST_SD, "MmcTransferState\n", State)); break; break; case MmcSendingDataState: DEBUG ((DEBUG_MMCHOST_SD, "MmcSendingDataState\n", State)); break; case MmcReceiveDataState: DEBUG ((DEBUG_MMCHOST_SD, "MmcReceiveDataState\n", State)); break; case MmcProgrammingState: DEBUG ((DEBUG_MMCHOST_SD, "MmcProgrammingState\n", State)); break; case MmcDisconnectState: case MmcInvalidState: default: DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdNotifyState(): Invalid State: %d\n", State)); ASSERT (0); } 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 ) { return TRUE; } EFI_MMC_HOST_PROTOCOL gMmcHost = { MMC_HOST_PROTOCOL_REVISION, SdIsCardPresent, SdIsReadOnly, SdBuildDevicePath, SdNotifyState, SdSendCommand, SdReceiveResponse, SdReadBlockData, SdWriteBlockData, SdSetIos, SdIsMultiBlock }; EFI_STATUS SdHostInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle = NULL; if (PcdGet32 (PcdSdIsArasan)) { DEBUG ((DEBUG_INFO, "SD is not routed to SdHost\n")); return EFI_REQUEST_UNLOAD_IMAGE; } Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL, (VOID**)&mFwProtocol); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return Status; } DEBUG ((DEBUG_MMCHOST_SD, "SdHost: Initialize\n")); DEBUG ((DEBUG_MMCHOST_SD, "Config:\n")); DEBUG ((DEBUG_MMCHOST_SD, " - FIFO_MAX_POLL_COUNT=%d\n", FIFO_MAX_POLL_COUNT)); DEBUG ((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_POLL_US=%dus\n", CMD_STALL_AFTER_POLL_US)); DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MIN_POLL_TOTAL_TIME_US=%dms\n", CMD_MIN_POLL_TOTAL_TIME_US / 1000)); DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MAX_POLL_COUNT=%d\n", CMD_MAX_POLL_COUNT)); DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MAX_RETRY_COUNT=%d\n", CMD_MAX_RETRY_COUNT)); DEBUG ((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_RETRY_US=%dus\n", CMD_STALL_AFTER_RETRY_US)); Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gRaspberryPiMmcHostProtocolGuid, &gMmcHost, NULL ); ASSERT_EFI_ERROR (Status); return Status; }