diff options
author | AJFISH <AJFISH@6f19259b-4bc3-4df7-8a09-765794883524> | 2009-12-06 01:57:05 +0000 |
---|---|---|
committer | AJFISH <AJFISH@6f19259b-4bc3-4df7-8a09-765794883524> | 2009-12-06 01:57:05 +0000 |
commit | 2ef2b01e07c02db339f34004445734a2dbdd80e1 (patch) | |
tree | 19532a6be8d8bdb0aef04bd00c1efb582f6dc841 /BeagleBoardPkg/MMCHSDxe/MMCHS.c | |
parent | f7753a96ba1653ddd31b01c198a352f6332ac404 (diff) | |
download | edk2-platforms-2ef2b01e07c02db339f34004445734a2dbdd80e1.tar.xz |
Adding support for BeagleBoard.
ArmPkg - Supoprt for ARM specific things that can change as the architecture changes. Plus semihosting JTAG drivers.
EmbeddedPkg - Generic support for an embeddded platform. Including a light weight command line shell.
BeagleBoardPkg - Platform specifics for BeagleBoard. SD Card works, but USB has issues. Looks like a bug in the open source USB stack (Our internal stack works fine).
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@9518 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'BeagleBoardPkg/MMCHSDxe/MMCHS.c')
-rw-r--r-- | BeagleBoardPkg/MMCHSDxe/MMCHS.c | 1018 |
1 files changed, 1018 insertions, 0 deletions
diff --git a/BeagleBoardPkg/MMCHSDxe/MMCHS.c b/BeagleBoardPkg/MMCHSDxe/MMCHS.c new file mode 100644 index 0000000000..77c5fbdbfa --- /dev/null +++ b/BeagleBoardPkg/MMCHSDxe/MMCHS.c @@ -0,0 +1,1018 @@ +/** @file + + Copyright (c) 2008-2009, Apple Inc. All rights reserved. + + All rights reserved. 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 <Uefi.h> + +#include "MMCHS.h" + +EFI_BLOCK_IO_MEDIA MMCHSMedia = { + SIGNATURE_32('s','d','i','o'), // MediaId + FALSE, // RemovableMedia + TRUE, // MediaPresent + TRUE, // LogicalPartition + FALSE, // ReadOnly + FALSE, // WriteCaching + 512, // BlockSize + 4, // IoAlign + 0, // Pad + 0 // LastBlock +}; + +typedef struct { + VENDOR_DEVICE_PATH Mmc; + EFI_DEVICE_PATH End; +} MMCHS_DEVICE_PATH; + +MMCHS_DEVICE_PATH gMmcHsDevicePath = +{ + { + HARDWARE_DEVICE_PATH, + HW_VENDOR_DP, + (UINT8)(sizeof(VENDOR_DEVICE_PATH)), + (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8), + 0xb615f1f5, 0x5088, 0x43cd, 0x80, 0x9c, 0xa1, 0x6e, 0x52, 0x48, 0x7d, 0x00 + }, + { + END_DEVICE_PATH_TYPE, + END_ENTIRE_DEVICE_PATH_SUBTYPE, + sizeof (EFI_DEVICE_PATH_PROTOCOL), + 0 + } +}; + +CARD_INFO *gCardInfo; +EMBEDDED_EXTERNAL_DEVICE *gTPS65950; + +// +// Internal Functions +// + +STATIC +VOID +ParseCardCIDData ( + UINT32 Response0, + UINT32 Response1, + UINT32 Response2, + UINT32 Response3 + ) +{ + gCardInfo->CIDData.MDT = ((Response0 >> 8) & 0xFFF); + gCardInfo->CIDData.PSN = (((Response0 >> 24) & 0xFF) | ((Response1 & 0xFFFFFF) << 8)); + gCardInfo->CIDData.PRV = ((Response1 >> 24) & 0xFF); + gCardInfo->CIDData.PNM[4] = ((Response2) & 0xFF); + gCardInfo->CIDData.PNM[3] = ((Response2 >> 8) & 0xFF); + gCardInfo->CIDData.PNM[2] = ((Response2 >> 16) & 0xFF); + gCardInfo->CIDData.PNM[1] = ((Response2 >> 24) & 0xFF); + gCardInfo->CIDData.PNM[0] = ((Response3) & 0xFF); + gCardInfo->CIDData.OID = ((Response3 >> 8) & 0xFFFF); + gCardInfo->CIDData.MID = ((Response3 >> 24) & 0xFF); +} + +STATIC +VOID +UpdateMMCHSClkFrequency ( + UINTN NewCLKD + ) +{ + //Set Clock enable to 0x0 to not provide the clock to the card + MmioAnd32(MMCHS_SYSCTL, ~CEN); + + //Set new clock frequency. + MmioAndThenOr32(MMCHS_SYSCTL, ~CLKD_MASK, NewCLKD << 6); + + //Poll till Internal Clock Stable + while ((MmioRead32(MMCHS_SYSCTL) & ICS_MASK) != ICS); + + //Set Clock enable to 0x1 to provide the clock to the card + MmioOr32(MMCHS_SYSCTL, CEN); +} + +STATIC +EFI_STATUS +SendCmd ( + UINTN Cmd, + UINTN CmdInterruptEnableVal, + UINTN CmdArgument + ) +{ + UINTN MmcStatus; + UINTN RetryCount = 0; + + //Check if command line is in use or not. Poll till command line is available. + while ((MmioRead32(MMCHS_PSTATE) & DATI_MASK) == DATI_NOT_ALLOWED); + + //Provide the block size. + MmioWrite32(MMCHS_BLK, BLEN_512BYTES); + + //Setting Data timeout counter value to max value. + MmioAndThenOr32(MMCHS_SYSCTL, ~DTO_MASK, DTO_VAL); + + //Clear Status register. + MmioWrite32(MMCHS_STAT, 0xFFFFFFFF); + + //Set command argument register + MmioWrite32(MMCHS_ARG, CmdArgument); + + //Enable interrupt enable events to occur + MmioWrite32(MMCHS_IE, CmdInterruptEnableVal); + + //Send a command + MmioWrite32(MMCHS_CMD, Cmd); + + //Check for the command status. + while (RetryCount < MAX_RETRY_COUNT) { + do { + MmcStatus = MmioRead32(MMCHS_STAT); + } while (MmcStatus == 0); + + //Read status of command response + if ((MmcStatus & ERRI) != 0) { + + //Perform soft-reset for mmci_cmd line. + MmioOr32(MMCHS_SYSCTL, SRC); + while ((MmioRead32(MMCHS_SYSCTL) & SRC)); + + DEBUG ((EFI_D_INFO, "MmcStatus: %x\n", MmcStatus)); + return EFI_DEVICE_ERROR; + } + + //Check if command is completed. + if ((MmcStatus & CC) == CC) { + MmioWrite32(MMCHS_STAT, CC); + break; + } + + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +STATIC +VOID +GetBlockInformation ( + UINTN *BlockSize, + UINTN *NumBlocks + ) +{ + CSD_SDV2 *CsdSDV2Data; + UINTN CardSize; + + if (gCardInfo->CardType == SD_CARD_2_HIGH) { + CsdSDV2Data = (CSD_SDV2 *)&gCardInfo->CSDData; + + //Populate BlockSize. + *BlockSize = (0x1UL << CsdSDV2Data->READ_BL_LEN); + + //Calculate Total number of blocks. + CardSize = CsdSDV2Data->C_SIZELow16 | (CsdSDV2Data->C_SIZEHigh6 << 2); + *NumBlocks = ((CardSize + 1) * 1024); + } else { + //Populate BlockSize. + *BlockSize = (0x1UL << gCardInfo->CSDData.READ_BL_LEN); + + //Calculate Total number of blocks. + CardSize = gCardInfo->CSDData.C_SIZELow2 | (gCardInfo->CSDData.C_SIZEHigh10 << 2); + *NumBlocks = (CardSize + 1) * (1 << (gCardInfo->CSDData.C_SIZE_MULT + 2)); + } + + //For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes. + if (*BlockSize > 512) { + *NumBlocks = MultU64x32(*NumBlocks, *BlockSize/2); + *BlockSize = 512; + } + + DEBUG ((EFI_D_INFO, "Card type: %x, BlockSize: %x, NumBlocks: %x\n", gCardInfo->CardType, *BlockSize, *NumBlocks)); +} + +STATIC +VOID +CalculateCardCLKD ( + UINTN *ClockFrequencySelect + ) +{ + UINT8 MaxDataTransferRate; + UINTN TransferRateValue = 0; + UINTN TimeValue = 0 ; + UINTN Frequency = 0; + + MaxDataTransferRate = gCardInfo->CSDData.TRAN_SPEED; + + //Calculate Transfer rate unit (Bits 2:0 of TRAN_SPEED) + switch (MaxDataTransferRate & 0x7) { + case 0: + TransferRateValue = 100 * 1000; + break; + + case 1: + TransferRateValue = 1 * 1000 * 1000; + break; + + case 2: + TransferRateValue = 10 * 1000 * 1000; + break; + + case 3: + TransferRateValue = 100 * 1000 * 1000; + break; + + default: + DEBUG((EFI_D_ERROR, "Invalid parameter.\n")); + ASSERT(FALSE); + } + + //Calculate Time value (Bits 6:3 of TRAN_SPEED) + switch ((MaxDataTransferRate >> 3) & 0xF) { + case 1: + TimeValue = 10; + break; + + case 2: + TimeValue = 12; + break; + + case 3: + TimeValue = 13; + break; + + case 4: + TimeValue = 15; + break; + + case 5: + TimeValue = 20; + break; + + case 6: + TimeValue = 25; + break; + + case 7: + TimeValue = 30; + break; + + case 8: + TimeValue = 35; + break; + + case 9: + TimeValue = 40; + break; + + case 10: + TimeValue = 45; + break; + + case 11: + TimeValue = 50; + break; + + case 12: + TimeValue = 55; + break; + + case 13: + TimeValue = 60; + break; + + case 14: + TimeValue = 70; + break; + + case 15: + TimeValue = 80; + break; + + default: + DEBUG((EFI_D_ERROR, "Invalid parameter.\n")); + ASSERT(FALSE); + } + + Frequency = TransferRateValue * TimeValue/10; + + //Calculate Clock divider value to program in MMCHS_SYSCTL[CLKD] field. + *ClockFrequencySelect = ((MMC_REFERENCE_CLK/Frequency) + 1); + + DEBUG ((EFI_D_INFO, "MaxDataTransferRate: 0x%x, Frequency: %d KHz, ClockFrequencySelect: %x\n", MaxDataTransferRate, Frequency/1000, *ClockFrequencySelect)); +} + +STATIC +VOID +GetCardConfigurationData ( + VOID + ) +{ + UINTN BlockSize; + UINTN NumBlocks; + UINTN ClockFrequencySelect; + + //Calculate BlockSize and Total number of blocks in the detected card. + GetBlockInformation(&BlockSize, &NumBlocks); + gCardInfo->BlockSize = BlockSize; + gCardInfo->NumBlocks = NumBlocks; + + //Calculate Card clock divider value. + CalculateCardCLKD(&ClockFrequencySelect); + gCardInfo->ClockFrequencySelect = ClockFrequencySelect; +} + +STATIC +EFI_STATUS +InitializeMMCHS ( + VOID + ) +{ + UINT8 Data = 0; + EFI_STATUS Status; + + //Select Device group to belong to P1 device group in Power IC. + Data = DEV_GRP_P1; + Status = gTPS65950->Write(gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEV_GRP), 1, &Data); + ASSERT_EFI_ERROR(Status); + + //Configure voltage regulator for MMC1 in Power IC to output 3.0 voltage. + Data = VSEL_3_00V; + Status = gTPS65950->Write(gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID4, VMMC1_DEDICATED_REG), 1, &Data); + ASSERT_EFI_ERROR(Status); + + //After ramping up voltage, set VDDS stable bit to indicate that voltage level is stable. + MmioOr32(CONTROL_PBIAS_LITE, (PBIASLITEVMODE0 | PBIASLITEPWRDNZ0 | PBIASSPEEDCTRL0 | PBIASLITEVMODE1 | PBIASLITEWRDNZ1)); + + //Software reset of the MMCHS host controller. + MmioWrite32(MMCHS_SYSCONFIG, SOFTRESET); + gBS->Stall(1000); + while ((MmioRead32(MMCHS_SYSSTATUS) & RESETDONE_MASK) != RESETDONE); + + //Soft reset for all. + MmioWrite32(MMCHS_SYSCTL, SRA); + gBS->Stall(1000); + while ((MmioRead32(MMCHS_SYSCTL) & SRA) != 0x0); + + //Voltage capabilities initialization. Activate VS18 and VS30. + MmioOr32(MMCHS_CAPA, (VS30 | VS18)); + + //Wakeup configuration + MmioOr32(MMCHS_SYSCONFIG, ENAWAKEUP); + MmioOr32(MMCHS_HCTL, IWE); + + //MMCHS Controller default initialization + MmioOr32(MMCHS_CON, (OD | DW8_1_4_BIT | CEATA_OFF)); + + MmioWrite32(MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_OFF)); + + //Enable internal clock + MmioOr32(MMCHS_SYSCTL, ICE); + + //Set the clock frequency to 80KHz. + UpdateMMCHSClkFrequency(CLKD_80KHZ); + + //Enable SD bus power. + MmioOr32(MMCHS_HCTL, (SDBP_ON)); + + //Poll till SD bus power bit is set. + while ((MmioRead32(MMCHS_HCTL) & SDBP_MASK) != SDBP_ON); + + return Status; +} + +STATIC +EFI_STATUS +PerformCardIdenfication ( + VOID + ) +{ + EFI_STATUS Status; + UINTN CmdArgument = 0; + UINTN Response = 0; + UINTN RetryCount = 0; + BOOLEAN SDCmd8Supported = FALSE; + + //Enable interrupts. + MmioWrite32(MMCHS_IE, (BADA_EN | CERR_EN | DEB_EN | DCRC_EN | DTO_EN | CIE_EN | + CEB_EN | CCRC_EN | CTO_EN | BRR_EN | BWR_EN | TC_EN | CC_EN)); + + //Controller INIT procedure start. + MmioOr32(MMCHS_CON, INIT); + MmioWrite32(MMCHS_CMD, 0x00000000); + while (!(MmioRead32(MMCHS_STAT) & CC)); + + //Wait for 1 ms + gBS->Stall(1000); + + //Set CC bit to 0x1 to clear the flag + MmioOr32(MMCHS_STAT, CC); + + //Retry INIT procedure. + MmioWrite32(MMCHS_CMD, 0x00000000); + while (!(MmioRead32(MMCHS_STAT) & CC)); + + //End initialization sequence + MmioAnd32(MMCHS_CON, ~INIT); + + MmioOr32(MMCHS_HCTL, (SDVS_3_0_V | DTW_1_BIT | SDBP_ON)); + + //Change clock frequency to 400KHz to fit protocol + UpdateMMCHSClkFrequency(CLKD_400KHZ); + + MmioOr32(MMCHS_CON, OD); + + //Send CMD0 command. + Status = SendCmd(CMD0, CMD0_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "Cmd0 fails.\n")); + return Status; + } + + DEBUG ((EFI_D_INFO, "CMD0 response: %x\n", MmioRead32(MMCHS_RSP10))); + + //Send CMD5 command. + Status = SendCmd(CMD5, CMD5_INT_EN, CmdArgument); + if (Status == EFI_SUCCESS) { + DEBUG ((EFI_D_ERROR, "CMD5 Success. SDIO card. Follow SDIO card specification.\n")); + DEBUG ((EFI_D_INFO, "CMD5 response: %x\n", MmioRead32(MMCHS_RSP10))); + //NOTE: Returning unsupported error for now. Need to implement SDIO specification. + return EFI_UNSUPPORTED; + } else { + DEBUG ((EFI_D_INFO, "CMD5 fails. Not an SDIO card.\n")); + } + + MmioOr32(MMCHS_SYSCTL, SRC); + gBS->Stall(1000); + while ((MmioRead32(MMCHS_SYSCTL) & SRC)); + + //Send CMD8 command. (New v2.00 command for Voltage check) + //Only 2.7V - 3.6V is supported for SD2.0, only SD 2.0 card can pass. + //MMC & SD1.1 card will fail this command. + CmdArgument = CMD8_ARG; + Status = SendCmd(CMD8, CMD8_INT_EN, CmdArgument); + if (Status == EFI_SUCCESS) { + Response = MmioRead32(MMCHS_RSP10); + DEBUG ((EFI_D_INFO, "CMD8 success. CMD8 response: %x\n", Response)); + if (Response != CmdArgument) { + return EFI_DEVICE_ERROR; + } + DEBUG ((EFI_D_INFO, "Card is SD2.0\n")); + SDCmd8Supported = TRUE; //Supports high capacity. + } else { + DEBUG ((EFI_D_INFO, "CMD8 fails. Not an SD2.0 card.\n")); + } + + MmioOr32(MMCHS_SYSCTL, SRC); + gBS->Stall(1000); + while ((MmioRead32(MMCHS_SYSCTL) & SRC)); + + //Poll till card is busy + while (RetryCount < MAX_RETRY_COUNT) { + //Send CMD55 command. + CmdArgument = 0; + Status = SendCmd(CMD55, CMD55_INT_EN, CmdArgument); + if (Status == EFI_SUCCESS) { + DEBUG ((EFI_D_INFO, "CMD55 success. CMD55 response: %x\n", MmioRead32(MMCHS_RSP10))); + gCardInfo->CardType = SD_CARD; + } else { + DEBUG ((EFI_D_INFO, "CMD55 fails.\n")); + gCardInfo->CardType = MMC_CARD; + } + + //Send appropriate command for the card type which got detected. + if (gCardInfo->CardType == SD_CARD) { + CmdArgument = ((UINTN *) &(gCardInfo->OCRData))[0]; + + //Set HCS bit. + if (SDCmd8Supported) { + CmdArgument |= HCS; + } + + Status = SendCmd(ACMD41, ACMD41_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_INFO, "ACMD41 fails.\n")); + return Status; + } + ((UINT32 *) &(gCardInfo->OCRData))[0] = MmioRead32(MMCHS_RSP10); + DEBUG ((EFI_D_INFO, "SD card detected. ACMD41 OCR: %x\n", ((UINT32 *) &(gCardInfo->OCRData))[0])); + } else if (gCardInfo->CardType == MMC_CARD) { + CmdArgument = 0; + Status = SendCmd(CMD1, CMD1_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_INFO, "CMD1 fails.\n")); + return Status; + } + Response = MmioRead32(MMCHS_RSP10); + DEBUG ((EFI_D_INFO, "MMC card detected.. CMD1 response: %x\n", Response)); + + //NOTE: For now, I am skipping this since I only have an SD card. + //Compare card OCR and host OCR (Section 22.6.1.3.2.4) + return EFI_UNSUPPORTED; //For now, MMC is not supported. + } + + //Poll the card until it is out of its power-up sequence. + if (gCardInfo->OCRData.Busy == 1) { + + if (SDCmd8Supported) { + gCardInfo->CardType = SD_CARD_2; + } + + //Card is ready. Check CCS (Card capacity status) bit (bit#30). + //SD 2.0 standard card will response with CCS 0, SD high capacity card will respond with CCS 1. + if (gCardInfo->OCRData.AccessMode & BIT1) { + gCardInfo->CardType = SD_CARD_2_HIGH; + DEBUG ((EFI_D_INFO, "High capacity card.\n")); + } else { + DEBUG ((EFI_D_INFO, "Standard capacity card.\n")); + } + + break; + } + + gBS->Stall(1000); + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + DEBUG ((EFI_D_ERROR, "Timeout error. RetryCount: %d\n", RetryCount)); + return EFI_TIMEOUT; + } + + //Read CID data. + CmdArgument = 0; + Status = SendCmd(CMD2, CMD2_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD2 fails. Status: %x\n", Status)); + return Status; + } + + DEBUG ((EFI_D_INFO, "CMD2 response: %x %x %x %x\n", MmioRead32(MMCHS_RSP10), MmioRead32(MMCHS_RSP32), MmioRead32(MMCHS_RSP54), MmioRead32(MMCHS_RSP76))); + + //Parse CID register data. + ParseCardCIDData(MmioRead32(MMCHS_RSP10), MmioRead32(MMCHS_RSP32), MmioRead32(MMCHS_RSP54), MmioRead32(MMCHS_RSP76)); + + //Read RCA + CmdArgument = 0; + Status = SendCmd(CMD3, CMD3_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD3 fails. Status: %x\n", Status)); + return Status; + } + + //Set RCA for the detected card. RCA is CMD3 response. + gCardInfo->RCA = (MmioRead32(MMCHS_RSP10) >> 16); + DEBUG ((EFI_D_INFO, "CMD3 response: RCA %x\n", gCardInfo->RCA)); + + //MMC Bus setting change after card identification. + MmioAnd32(MMCHS_CON, ~OD); + MmioOr32(MMCHS_HCTL, SDVS_3_0_V); + UpdateMMCHSClkFrequency(CLKD_400KHZ); //Set the clock frequency to 400KHz. + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +GetCardSpecificData ( + VOID + ) +{ + EFI_STATUS Status; + UINTN CmdArgument; + + //Send CMD9 to retrieve CSD. + CmdArgument = gCardInfo->RCA << 16; + Status = SendCmd(CMD9, CMD9_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD9 fails. Status: %x\n", Status)); + return Status; + } + + //Populate 128-bit CSD register data. + ((UINT32 *)&(gCardInfo->CSDData))[0] = MmioRead32(MMCHS_RSP10); + ((UINT32 *)&(gCardInfo->CSDData))[1] = MmioRead32(MMCHS_RSP32); + ((UINT32 *)&(gCardInfo->CSDData))[2] = MmioRead32(MMCHS_RSP54); + ((UINT32 *)&(gCardInfo->CSDData))[3] = MmioRead32(MMCHS_RSP76); + + DEBUG ((EFI_D_INFO, "CMD9 response: %x %x %x %x\n", MmioRead32(MMCHS_RSP10), MmioRead32(MMCHS_RSP32), MmioRead32(MMCHS_RSP54), MmioRead32(MMCHS_RSP76))); + + //Calculate total number of blocks and max. data transfer rate supported by the detected card. + GetCardConfigurationData(); + + //Change MMCHS clock frequency to what detected card can support. + UpdateMMCHSClkFrequency(gCardInfo->ClockFrequencySelect); + + return Status; +} + +STATIC +EFI_STATUS +PerformCardConfiguration ( + VOID + ) +{ + UINTN CmdArgument = 0; + EFI_STATUS Status; + + //Send CMD7 + CmdArgument = gCardInfo->RCA << 16; + Status = SendCmd(CMD7, CMD7_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD7 fails. Status: %x\n", Status)); + return Status; + } + + //Send CMD16 to set the block length + CmdArgument = gCardInfo->BlockSize; + Status = SendCmd(CMD16, CMD16_INT_EN, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD16 fails. Status: %x\n", Status)); + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +ReadBlockData( + IN EFI_BLOCK_IO_PROTOCOL *This, + OUT VOID *Buffer + ) +{ + UINTN MmcStatus; + UINTN *DataBuffer = Buffer; + UINTN DataSize = This->Media->BlockSize/4; + UINTN Count; + UINTN RetryCount = 0; + + //Check controller status to make sure there is no error. + while (RetryCount < MAX_RETRY_COUNT) { + do { + //Read Status. + MmcStatus = MmioRead32(MMCHS_STAT); + } while(MmcStatus == 0); + + //Check if Buffer read ready (BRR) bit is set? + if (MmcStatus & BRR) { + + //Clear BRR bit + MmioOr32(MMCHS_STAT, BRR); + + //Read block worth of data. + for (Count = 0; Count < DataSize; Count++) { + *DataBuffer++ = MmioRead32(MMCHS_DATA); + } + break; + } + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +WriteBlockData( + IN EFI_BLOCK_IO_PROTOCOL *This, + OUT VOID *Buffer + ) +{ + UINTN MmcStatus; + UINTN *DataBuffer = Buffer; + UINTN DataSize = This->Media->BlockSize/4; + UINTN Count; + UINTN RetryCount = 0; + + //Check controller status to make sure there is no error. + while (RetryCount < MAX_RETRY_COUNT) { + do { + //Read Status. + MmcStatus = MmioRead32(MMCHS_STAT); + } while(MmcStatus == 0); + + //Check if Buffer write ready (BWR) bit is set? + if (MmcStatus & BWR) { + + //Clear BWR bit + MmioOr32(MMCHS_STAT, BWR); + + //Write block worth of data. + for (Count = 0; Count < DataSize; Count++) { + MmioWrite32(MMCHS_DATA, *DataBuffer++); + } + + break; + } + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +TransferBlockData( + IN EFI_BLOCK_IO_PROTOCOL *This, + OUT VOID *Buffer, + IN OPERATION_TYPE OperationType + ) +{ + EFI_STATUS Status; + UINTN MmcStatus; + UINTN RetryCount = 0; + + //Read or Write data. + if (OperationType == READ) { + Status = ReadBlockData(This, Buffer); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "ReadBlockData fails.\n")); + return Status; + } + } else if (OperationType == WRITE) { + Status = WriteBlockData(This, Buffer); + if (EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR, "WriteBlockData fails.\n")); + return Status; + } + } + + //Check for the Transfer completion. + while (RetryCount < MAX_RETRY_COUNT) { + //Read Status + do { + MmcStatus = MmioRead32(MMCHS_STAT); + } while (MmcStatus == 0); + + //Check if Transfer complete (TC) bit is set? + if (MmcStatus & TC) { + break; + } else { + DEBUG ((EFI_D_ERROR, "MmcStatus for TC: %x\n", MmcStatus)); + //Check if DEB, DCRC or DTO interrupt occured. + if ((MmcStatus & DEB) | (MmcStatus & DCRC) | (MmcStatus & DTO)) { + //There was an error during the data transfer. + + //Set SRD bit to 1 and wait until it return to 0x0. + MmioOr32(MMCHS_SYSCTL, SRD); + while((MmioRead32(MMCHS_SYSCTL) & SRD) != 0x0); + + return EFI_DEVICE_ERROR; + } + } + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + DEBUG ((EFI_D_ERROR, "TransferBlockData timed out.\n")); + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SdReadWrite ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINTN Lba, + OUT VOID *Buffer, + IN UINTN BufferSize, + IN OPERATION_TYPE OperationType + ) +{ + EFI_STATUS Status; + UINTN RetryCount = 0; + UINTN NumBlocks; + UINTN Cmd = 0; + UINTN CmdInterruptEnable = 0; + UINTN CmdArgument = 0; + + //Check if the data lines are not in use. + while ((RetryCount++ < MAX_RETRY_COUNT) && ((MmioRead32(MMCHS_PSTATE) & DATI_MASK) != DATI_ALLOWED)); + if (RetryCount == MAX_RETRY_COUNT) { + return EFI_TIMEOUT; + } + + //Populate the command information based on the operation type. + if (OperationType == READ) { + Cmd = CMD17; //Single block read + CmdInterruptEnable = CMD17_INT_EN; + } else if (OperationType == WRITE) { + Cmd = CMD24; //Single block write + CmdInterruptEnable = CMD24_INT_EN; + } + + //Calculate total number of blocks its going to read. + NumBlocks = (BufferSize + (This->Media->BlockSize - 1))/This->Media->BlockSize; + + //Set command argument based on the card access mode (Byte mode or Block mode) + if (gCardInfo->OCRData.AccessMode & BIT1) { + CmdArgument = (UINTN)Lba; + } else { + CmdArgument = (UINTN)Lba * This->Media->BlockSize; + } + + while(NumBlocks) { + //Send Command. + Status = SendCmd(Cmd, CmdInterruptEnable, CmdArgument); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "CMD fails. Status: %x\n", Status)); + return Status; + } + + //Transfer a block worth of data. + Status = TransferBlockData(This, Buffer, OperationType); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "TransferBlockData fails. %x\n", Status)); + return Status; + } + + //Adjust command argument. + if (gCardInfo->OCRData.AccessMode & BIT1) { + CmdArgument++; //Increase BlockIndex by one. + } else { + CmdArgument += This->Media->BlockSize; //Increase BlockIndex by BlockSize + } + + //Adjust Buffer. + Buffer = (UINT8 *)Buffer + This->Media->BlockSize; + NumBlocks--; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +MMCHSReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +MMCHSReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + + if (Buffer == NULL) + { + return EFI_INVALID_PARAMETER; + } + + if (Lba > This->Media->LastBlock) + { + return EFI_INVALID_PARAMETER; + } + + if ((BufferSize % This->Media->BlockSize) != 0) + { + return EFI_BAD_BUFFER_SIZE; + } + + //Perform Read operation. + Status = SdReadWrite(This, (UINTN)Lba, Buffer, BufferSize, READ); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "Read operation fails.\n")); + } + + return Status; +} + +EFI_STATUS +EFIAPI +MMCHSWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (Lba > This->Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((BufferSize % This->Media->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (This->Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + //Perform write operation. + Status = SdReadWrite(This, (UINTN)Lba, Buffer, BufferSize, WRITE); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "Write operation fails.\n")); + } + + return Status; +} + +EFI_STATUS +EFIAPI +MMCHSFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} + +EFI_BLOCK_IO_PROTOCOL BlockIo = +{ + EFI_BLOCK_IO_INTERFACE_REVISION, // Revision + &MMCHSMedia, // *Media + MMCHSReset, // Reset + MMCHSReadBlocks, // ReadBlocks + MMCHSWriteBlocks, // WriteBlocks + MMCHSFlushBlocks // FlushBlocks +}; + +EFI_STATUS +MMCHSInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol(&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950); + ASSERT_EFI_ERROR(Status); + + gCardInfo = (CARD_INFO *)AllocateZeroPool(sizeof(CARD_INFO)); + if (gCardInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + //Initialize MMC host controller. + Status = InitializeMMCHS(); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "Initialize MMC host controller fails. Status: %x\n", Status)); + return Status; + } + + //Card idenfication + Status = PerformCardIdenfication(); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "No MMC/SD card detected.\n")); + return EFI_SUCCESS; //NOTE: Check if this is correct.. + } + + //Get CSD (Card specific data) for the detected card. + Status = GetCardSpecificData(); + if (EFI_ERROR(Status)) { + return Status; + } + + //Configure the card in data transfer mode. + Status = PerformCardConfiguration(); + if (EFI_ERROR(Status)) { + return Status; + } + + //Patch the Media structure. + MMCHSMedia.LastBlock = (gCardInfo->NumBlocks - 1); + MMCHSMedia.BlockSize = gCardInfo->BlockSize; + + //Publish BlockIO. + Status = gBS->InstallMultipleProtocolInterfaces(&ImageHandle, + &gEfiBlockIoProtocolGuid, &BlockIo, + &gEfiDevicePathProtocolGuid, &gMmcHsDevicePath, + NULL); + return Status; +} |