mirror of https://github.com/acidanthera/audk.git
1791 lines
45 KiB
C
1791 lines
45 KiB
C
/** @file
|
|
|
|
The SD host controller driver model and HC protocol routines.
|
|
|
|
Copyright (c) 2013-2016 Intel Corporation.
|
|
|
|
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 "SDController.h"
|
|
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gSDControllerDriverBinding = {
|
|
SDControllerSupported,
|
|
SDControllerStart,
|
|
SDControllerStop,
|
|
0x20,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
EFI_SD_HOST_IO_PROTOCOL mSDHostIo = {
|
|
EFI_SD_HOST_IO_PROTOCOL_REVISION_01,
|
|
{
|
|
0, // HighSpeedSupport
|
|
0, // V18Support
|
|
0, // V30Support
|
|
0, // V33Support
|
|
0, // Reserved0
|
|
0, // BusWidth4
|
|
0, // BusWidth8
|
|
0, // Reserved1
|
|
0, // Reserved1
|
|
(512 * 1024) //BoundarySize
|
|
},
|
|
SendCommand,
|
|
SetClockFrequency,
|
|
SetBusWidth,
|
|
SetHostVoltage,
|
|
ResetSDHost,
|
|
EnableAutoStopCmd,
|
|
DetectCardAndInitHost,
|
|
SetBlockLength,
|
|
SetHighSpeedMode,
|
|
SetDDRMode
|
|
};
|
|
|
|
/**
|
|
Find sdclk_freq_sel and upr_sdclk_freq_sel bits
|
|
for Clock Control Register (CLK_CTL)Offset 2Ch when using 8bit or 10bit
|
|
divided clock mode.
|
|
|
|
@param BaseClockFreg Base Clock Frequency in Hz For SD Clock in the
|
|
Capabilities register.
|
|
@param TargetFreq Target Frequency in Hz to reach.
|
|
@param Is8BitMode True if 8-bit Divided Clock Mode else 10bit mode.
|
|
@param Bits sdclk_freq_sel and upr_sdclk_freq_sel bits for
|
|
TargetFreq.
|
|
|
|
@return EFI_SUCCESS // Bits setup.
|
|
@return EFI_UNSUPPORTED // Cannot divide base clock to reach target clock.
|
|
**/
|
|
EFI_STATUS
|
|
DividedClockModeBits (
|
|
IN CONST UINTN BaseClockFreg,
|
|
IN CONST UINTN TargetFreq,
|
|
IN CONST BOOLEAN Is8BitMode,
|
|
OUT UINT16 *Bits
|
|
)
|
|
{
|
|
UINTN N;
|
|
UINTN CurrFreq;
|
|
|
|
*Bits = 0;
|
|
CurrFreq = BaseClockFreg;
|
|
N = 0;
|
|
//
|
|
// N == 0 same for 8bit & 10bit mode i.e. BaseClockFreg of controller.
|
|
//
|
|
if (TargetFreq < CurrFreq) {
|
|
if (Is8BitMode) {
|
|
N = 1;
|
|
do {
|
|
//
|
|
// N values for 8bit mode when N > 0.
|
|
// Bit[15:8] SDCLK Frequency Select at offset 2Ch
|
|
// 80h - base clock divided by 256
|
|
// 40h - base clock divided by 128
|
|
// 20h - base clock divided by 64
|
|
// 10h - base clock divided by 32
|
|
// 08h - base clock divided by 16
|
|
// 04h - base clock divided by 8
|
|
// 02h - base clock divided by 4
|
|
// 01h - base clock divided by 2
|
|
//
|
|
CurrFreq = BaseClockFreg / (2 * N);
|
|
if (TargetFreq >= CurrFreq) {
|
|
break;
|
|
}
|
|
N *= 2;
|
|
if (N > V_MMIO_CLKCTL_MAX_8BIT_FREQ_SEL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
} while (TRUE);
|
|
} else {
|
|
N = 1;
|
|
CurrFreq = BaseClockFreg / (2 * N);
|
|
//
|
|
// (try N = 0 or 1 first since don't want divide by 0).
|
|
//
|
|
if (TargetFreq < CurrFreq) {
|
|
//
|
|
// If still no match then calculate it for 10bit.
|
|
// N values for 10bit mode.
|
|
// N 1/2N Divided Clock (Duty 50%).
|
|
// from Spec "The length of divider is extended to 10 bits and all
|
|
// divider values shall be supported.
|
|
//
|
|
N = (BaseClockFreg / TargetFreq) / 2;
|
|
|
|
//
|
|
// Can only be N or N+1;
|
|
//
|
|
CurrFreq = BaseClockFreg / (2 * N);
|
|
if (TargetFreq < CurrFreq) {
|
|
N++;
|
|
CurrFreq = BaseClockFreg / (2 * N);
|
|
}
|
|
|
|
if (N > V_MMIO_CLKCTL_MAX_10BIT_FREQ_SEL) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Set upper bits of SDCLK Frequency Select (bits 7:6 of reg 0x2c).
|
|
//
|
|
*Bits |= ((UINT16) ((N >> 2) & B_MMIO_CLKCTL_UPR_SDCLK_FREQ_SEL_MASK));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set lower bits of SDCLK Frequency Select (bits 15:8 of reg 0x2c).
|
|
//
|
|
*Bits |= ((UINT16) ((UINT8) N) << 8);
|
|
DEBUG (
|
|
(EFI_D_INFO,
|
|
"SDIO:DividedClockModeBits: %dbit mode Want %dHz Got %dHz bits = %04x\r\n",
|
|
(Is8BitMode) ? 8 : 10,
|
|
TargetFreq,
|
|
CurrFreq,
|
|
(UINTN) *Bits
|
|
));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Print type of error and command index
|
|
|
|
@param CommandIndex Command index to set the command index field of command register.
|
|
@param ErrorCode Error interrupt status read from host controller
|
|
|
|
@return EFI_DEVICE_ERROR
|
|
@return EFI_TIMEOUT
|
|
@return EFI_CRC_ERROR
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetErrorReason (
|
|
IN UINT16 CommandIndex,
|
|
IN UINT16 ErrorCode
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = EFI_DEVICE_ERROR;
|
|
|
|
DEBUG((EFI_D_ERROR, "[%2d] -- ", CommandIndex));
|
|
|
|
if (ErrorCode & BIT0) {
|
|
Status = EFI_TIMEOUT;
|
|
DEBUG((EFI_D_ERROR, "Command Timeout Erro"));
|
|
}
|
|
|
|
if (ErrorCode & BIT1) {
|
|
Status = EFI_CRC_ERROR;
|
|
DEBUG((EFI_D_ERROR, "Command CRC Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT2) {
|
|
DEBUG((EFI_D_ERROR, "Command End Bit Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT3) {
|
|
DEBUG((EFI_D_ERROR, "Command Index Error"));
|
|
}
|
|
if (ErrorCode & BIT4) {
|
|
Status = EFI_TIMEOUT;
|
|
DEBUG((EFI_D_ERROR, "Data Timeout Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT5) {
|
|
Status = EFI_CRC_ERROR;
|
|
DEBUG((EFI_D_ERROR, "Data CRC Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT6) {
|
|
DEBUG((EFI_D_ERROR, "Data End Bit Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT7) {
|
|
DEBUG((EFI_D_ERROR, "Current Limit Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT8) {
|
|
DEBUG((EFI_D_ERROR, "Auto CMD12 Error"));
|
|
}
|
|
|
|
if (ErrorCode & BIT9) {
|
|
DEBUG((EFI_D_ERROR, "ADMA Error"));
|
|
}
|
|
|
|
DEBUG((EFI_D_ERROR, "\n"));
|
|
|
|
return Status;
|
|
}
|
|
/**
|
|
Enable/Disable High Speed transfer mode
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param Enable TRUE to Enable, FALSE to Disable
|
|
|
|
@return EFI_SUCCESS
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetHighSpeedMode (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
UINT32 Data;
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if (Enable) {
|
|
if (PcdGetBool(PcdSdHciQuirkNoHiSpd)) {
|
|
DEBUG ((EFI_D_INFO, "SDIO: Quirk never set High Speed Enable bit\r\n"));
|
|
return EFI_SUCCESS;
|
|
}
|
|
DEBUG ((EFI_D_INFO, "Enable High Speed transfer mode ... \r\n"));
|
|
Data |= BIT2;
|
|
} else {
|
|
Data &= ~BIT2;
|
|
}
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetDDRMode (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
UINT16 Data;
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL2,
|
|
1,
|
|
&Data
|
|
);
|
|
Data &= 0xFFF0;
|
|
if (Enable) {
|
|
Data |= 0x0004; // Enable DDR50 by default, later should enable other mode like HS200/400
|
|
Data |= BIT3; // Enable 1.8V Signaling
|
|
}
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL2,
|
|
1,
|
|
&Data
|
|
);
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
Power on/off the LED associated with the slot
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param Enable TRUE to set LED on, FALSE to set LED off
|
|
|
|
@return EFI_SUCCESS
|
|
**/
|
|
EFI_STATUS
|
|
HostLEDEnable (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 Data;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if (Enable) {
|
|
//
|
|
//LED On
|
|
//
|
|
Data |= BIT0;
|
|
} else {
|
|
//
|
|
//LED Off
|
|
//
|
|
Data &= ~BIT0;
|
|
}
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
The main function used to send the command to the card inserted into the SD host slot.
|
|
It will assemble the arguments to set the command register and wait for the command
|
|
and transfer completed until timeout. Then it will read the response register to fill
|
|
the ResponseData.
|
|
|
|
@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_OUT_OF_RESOURCES
|
|
@retval EFI_TIMEOUT
|
|
@retval EFI_DEVICE_ERROR
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SendCommand (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
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 OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
The main function used to send the command to the card inserted into the SD host
|
|
slot.
|
|
It will assemble the arguments to set the command register and wait for the command
|
|
and transfer completed until timeout. Then it will read the response register to fill
|
|
the ResponseData
|
|
|
|
Arguments:
|
|
This - Pointer to EFI_SD_HOST_IO_PROTOCOL
|
|
CommandIndex - The command index to set the command index field of command register
|
|
Argument - Command argument to set the argument field of command register
|
|
DataType - TRANSFER_TYPE, indicates no data, data in or data out
|
|
Buffer - Contains the data read from / write to the device
|
|
BufferSize - The size of the buffer
|
|
ResponseType - RESPONSE_TYPE
|
|
TimeOut - Time out value in 1 ms unit
|
|
ResponseData - Depending on the ResponseType, such as CSD or card status
|
|
|
|
Returns:
|
|
EFI_SUCCESS
|
|
EFI_INVALID_PARAMETER
|
|
EFI_OUT_OF_RESOURCES
|
|
EFI_TIMEOUT
|
|
EFI_DEVICE_ERROR
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 ResponseDataCount;
|
|
UINT32 Data;
|
|
UINT64 Data64;
|
|
UINT8 Index;
|
|
INTN TimeOut2;
|
|
BOOLEAN AutoCMD12Enable = FALSE;
|
|
|
|
|
|
Status = EFI_SUCCESS;
|
|
ResponseDataCount = 1;
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
AutoCMD12Enable = (CommandIndex & AUTO_CMD12_ENABLE) ? TRUE : FALSE;
|
|
CommandIndex = CommandIndex & CMD_INDEX_MASK;
|
|
|
|
if (Buffer != NULL && DataType == NoData) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((EFI_D_ERROR, "SendCommand: invalid parameter \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (((UINTN)Buffer & (This->HostCapability.BoundarySize - 1)) != (UINTN)NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((EFI_D_ERROR, "SendCommand: invalid parameter \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "SendCommand: Command Index = %d \r\n", CommandIndex));
|
|
//
|
|
TimeOut2 = 1000; // 10 ms
|
|
do {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_PSTATE,
|
|
1,
|
|
&Data
|
|
);
|
|
gBS->Stall (10);
|
|
}while ((TimeOut2-- > 0) && (Data & BIT0));
|
|
TimeOut2 = 1000; // 10 ms
|
|
do {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_PSTATE,
|
|
1,
|
|
&Data
|
|
);
|
|
gBS->Stall (10);
|
|
}while ((TimeOut2-- > 0) && (Data & BIT1));
|
|
//Clear status bits
|
|
//
|
|
Data = 0xFFFF;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_NINTSTS,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
Data = 0xFFFF;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_ERINTSTS,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
|
|
if (Buffer != NULL) {
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_DMAADR,
|
|
1,
|
|
&Buffer
|
|
);
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_BLKSZ,
|
|
1,
|
|
&Data
|
|
);
|
|
Data &= ~(0xFFF);
|
|
if (BufferSize <= SDHostData->BlockLength) {
|
|
Data |= (BufferSize | 0x7000);
|
|
} else {
|
|
Data |= (SDHostData->BlockLength | 0x7000);
|
|
}
|
|
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_BLKSZ,
|
|
1,
|
|
&Data
|
|
);
|
|
if (BufferSize <= SDHostData->BlockLength) {
|
|
Data = 1;
|
|
} else {
|
|
Data = BufferSize / SDHostData->BlockLength;
|
|
}
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_BLKCNT,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
} else {
|
|
Data = 0;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_BLKSZ,
|
|
1,
|
|
&Data
|
|
);
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_BLKCNT,
|
|
1,
|
|
&Data
|
|
);
|
|
}
|
|
|
|
//
|
|
//Argument
|
|
//
|
|
Data = Argument;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_CMDARG,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_XFRMODE,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
|
|
DEBUG ((EFI_D_INFO, "Transfer mode read = 0x%x \r\n", (Data & 0xFFFF)));
|
|
//
|
|
//BIT0 - DMA Enable
|
|
//BIT2 - Auto Cmd12
|
|
//
|
|
if (DataType == InData) {
|
|
Data |= BIT4 | BIT0;
|
|
} else if (DataType == OutData){
|
|
Data &= ~BIT4;
|
|
Data |= BIT0;
|
|
} else {
|
|
Data &= ~(BIT4 | BIT0);
|
|
}
|
|
|
|
if (BufferSize <= SDHostData->BlockLength) {
|
|
Data &= ~ (BIT5 | BIT1 | BIT2);
|
|
Data |= BIT1; // Enable block count always
|
|
} else {
|
|
if (SDHostData->IsAutoStopCmd && AutoCMD12Enable) {
|
|
Data |= (BIT5 | BIT1 | BIT2);
|
|
} else {
|
|
Data |= (BIT5 | BIT1);
|
|
}
|
|
}
|
|
|
|
DEBUG ((EFI_D_INFO, "Transfer mode write = 0x%x \r\n", (Data & 0xffff)));
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_XFRMODE,
|
|
1,
|
|
&Data
|
|
);
|
|
//
|
|
//Command
|
|
//
|
|
//ResponseTypeSelect IndexCheck CRCCheck ResponseType
|
|
// 00 0 0 NoResponse
|
|
// 01 0 1 R2
|
|
// 10 0 0 R3, R4
|
|
// 10 1 1 R1, R5, R6, R7
|
|
// 11 1 1 R1b, R5b
|
|
//
|
|
switch (ResponseType) {
|
|
case ResponseNo:
|
|
Data = (CommandIndex << 8);
|
|
ResponseDataCount = 0;
|
|
break;
|
|
|
|
case ResponseR1:
|
|
case ResponseR5:
|
|
case ResponseR6:
|
|
case ResponseR7:
|
|
Data = (CommandIndex << 8) | BIT1 | BIT4| BIT3;
|
|
ResponseDataCount = 1;
|
|
break;
|
|
|
|
case ResponseR1b:
|
|
case ResponseR5b:
|
|
Data = (CommandIndex << 8) | BIT0 | BIT1 | BIT4| BIT3;
|
|
ResponseDataCount = 1;
|
|
break;
|
|
|
|
case ResponseR2:
|
|
Data = (CommandIndex << 8) | BIT0 | BIT3;
|
|
ResponseDataCount = 4;
|
|
break;
|
|
|
|
case ResponseR3:
|
|
case ResponseR4:
|
|
Data = (CommandIndex << 8) | BIT1;
|
|
ResponseDataCount = 1;
|
|
break;
|
|
|
|
default:
|
|
ASSERT (0);
|
|
Status = EFI_INVALID_PARAMETER;
|
|
DEBUG ((EFI_D_ERROR, "SendCommand: invalid parameter \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (DataType != NoData) {
|
|
Data |= BIT5;
|
|
}
|
|
|
|
HostLEDEnable (This, TRUE);
|
|
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_SDCMD,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
|
|
Data = 0;
|
|
do {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_ERINTSTS,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if ((Data & 0x07FF) != 0) {
|
|
Status = GetErrorReason (CommandIndex, (UINT16)Data);
|
|
DEBUG ((EFI_D_ERROR, "SendCommand: Error happens \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_NINTSTS,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if ((Data & BIT0) == BIT0) {
|
|
//
|
|
//Command completed, can read response
|
|
//
|
|
if (DataType == NoData) {
|
|
break;
|
|
} else {
|
|
//
|
|
//Transfer completed
|
|
//
|
|
if ((Data & BIT1) == BIT1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
gBS->Stall (1 * 1000);
|
|
|
|
TimeOut --;
|
|
|
|
} while (TimeOut > 0);
|
|
|
|
if (TimeOut == 0) {
|
|
Status = EFI_TIMEOUT;
|
|
DEBUG ((EFI_D_ERROR, "SendCommand: Time out \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
if (ResponseData != NULL) {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_RESP,
|
|
ResponseDataCount,
|
|
ResponseData
|
|
);
|
|
if (ResponseType == ResponseR2) {
|
|
//
|
|
// Adjustment for R2 response
|
|
//
|
|
Data = 1;
|
|
for (Index = 0; Index < ResponseDataCount; Index++) {
|
|
Data64 = LShiftU64(*ResponseData, 8);
|
|
*ResponseData = (UINT32)((Data64 & 0xFFFFFFFF) | Data);
|
|
Data = (UINT32)RShiftU64 (Data64, 32);
|
|
ResponseData++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
HostLEDEnable (This, FALSE);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Set max clock frequency of the host, the actual frequency may not be the same as MaxFrequency.
|
|
It depends on the max frequency the host can support, divider, and host speed mode.
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param MaxFrequency Max frequency in HZ.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetClockFrequency (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN UINT32 MaxFrequency
|
|
)
|
|
{
|
|
UINT32 Data;
|
|
UINT16 FreqSelBits;
|
|
EFI_STATUS Status;
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 TimeOutCount;
|
|
UINT32 Revision;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
Data = 0;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_CTRLRVER,
|
|
1,
|
|
&Revision
|
|
);
|
|
Revision &= 0x000000FF;
|
|
|
|
Status = DividedClockModeBits (
|
|
SDHostData->BaseClockInMHz * 1000 * 1000,
|
|
MaxFrequency,
|
|
(Revision < SDHCI_SPEC_300),
|
|
&FreqSelBits
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Cannot reach MaxFrequency with SDHostData->BaseClockInMHz.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
Data = 0;
|
|
|
|
//
|
|
//Enable internal clock and Stop Clock Enable
|
|
//
|
|
Data = BIT0;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
TimeOutCount = TIME_OUT_1S;
|
|
do {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
gBS->Stall (1 * 1000);
|
|
TimeOutCount --;
|
|
if (TimeOutCount == 0) {
|
|
DEBUG ((EFI_D_ERROR, "SetClockFrequency: Time out \r\n"));
|
|
return EFI_TIMEOUT;
|
|
}
|
|
} while ((Data & BIT1) != BIT1);
|
|
|
|
DEBUG ((EFI_D_INFO, "Base Clock In MHz: %d\r\n", SDHostData->BaseClockInMHz));
|
|
|
|
Data = (BIT0 | ((UINT32) FreqSelBits));
|
|
DEBUG ((EFI_D_INFO, "Data write to MMIO_CLKCTL: 0x%04x \r\n", Data));
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
TimeOutCount = TIME_OUT_1S;
|
|
do {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
gBS->Stall (1 * 1000);
|
|
TimeOutCount --;
|
|
if (TimeOutCount == 0) {
|
|
DEBUG ((EFI_D_ERROR, "SetClockFrequency: Time out \r\n"));
|
|
return EFI_TIMEOUT;
|
|
}
|
|
} while ((Data & BIT1) != BIT1);
|
|
gBS->Stall (20 * 1000);
|
|
Data |= BIT2;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set bus width of the host controller
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param BusWidth Bus width in 1, 4, 8 bits.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetBusWidth (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN UINT32 BusWidth
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 Data;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
|
|
|
|
if ((BusWidth != 1) && (BusWidth != 4) && (BusWidth != 8)) {
|
|
DEBUG ((EFI_D_ERROR, "SetBusWidth: Invalid parameter \r\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((SDHostData->SDHostIo.HostCapability.BusWidth8 == FALSE) && (BusWidth == 8)) {
|
|
DEBUG ((EFI_D_ERROR, "SetBusWidth: Invalid parameter \r\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PciIo = SDHostData->PciIo;
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
//
|
|
// BIT5 8-bit MMC Support (MMC8):
|
|
// If set, IOH supports 8-bit MMC. When cleared, IOH does not support this feature
|
|
//
|
|
if (BusWidth == 8) {
|
|
DEBUG ((EFI_D_INFO, "Bus Width is 8-bit ... \r\n"));
|
|
Data |= BIT5;
|
|
} else if (BusWidth == 4) {
|
|
DEBUG ((EFI_D_INFO, "Bus Width is 4-bit ... \r\n"));
|
|
Data &= ~BIT5;
|
|
Data |= BIT1;
|
|
} else {
|
|
DEBUG ((EFI_D_INFO, "Bus Width is 1-bit ... \r\n"));
|
|
Data &= ~BIT5;
|
|
Data &= ~BIT1;
|
|
}
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_HOSTCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Set voltage which could supported by the host controller.
|
|
Support 0(Power off the host), 1.8V, 3.0V, 3.3V
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param Voltage Units in 0.1 V.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_INVALID_PARAMETER
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetHostVoltage (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN UINT32 Voltage
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT8 Data;
|
|
EFI_STATUS Status;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
Status = EFI_SUCCESS;
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_PWRCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if (Voltage == 0) {
|
|
//
|
|
//Power Off the host
|
|
//
|
|
Data &= ~BIT0;
|
|
} else if (Voltage <= 18 && This->HostCapability.V18Support) {
|
|
//
|
|
//1.8V
|
|
//
|
|
Data |= (BIT1 | BIT3 | BIT0);
|
|
} else if (Voltage > 18 && Voltage <= 30 && This->HostCapability.V30Support) {
|
|
//
|
|
//3.0V
|
|
//
|
|
Data |= (BIT2 | BIT3 | BIT0);
|
|
} else if (Voltage > 30 && Voltage <= 33 && This->HostCapability.V33Support) {
|
|
//
|
|
//3.3V
|
|
//
|
|
Data |= (BIT1 | BIT2 | BIT3 | BIT0);
|
|
} else {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto Exit;
|
|
}
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_PWRCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
gBS->Stall (10 * 1000);
|
|
|
|
Exit:
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Reset the host controller.
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param ResetAll TRUE to reset all.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ResetSDHost (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN RESET_TYPE ResetType
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 Data;
|
|
UINT16 ErrStatus;
|
|
UINT32 Mask;
|
|
UINT32 TimeOutCount;
|
|
UINT16 SaveClkCtl;
|
|
UINT16 ZeroClkCtl;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
Mask = 0;
|
|
ErrStatus = 0;
|
|
|
|
if (ResetType == Reset_Auto) {
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_ERINTSTS,
|
|
1,
|
|
&ErrStatus
|
|
);
|
|
if ((ErrStatus & 0xF) != 0) {
|
|
//
|
|
//Command Line
|
|
//
|
|
Mask |= BIT1;
|
|
}
|
|
if ((ErrStatus & 0x70) != 0) {
|
|
//
|
|
//Data Line
|
|
//
|
|
Mask |= BIT2;
|
|
}
|
|
}
|
|
|
|
|
|
if (ResetType == Reset_DAT || ResetType == Reset_DAT_CMD) {
|
|
Mask |= BIT2;
|
|
}
|
|
if (ResetType == Reset_CMD || ResetType == Reset_DAT_CMD) {
|
|
Mask |= BIT1;
|
|
}
|
|
if (ResetType == Reset_All) {
|
|
Mask = BIT0;
|
|
}
|
|
|
|
if (Mask == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// To improve SD stability, we zero the MMIO_CLKCTL register and
|
|
// stall for 50 microseconds before resetting the controller. We
|
|
// restore the register setting following the reset operation.
|
|
//
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&SaveClkCtl
|
|
);
|
|
|
|
ZeroClkCtl = (UINT16) 0;
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&ZeroClkCtl
|
|
);
|
|
|
|
gBS->Stall (50);
|
|
|
|
//
|
|
// Reset the SD host controller
|
|
//
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_SWRST,
|
|
1,
|
|
&Mask
|
|
);
|
|
|
|
Data = 0;
|
|
TimeOutCount = TIME_OUT_1S;
|
|
do {
|
|
|
|
gBS->Stall (1 * 1000);
|
|
|
|
TimeOutCount --;
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_SWRST,
|
|
1,
|
|
&Data
|
|
);
|
|
if ((Data & Mask) == 0) {
|
|
break;
|
|
}
|
|
} while (TimeOutCount > 0);
|
|
|
|
//
|
|
// We now restore the MMIO_CLKCTL register which we set to 0 above.
|
|
//
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CLKCTL,
|
|
1,
|
|
&SaveClkCtl
|
|
);
|
|
|
|
if (TimeOutCount == 0) {
|
|
DEBUG ((EFI_D_ERROR, "ResetSDHost: Time out \r\n"));
|
|
return EFI_TIMEOUT;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Enable auto stop on the host controller.
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param Enable TRUE to enable, FALSE to disable.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EnableAutoStopCmd (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
|
|
SDHostData->IsAutoStopCmd = Enable;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Set the Block length on the host controller.
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
@param BlockLength card supportes block length.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_TIMEOUT
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SetBlockLength (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This,
|
|
IN UINT32 BlockLength
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
|
|
DEBUG ((EFI_D_INFO, "Block length on the host controller: %d \r\n", BlockLength));
|
|
SDHostData->BlockLength = BlockLength;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Find whether these is a card inserted into the slot. If so init the host.
|
|
If not, return EFI_NOT_FOUND.
|
|
|
|
@param This A pointer to the EFI_SD_HOST_IO_PROTOCOL instance.
|
|
|
|
@retval EFI_SUCCESS
|
|
@retval EFI_NOT_FOUND
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
DetectCardAndInitHost (
|
|
IN EFI_SD_HOST_IO_PROTOCOL *This
|
|
)
|
|
{
|
|
SDHOST_DATA *SDHostData;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
UINT32 Data;
|
|
EFI_STATUS Status;
|
|
UINT8 Voltages[] = { 33, 30, 18 };
|
|
UINTN Loop;
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS (This);
|
|
PciIo = SDHostData->PciIo;
|
|
Status = EFI_NOT_FOUND;
|
|
|
|
Data = 0;
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_PSTATE,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
if ((Data & (BIT16 | BIT17 | BIT18)) != (BIT16 | BIT17 | BIT18)) {
|
|
//
|
|
// Has no card inserted
|
|
//
|
|
DEBUG ((EFI_D_INFO, "DetectCardAndInitHost: No Cards \r\n"));
|
|
Status = EFI_NOT_FOUND;
|
|
goto Exit;
|
|
}
|
|
DEBUG ((EFI_D_INFO, "DetectCardAndInitHost: Find Cards \r\n"));
|
|
|
|
Status = EFI_NOT_FOUND;
|
|
for (Loop = 0; Loop < sizeof (Voltages); Loop++) {
|
|
DEBUG ((
|
|
EFI_D_INFO,
|
|
"DetectCardAndInitHost: SetHostVoltage %d.%dV \r\n",
|
|
Voltages[Loop] / 10,
|
|
Voltages[Loop] % 10
|
|
));
|
|
Status = SetHostVoltage (This, Voltages[Loop]);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_INFO, "DetectCardAndInitHost set voltages: [failed]\n"));
|
|
} else {
|
|
DEBUG ((EFI_D_INFO, "DetectCardAndInitHost set voltages: [success]\n"));
|
|
break;
|
|
}
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "DetectCardAndInitHost: Fail to set voltage \r\n"));
|
|
goto Exit;
|
|
}
|
|
|
|
Status = SetClockFrequency (This, FREQUENCY_OD);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((EFI_D_ERROR, "DetectCardAndInitHost: Fail to set frequency \r\n"));
|
|
goto Exit;
|
|
}
|
|
SetBusWidth (This, 1);
|
|
|
|
//
|
|
//Enable normal status change
|
|
//
|
|
|
|
Data = (BIT0 | BIT1);
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_NINTEN,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
//
|
|
//Enable error status change
|
|
//
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_ERINTEN,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
Data |= (BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7 | BIT8);
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_ERINTEN,
|
|
1,
|
|
&Data
|
|
);
|
|
|
|
//
|
|
//Data transfer Timeout control
|
|
//
|
|
Data = 0x0E;
|
|
|
|
PciIo->Mem.Write (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
0,
|
|
(UINT64)MMIO_TOCTL,
|
|
1,
|
|
&Data
|
|
);
|
|
//
|
|
//Set Default Bus width as 1 bit
|
|
//
|
|
|
|
Exit:
|
|
return Status;
|
|
|
|
}
|
|
|
|
/**
|
|
Entry point for EFI drivers.
|
|
|
|
@param ImageHandle EFI_HANDLE.
|
|
@param SystemTable EFI_SYSTEM_TABLE.
|
|
|
|
@retval EFI_SUCCESS Driver is successfully loaded.
|
|
@return Others Failed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeSDController (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
return EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gSDControllerDriverBinding,
|
|
ImageHandle,
|
|
&gSDControllerName,
|
|
&gSDControllerName2
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
Test to see if this driver supports ControllerHandle. Any
|
|
ControllerHandle that has SDHostIoProtocol installed will be supported.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to test.
|
|
@param RemainingDevicePath Not used.
|
|
|
|
@return EFI_SUCCESS This driver supports this device.
|
|
@return EFI_UNSUPPORTED This driver does not support this device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SDControllerSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS OpenStatus;
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
PCI_CLASSC PciClass;
|
|
EFI_SD_HOST_IO_PROTOCOL *SdHostIo;
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiSDHostIoProtocolGuid,
|
|
(VOID **)&SdHostIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
DEBUG (( DEBUG_INFO, "SdHost controller is already started\n"));
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
//
|
|
// Test whether there is PCI IO Protocol attached on the controller handle.
|
|
//
|
|
OpenStatus = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (OpenStatus)) {
|
|
return OpenStatus;
|
|
}
|
|
|
|
Status = PciIo->Pci.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint8,
|
|
PCI_CLASSCODE_OFFSET,
|
|
sizeof (PCI_CLASSC) / sizeof (UINT8),
|
|
&PciClass
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ON_EXIT;
|
|
}
|
|
|
|
//
|
|
// Test whether the controller belongs to SD type
|
|
//
|
|
if ((PciClass.BaseCode != PCI_CLASS_SYSTEM_PERIPHERAL) ||
|
|
(PciClass.SubClassCode != PCI_SUBCLASS_SD_HOST_CONTROLLER) ||
|
|
((PciClass.PI != PCI_IF_STANDARD_HOST_NO_DMA) && (PciClass.PI != PCI_IF_STANDARD_HOST_SUPPORT_DMA))
|
|
) {
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
}
|
|
|
|
ON_EXIT:
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return Status;
|
|
}
|
|
/**
|
|
Starting the SD Host Controller Driver.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to test.
|
|
@param RemainingDevicePath Not used.
|
|
|
|
@retval EFI_SUCCESS This driver supports this device.
|
|
@retval EFI_UNSUPPORTED This driver does not support this device.
|
|
@retval EFI_DEVICE_ERROR This driver cannot be started due to device Error.
|
|
EFI_OUT_OF_RESOURCES- Failed due to resource shortage.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SDControllerStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
SDHOST_DATA *SDHostData;
|
|
UINT32 Data;
|
|
|
|
|
|
SDHostData = NULL;
|
|
Data = 0;
|
|
|
|
//
|
|
// Open PCI I/O Protocol and save pointer to open protocol
|
|
// in private data area.
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
(VOID **) &PciIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Enable the SD Host Controller MMIO space
|
|
//
|
|
Status = PciIo->Attributes (
|
|
PciIo,
|
|
EfiPciIoAttributeOperationEnable,
|
|
EFI_PCI_DEVICE_ENABLE,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
SDHostData = (SDHOST_DATA*)AllocateZeroPool(sizeof (SDHOST_DATA));
|
|
if (SDHostData == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
SDHostData->Signature = SDHOST_DATA_SIGNATURE;
|
|
SDHostData->PciIo = PciIo;
|
|
|
|
CopyMem (&SDHostData->SDHostIo, &mSDHostIo, sizeof (EFI_SD_HOST_IO_PROTOCOL));
|
|
|
|
ResetSDHost (&SDHostData->SDHostIo, Reset_All);
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint16,
|
|
0,
|
|
(UINT64)MMIO_CTRLRVER,
|
|
1,
|
|
&Data
|
|
);
|
|
SDHostData->SDHostIo.HostCapability.HostVersion = Data & 0xFF;
|
|
DEBUG ((EFI_D_INFO, "SdHostDriverBindingStart: HostVersion 0x%x \r\n", SDHostData->SDHostIo.HostCapability.HostVersion));
|
|
|
|
PciIo->Mem.Read (
|
|
PciIo,
|
|
EfiPciIoWidthUint32,
|
|
0,
|
|
(UINT64)MMIO_CAP,
|
|
1,
|
|
&Data
|
|
);
|
|
DEBUG ((EFI_D_INFO, "SdHostDriverBindingStart: MMIO_CAP 0x%x \r\n", Data));
|
|
if ((Data & BIT18) != 0) {
|
|
SDHostData->SDHostIo.HostCapability.BusWidth8 = TRUE;
|
|
}
|
|
|
|
if ((Data & BIT21) != 0) {
|
|
SDHostData->SDHostIo.HostCapability.HighSpeedSupport = TRUE;
|
|
}
|
|
|
|
if ((Data & BIT24) != 0) {
|
|
SDHostData->SDHostIo.HostCapability.V33Support = TRUE;
|
|
}
|
|
|
|
if ((Data & BIT25) != 0) {
|
|
SDHostData->SDHostIo.HostCapability.V30Support = TRUE;
|
|
}
|
|
|
|
if ((Data & BIT26) != 0) {
|
|
SDHostData->SDHostIo.HostCapability.V18Support = TRUE;
|
|
}
|
|
|
|
SDHostData->SDHostIo.HostCapability.BusWidth4 = TRUE;
|
|
|
|
if(SDHostData->SDHostIo.HostCapability.HostVersion < SDHCI_SPEC_300) {
|
|
|
|
|
|
|
|
SDHostData->BaseClockInMHz = (Data >> 8) & 0x3F;
|
|
}
|
|
else {
|
|
SDHostData->BaseClockInMHz = (Data >> 8) & 0xFF;
|
|
|
|
}
|
|
|
|
SDHostData->BlockLength = 512 << ((Data >> 16) & 0x03);
|
|
DEBUG ((EFI_D_INFO, "SdHostDriverBindingStart: BlockLength 0x%x \r\n", SDHostData->BlockLength));
|
|
SDHostData->IsAutoStopCmd = TRUE;
|
|
|
|
Status = gBS->InstallProtocolInterface (
|
|
&Controller,
|
|
&gEfiSDHostIoProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&SDHostData->SDHostIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Install the component name protocol
|
|
//
|
|
SDHostData->ControllerNameTable = NULL;
|
|
|
|
AddUnicodeString2 (
|
|
"eng",
|
|
gSDControllerName.SupportedLanguages,
|
|
&SDHostData->ControllerNameTable,
|
|
L"SD Host Controller",
|
|
TRUE
|
|
);
|
|
AddUnicodeString2 (
|
|
"en",
|
|
gSDControllerName2.SupportedLanguages,
|
|
&SDHostData->ControllerNameTable,
|
|
L"SD Host Controller",
|
|
FALSE
|
|
);
|
|
|
|
Exit:
|
|
if (EFI_ERROR (Status)) {
|
|
if (SDHostData != NULL) {
|
|
FreePool (SDHostData);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Stop this driver on ControllerHandle. Support stopping any child handles
|
|
created by this driver.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Controller Handle of device to stop driver on.
|
|
@param NumberOfChildren Number of Children in the ChildHandleBuffer.
|
|
@param ChildHandleBuffer List of handles for the children we need to stop.
|
|
|
|
@return EFI_SUCCESS
|
|
@return others
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
SDControllerStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE Controller,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SD_HOST_IO_PROTOCOL *SDHostIo;
|
|
SDHOST_DATA *SDHostData;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Controller,
|
|
&gEfiSDHostIoProtocolGuid,
|
|
(VOID **) &SDHostIo,
|
|
This->DriverBindingHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
//
|
|
// Test whether the Controller handler passed in is a valid
|
|
// Usb controller handle that should be supported, if not,
|
|
// return the error status directly
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
SetHostVoltage (SDHostIo, 0);
|
|
|
|
SDHostData = SDHOST_DATA_FROM_THIS(SDHostIo);
|
|
|
|
//
|
|
// Uninstall Block I/O protocol from the device handle
|
|
//
|
|
Status = gBS->UninstallProtocolInterface (
|
|
Controller,
|
|
&gEfiSDHostIoProtocolGuid,
|
|
SDHostIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
FreeUnicodeStringTable (SDHostData->ControllerNameTable);
|
|
|
|
FreePool (SDHostData);
|
|
|
|
gBS->CloseProtocol (
|
|
Controller,
|
|
&gEfiPciIoProtocolGuid,
|
|
This->DriverBindingHandle,
|
|
Controller
|
|
);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
|