audk/Omap35xxPkg/MmcHostDxe/MmcHostDxe.c

668 lines
15 KiB
C
Executable File

/** @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;
}