/** @file * * Copyright (c) 2008 - 2009, Apple Inc. All rights reserved. * Copyright (c) 2011, ARM Limited. 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 "MmcHostDxe.h" EMBEDDED_EXTERNAL_DEVICE *gTPS65950; UINT8 MaxDataTransferRate = 0; UINT32 Rca = 0; BOOLEAN BitModeSet = FALSE; typedef struct { VENDOR_DEVICE_PATH Mmc; EFI_DEVICE_PATH End; } MMCHS_DEVICE_PATH; MMCHS_DEVICE_PATH gMMCDevicePath = { { 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 } }; BOOLEAN IgnoreCommand ( UINT32 Command ) { switch(Command) { case MMC_CMD12: return TRUE; case MMC_CMD13: return TRUE; default: return FALSE; } } UINT32 TranslateCommand ( UINT32 Command ) { UINT32 Translation; switch(Command) { case MMC_CMD2: Translation = CMD2; break; case MMC_CMD3: Translation = CMD3; break; /*case MMC_CMD6: Translation = CMD6; break;*/ case MMC_CMD7: Translation = CMD7; break; case MMC_CMD8: Translation = CMD8; break; case MMC_CMD9: Translation = CMD9; break; /*case MMC_CMD12: Translation = CMD12; break; case MMC_CMD13: Translation = CMD13; break;*/ case MMC_CMD16: Translation = CMD16; break; case MMC_CMD17: Translation = 0x113A0014;//CMD17; break; case MMC_CMD24: Translation = CMD24 | 4; break; case MMC_CMD55: Translation = CMD55; break; case MMC_ACMD41: Translation = ACMD41; break; default: Translation = Command; } return Translation; } VOID CalculateCardCLKD ( UINTN *ClockFrequencySelect ) { DEBUG((EFI_D_ERROR, "CalculateCardCLKD()\n")); UINTN TransferRateValue = 0; UINTN TimeValue = 0 ; UINTN Frequency = 0; // For SD Cards we would need to send CMD6 to set // speeds abouve 25MHz. High Speed mode 50 MHz and up //Calculate Transfer rate unit (Bits 2:0 of TRAN_SPEED) switch (MaxDataTransferRate & 0x7) { // 2 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) { // 6 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)); } VOID UpdateMMCHSClkFrequency ( UINTN NewCLKD ) { DEBUG((EFI_D_ERROR, "UpdateMMCHSClkFrequency()\n")); //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); } EFI_STATUS InitializeMMCHS ( VOID ) { DEBUG((EFI_D_ERROR, "InitializeMMCHS()\n")); 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)); // Enable WP GPIO MmioAndThenOr32 (GPIO1_BASE + GPIO_OE, ~BIT23, BIT23); // Enable Card Detect Data = CARD_DETECT_ENABLE; gTPS65950->Write (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, TPS65950_GPIO_CTRL), 1, &Data); return Status; } BOOLEAN MMCIsCardPresent ( VOID ) { //DEBUG((EFI_D_ERROR, "MMCIsCardPresent()\n")); EFI_STATUS Status; UINT8 Data; // // Card detect is a GPIO0 on the TPS65950 // Status = gTPS65950->Read (gTPS65950, EXTERNAL_DEVICE_REGISTER(I2C_ADDR_GRP_ID2, GPIODATAIN1), 1, &Data); if (EFI_ERROR (Status)) { return FALSE; } return !(Data & CARD_DETECT_BIT); } BOOLEAN MMCIsReadOnly ( VOID ) { /* Note: * On our BeagleBoard the SD card WP pin is always read as TRUE. * Probably something wrong with GPIO configuration. * BeagleBoard-xM uses microSD cards so there is no write protect at all. * Hence commenting out SD card WP pin read status. */ //return (MmioRead32 (GPIO1_BASE + GPIO_DATAIN) & BIT23) == BIT23; return 0; } // TODO EFI_GUID mPL180MciDevicePathGuid = EFI_CALLER_ID_GUID; EFI_STATUS MMCBuildDevicePath ( IN EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode; NewDevicePathNode = CreateDeviceNode(HARDWARE_DEVICE_PATH,HW_VENDOR_DP,sizeof(VENDOR_DEVICE_PATH)); CopyGuid(&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid,&mPL180MciDevicePathGuid); *DevicePath = NewDevicePathNode; return EFI_SUCCESS; } EFI_STATUS MMCSendCommand ( IN MMC_CMD MmcCmd, IN UINT32 Argument ) { if (IgnoreCommand(MmcCmd)) return EFI_SUCCESS; MmcCmd = TranslateCommand(MmcCmd); //DEBUG((EFI_D_ERROR, "MMCSendCommand(%d)\n", MmcCmd)); 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, Argument); //TODO: fix this //Enable interrupt enable events to occur //MmioWrite32 (MMCHS_IE, CmdInterruptEnableVal); //Send a command MmioWrite32 (MMCHS_CMD, MmcCmd); //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: 0x%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) { DEBUG((EFI_D_ERROR, "MMCSendCommand: Timeout\n")); return EFI_TIMEOUT; } return EFI_SUCCESS; } EFI_STATUS MMCNotifyState ( IN MMC_STATE State ) { EFI_STATUS Status; UINTN freqSel; switch(State) { case MmcInvalidState: ASSERT(0); break; case MmcHwInitializationState: BitModeSet = FALSE; DEBUG((EFI_D_ERROR, "MMCHwInitializationState()\n")); Status = InitializeMMCHS (); if (EFI_ERROR(Status)) { DEBUG ((EFI_D_ERROR, "Initialize MMC host controller fails. Status: %x\n", Status)); return Status; } //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); //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); break; case MmcIdleState: break; case MmcReadyState: break; case MmcIdentificationState: break; case MmcStandByState: CalculateCardCLKD(&freqSel); UpdateMMCHSClkFrequency(freqSel); break; case MmcTransferState: if (!BitModeSet) { Status = MMCSendCommand (CMD55, Rca << 16); if (!EFI_ERROR (Status)) { // set device into 4-bit data bus mode Status = MMCSendCommand (ACMD6, 0x2); if (!EFI_ERROR (Status)) { // Set host controler into 4-bit mode MmioOr32 (MMCHS_HCTL, DTW_4_BIT); DEBUG ((EFI_D_INFO, "SD Memory Card set to 4-bit mode\n")); BitModeSet = TRUE; } } } break; case MmcSendingDataState: break; case MmcReceiveDataState: break; case MmcProgrammingState: break; case MmcDisconnectState: default: ASSERT(0); } return EFI_SUCCESS; } EFI_STATUS MMCReceiveResponse ( IN MMC_RESPONSE_TYPE Type, IN UINT32* Buffer ) { //DEBUG((EFI_D_ERROR, "MMCReceiveResponse()\n")); if (Buffer == NULL) { DEBUG((EFI_D_ERROR, "Buffer was NULL\n")); return EFI_INVALID_PARAMETER; } if (Type == MMC_RESPONSE_TYPE_R2) { Buffer[0] = MmioRead32 (MMCHS_RSP10); Buffer[1] = MmioRead32 (MMCHS_RSP32); Buffer[2] = MmioRead32 (MMCHS_RSP54); Buffer[3] = MmioRead32 (MMCHS_RSP76); } else { Buffer[0] = MmioRead32 (MMCHS_RSP10); } if (Type == MMC_RESPONSE_TYPE_CSD) { MaxDataTransferRate = Buffer[3] & 0xFF; } else if (Type == MMC_RESPONSE_TYPE_RCA) { Rca = Buffer[0] >> 16; } return EFI_SUCCESS; } EFI_STATUS MMCReadBlockData ( IN EFI_LBA Lba, IN UINTN Length, IN UINT32* Buffer ) { //DEBUG((EFI_D_ERROR, "MMCReadBlockData(LBA: 0x%x, ", Lba)); //DEBUG((EFI_D_ERROR, "Length: 0x%x, ", Length)); //DEBUG((EFI_D_ERROR, "Buffer: 0x%x)\n", Buffer)); UINTN MmcStatus; 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); for (Count = 0; Count < Length / 4; Count++) { *Buffer++ = MmioRead32(MMCHS_DATA); } break; } RetryCount++; } if (RetryCount == MAX_RETRY_COUNT) { return EFI_TIMEOUT; } return EFI_SUCCESS; } EFI_STATUS MMCWriteBlockData ( IN EFI_LBA Lba, IN UINTN Length, IN UINT32* Buffer ) { UINTN MmcStatus; 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 < Length / 4; Count++) { MmioWrite32 (MMCHS_DATA, *Buffer++); } break; } RetryCount++; } if (RetryCount == MAX_RETRY_COUNT) { return EFI_TIMEOUT; } return EFI_SUCCESS; } EFI_MMC_HOST_PROTOCOL gMMCHost = { MMCIsCardPresent, MMCIsReadOnly, MMCBuildDevicePath, MMCNotifyState, MMCSendCommand, MMCReceiveResponse, MMCReadBlockData, MMCWriteBlockData }; EFI_STATUS MMCInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { DEBUG((EFI_D_ERROR, "MMCInitialize()\n")); EFI_STATUS Status; EFI_HANDLE Handle = NULL; Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950); ASSERT_EFI_ERROR(Status); Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gEfiMmcHostProtocolGuid, &gMMCHost, NULL ); ASSERT_EFI_ERROR (Status); return Status; }