/** Implements EFI_BLOCK_IO_PROTOCOL.Reset() function. @param This The EFI_BLOCK_IO_PROTOCOL instance. @param ExtendedVerification Indicates that the driver may perform a more exhaustive. verification operation of the device during reset. (This parameter is ingored in this driver.) @retval EFI_SUCCESS Success **/ EFI_STATUS EFIAPI MMCSDBlockReset ( IN EFI_BLOCK_IO_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) { CARD_DATA *CardData; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; CardData = CARD_DATA_FROM_THIS(This); SDHostIo = CardData->SDHostIo; return SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD); }
/** Send command by using Host IO protocol @param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance. @param CommandIndex The command index to set the command index field of command register. @param Argument Command argument to set the argument field of command register. @param DataType TRANSFER_TYPE, indicates no data, data in or data out. @param Buffer Contains the data read from / write to the device. @param BufferSize The size of the buffer. @param ResponseType RESPONSE_TYPE. @param TimeOut Time out value in 1 ms unit. @param ResponseData Depending on the ResponseType, such as CSD or card status. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_UNSUPPORTED @retval EFI_DEVICE_ERROR **/ EFI_STATUS SendCommand ( IN CARD_DATA *CardData, IN UINT16 CommandIndex, IN UINT32 Argument, IN TRANSFER_TYPE DataType, IN UINT8 *Buffer, OPTIONAL IN UINT32 BufferSize, IN RESPONSE_TYPE ResponseType, IN UINT32 TimeOut, OUT UINT32 *ResponseData ) { EFI_STATUS Status; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; SDHostIo = CardData->SDHostIo; if (CardData->CardType != MMCCard && CardData->CardType != MMCCardHighCap) { CommandIndex |= AUTO_CMD12_ENABLE; } Status = SDHostIo->SendCommand ( SDHostIo, CommandIndex, Argument, DataType, Buffer, BufferSize, ResponseType, TimeOut, ResponseData ); if (!EFI_ERROR (Status)) { if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) { ASSERT(ResponseData != NULL); Status = CheckCardStatus (*ResponseData); } } else { SDHostIo->ResetSDHost (SDHostIo, Reset_DAT_CMD); } return Status; }
/** This function set the bus and device width for MMC card @param CardData Pointer to CARD_DATA. @param Width 1, 4, 8 bits. @retval EFI_SUCCESS @retval EFI_UNSUPPORTED @retval EFI_INVALID_PARAMETER **/ EFI_STATUS MMCCardSetBusWidth ( IN CARD_DATA *CardData, IN UINT8 BusWidth, IN BOOLEAN EnableDDRMode ) { EFI_STATUS Status; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; SWITCH_ARGUMENT SwitchArgument; UINT8 Value; SDHostIo = CardData->SDHostIo; Value = 0; switch (BusWidth) { case 8: if (EnableDDRMode) Value = 6; else Value = 2; break; case 4: if (EnableDDRMode) Value = 5; else Value = 1; break; case 1: if (EnableDDRMode) // Bus width 1 is not supported in ddr mode return EFI_UNSUPPORTED; Value = 0; break; default: ASSERT(0); } ZeroMem(&SwitchArgument, sizeof (SWITCH_ARGUMENT)); SwitchArgument.CmdSet = 0; SwitchArgument.Value = Value; SwitchArgument.Index = (UINT32)((UINTN) (&(CardData->ExtCSDRegister.BUS_WIDTH)) - (UINTN)(&(CardData->ExtCSDRegister))); SwitchArgument.Access = WriteByte_Mode; Status = SendCommand ( CardData, SWITCH, *(UINT32*)&SwitchArgument, NoData, NULL, 0, ResponseR1b, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (!EFI_ERROR (Status)) { Status = SendCommand ( CardData, SEND_STATUS, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SWITCH %d bits Fail\n", BusWidth)); goto Exit; } else { DEBUG((EFI_D_ERROR, "MMCCardSetBusWidth:SWITCH Card Status:0x%x\n", *(UINT32*)&(CardData->CardStatus))); Status = SDHostIo->SetBusWidth (SDHostIo, BusWidth); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SWITCH set %d bits Fail\n", BusWidth)); goto Exit; } gBS->Stall (5 * 1000); } } if (!EnableDDRMode) { // CMD19 and CMD14 are illegal commands in ddr mode //if (EFI_ERROR (Status)) { // DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest: Fail to enable high speed mode\n")); // goto Exit; //} Status = MMCCardBusWidthTest (CardData, BusWidth); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "MMCCardBusWidthTest %d bit Fail\n", BusWidth)); goto Exit; } } CardData->CurrentBusWidth = BusWidth; Exit: return Status; }
/** Send the card APP_CMD command with the following command indicated by CommandIndex @param CardData Pointer to CARD_DATA. @param CommandIndex The command index to set the command index field of command register. @param Argument Command argument to set the argument field of command register. @param DataType TRANSFER_TYPE, indicates no data, data in or data out. @param Buffer Contains the data read from / write to the device. @param BufferSize The size of the buffer. @param ResponseType RESPONSE_TYPE. @param TimeOut Time out value in 1 ms unit. @param ResponseData Depending on the ResponseType, such as CSD or card status. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_UNSUPPORTED @retval EFI_DEVICE_ERROR **/ EFI_STATUS SendAppCommand ( IN CARD_DATA *CardData, IN UINT16 CommandIndex, IN UINT32 Argument, IN TRANSFER_TYPE DataType, IN UINT8 *Buffer, OPTIONAL IN UINT32 BufferSize, IN RESPONSE_TYPE ResponseType, IN UINT32 TimeOut, OUT UINT32 *ResponseData ) { EFI_STATUS Status; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; UINT8 Index; SDHostIo = CardData->SDHostIo; Status = EFI_SUCCESS; for (Index = 0; Index < 2; Index++) { Status = SDHostIo->SendCommand ( SDHostIo, APP_CMD, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (!EFI_ERROR (Status)) { Status = CheckCardStatus (*(UINT32*)&(CardData->CardStatus)); if (CardData->CardStatus.SAPP_CMD != 1) { Status = EFI_DEVICE_ERROR; } if (!EFI_ERROR (Status)) { break; } } else { SDHostIo->ResetSDHost (SDHostIo, Reset_Auto); } } if (EFI_ERROR (Status)) { return Status; } if (CardData->CardType != MMCCard && CardData->CardType != MMCCardHighCap) { CommandIndex |= AUTO_CMD12_ENABLE; } Status = SDHostIo->SendCommand ( SDHostIo, CommandIndex, Argument, DataType, Buffer, BufferSize, ResponseType, TimeOut, ResponseData ); if (!EFI_ERROR (Status)) { if (ResponseType == ResponseR1 || ResponseType == ResponseR1b) { ASSERT(ResponseData != NULL); Status = CheckCardStatus (*ResponseData); } } else { SDHostIo->ResetSDHost (SDHostIo, Reset_Auto); } return Status; }
/** MMC/SD card init function @param CardData Pointer to CARD_DATA. @return EFI_SUCCESS @return others **/ EFI_STATUS MMCSDCardInit ( IN CARD_DATA *CardData ) { EFI_STATUS Status; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; SWITCH_ARGUMENT SwitchArgument; UINT32 Data; UINT32 Argument; UINT32 nIndex; UINT8 PowerValue; BOOLEAN EnableDDRMode; ASSERT(CardData != NULL); SDHostIo = CardData->SDHostIo; EnableDDRMode = FALSE; CardData->CardType = UnknownCard; Status = GetCardType (CardData); if (EFI_ERROR (Status)) { goto Exit; } DEBUG((DEBUG_INFO, "CardData->CardType 0x%x\n", CardData->CardType)); ASSERT (CardData->CardType != UnknownCard); // //MMC, SD card need host auto stop command support // SDHostIo->EnableAutoStopCmd (SDHostIo, TRUE); if (CardData->CardType == MMCCard) { Status = MMCCardVoltageSelection (CardData); if (EFI_ERROR(Status)) { goto Exit; } } // // Get CID Register // Status = SendCommand ( CardData, ALL_SEND_CID, 0, NoData, NULL, 0, ResponseR2, TIMEOUT_COMMAND, (UINT32*)&(CardData->CIDRegister) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "ALL_SEND_CID Fail Status = 0x%x\n", Status)); goto Exit; } else { // Dump out the Card ID data DEBUG((EFI_D_INFO, "Product Name: ")); for ( nIndex=0; nIndex<6; nIndex++ ) { DEBUG((EFI_D_INFO, "%c", CardData->CIDRegister.PNM[nIndex])); } DEBUG((EFI_D_INFO, "\nApplication ID : %d\n", CardData->CIDRegister.OID)); DEBUG((EFI_D_INFO, "Manufacturer ID: %d\n", CardData->CIDRegister.MID)); DEBUG((EFI_D_INFO, "Revision ID : %d\n", CardData->CIDRegister.PRV)); DEBUG((EFI_D_INFO, "Serial Number : %d\n", CardData->CIDRegister.PSN)); } // //SET_RELATIVE_ADDR // if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) { // //Hard code the RCA address // CardData->Address = 1; // // Set RCA Register // Status = SendCommand ( CardData, SET_RELATIVE_ADDR, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status)); goto Exit; } } else { Data = 0; Status = SendCommand ( CardData, SET_RELATIVE_ADDR, 0, NoData, NULL, 0, ResponseR6, TIMEOUT_COMMAND, &Data ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status)); goto Exit; } CardData->Address = (UINT16)(Data >> 16); *(UINT32*)&CardData->CardStatus = Data & 0x1FFF; CardData->CardStatus.ERROR = (Data >> 13) & 0x1; CardData->CardStatus.ILLEGAL_COMMAND = (Data >> 14) & 0x1; CardData->CardStatus.COM_CRC_ERROR = (Data >> 15) & 0x1; Status = CheckCardStatus (*(UINT32*)&CardData->CardStatus); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SET_RELATIVE_ADDR Fail Status = 0x%x\n", Status)); goto Exit; } } // // Get CSD Register // Status = SendCommand ( CardData, SEND_CSD, (CardData->Address << 16), NoData, NULL, 0, ResponseR2, TIMEOUT_COMMAND, (UINT32*)&(CardData->CSDRegister) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SEND_CSD Fail Status = 0x%x\n", Status)); goto Exit; } DEBUG((EFI_D_INFO, "CardData->CSDRegister.SPEC_VERS = 0x%x\n", CardData->CSDRegister.SPEC_VERS)); DEBUG((EFI_D_INFO, "CardData->CSDRegister.CSD_STRUCTURE = 0x%x\n", CardData->CSDRegister.CSD_STRUCTURE)); Status = CaculateCardParameter (CardData); if (EFI_ERROR (Status)) { goto Exit; } // // It is platform and hardware specific, need hadrware engineer input // if (CardData->CSDRegister.DSR_IMP == 1) { // // Default is 0x404 // Status = SendCommand ( CardData, SET_DSR, (DEFAULT_DSR_VALUE << 16), NoData, NULL, 0, ResponseNo, TIMEOUT_COMMAND, NULL ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SET_DSR Fail Status = 0x%x\n", Status)); // // Assume can operate even fail // } } // //Change clock frequency from 400KHz to max supported when not in high speed mode // Status = SDHostIo->SetClockFrequency (SDHostIo, CardData->MaxFrequency); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "MMCSDCardInit:Fail to SetClockFrequency \n")); goto Exit; } // //Put the card into tran state // Status = SendCommand ( CardData, SELECT_DESELECT_CARD, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SELECT_DESELECT_CARD Fail Status = 0x%x\n", Status)); goto Exit; } // // No spec requirment, can be adjusted // gBS->Stall (5 * 1000); // // No need to do so // // Status = SendCommand ( CardData, SEND_STATUS, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SELECT_DESELECT_CARD SEND_STATUS Fail Status = 0x%x\n", Status)); goto Exit; } // //if the SPEC_VERS indicates a version 4.0 or higher //The card is a high speed card and support Switch //and Send_ext_csd command //otherwise it is an old card // if (CardData->CardType == MMCCard || CardData->CardType == MMCCardHighCap) { // //Only V4.0 and above supports more than 1 bits and high speed // if (CardData->CSDRegister.SPEC_VERS >= 4) { // //Get ExtCSDRegister // Status = SendCommand ( CardData, SEND_EXT_CSD, 0x0, InData, CardData->AlignedBuffer, sizeof (EXT_CSD), ResponseR1, TIMEOUT_DATA, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SEND_EXT_CSD Fail Status = 0x%x\n", Status)); goto Exit; } CopyMem (&(CardData->ExtCSDRegister), CardData->AlignedBuffer, sizeof (EXT_CSD)); // // Recaculate the block number for >2G MMC card // Data = (CardData->ExtCSDRegister.SEC_COUNT[0]) | (CardData->ExtCSDRegister.SEC_COUNT[1] << 8) | (CardData->ExtCSDRegister.SEC_COUNT[2] << 16) | (CardData->ExtCSDRegister.SEC_COUNT[3] << 24); if (Data != 0) { CardData->BlockNumber = Data; } DEBUG((DEBUG_INFO, "CardData->BlockNumber %d\n", Data)); DEBUG((EFI_D_ERROR, "CardData->ExtCSDRegister.CARD_TYPE -> %d\n", (UINTN)CardData->ExtCSDRegister.CARD_TYPE)); if ((CardData->ExtCSDRegister.CARD_TYPE & BIT2)|| (CardData->ExtCSDRegister.CARD_TYPE & BIT3)) { //DEBUG((DEBUG_INFO, "To enable DDR mode\n")); //EnableDDRMode = TRUE; } // // Check current chipset capability and the plugged-in card // whether supports HighSpeed // if (SDHostIo->HostCapability.HighSpeedSupport) { // //Change card timing to high speed interface timing // ZeroMem(&SwitchArgument, sizeof (SWITCH_ARGUMENT)); SwitchArgument.CmdSet = 0; SwitchArgument.Value = 1; SwitchArgument.Index = (UINT32)((UINTN) (&(CardData->ExtCSDRegister.HS_TIMING)) - (UINTN)(&(CardData->ExtCSDRegister))); SwitchArgument.Access = WriteByte_Mode; Status = SendCommand ( CardData, SWITCH, *(UINT32*)&SwitchArgument, NoData, NULL, 0, ResponseR1b, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "MMCSDCardInit:SWITCH frequency Fail Status = 0x%x\n", Status)); } gBS->Stall (5 * 1000); if (!EFI_ERROR (Status)) { Status = SendCommand ( CardData, SEND_STATUS, (CardData->Address << 16), NoData, NULL, 0, ResponseR1, TIMEOUT_COMMAND, (UINT32*)&(CardData->CardStatus) ); if (!EFI_ERROR (Status)) { if (EnableDDRMode) { DEBUG((EFI_D_ERROR, "Enable ddr mode on host controller\n")); SDHostIo->SetDDRMode (SDHostIo, TRUE); } else { DEBUG((EFI_D_ERROR, "Enable high speed mode on host controller\n")); SDHostIo->SetHighSpeedMode (SDHostIo, TRUE); } // // Change host clock to support high speed and enable chispet to // support speed // if ((CardData->ExtCSDRegister.CARD_TYPE & BIT1) != 0) { Status = SDHostIo->SetClockFrequency (SDHostIo, FREQUENCY_MMC_PP_HIGH); } else if ((CardData->ExtCSDRegister.CARD_TYPE & BIT0) != 0) { Status = SDHostIo->SetClockFrequency (SDHostIo, FREQUENCY_MMC_PP); } else { Status = EFI_UNSUPPORTED; } if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "MMCSDCardInit:Fail to SetClockFrequency \n")); goto Exit; } // // It seems no need to stall after changing bus freqeuncy. // It is said that the freqeuncy can be changed at any time. Just appends 8 clocks after command. // But SetClock alreay has delay. // } } } // // Prefer wide bus width for performance // // // Set to BusWidth bits mode, only version 4.0 or above support more than 1 bits // if (SDHostIo->HostCapability.BusWidth8 == TRUE) { Status = MMCCardSetBusWidth (CardData, 8, EnableDDRMode); if (EFI_ERROR (Status)) { // // CE-ATA may support 8 bits and 4 bits, but has no software method for detection // Status = MMCCardSetBusWidth (CardData, 4, EnableDDRMode); if (EFI_ERROR (Status)) { goto Exit; } } } else if (SDHostIo->HostCapability.BusWidth4 == TRUE) { Status = MMCCardSetBusWidth (CardData, 4, EnableDDRMode); if (EFI_ERROR (Status)) { goto Exit; } } PowerValue = 0; if (CardData->CurrentBusWidth == 8) { if ((CardData->ExtCSDRegister.CARD_TYPE & BIT1) != 0) { PowerValue = CardData->ExtCSDRegister.PWR_CL_52_360; PowerValue = PowerValue >> 4; } else if ((CardData->ExtCSDRegister.CARD_TYPE & BIT0) != 0) { PowerValue = CardData->ExtCSDRegister.PWR_CL_26_360; PowerValue = PowerValue >> 4; }
/** MMC card high/low voltage selection function @param CardData Pointer to CARD_DATA. @retval EFI_SUCCESS @retval EFI_INVALID_PARAMETER @retval EFI_UNSUPPORTED @retval EFI_BAD_BUFFER_SIZE **/ EFI_STATUS MMCCardVoltageSelection ( IN CARD_DATA *CardData ) { EFI_STATUS Status; EFI_SD_HOST_IO_PROTOCOL *SDHostIo; UINT8 Index; UINT8 Retry; UINT32 TimeOut; SDHostIo = CardData->SDHostIo; Status = EFI_SUCCESS; // //First try the high voltage, then if supported choose the low voltage // for (Index = 0; Index < 2; Index++) { for (Retry = 0; Retry < 3; Retry++) { // // To bring back the normal MMC card to work // after sending the SD command. Otherwise some // card could not work Status = SendCommand ( SDHostIo, GO_IDLE_STATE, 0, NoData, NULL, 0, ResponseNo, TIMEOUT_COMMAND, NULL ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "GO_IDLE_STATE Fail Status = 0x%x\n", Status)); continue; } // //CE-ATA device needs long delay // gBS->Stall ((Retry + 1) * 50 * 1000); // //Get OCR register to check voltage support, first time the OCR is 0 // Status = SendCommand ( SDHostIo, SEND_OP_COND, 0, NoData, NULL, 0, ResponseR3, TIMEOUT_COMMAND, (UINT32*)&(CardData->OCRRegister) ); if (!EFI_ERROR (Status)) { break; } } if (Retry == 3) { DEBUG((EFI_D_ERROR, "SEND_OP_COND Fail Status = 0x%x\n", Status)); Status = EFI_DEVICE_ERROR; goto Exit; } if (CardData->OCRRegister.V170_V195 == 1) { CardData->DualVoltage = TRUE; } if (CardData->OCRRegister.V270_V360 != 0x1F && CardData->OCRRegister.V200_V260 != 0) { DEBUG((EFI_D_ERROR, "Incompatiable voltage device\n")); PutCardInactive (CardData); Status = EFI_INCOMPATIBLE_VERSION; goto Exit; } if (Index == 0) { // //Choose the high voltage first // CardData->OCRRegister.V170_V195 = 0; } else { // //Choose the low voltage // CardData->OCRRegister.V170_V195 = 1; CardData->OCRRegister.V270_V360 = 0; } // //TimeOut Value, 5000 * 100 * 1000 = 5 s // TimeOut = 5000; do { Status = SendCommand ( SDHostIo, SEND_OP_COND, *(UINT32*)&(CardData->OCRRegister), NoData, NULL, 0, ResponseR3, TIMEOUT_COMMAND, (UINT32*)&(CardData->OCRRegister) ); if (EFI_ERROR (Status)) { DEBUG((EFI_D_ERROR, "SEND_OP_COND Fail Status = 0x%x\n", Status)); goto Exit; } gBS->Stall (1 * 1000); TimeOut--; DEBUG((EFI_D_ERROR, "Card is always in busy state, timeout:%d(in 5000)\n", TimeOut)); if (TimeOut == 0) { Status = EFI_TIMEOUT; goto Exit; } } while (CardData->OCRRegister.Busy != 1); if (CardData->DualVoltage == TRUE && SDHostIo->HostCapability.V18Support == TRUE) { // //Power Off the card and then power on into low voltage // SDHostIo->SetHostVoltage (SDHostIo, 0); gBS->Stall (1 * 1000); SDHostIo->SetHostVoltage (SDHostIo, 18); } else { // //Not support the low voltage, exit // break; } } Exit: return Status; }