/** @file * * Copyright (c) 2008 - 2009, Apple Inc. All rights reserved. * Copyright (c) 2011 - 2014, 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 mMaxDataTransferRate = 0; UINT32 mRca = 0; BOOLEAN mBitModeSet = 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 ) { UINTN TransferRateValue = 0; UINTN TimeValue = 0 ; UINTN Frequency = 0; DEBUG ((DEBUG_BLKIO, "CalculateCardCLKD()\n")); // 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 (mMaxDataTransferRate & 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 ((DEBUG_BLKIO, "Invalid parameter.\n")); ASSERT(FALSE); return; } //Calculate Time value (Bits 6:3 of TRAN_SPEED) switch ((mMaxDataTransferRate >> 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 ((DEBUG_BLKIO, "Invalid parameter.\n")); ASSERT(FALSE); return; } Frequency = TransferRateValue * TimeValue/10; // Calculate Clock divider value to program in MMCHS_SYSCTL[CLKD] field. *ClockFrequencySelect = ((MMC_REFERENCE_CLK/Frequency) + 1); DEBUG ((DEBUG_BLKIO, "mMaxDataTransferRate: 0x%x, Frequency: %d KHz, ClockFrequencySelect: %x\n", mMaxDataTransferRate, Frequency/1000, *ClockFrequencySelect)); } VOID UpdateMMCHSClkFrequency ( UINTN NewCLKD ) { DEBUG ((DEBUG_BLKIO, "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 ) { UINT8 Data; EFI_STATUS Status; DEBUG ((DEBUG_BLKIO, "InitializeMMCHS()\n")); // 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 ( IN EFI_MMC_HOST_PROTOCOL *This ) { 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 ( IN EFI_MMC_HOST_PROTOCOL *This ) { /* 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_MMC_HOST_PROTOCOL *This, 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 EFI_MMC_HOST_PROTOCOL *This, IN MMC_CMD MmcCmd, IN UINT32 Argument ) { UINTN MmcStatus; UINTN RetryCount = 0; if (IgnoreCommand(MmcCmd)) return EFI_SUCCESS; MmcCmd = TranslateCommand(MmcCmd); //DEBUG ((EFI_D_ERROR, "MMCSendCommand(%d)\n", MmcCmd)); // 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 ((DEBUG_BLKIO, "MMCSendCommand: Timeout\n")); return EFI_TIMEOUT; } return EFI_SUCCESS; } EFI_STATUS MMCNotifyState ( IN EFI_MMC_HOST_PROTOCOL *This, IN MMC_STATE State ) { EFI_STATUS Status; UINTN FreqSel; switch(State) { case MmcInvalidState: ASSERT(0); break; case MmcHwInitializationState: mBitModeSet = FALSE; DEBUG ((DEBUG_BLKIO, "MMCHwInitializationState()\n")); Status = InitializeMMCHS (); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_BLKIO, "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 (!mBitModeSet) { Status = MMCSendCommand (This, CMD55, mRca << 16); if (!EFI_ERROR (Status)) { // Set device into 4-bit data bus mode Status = MMCSendCommand (This, ACMD6, 0x2); if (!EFI_ERROR (Status)) { // Set host controler into 4-bit mode MmioOr32 (MMCHS_HCTL, DTW_4_BIT); DEBUG ((DEBUG_BLKIO, "SD Memory Card set to 4-bit mode\n")); mBitModeSet = TRUE; } } } break; case MmcSendingDataState: break; case MmcReceiveDataState: break; case MmcProgrammingState: break; case MmcDisconnectState: default: ASSERT(0); } return EFI_SUCCESS; } EFI_STATUS MMCReceiveResponse ( IN EFI_MMC_HOST_PROTOCOL *This, IN MMC_RESPONSE_TYPE Type, IN UINT32* Buffer ) { if (Buffer == NULL) { 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) { mMaxDataTransferRate = Buffer[3] & 0xFF; } else if (Type == MMC_RESPONSE_TYPE_RCA) { mRca = Buffer[0] >> 16; } return EFI_SUCCESS; } EFI_STATUS MMCReadBlockData ( IN EFI_MMC_HOST_PROTOCOL *This, IN EFI_LBA Lba, IN UINTN Length, IN UINT32* Buffer ) { UINTN MmcStatus; UINTN Count; UINTN RetryCount = 0; DEBUG ((DEBUG_BLKIO, "MMCReadBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", Lba, Length, Buffer)); // 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_MMC_HOST_PROTOCOL *This, 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 = { MMC_HOST_PROTOCOL_REVISION, MMCIsCardPresent, MMCIsReadOnly, MMCBuildDevicePath, MMCNotifyState, MMCSendCommand, MMCReceiveResponse, MMCReadBlockData, MMCWriteBlockData }; EFI_STATUS MMCInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle = NULL; DEBUG ((DEBUG_BLKIO, "MMCInitialize()\n")); Status = gBS->LocateProtocol (&gEmbeddedExternalDeviceProtocolGuid, NULL, (VOID **)&gTPS65950); ASSERT_EFI_ERROR(Status); Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gEfiMmcHostProtocolGuid, &gMMCHost, NULL ); ASSERT_EFI_ERROR (Status); return Status; }