From 48555339beb65f90bd42995a15bd38cd45e378f5 Mon Sep 17 00:00:00 2001 From: Feng Tian Date: Wed, 23 Mar 2016 10:47:05 +0800 Subject: [PATCH] MdeModulePkg/SdMmc: Add EDKII SD/MMC stack This stack includes: 1. Dxe phase support by: 1) SdMmcPciHcDxe driver to consume PciIo and produce SdMmcPassThru. 2) SdDxe driver to consume SdMmcPassThru to produce BlkIo1/BlkIo2. 3) EmmcDxe driver to consume SdMmcPassThru to produce BlkIo1/BlkIo2/SSP. 2. Pei phase support 1) SdBlockIoPei driver to consume SdMmcHostController Ppi and produce VirutalBlkIo1&2. 2) EmmcBlockIoPei driver to consume SdMmcHostController Ppi and produce VirutalBlkIo1&2. 3) SdMmcPciHcPei driver to produce SdMmcHostController Ppi. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Feng Tian Reviewed-by: Hao Wu --- .../Bus/Pci/SdMmcPciHcDxe/ComponentName.c | 211 ++ .../Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c | 1152 +++++++ MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c | 1165 +++++++ .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.c | 1261 ++++++++ .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h | 784 +++++ .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf | 72 + .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni | 23 + .../Pci/SdMmcPciHcDxe/SdMmcPciHcDxeExtra.uni | 19 + .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c | 1901 +++++++++++ .../Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h | 546 ++++ .../Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.c | 212 ++ .../Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.h | 86 + .../Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf | 56 + .../Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.uni | 22 + .../Pci/SdMmcPciHcPei/SdMmcPciHcPeiExtra.uni | 21 + .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c | 807 +++++ .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h | 381 +++ .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf | 62 + .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni | 21 + .../Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni | 21 + .../Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c | 455 +++ .../Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h | 61 + MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c | 2833 ++++++++++++++++ MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h | 345 ++ MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c | 241 ++ MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c | 1591 +++++++++ MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h | 466 +++ MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c | 1175 +++++++ MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h | 495 +++ MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf | 66 + MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni | 20 + MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni | 19 + .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.c | 617 ++++ .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.h | 377 +++ .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf | 62 + .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni | 21 + .../Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni | 21 + MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c | 455 +++ MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h | 61 + MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c | 2874 +++++++++++++++++ MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h | 354 ++ MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c | 240 ++ MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c | 971 ++++++ MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h | 221 ++ MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c | 888 +++++ MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h | 469 +++ MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf | 65 + MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni | 20 + MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni | 20 + .../Include/Ppi/SdMmcHostController.h | 64 + MdeModulePkg/MdeModulePkg.dec | 9 + MdeModulePkg/MdeModulePkg.dsc | 6 + 52 files changed, 24405 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxeExtra.uni create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.c create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.h create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.uni create mode 100644 MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPeiExtra.uni create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni create mode 100644 MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c create mode 100644 MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni create mode 100644 MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni create mode 100644 MdeModulePkg/Include/Ppi/SdMmcHostController.h diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/ComponentName.c new file mode 100644 index 0000000000..3329929119 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/ComponentName.c @@ -0,0 +1,211 @@ +/** @file + UEFI Component Name(2) protocol implementation for SD/MMC host controller driver. + + Copyright (c) 2015, Intel Corporation. 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 "SdMmcPciHcDxe.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gSdMmcPciHcComponentName = { + SdMmcPciHcComponentNameGetDriverName, + SdMmcPciHcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSdMmcPciHcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SdMmcPciHcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SdMmcPciHcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSdMmcPciHcDriverNameTable[] = { + { "eng;en", L"Edkii Sd/Mmc Host Controller Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSdMmcPciHcControllerNameTable[] = { + { "eng;en", L"Edkii Sd/Mmc Host Controller" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mSdMmcPciHcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gSdMmcPciHcComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + if (Language == NULL || ControllerName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gSdMmcPciHcDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mSdMmcPciHcControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gSdMmcPciHcComponentName) + ); +} diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c new file mode 100644 index 0000000000..443f93f035 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/EmmcDevice.c @@ -0,0 +1,1152 @@ +/** @file + This file provides some helper functions which are specific for EMMC device. + + Copyright (c) 2015, Intel Corporation. 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 "SdMmcPciHcDxe.h" + +/** + Send command GO_IDLE_STATE (CMD0 with argument of 0x00000000) to the device to + make it go to Idle State. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The EMMC device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +EmmcReset ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc; + SdMmcCmdBlk.ResponseType = 0; + SdMmcCmdBlk.CommandArgument = 0; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_OP_COND to the EMMC device to get the data of the OCR register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in, out] Argument On input, the argument of SEND_OP_COND is to send to the device. + On output, the argument is the value of OCR register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetOcr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN OUT UINT32 *Argument + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3; + SdMmcCmdBlk.CommandArgument = *Argument; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + *Argument = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to send the + data of their CID registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetAllCid ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_ALL_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = 0; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative device + Address (RCA). + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSetRca ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_CSD to the EMMC device to get the data of the CSD register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of this + buffer as the content of this byte is meaningless even + if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + OUT EMMC_CSD *Csd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1); + } + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the EMMC device to select/deselect it. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSelect ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_EXT_CSD to the EMMC device to get the data of the EXT_CSD register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] ExtCsd The buffer to store the content of the EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetExtCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + OUT EMMC_EXT_CSD *ExtCsd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0x00000000; + + Packet.InDataBuffer = ExtCsd; + Packet.InTransferLength = sizeof (EMMC_EXT_CSD); + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + return Status; +} + +/** + Send command SWITCH to the EMMC device to switch the mode of operation of the + selected Device or modifies the EXT_CSD registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Access The access mode of SWTICH command. + @param[in] Index The offset of the field to be access. + @param[in] Value The value to be set to the specified field of EXT_CSD register. + @param[in] CmdSet The value of CmdSet field of EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT8 Access, + IN UINT8 Index, + IN UINT8 Value, + IN UINT8 CmdSet + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SWITCH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + SdMmcCmdBlk.CommandArgument = (Access << 24) | (Index << 16) | (Value << 8) | CmdSet; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed EMMC device to get its status register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal sampling point + detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSendTuningBlk ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT8 BusWidth + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[128]; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + if (BusWidth == 8) { + Packet.InTransferLength = sizeof (TuningBlock); + } else { + Packet.InTransferLength = 64; + } + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Tunning the clock to get HS200 optimal sampling point. + + Command SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes the + tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcTuningClkForHs200 ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl2; + UINT8 Retry; + + // + // Notify the host that the sampling clock tuning procedure starts. + // + HostCtrl2 = BIT6; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Ask the device to send a sequence of tuning blocks till the tuning procedure is done. + // + Retry = 0; + do { + Status = EmmcSendTuningBlk (PassThru, Slot, BusWidth); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcTuningClkForHs200: Send tuning block fails with %r\n", Status)); + return Status; + } + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + break; + } + } while (++Retry < 40); + + if (Retry == 40) { + Status = EFI_TIMEOUT; + DEBUG ((EFI_D_ERROR, "EmmcTuningClkForHs200: Send tuning block exceeds 40 times\n")); + } + + return Status; +} + +/** + Switch the bus width to specified width. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9 and SD Host Controller + Simplified Spec 3.0 Figure 3-7 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] IsDdr If TRUE, use dual data rate data simpling method. Otherwise + use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchBusWidth ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, BusWidth); + if (BusWidth == 4) { + Value = 1; + } else if (BusWidth == 8) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + + if (IsDdr) { + Value += 4; + } + + CmdSet = 0; + Status = EmmcSwitch (PassThru, Slot, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchBusWidth: Switch to bus width %d fails with %r\n", BusWidth, Status)); + return Status; + } + + Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchBusWidth: Send status fails with %r\n", Status)); + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus & BIT7) != 0) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchBusWidth: The switch operation fails as DevStatus is 0x%08x\n", DevStatus)); + return EFI_DEVICE_ERROR; + } + + Status = SdMmcHcSetBusWidth (PciIo, Slot, BusWidth); + + return Status; +} + +/** + Switch the clock frequency to the specified value. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6 and SD Host Controller + Simplified Spec 3.0 Figure 3-3 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] HsTiming The value to be written to HS_TIMING field of EXT_CSD register. + @param[in] ClockFreq The max clock frequency to be set, the unit is MHz. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchClockFreq ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT8 HsTiming, + IN UINT32 ClockFreq + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + SD_MMC_HC_PRIVATE_DATA *Private; + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, HsTiming); + Value = HsTiming; + CmdSet = 0; + + Status = EmmcSwitch (PassThru, Slot, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchClockFreq: Switch to hstiming %d fails with %r\n", HsTiming, Status)); + return Status; + } + + Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchClockFreq: Send status fails with %r\n", Status)); + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus & BIT7) != 0) { + DEBUG ((EFI_D_ERROR, "EmmcSwitchClockFreq: The switch operation fails as DevStatus is 0x%08x\n", DevStatus)); + return EFI_DEVICE_ERROR; + } + // + // Convert the clock freq unit from MHz to KHz. + // + Status = SdMmcHcClockSupply (PciIo, Slot, ClockFreq * 1000, Private->Capability[Slot]); + + return Status; +} + +/** + Switch to the High Speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] IsDdr If TRUE, use dual data rate data simpling method. Otherwise + use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHighSpeed ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT32 ClockFreq, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl1; + UINT8 HostCtrl2; + + Status = EmmcSwitchBusWidth (PciIo, PassThru, Slot, Rca, IsDdr, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to Hight Speed timing + // + HostCtrl1 = BIT2; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Clean UHS Mode Select field of Host Control 2 reigster before update + // + HostCtrl2 = (UINT8)~0x7; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set UHS Mode Select field of Host Control 2 reigster to SDR12/25/50 + // + if (IsDdr) { + HostCtrl2 = BIT2; + } else if (ClockFreq == 52) { + HostCtrl2 = BIT0; + } else { + HostCtrl2 = 0; + } + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + HsTiming = 1; + Status = EmmcSwitchClockFreq (PciIo, PassThru, Slot, Rca, HsTiming, ClockFreq); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + Switch to the HS200 timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHS200 ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT32 ClockFreq, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl2; + UINT16 ClockCtrl; + + if ((BusWidth != 4) && (BusWidth != 8)) { + return EFI_INVALID_PARAMETER; + } + + Status = EmmcSwitchBusWidth (PciIo, PassThru, Slot, Rca, FALSE, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to HS200/SDR104 timing + // + // + // Stop bus clock at first + // + Status = SdMmcHcStopClock (PciIo, Slot); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clean UHS Mode Select field of Host Control 2 reigster before update + // + HostCtrl2 = (UINT8)~0x7; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set UHS Mode Select field of Host Control 2 reigster to SDR104 + // + HostCtrl2 = BIT0 | BIT1; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Wait Internal Clock Stable in the Clock Control register to be 1 before set SD Clock Enable bit + // + Status = SdMmcHcWaitMmioSet ( + PciIo, + Slot, + SD_MMC_HC_CLOCK_CTRL, + sizeof (ClockCtrl), + BIT1, + BIT1, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + HsTiming = 2; + Status = EmmcSwitchClockFreq (PciIo, PassThru, Slot, Rca, HsTiming, ClockFreq); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcTuningClkForHs200 (PciIo, PassThru, Slot, BusWidth); + + return Status; +} + +/** + Switch to the HS400 timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHS400 ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT32 ClockFreq + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl2; + + Status = EmmcSwitchToHS200 (PciIo, PassThru, Slot, Rca, ClockFreq, 8); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to Hight Speed timing and set the clock frequency to a value less than 52MHz. + // + HsTiming = 1; + Status = EmmcSwitchClockFreq (PciIo, PassThru, Slot, Rca, HsTiming, 52); + if (EFI_ERROR (Status)) { + return Status; + } + // + // HS400 mode must use 8 data lines. + // + Status = EmmcSwitchBusWidth (PciIo, PassThru, Slot, Rca, TRUE, 8); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clean UHS Mode Select field of Host Control 2 reigster before update + // + HostCtrl2 = (UINT8)~0x7; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set UHS Mode Select field of Host Control 2 reigster to HS400 + // + HostCtrl2 = BIT0 | BIT2; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + HsTiming = 3; + Status = EmmcSwitchClockFreq (PciIo, PassThru, Slot, Rca, HsTiming, ClockFreq); + + return Status; +} + +/** + Switch the high speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSetBusMode ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca + ) +{ + EFI_STATUS Status; + EMMC_CSD Csd; + EMMC_EXT_CSD ExtCsd; + UINT8 HsTiming; + BOOLEAN IsDdr; + UINT32 ClockFreq; + UINT8 BusWidth; + SD_MMC_HC_PRIVATE_DATA *Private; + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru); + + Status = EmmcGetCsd (PassThru, Slot, Rca, &Csd); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSetBusMode: GetCsd fails with %r\n", Status)); + return Status; + } + + Status = EmmcSelect (PassThru, Slot, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSetBusMode: Select fails with %r\n", Status)); + return Status; + } + + ASSERT (Private->Capability[Slot].BaseClkFreq != 0); + // + // Check if the Host Controller support 8bits bus width. + // + if (Private->Capability[Slot].BusWidth8 != 0) { + BusWidth = 8; + } else { + BusWidth = 4; + } + // + // Get Deivce_Type from EXT_CSD register. + // + Status = EmmcGetExtCsd (PassThru, Slot, &ExtCsd); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcSetBusMode: GetExtCsd fails with %r\n", Status)); + return Status; + } + // + // Calculate supported bus speed/bus width/clock frequency. + // + HsTiming = 0; + IsDdr = FALSE; + ClockFreq = 0; + if (((ExtCsd.DeviceType & (BIT4 | BIT5)) != 0) && (Private->Capability[Slot].Sdr104 != 0)) { + HsTiming = 2; + IsDdr = FALSE; + ClockFreq = 200; + } else if (((ExtCsd.DeviceType & (BIT2 | BIT3)) != 0) && (Private->Capability[Slot].Ddr50 != 0)) { + HsTiming = 1; + IsDdr = TRUE; + ClockFreq = 52; + } else if (((ExtCsd.DeviceType & BIT1) != 0) && (Private->Capability[Slot].HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 52; + } else if (((ExtCsd.DeviceType & BIT0) != 0) && (Private->Capability[Slot].HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 26; + } + // + // Check if both of the device and the host controller support HS400 DDR mode. + // + if (((ExtCsd.DeviceType & (BIT6 | BIT7)) != 0) && (Private->Capability[Slot].Hs400 != 0)) { + // + // The host controller supports 8bits bus. + // + ASSERT (BusWidth == 8); + HsTiming = 3; + IsDdr = TRUE; + ClockFreq = 200; + } + + if ((ClockFreq == 0) || (HsTiming == 0)) { + // + // Continue using default setting. + // + return EFI_SUCCESS; + } + + DEBUG ((EFI_D_INFO, "EmmcSetBusMode: HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n", HsTiming, ClockFreq, BusWidth, IsDdr ? "TRUE":"FALSE")); + + if (HsTiming == 3) { + // + // Execute HS400 timing switch procedure + // + Status = EmmcSwitchToHS400 (PciIo, PassThru, Slot, Rca, ClockFreq); + } else if (HsTiming == 2) { + // + // Execute HS200 timing switch procedure + // + Status = EmmcSwitchToHS200 (PciIo, PassThru, Slot, Rca, ClockFreq, BusWidth); + } else { + // + // Execute High Speed timing switch procedure + // + Status = EmmcSwitchToHighSpeed (PciIo, PassThru, Slot, Rca, ClockFreq, BusWidth, IsDdr); + } + + DEBUG ((EFI_D_INFO, "EmmcSetBusMode: Switch to %a %r\n", (HsTiming == 3) ? "HS400" : ((HsTiming == 2) ? "HS200" : "HighSpeed"), Status)); + + return Status; +} + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcIdentification ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT32 Ocr; + UINT16 Rca; + + PciIo = Private->PciIo; + PassThru = &Private->PassThru; + + Status = EmmcReset (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_VERBOSE, "EmmcIdentification: Executing Cmd0 fails with %r\n", Status)); + return Status; + } + + Ocr = 0; + do { + Status = EmmcGetOcr (PassThru, Slot, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_VERBOSE, "EmmcIdentification: Executing Cmd1 fails with %r\n", Status)); + return Status; + } + Ocr |= BIT30; + } while ((Ocr & BIT31) == 0); + + Status = EmmcGetAllCid (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_VERBOSE, "EmmcIdentification: Executing Cmd2 fails with %r\n", Status)); + return Status; + } + // + // Slot starts from 0 and valid RCA starts from 1. + // Here we takes a simple formula to calculate the RCA. + // Don't support multiple devices on the slot, that is + // shared bus slot feature. + // + Rca = Slot + 1; + Status = EmmcSetRca (PassThru, Slot, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcIdentification: Executing Cmd3 fails with %r\n", Status)); + return Status; + } + // + // Enter Data Tranfer Mode. + // + DEBUG ((EFI_D_INFO, "EmmcIdentification: Found a EMMC device at slot [%d], RCA [%d]\n", Slot, Rca)); + Private->Slot[Slot].CardType = EmmcCardType; + + Status = EmmcSetBusMode (PciIo, PassThru, Slot, Rca); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c new file mode 100644 index 0000000000..07bd07ad41 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdDevice.c @@ -0,0 +1,1165 @@ +/** @file + This file provides some helper functions which are specific for SD card device. + + Copyright (c) 2015, Intel Corporation. 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 "SdMmcPciHcDxe.h" + +/** + Send command GO_IDLE_STATE to the device to make it go to Idle State. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The SD device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +SdCardReset ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_IF_COND to the device to inquiry the SD Memory Card interface + condition. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] SupplyVoltage The supplied voltage by the host. + @param[in] CheckPattern The check pattern to be sent to the device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardVoltageCheck ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT8 SupplyVoltage, + IN UINT8 CheckPattern + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_IF_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR7; + SdMmcCmdBlk.CommandArgument = (SupplyVoltage << 8) | CheckPattern; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + if (!EFI_ERROR (Status)) { + if (SdMmcStatusBlk.Resp0 != SdMmcCmdBlk.CommandArgument) { + return EFI_DEVICE_ERROR; + } + } + + return Status; +} + +/** + Send command SDIO_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SDIO Simplified Spec 3 Section 3.2 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.8v. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdioSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SDIO_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR4; + + Switch = S18R ? BIT24 : 0; + + SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SD_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.8v. + @param[in] Xpc The boolean to show if it should provide 0.36w power control. + @param[in] Hcs The boolean to show if it support host capacity info. + @param[out] Ocr The buffer to store returned OCR register value. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R, + IN BOOLEAN Xpc, + IN BOOLEAN Hcs, + OUT UINT32 *Ocr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + UINT32 MaxPower; + UINT32 HostCapacity; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3; + + Switch = S18R ? BIT24 : 0; + MaxPower = Xpc ? BIT28 : 0; + HostCapacity = Hcs ? BIT30 : 0; + + SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch | MaxPower | HostCapacity; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + *Ocr = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the SD devices to send the + data of their CID registers. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardAllSendCid ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_ALL_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the SD device to assign a Relative device + Address (RCA). + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] Rca The relative device address to assign. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetRca ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + OUT UINT16 *Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of this + buffer as the content of this byte is meaningless even + if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardGetCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + OUT SD_CSD *Csd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + @param[out] Scr The buffer to store the content of the SCR register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardGetScr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + OUT SD_SCR *Scr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SEND_SCR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + Packet.InDataBuffer = Scr; + Packet.InTransferLength = sizeof (SD_SCR); + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the SD device to select/deselect it. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSelect ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command VOLTAGE_SWITCH to the SD device to switch the voltage of the device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardVoltageSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_VOLTAGE_SWITCH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SET_BUS_WIDTH to the SD device to set the bus width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[in] BusWidth The bus width to be set, it could be 1 or 4. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetBusWidth ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 Value; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SET_BUS_WIDTH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (BusWidth == 1) { + Value = 0; + } else if (BusWidth == 4) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + + SdMmcCmdBlk.CommandArgument = Value & 0x3; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + return Status; +} + +/** + Send command SWITCH_FUNC to the SD device to check switchable function or switch card function. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] AccessMode The value for access mode group. + @param[in] CommandSystem The value for command set group. + @param[in] DriveStrength The value for drive length group. + @param[in] PowerLimit The value for power limit group. + @param[in] Mode Switch or check function. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT8 AccessMode, + IN UINT8 CommandSystem, + IN UINT8 DriveStrength, + IN UINT8 PowerLimit, + IN BOOLEAN Mode + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 ModeValue; + UINT8 Data[64]; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SWITCH_FUNC; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + ModeValue = Mode ? BIT31 : 0; + SdMmcCmdBlk.CommandArgument = (AccessMode & 0xF) | ((PowerLimit & 0xF) << 4) | \ + ((DriveStrength & 0xF) << 8) | ((DriveStrength & 0xF) << 12) | \ + ModeValue; + + Packet.InDataBuffer = Data; + Packet.InTransferLength = sizeof (Data); + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed SD device to get its status register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the SD device for HS200 optimal sampling point + detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendTuningBlk ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[64]; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + Packet.InTransferLength = sizeof (TuningBlock); + + Status = SdMmcPassThruPassThru (PassThru, Slot, &Packet, NULL); + + return Status; +} + +/** + Tunning the sampling point of SDR104 or SDR50 bus speed mode. + + Command SD_SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes the + tuning procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 3-7 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardTuningClock ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl2; + UINT8 Retry; + + // + // Notify the host that the sampling clock tuning procedure starts. + // + HostCtrl2 = BIT6; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Ask the device to send a sequence of tuning blocks till the tuning procedure is done. + // + Retry = 0; + do { + Status = SdCardSendTuningBlk (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardSendTuningBlk: Send tuning block fails with %r\n", Status)); + return Status; + } + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + break; + } + } while (++Retry < 40); + + if (Retry == 40) { + Status = EFI_TIMEOUT; + DEBUG ((EFI_D_ERROR, "SdCardTuningClock: Send tuning block exceeds 40 times\n")); + } + + return Status; +} + +/** + Switch the bus width to specified width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 3-7 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSwitchBusWidth ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT32 DevStatus; + + Status = SdCardSetBusWidth (PassThru, Slot, Rca, BusWidth); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardSwitchBusWidth: Switch to bus width %d fails with %r\n", BusWidth, Status)); + return Status; + } + + Status = SdCardSendStatus (PassThru, Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardSwitchBusWidth: Send status fails with %r\n", Status)); + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus >> 16) != 0) { + DEBUG ((EFI_D_ERROR, "SdCardSwitchBusWidth: The switch operation fails as DevStatus is 0x%08x\n", DevStatus)); + return EFI_DEVICE_ERROR; + } + + Status = SdMmcHcSetBusWidth (PciIo, Slot, BusWidth); + + return Status; +} + +/** + Switch the high speed timing according to request. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] S18A The boolean to show if it's a UHS-I SD card. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetBusMode ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Slot, + IN UINT16 Rca, + IN BOOLEAN S18A + ) +{ + EFI_STATUS Status; + SD_MMC_HC_SLOT_CAP *Capability; + UINT32 ClockFreq; + UINT8 BusWidth; + UINT8 AccessMode; + UINT8 HostCtrl1; + UINT8 HostCtrl2; + SD_MMC_HC_PRIVATE_DATA *Private; + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru); + + Capability = &Private->Capability[Slot]; + + Status = SdCardSelect (PassThru, Slot, Rca); + if (EFI_ERROR (Status)) { + return Status; + } + + BusWidth = 4; + + Status = SdCardSwitchBusWidth (PciIo, PassThru, Slot, Rca, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Calculate supported bus speed/bus width/clock frequency. + // + ClockFreq = 0; + if (S18A && (Capability->Sdr104 != 0)) { + ClockFreq = 208; + AccessMode = 3; + } else if (S18A && (Capability->Sdr50 != 0)) { + ClockFreq = 100; + AccessMode = 2; + } else if (S18A && (Capability->Ddr50 != 0)) { + ClockFreq = 50; + AccessMode = 4; + } else { + ClockFreq = 50; + AccessMode = 1; + } + + DEBUG ((EFI_D_INFO, "SdCardSetBusMode: AccessMode %d ClockFreq %d BusWidth %d\n", AccessMode, ClockFreq, BusWidth)); + + Status = SdCardSwitch (PassThru, Slot, AccessMode, 0, 0, 0, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set to Hight Speed timing + // + if (AccessMode == 1) { + HostCtrl1 = BIT2; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + HostCtrl2 = (UINT8)~0x7; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl2 = AccessMode; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdMmcHcClockSupply (PciIo, Slot, ClockFreq * 1000, *Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((AccessMode == 3) || ((AccessMode == 2) && (Capability->TuningSDR50 != 0))) { + Status = SdCardTuningClock (PciIo, PassThru, Slot); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return Status; +} + +/** + Execute SD device identification procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 3.6 for details. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS There is a SD card. + @retval Others There is not a SD card. + +**/ +EFI_STATUS +SdCardIdentification ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT32 Ocr; + UINT16 Rca; + BOOLEAN Xpc; + BOOLEAN S18r; + UINT64 MaxCurrent; + UINT16 ControllerVer; + UINT8 PowerCtrl; + UINT32 PresentState; + UINT8 HostCtrl2; + + PciIo = Private->PciIo; + PassThru = &Private->PassThru; + // + // 1. Send Cmd0 to the device + // + Status = SdCardReset (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdCardIdentification: Executing Cmd0 fails with %r\n", Status)); + return Status; + } + // + // 2. Send Cmd8 to the device + // + Status = SdCardVoltageCheck (PassThru, Slot, 0x1, 0xFF); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdCardIdentification: Executing Cmd8 fails with %r\n", Status)); + return Status; + } + // + // 3. Send SDIO Cmd5 to the device to the SDIO device OCR register. + // + Status = SdioSendOpCond (PassThru, Slot, 0, FALSE); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdCardIdentification: Found SDIO device, ignore it as we don't support\n")); + return EFI_DEVICE_ERROR; + } + // + // 4. Send Acmd41 with voltage window 0 to the device + // + Status = SdCardSendOpCond (PassThru, Slot, 0, 0, FALSE, FALSE, FALSE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdCardIdentification: Executing SdCardSendOpCond fails with %r\n", Status)); + return EFI_DEVICE_ERROR; + } + + if (Private->Capability[Slot].Voltage33 != 0) { + // + // Support 3.3V + // + MaxCurrent = ((UINT32)Private->MaxCurrent[Slot] & 0xFF) * 4; + } else if (Private->Capability[Slot].Voltage30 != 0) { + // + // Support 3.0V + // + MaxCurrent = (((UINT32)Private->MaxCurrent[Slot] >> 8) & 0xFF) * 4; + } else if (Private->Capability[Slot].Voltage18 != 0) { + // + // Support 1.8V + // + MaxCurrent = (((UINT32)Private->MaxCurrent[Slot] >> 16) & 0xFF) * 4; + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + if (MaxCurrent >= 150) { + Xpc = TRUE; + } else { + Xpc = FALSE; + } + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((ControllerVer & 0xFF) == 2) { + S18r = TRUE; + } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + S18r = FALSE; + } else { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + // + // 5. Repeatly send Acmd41 with supply voltage window to the device. + // Note here we only support the cards complied with SD physical + // layer simplified spec version 2.0 and version 3.0 and above. + // + do { + Status = SdCardSendOpCond (PassThru, Slot, 0, Ocr, S18r, Xpc, TRUE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: SdCardSendOpCond fails with %r Ocr %x, S18r %x, Xpc %x\n", Status, Ocr, S18r, Xpc)); + return EFI_DEVICE_ERROR; + } + } while ((Ocr & BIT31) == 0); + + // + // 6. If the S18A bit is set and the Host Controller supports 1.8V signaling + // (One of support bits is set to 1: SDR50, SDR104 or DDR50 in the + // Capabilities register), switch its voltage to 1.8V. + // + if ((Private->Capability[Slot].Sdr50 != 0 || + Private->Capability[Slot].Sdr104 != 0 || + Private->Capability[Slot].Ddr50 != 0) && + ((Ocr & BIT24) != 0)) { + Status = SdCardVoltageSwitch (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: Executing SdCardVoltageSwitch fails with %r\n", Status)); + Status = EFI_DEVICE_ERROR; + goto Error; + } else { + Status = SdMmcHcStopClock (PciIo, Slot); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Error; + } + + SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (((PresentState >> 20) & 0xF) != 0) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: SwitchVoltage fails with PresentState = 0x%x\n", PresentState)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + HostCtrl2 = BIT3; + SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + + gBS->Stall (5000); + + SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if ((HostCtrl2 & BIT3) == 0) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: SwitchVoltage fails with HostCtrl2 = 0x%x\n", HostCtrl2)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + + SdMmcHcInitClockFreq (PciIo, Slot, Private->Capability[Slot]); + + gBS->Stall (1); + + SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (((PresentState >> 20) & 0xF) != 0xF) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: SwitchVoltage fails with PresentState = 0x%x, It should be 0xF\n", PresentState)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + } + DEBUG ((EFI_D_INFO, "SdCardIdentification: Switch to 1.8v signal voltage success\n")); + } + + Status = SdCardAllSendCid (PassThru, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: Executing SdCardAllSendCid fails with %r\n", Status)); + return Status; + } + + Status = SdCardSetRca (PassThru, Slot, &Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdCardIdentification: Executing SdCardSetRca fails with %r\n", Status)); + return Status; + } + // + // Enter Data Tranfer Mode. + // + DEBUG ((EFI_D_INFO, "SdCardIdentification: Found a SD device at slot [%d]\n", Slot)); + Private->Slot[Slot].CardType = SdCardType; + + Status = SdCardSetBusMode (PciIo, PassThru, Slot, Rca, ((Ocr & BIT24) != 0)); + + return Status; + +Error: + // + // Set SD Bus Power = 0 + // + PowerCtrl = (UINT8)~BIT0; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_POWER_CTRL, sizeof (PowerCtrl), &PowerCtrl); + return EFI_DEVICE_ERROR; +} + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.c new file mode 100644 index 0000000000..0a2f034cfc --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.c @@ -0,0 +1,1261 @@ +/** @file + This driver is used to manage SD/MMC PCI host controllers which are compliance + with SD Host Controller Simplified Specification version 3.00. + + It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. + + Copyright (c) 2015, Intel Corporation. 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 "SdMmcPciHcDxe.h" + +// +// Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gSdMmcPciHcDriverBinding = { + SdMmcPciHcDriverBindingSupported, + SdMmcPciHcDriverBindingStart, + SdMmcPciHcDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for SD/MMC host controller private data. +// +SD_MMC_HC_PRIVATE_DATA gSdMmcPciHcTemplate = { + SD_MMC_HC_PRIVATE_SIGNATURE, // Signature + NULL, // ControllerHandle + NULL, // PciIo + { // PassThru + sizeof (UINT32), + SdMmcPassThruPassThru, + SdMmcPassThruGetNextSlot, + SdMmcPassThruBuildDevicePath, + SdMmcPassThruGetSlotNumber, + SdMmcPassThruResetDevice + }, + 0, // PciAttributes + 0, // PreviousSlot + NULL, // TimerEvent + NULL, // ConnectEvent + // Queue + INITIALIZE_LIST_HEAD_VARIABLE (gSdMmcPciHcTemplate.Queue), + { // Slot + {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, + {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0}, {0, UnknownSlot, 0, 0} + }, + { // Capability + {0}, + }, + { // MaxCurrent + 0, + }, + 0 // ControllerVersion +}; + +SD_DEVICE_PATH mSdDpTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_SD_DP, + { + (UINT8) (sizeof (SD_DEVICE_PATH)), + (UINT8) ((sizeof (SD_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +EMMC_DEVICE_PATH mEmmcDpTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_EMMC_DP, + { + (UINT8) (sizeof (EMMC_DEVICE_PATH)), + (UINT8) ((sizeof (EMMC_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +// +// Prioritized function list to detect card type. +// User could add other card detection logic here. +// +CARD_TYPE_DETECT_ROUTINE mCardTypeDetectRoutineTable[] = { + EmmcIdentification, + SdCardIdentification, + NULL +}; + +/** + The entry point for SD host controller driver, used to install this driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +InitializeSdMmcPciHcDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gSdMmcPciHcDriverBinding, + ImageHandle, + &gSdMmcPciHcComponentName, + &gSdMmcPciHcComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Call back function when the timer event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +ProcessAsyncTaskList ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + SD_MMC_HC_TRB *Trb; + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + BOOLEAN InfiniteWait; + EFI_EVENT TrbEvent; + + Private = (SD_MMC_HC_PRIVATE_DATA*)Context; + + // + // Check if the first entry in the async I/O queue is done or not. + // + Status = EFI_SUCCESS; + Trb = NULL; + Link = GetFirstNode (&Private->Queue); + if (!IsNull (&Private->Queue, Link)) { + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + if (Private->Slot[Trb->Slot].MediaPresent == FALSE) { + Status = EFI_NO_MEDIA; + goto Done; + } + if (!Trb->Started) { + // + // Check whether the cmd/data line is ready for transfer. + // + Status = SdMmcCheckTrbEnv (Private, Trb); + if (!EFI_ERROR (Status)) { + Trb->Started = TRUE; + Status = SdMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + goto Done; + } + } + Status = SdMmcCheckTrbResult (Private, Trb); + } + +Done: + if ((Trb != NULL) && (Status == EFI_NOT_READY)) { + Packet = Trb->Packet; + if (Packet->Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + if ((!InfiniteWait) && (Trb->Timeout-- == 0)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = EFI_TIMEOUT; + TrbEvent = Trb->Event; + SdMmcFreeTrb (Trb); + DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n", TrbEvent)); + gBS->SignalEvent (TrbEvent); + return; + } + } + if ((Trb != NULL) && (Status != EFI_NOT_READY)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = Status; + TrbEvent = Trb->Event; + SdMmcFreeTrb (Trb); + DEBUG ((EFI_D_VERBOSE, "ProcessAsyncTaskList(): Signal Event %p with %r\n", TrbEvent, Status)); + gBS->SignalEvent (TrbEvent); + } + return; +} + +/** + Sd removable device enumeration callback function when the timer event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +SdMmcPciHcEnumerateDevice ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_STATUS Status; + UINT8 Slot; + BOOLEAN MediaPresent; + UINT32 RoutineNum; + CARD_TYPE_DETECT_ROUTINE *Routine; + UINTN Index; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + + Private = (SD_MMC_HC_PRIVATE_DATA*)Context; + + for (Slot = 0; Slot < SD_MMC_HC_MAX_SLOT; Slot++) { + if ((Private->Slot[Slot].Enable) && (Private->Slot[Slot].SlotType == RemovableSlot)) { + Status = SdMmcHcCardDetect (Private->PciIo, Slot, &MediaPresent); + if ((Status == EFI_MEDIA_CHANGED) && (MediaPresent == FALSE)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcEnumerateDevice: device disconnected at slot %d of pci %p\n", Slot, Private->PciIo)); + Private->Slot[Slot].MediaPresent = FALSE; + // + // Signal all async task events at the slot with EFI_NO_MEDIA status. + // + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + if (Trb->Slot == Slot) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = EFI_NO_MEDIA; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + } + // + // Notify the upper layer the connect state change through ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + if ((Status == EFI_MEDIA_CHANGED) && (MediaPresent == TRUE)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcEnumerateDevice: device connected at slot %d of pci %p\n", Slot, Private->PciIo)); + // + // Reinitialize slot and restart identification process for the new attached device + // + Status = SdMmcHcInitHost (Private->PciIo, Slot, Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].MediaPresent = TRUE; + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private, Slot); + if (!EFI_ERROR (Status)) { + break; + } + } + } + + // + // Notify the upper layer the connect state change through ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + } + } + + return; +} +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 PciData; + + PciIo = NULL; + ParentDevicePath = NULL; + + // + // SdPciHcDxe is a device driver, and should ingore the + // "RemainingDevicePath" according to EFI spec. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID *) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error. + // + return Status; + } + // + // Close the protocol because we don't use it here. + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Now test the EfiPciIoProtocol. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Now further check the PCI header: Base class (offset 0x08) and + // Sub Class (offset 0x05). This controller should be an SD/MMC PCI + // Host Controller. + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + // + // Since we already got the PciData, we can close protocol to avoid to carry it + // on for multiple exit points. + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Examine SD PCI Host Controller PCI Configuration table fields. + // + if ((PciData.Hdr.ClassCode[2] == PCI_CLASS_SYSTEM_PERIPHERAL) && + (PciData.Hdr.ClassCode[1] == PCI_SUBCLASS_SD_HOST_CONTROLLER) && + ((PciData.Hdr.ClassCode[0] == 0x00) || (PciData.Hdr.ClassCode[0] == 0x01))) { + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Supports; + UINT64 PciAttributes; + UINT8 SlotNum; + UINT8 FirstBar; + UINT8 Slot; + UINT8 Index; + CARD_TYPE_DETECT_ROUTINE *Routine; + UINT32 RoutineNum; + BOOLEAN MediaPresent; + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStart: Start\n")); + + // + // Open PCI I/O Protocol and save pointer to open protocol + // in private data area. + // + PciIo = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Enable the SD Host Controller MMIO space + // + Private = NULL; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &PciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + + if (!EFI_ERROR (Status)) { + Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } else { + goto Done; + } + + Private = AllocateCopyPool (sizeof (SD_MMC_HC_PRIVATE_DATA), &gSdMmcPciHcTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Private->ControllerHandle = Controller; + Private->PciIo = PciIo; + Private->PciAttributes = PciAttributes; + InitializeListHead (&Private->Queue); + + // + // Get SD/MMC Pci Host Controller Slot info + // + Status = SdMmcHcGetSlotInfo (PciIo, &FirstBar, &SlotNum); + if (EFI_ERROR (Status)) { + goto Done; + } + + for (Slot = FirstBar; Slot < (FirstBar + SlotNum); Slot++) { + Private->Slot[Slot].Enable = TRUE; + + Status = SdMmcHcGetCapability (PciIo, Slot, &Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + DumpCapabilityReg (Slot, &Private->Capability[Slot]); + + Status = SdMmcHcGetMaxCurrent (PciIo, Slot, &Private->MaxCurrent[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].SlotType = Private->Capability[Slot].SlotType; + if ((Private->Slot[Slot].SlotType != RemovableSlot) && (Private->Slot[Slot].SlotType != EmbeddedSlot)) { + DEBUG ((EFI_D_INFO, "SdMmcPciHcDxe doesn't support the slot type [%d]!!!\n", Private->Slot[Slot].SlotType)); + continue; + } + + // + // Reset the specified slot of the SD/MMC Pci Host Controller + // + Status = SdMmcHcReset (PciIo, Slot); + if (EFI_ERROR (Status)) { + continue; + } + // + // Check whether there is a SD/MMC card attached + // + Status = SdMmcHcCardDetect (PciIo, Slot, &MediaPresent); + if ((Status == EFI_MEDIA_CHANGED) && (MediaPresent == FALSE)) { + DEBUG ((EFI_D_ERROR, "SdMmcHcCardDetect: No device attached in Slot[%d]!!!\n", Slot)); + continue; + } + + Status = SdMmcHcInitHost (PciIo, Slot, Private->Capability[Slot]); + if (EFI_ERROR (Status)) { + continue; + } + + Private->Slot[Slot].MediaPresent = TRUE; + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private, Slot); + if (!EFI_ERROR (Status)) { + break; + } + } + } + } + + // + // Start the asynchronous I/O monitor + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + ProcessAsyncTaskList, + Private, + &Private->TimerEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->TimerEvent, TimerPeriodic, SD_MMC_HC_ASYNC_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Start the Sd removable device connection enumeration + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + SdMmcPciHcEnumerateDevice, + Private, + &Private->ConnectEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->ConnectEvent, TimerPeriodic, SD_MMC_HC_ENUM_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru), + NULL + ); + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStart: %r End on %x\n", Status, Controller)); + +Done: + if (EFI_ERROR (Status)) { + if ((Private != NULL) && (Private->PciAttributes != 0)) { + // + // Restore original PCI attributes + // + PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if ((Private != NULL) && (Private->TimerEvent != NULL)) { + gBS->CloseEvent (Private->TimerEvent); + } + + if ((Private != NULL) && (Private->ConnectEvent != NULL)) { + gBS->CloseEvent (Private->ConnectEvent); + } + + if (Private != NULL) { + FreePool (Private); + } + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_MMC_HC_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStop: Start\n")); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Close Non-Blocking timer and free Task list. + // + if (Private->TimerEvent != NULL) { + gBS->CloseEvent (Private->TimerEvent); + Private->TimerEvent = NULL; + } + if (Private->ConnectEvent != NULL) { + gBS->CloseEvent (Private->ConnectEvent); + Private->ConnectEvent = NULL; + } + // + // As the timer is closed, there is no needs to use TPL lock to + // protect the critical region "queue". + // + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + + // + // Uninstall Block I/O protocol from the device handle + // + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru) + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + // + // Restore original PCI attributes + // + PciIo = Private->PciIo; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + FreePool (Private); + + DEBUG ((EFI_D_INFO, "SdMmcPciHcDriverBindingStop: End with %r\n", Status)); + + return Status; +} + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the SD card + specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the SD controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by SD card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruPassThru ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + SD_MMC_HC_PRIVATE_DATA *Private; + SD_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if ((This == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SdMmcCmdBlk == NULL) || (Packet->SdMmcStatusBlk == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + + Trb = SdMmcCreateTrb (Private, Slot, Packet, Event); + if (Trb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Immediately return for async I/O. + // + if (Event != NULL) { + return EFI_SUCCESS; + } + + // + // Wait async I/O list is empty before execute sync I/O operation. + // + while (TRUE) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (IsListEmpty (&Private->Queue)) { + gBS->RestoreTPL (OldTpl); + break; + } + gBS->RestoreTPL (OldTpl); + } + + Status = SdMmcWaitTrbEnv (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdMmcWaitTrbResult (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + FreePages (Trb->AdmaDesc, Trb->AdmaPages); + } + + if (Trb != NULL) { + FreePool (Trb); + } + + return Status; +} + +/** + Used to retrieve next slot numbers supported by the SD controller. The function + returns information about all available slots (populated or not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD controller + is returned. + + If Slot is a slot number that was returned on a previous call to GetNextSlot(), then + the slot number of the next slot on the SD controller is returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to GetNextSlot(), + EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then EFI_NOT_FOUND + is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD controller. + On output, a pointer to the next slot number on the SD controller. + An input value of 0xFF retrieves the first slot number on the SD + controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a previous call + to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + UINT8 Index; + + if ((This == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (*Slot == 0xFF) { + for (Index = 0; Index < SD_MMC_HC_MAX_SLOT; Index++) { + if (Private->Slot[Index].Enable) { + *Slot = Index; + Private->PreviousSlot = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; + } else if (*Slot == Private->PreviousSlot) { + for (Index = *Slot + 1; Index < SD_MMC_HC_MAX_SLOT; Index++) { + if (Private->Slot[Index].Enable) { + *Slot = Index; + Private->PreviousSlot = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; + } else { + return EFI_INVALID_PARAMETER; + } +} + +/** + Used to allocate and build a device path node for an SD card on the SD controller. + + The BuildDevicePath() function allocates and builds a single device node for the SD + card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, then EFI_NOT_FOUND + is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES + is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of + DevicePath are initialized to describe the SD card specified by Slot, and EFI_SUCCESS is + returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card for which a device + path node is to be allocated and built. + @param[in,out] DevicePath A pointer to a single device path node that describes the SD + card specified by Slot. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD card specified by + Slot was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruBuildDevicePath ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + + if ((This == NULL) || (DevicePath == NULL) || (Slot >= SD_MMC_HC_MAX_SLOT)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if ((!Private->Slot[Slot].Enable) || (!Private->Slot[Slot].MediaPresent)) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[Slot].CardType == SdCardType) { + SdNode = AllocateCopyPool (sizeof (SD_DEVICE_PATH), &mSdDpTemplate); + if (SdNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SdNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) SdNode; + } else if (Private->Slot[Slot].CardType == EmmcCardType) { + EmmcNode = AllocateCopyPool (sizeof (EMMC_DEVICE_PATH), &mEmmcDpTemplate); + if (EmmcNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + EmmcNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) EmmcNode; + } else { + // + // Currently we only support SD and EMMC two device nodes. + // + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + This function retrieves an SD card slot number based on the input device path. + + The GetSlotNumber() function retrieves slot number for the SD card specified by + the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver supports, + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes a SD + card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD card on + the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that the SD + Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetSlotNumber ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 *Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + UINT8 SlotNumber; + + if ((This == NULL) || (DevicePath == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + // + // Check whether the DevicePath belongs to SD_DEVICE_PATH or EMMC_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + ((DevicePath->SubType != MSG_SD_DP) && + (DevicePath->SubType != MSG_EMMC_DP)) || + (DevicePathNodeLength(DevicePath) != sizeof(SD_DEVICE_PATH)) || + (DevicePathNodeLength(DevicePath) != sizeof(EMMC_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + if (DevicePath->SubType == MSG_SD_DP) { + SdNode = (SD_DEVICE_PATH *) DevicePath; + SlotNumber = SdNode->SlotNumber; + } else { + EmmcNode = (EMMC_DEVICE_PATH *) DevicePath; + SlotNumber = EmmcNode->SlotNumber; + } + + if (SlotNumber >= SD_MMC_HC_MAX_SLOT) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[SlotNumber].Enable) { + *Slot = SlotNumber; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, EFI_UNSUPPORTED is + returned. + + If Slot is not in a valid slot number for this SD controller, EFI_INVALID_PARAMETER + is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card to be reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruResetDevice ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot + ) +{ + SD_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + // + // Free all async I/O requests in the queue + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = SD_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + SdMmcFreeTrb (Trb); + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h new file mode 100644 index 0000000000..2cca82383c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.h @@ -0,0 +1,784 @@ +/** @file + + Provides some data structure definitions used by the SD/MMC host controller driver. + +Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_MMC_PCI_HC_DXE_H_ +#define _SD_MMC_PCI_HC_DXE_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "SdMmcPciHci.h" + +extern EFI_COMPONENT_NAME_PROTOCOL gSdMmcPciHcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gSdMmcPciHcComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gSdMmcPciHcDriverBinding; + +#define SD_MMC_HC_PRIVATE_SIGNATURE SIGNATURE_32 ('s', 'd', 't', 'f') + +#define SD_MMC_HC_PRIVATE_FROM_THIS(a) \ + CR(a, SD_MMC_HC_PRIVATE_DATA, PassThru, SD_MMC_HC_PRIVATE_SIGNATURE) + +// +// Generic time out value, 1 microsecond as unit. +// +#define SD_MMC_HC_GENERIC_TIMEOUT 1 * 1000 * 1000 + +// +// SD/MMC async transfer timer interval, set by experience. +// The unit is 100us, takes 1ms as interval. +// +#define SD_MMC_HC_ASYNC_TIMER EFI_TIMER_PERIOD_MILLISECONDS(1) +// +// SD/MMC removable device enumeration timer interval, set by experience. +// The unit is 100us, takes 100ms as interval. +// +#define SD_MMC_HC_ENUM_TIMER EFI_TIMER_PERIOD_MILLISECONDS(100) + +typedef enum { + UnknownCardType, + SdCardType, + SdioCardType, + MmcCardType, + EmmcCardType +} SD_MMC_CARD_TYPE; + +typedef enum { + RemovableSlot, + EmbeddedSlot, + SharedBusSlot, + UnknownSlot +} EFI_SD_MMC_SLOT_TYPE; + +typedef struct { + BOOLEAN Enable; + EFI_SD_MMC_SLOT_TYPE SlotType; + BOOLEAN MediaPresent; + SD_MMC_CARD_TYPE CardType; +} SD_MMC_HC_SLOT; + +typedef struct { + UINTN Signature; + + EFI_HANDLE ControllerHandle; + EFI_PCI_IO_PROTOCOL *PciIo; + + EFI_SD_MMC_PASS_THRU_PROTOCOL PassThru; + + UINT64 PciAttributes; + // + // The field is used to record the previous slot in GetNextSlot(). + // + UINT8 PreviousSlot; + // + // For Non-blocking operation. + // + EFI_EVENT TimerEvent; + // + // For Sd removable device enumeration. + // + EFI_EVENT ConnectEvent; + LIST_ENTRY Queue; + + SD_MMC_HC_SLOT Slot[SD_MMC_HC_MAX_SLOT]; + SD_MMC_HC_SLOT_CAP Capability[SD_MMC_HC_MAX_SLOT]; + UINT64 MaxCurrent[SD_MMC_HC_MAX_SLOT]; + + UINT32 ControllerVersion; +} SD_MMC_HC_PRIVATE_DATA; + +#define SD_MMC_HC_TRB_SIG SIGNATURE_32 ('T', 'R', 'B', 'T') + +// +// TRB (Transfer Request Block) contains information for the cmd request. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY TrbList; + + UINT8 Slot; + UINT16 BlockSize; + + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + EFI_PHYSICAL_ADDRESS DataPhy; + VOID *DataMap; + SD_MMC_HC_TRANSFER_MODE Mode; + + EFI_EVENT Event; + BOOLEAN Started; + UINT64 Timeout; + + SD_MMC_HC_ADMA_DESC_LINE *AdmaDesc; + EFI_PHYSICAL_ADDRESS AdmaDescPhy; + VOID *AdmaMap; + UINT32 AdmaPages; + + SD_MMC_HC_PRIVATE_DATA *Private; +} SD_MMC_HC_TRB; + +#define SD_MMC_HC_TRB_FROM_THIS(a) \ + CR(a, SD_MMC_HC_TRB, TrbList, SD_MMC_HC_TRB_SIG) + +// +// Task for Non-blocking mode. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + UINT8 Slot; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + BOOLEAN IsStart; + EFI_EVENT Event; + UINT64 RetryTimes; + BOOLEAN InfiniteWait; + VOID *Map; + VOID *MapAddress; +} SD_MMC_HC_QUEUE; + +// +// Prototypes +// +/** + Execute card identification procedure. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The card is identified correctly. + @retval Others The card can't be identified. + +**/ +typedef +EFI_STATUS +(*CARD_TYPE_DETECT_ROUTINE) ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ); + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the SD card + specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the SD controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by SD card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruPassThru ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve next slot numbers supported by the SD controller. The function + returns information about all available slots (populated or not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD controller + is returned. + + If Slot is a slot number that was returned on a previous call to GetNextSlot(), then + the slot number of the next slot on the SD controller is returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to GetNextSlot(), + EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then EFI_NOT_FOUND + is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD controller. + On output, a pointer to the next slot number on the SD controller. + An input value of 0xFF retrieves the first slot number on the SD + controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a previous call + to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ); + +/** + Used to allocate and build a device path node for an SD card on the SD controller. + + The BuildDevicePath() function allocates and builds a single device node for the SD + card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, then EFI_NOT_FOUND + is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then EFI_OUT_OF_RESOURCES + is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of + DevicePath are initialized to describe the SD card specified by Slot, and EFI_SUCCESS is + returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card for which a device + path node is to be allocated and built. + @param[in,out] DevicePath A pointer to a single device path node that describes the SD + card specified by Slot. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD card specified by + Slot was allocated and returned in DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruBuildDevicePath ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + This function retrieves an SD card slot number based on the input device path. + + The GetSlotNumber() function retrieves slot number for the SD card specified by + the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver supports, + EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] DevicePath A pointer to the device path node that describes a SD + card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD card on + the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that the SD + Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruGetSlotNumber ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 *Slot + ); + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, EFI_UNSUPPORTED is + returned. + + If Slot is not in a valid slot number for this SD controller, EFI_INVALID_PARAMETER + is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. + @param[in] Slot Specifies the slot number of the SD card to be reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +EFI_STATUS +EFIAPI +SdMmcPassThruResetDevice ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot + ); + +// +// Driver model protocol interfaces +// +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdMmcPciHcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Create a new TRB for the SD/MMC cmd request. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @return Created Trb or NULL. + +**/ +SD_MMC_HC_TRB * +SdMmcCreateTrb ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event + ); + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + +**/ +VOID +SdMmcFreeTrb ( + IN SD_MMC_HC_TRB *Trb + ); + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdMmcCheckTrbEnv ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ); + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdMmcWaitTrbEnv ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ); + +/** + Execute the specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the host controller. + +**/ +EFI_STATUS +SdMmcExecTrb ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ); + +/** + Check the TRB execution result. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdMmcCheckTrbResult ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ); + +/** + Wait for the TRB execution result. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdMmcWaitTrbResult ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ); + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcIdentification ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ); + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +SdCardIdentification ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf new file mode 100644 index 0000000000..e26e6a098c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf @@ -0,0 +1,72 @@ +## @file +# SdMmcPciHcDxe driver is used to manage those host controllers which comply with SD +# Host Controller Simplified Specifiction version 3.0. +# +# It will produce EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC/eMMC cmds +# to specified devices from upper layer. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SdMmcPciHcDxe + MODULE_UNI_FILE = SdMmcPciHcDxe.uni + FILE_GUID = 8E325979-3FE1-4927-AAE2-8F5C4BD2AF0D + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeSdMmcPciHcDxe + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gSdMmcPciHcDxeDriverBinding +# COMPONENT_NAME = gSdMmcPciHcDxeComponentName +# COMPONENT_NAME2 = gSdMmcPciHcDxeComponentName2 +# +# + +[Sources] + SdMmcPciHcDxe.h + SdMmcPciHcDxe.c + EmmcDevice.c + SdDevice.c + SdMmcPciHci.h + SdMmcPciHci.c + ComponentName.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiPciIoProtocolGuid ## TO_START + gEfiSdMmcPassThruProtocolGuid ## BY_START + +# [Event] +# EVENT_TYPE_PERIODIC_TIMER ## SOMETIMES_CONSUMES + +[UserExtensions.TianoCore."ExtraFiles"] + SdMmcPciHcDxeExtra.uni diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni new file mode 100644 index 0000000000..57f9fa76a1 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.uni @@ -0,0 +1,23 @@ +// /** @file +// SdMmcPciHcDxe driver is used to manage those host controllers which comply with SD +// Host Controller Simplified Specifiction version 3.0. +// +// It will produce EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC/eMMC cmds +// to specified devices from upper layer. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "SD/MMC Pci Host Controller driver to manage SD/MMC host controllers" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver follows the UEFI driver model and produces SD/MMC Pass Thru protocol for upper layer bus driver." + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxeExtra.uni b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxeExtra.uni new file mode 100644 index 0000000000..c7aedb4cda --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxeExtra.uni @@ -0,0 +1,19 @@ +// /** @file +// SdMmcPciHcDxe Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SD/MMC Pci Host Controller Driver" + + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c new file mode 100644 index 0000000000..d2bd112c14 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c @@ -0,0 +1,1901 @@ +/** @file + This driver is used to manage SD/MMC PCI host controllers which are compliance + with SD Host Controller Simplified Specification version 3.00. + + It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. + + Copyright (c) 2015 - 2016, Intel Corporation. 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 "SdMmcPciHcDxe.h" + +/** + Dump the content of SD/MMC host controller's Capability Register. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The buffer to store the capability data. + +**/ +VOID +DumpCapabilityReg ( + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP *Capability + ) +{ + // + // Dump Capability Data + // + DEBUG ((EFI_D_INFO, " == Slot [%d] Capability is 0x%x ==\n", Slot, Capability)); + DEBUG ((EFI_D_INFO, " Timeout Clk Freq %d%a\n", Capability->TimeoutFreq, (Capability->TimeoutUnit) ? "MHz" : "KHz")); + DEBUG ((EFI_D_INFO, " Base Clk Freq %dMHz\n", Capability->BaseClkFreq)); + DEBUG ((EFI_D_INFO, " Max Blk Len %dbytes\n", 512 * (1 << Capability->MaxBlkLen))); + DEBUG ((EFI_D_INFO, " 8-bit Support %a\n", Capability->BusWidth8 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " ADMA2 Support %a\n", Capability->Adma2 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " SDMA Support %a\n", Capability->Sdma ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Suspend/Resume %a\n", Capability->SuspRes ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Voltage 3.3 %a\n", Capability->Voltage33 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Voltage 3.0 %a\n", Capability->Voltage30 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Voltage 1.8 %a\n", Capability->Voltage18 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " 64-bit Sys Bus %a\n", Capability->SysBus64 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Async Interrupt %a\n", Capability->AsyncInt ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " SlotType ")); + if (Capability->SlotType == 0x00) { + DEBUG ((EFI_D_INFO, "%a\n", "Removable Slot")); + } else if (Capability->SlotType == 0x01) { + DEBUG ((EFI_D_INFO, "%a\n", "Embedded Slot")); + } else if (Capability->SlotType == 0x02) { + DEBUG ((EFI_D_INFO, "%a\n", "Shared Bus Slot")); + } else { + DEBUG ((EFI_D_INFO, "%a\n", "Reserved")); + } + DEBUG ((EFI_D_INFO, " SDR50 Support %a\n", Capability->Sdr50 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " SDR104 Support %a\n", Capability->Sdr104 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " DDR50 Support %a\n", Capability->Ddr50 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Driver Type A %a\n", Capability->DriverTypeA ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Driver Type C %a\n", Capability->DriverTypeC ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Driver Type D %a\n", Capability->DriverTypeD ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Driver Type 4 %a\n", Capability->DriverType4 ? "TRUE" : "FALSE")); + if (Capability->TimerCount == 0) { + DEBUG ((EFI_D_INFO, " Retuning TimerCnt Disabled\n", 2 * (Capability->TimerCount - 1))); + } else { + DEBUG ((EFI_D_INFO, " Retuning TimerCnt %dseconds\n", 2 * (Capability->TimerCount - 1))); + } + DEBUG ((EFI_D_INFO, " SDR50 Tuning %a\n", Capability->TuningSDR50 ? "TRUE" : "FALSE")); + DEBUG ((EFI_D_INFO, " Retuning Mode Mode %d\n", Capability->RetuningMod + 1)); + DEBUG ((EFI_D_INFO, " Clock Multiplier M = %d\n", Capability->ClkMultiplier + 1)); + DEBUG ((EFI_D_INFO, " HS 400 %a\n", Capability->Hs400 ? "TRUE" : "FALSE")); + return; +} + +/** + Read SlotInfo register from SD/MMC host controller pci config space. + + @param[in] PciIo The PCI IO protocol instance. + @param[out] FirstBar The buffer to store the first BAR value. + @param[out] SlotNum The buffer to store the supported slot number. + + @retval EFI_SUCCESS The operation succeeds. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcGetSlotInfo ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT UINT8 *FirstBar, + OUT UINT8 *SlotNum + ) +{ + EFI_STATUS Status; + SD_MMC_HC_SLOT_INFO SlotInfo; + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + SD_MMC_HC_SLOT_OFFSET, + sizeof (SlotInfo), + &SlotInfo + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *FirstBar = SlotInfo.FirstBar; + *SlotNum = SlotInfo.SlotNum + 1; + ASSERT ((*FirstBar + *SlotNum) < SD_MMC_HC_MAX_SLOT); + return EFI_SUCCESS; +} + +/** + Read/Write specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcRwMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ) +{ + EFI_STATUS Status; + + if ((PciIo == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) { + return EFI_INVALID_PARAMETER; + } + + if (Read) { + Status = PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint8, + BarIndex, + (UINT64) Offset, + Count, + Data + ); + } else { + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint8, + BarIndex, + (UINT64) Offset, + Count, + Data + ); + } + + return Status; +} + +/** + Do OR operation with the value of the specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcOrMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 Or; + + Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + Or = *(UINT8*) OrData; + } else if (Count == 2) { + Or = *(UINT16*) OrData; + } else if (Count == 4) { + Or = *(UINT32*) OrData; + } else if (Count == 8) { + Or = *(UINT64*) OrData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data |= Or; + Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data); + + return Status; +} + +/** + Do AND operation with the value of the specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcAndMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *AndData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 And; + + Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + And = *(UINT8*) AndData; + } else if (Count == 2) { + And = *(UINT16*) AndData; + } else if (Count == 4) { + And = *(UINT32*) AndData; + } else if (Count == 8) { + And = *(UINT64*) AndData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data &= And; + Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, FALSE, Count, &Data); + + return Status; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + + @retval EFI_NOT_READY The MMIO register hasn't set to the expected value. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcCheckMmioSet ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue + ) +{ + EFI_STATUS Status; + UINT64 Value; + + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = 0; + Status = SdMmcHcRwMmio (PciIo, BarIndex, Offset, TRUE, Count, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcWaitMmioSet ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + Status = SdMmcHcCheckMmioSet ( + PciIo, + BarIndex, + Offset, + Count, + MaskValue, + TestValue + ); + if (Status != EFI_NOT_READY) { + return Status; + } + + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Software reset the specified SD/MMC host controller and enable all interrupts. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +SdMmcHcReset ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + UINT8 SwReset; + + SwReset = 0xFF; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_SW_RST, FALSE, sizeof (SwReset), &SwReset); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdMmcHcReset: write full 1 fails: %r\n", Status)); + return Status; + } + + Status = SdMmcHcWaitMmioSet ( + PciIo, + Slot, + SD_MMC_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0x00, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdMmcHcReset: reset done with %r\n", Status)); + return Status; + } + // + // Enable all interrupt after reset all. + // + Status = SdMmcHcEnableInterrupt (PciIo, Slot); + + return Status; +} + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcEnableInterrupt ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + UINT16 IntStatus; + + // + // Enable all bits in Error Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_ERR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Enable all bits in Normal Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + + return Status; +} + +/** + Get the capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcGetCapability ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT SD_MMC_HC_SLOT_CAP *Capability + ) +{ + EFI_STATUS Status; + UINT64 Cap; + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CAP, TRUE, sizeof (Cap), &Cap); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Capability, &Cap, sizeof (Cap)); + + return EFI_SUCCESS; +} + +/** + Get the maximum current capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] MaxCurrent The buffer to store the maximum current capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcGetMaxCurrent ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT UINT64 *MaxCurrent + ) +{ + EFI_STATUS Status; + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_MAX_CURRENT_CAP, TRUE, sizeof (UINT64), MaxCurrent); + + return Status; +} + +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +SdMmcHcCardDetect ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT BOOLEAN *MediaPresent + ) +{ + EFI_STATUS Status; + UINT16 Data; + UINT32 PresentState; + + // + // Check Normal Interrupt Status Register + // + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Data & (BIT6 | BIT7)) != 0) { + // + // Clear BIT6 and BIT7 by writing 1 to these two bits if set. + // + Data &= BIT6 | BIT7; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check Present State Register to see if there is a card presented. + // + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PresentState & BIT16) != 0) { + *MediaPresent = TRUE; + } else { + *MediaPresent = FALSE; + } + return EFI_MEDIA_CHANGED; + } + + return EFI_SUCCESS; +} + +/** + Stop SD/MMC card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +SdMmcHcStopClock ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + UINT32 PresentState; + UINT16 ClockCtrl; + + // + // Ensure no SD transactions are occurring on the SD Bus by + // waiting for Command Inhibit (DAT) and Command Inhibit (CMD) + // in the Present State register to be 0. + // + Status = SdMmcHcWaitMmioSet ( + PciIo, + Slot, + SD_MMC_HC_PRESENT_STATE, + sizeof (PresentState), + BIT0 | BIT1, + 0, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 0 + // + ClockCtrl = (UINT16)~BIT2; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + SD/MMC card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcClockSupply ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT64 ClockFreq, + IN SD_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 BaseClkFreq; + UINT32 SettingFreq; + UINT32 Divisor; + UINT32 Remainder; + UINT16 ControllerVer; + UINT16 ClockCtrl; + + // + // Calculate a divisor for SD clock frequency + // + ASSERT (Capability.BaseClkFreq != 0); + + BaseClkFreq = Capability.BaseClkFreq; + if ((ClockFreq > (BaseClkFreq * 1000)) || (ClockFreq == 0)) { + return EFI_INVALID_PARAMETER; + } + // + // Calculate the divisor of base frequency. + // + Divisor = 0; + SettingFreq = BaseClkFreq * 1000; + while (ClockFreq < SettingFreq) { + Divisor++; + + SettingFreq = (BaseClkFreq * 1000) / (2 * Divisor); + Remainder = (BaseClkFreq * 1000) % (2 * Divisor); + if ((ClockFreq == SettingFreq) && (Remainder == 0)) { + break; + } + if ((ClockFreq == SettingFreq) && (Remainder != 0)) { + SettingFreq ++; + } + } + + DEBUG ((EFI_D_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set SDCLK Frequency Select and Internal Clock Enable fields in Clock Control register. + // + if ((ControllerVer & 0xFF) == 2) { + ASSERT (Divisor <= 0x3FF); + ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2); + } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + // + // Only the most significant bit can be used as divisor. + // + if (((Divisor - 1) & Divisor) != 0) { + Divisor = 1 << (HighBitSet32 (Divisor) + 1); + } + ASSERT (Divisor <= 0x80); + ClockCtrl = (Divisor & 0xFF) << 8; + } else { + DEBUG ((EFI_D_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); + return EFI_UNSUPPORTED; + } + + // + // Stop bus clock at first + // + Status = SdMmcHcStopClock (PciIo, Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Supply clock frequency with specified divisor + // + ClockCtrl |= BIT0; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); + return Status; + } + + // + // Wait Internal Clock Stable in the Clock Control register to be 1 + // + Status = SdMmcHcWaitMmioSet ( + PciIo, + Slot, + SD_MMC_HC_CLOCK_CTRL, + sizeof (ClockCtrl), + BIT1, + BIT1, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + SD/MMC bus power control. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] PowerCtrl The value setting to the power control register. + + @retval TRUE There is a SD/MMC card attached. + @retval FALSE There is no a SD/MMC card attached. + +**/ +EFI_STATUS +SdMmcHcPowerControl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT8 PowerCtrl + ) +{ + EFI_STATUS Status; + + // + // Clr SD Bus Power + // + PowerCtrl &= (UINT8)~BIT0; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + PowerCtrl |= BIT0; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + + return Status; +} + +/** + Set the SD/MMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +SdMmcHcSetBusWidth ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT16 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (BusWidth == 1) { + HostCtrl1 = (UINT8)~(BIT5 | BIT1); + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 4) { + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 |= BIT1; + HostCtrl1 &= (UINT8)~BIT5; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 8) { + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 &= (UINT8)~BIT1; + HostCtrl1 |= BIT5; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** + Supply SD/MMC card with lowest clock frequency at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcInitClockFreq ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 InitFreq; + + // + // Calculate a divisor for SD clock frequency + // + if (Capability.BaseClkFreq == 0) { + // + // Don't support get Base Clock Frequency information via another method + // + return EFI_UNSUPPORTED; + } + // + // Supply 400KHz clock frequency at initialization phase. + // + InitFreq = 400; + Status = SdMmcHcClockSupply (PciIo, Slot, InitFreq, Capability); + return Status; +} + +/** + Supply SD/MMC card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcInitPowerVoltage ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT8 MaxVoltage; + UINT8 HostCtrl2; + + // + // Calculate supported maximum voltage according to SD Bus Voltage Select + // + if (Capability.Voltage33 != 0) { + // + // Support 3.3V + // + MaxVoltage = 0x0E; + } else if (Capability.Voltage30 != 0) { + // + // Support 3.0V + // + MaxVoltage = 0x0C; + } else if (Capability.Voltage18 != 0) { + // + // Support 1.8V + // + MaxVoltage = 0x0A; + HostCtrl2 = BIT3; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + gBS->Stall (5000); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + Status = SdMmcHcPowerControl (PciIo, Slot, MaxVoltage); + + return Status; +} + +/** + Initialize the Timeout Control register with most conservative value at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +SdMmcHcInitTimeoutCtrl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + UINT8 Timeout; + + Timeout = 0x0E; + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_TIMEOUT_CTRL, FALSE, sizeof (Timeout), &Timeout); + + return Status; +} + +/** + Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +SdMmcHcInitHost ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + + Status = SdMmcHcInitClockFreq (PciIo, Slot, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdMmcHcInitPowerVoltage (PciIo, Slot, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdMmcHcInitTimeoutCtrl (PciIo, Slot); + return Status; +} + +/** + Turn on/off LED. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] On The boolean to turn on/off LED. + + @retval EFI_SUCCESS The LED is turned on/off successfully. + @retval Others The LED isn't turned on/off successfully. + +**/ +EFI_STATUS +SdMmcHcLedOnOff ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN On + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (On) { + HostCtrl1 = BIT0; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else { + HostCtrl1 = (UINT8)~BIT0; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } + + return Status; +} + +/** + Build ADMA descriptor table for transfer. + + Refer to SD Host Controller Simplified spec 3.0 Section 1.13 for details. + + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The ADMA descriptor table is created successfully. + @retval Others The ADMA descriptor table isn't created successfully. + +**/ +EFI_STATUS +BuildAdmaDescTable ( + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_PHYSICAL_ADDRESS Data; + UINT64 DataLen; + UINT64 Entries; + UINT32 Index; + UINT64 Remaining; + UINT32 Address; + UINTN TableSize; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + UINTN Bytes; + + Data = Trb->DataPhy; + DataLen = Trb->DataLen; + PciIo = Trb->Private->PciIo; + // + // Only support 32bit ADMA Descriptor Table + // + if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + return EFI_INVALID_PARAMETER; + } + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) + // for 32-bit address descriptor table. + // + if ((Data & (BIT0 | BIT1)) != 0) { + DEBUG ((EFI_D_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data)); + } + + Entries = DivU64x32 ((DataLen + ADMA_MAX_DATA_PER_LINE - 1), ADMA_MAX_DATA_PER_LINE); + TableSize = (UINTN)MultU64x32 (Entries, sizeof (SD_MMC_HC_ADMA_DESC_LINE)); + Trb->AdmaPages = (UINT32)EFI_SIZE_TO_PAGES (TableSize); + Status = PciIo->AllocateBuffer ( + PciIo, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (TableSize), + (VOID **)&Trb->AdmaDesc, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ZeroMem (Trb->AdmaDesc, TableSize); + Bytes = TableSize; + Status = PciIo->Map ( + PciIo, + EfiPciIoOperationBusMasterCommonBuffer, + Trb->AdmaDesc, + &Bytes, + &Trb->AdmaDescPhy, + &Trb->AdmaMap + ); + + if (EFI_ERROR (Status) || (Bytes != TableSize)) { + // + // Map error or unable to map the whole RFis buffer into a contiguous region. + // + PciIo->FreeBuffer ( + PciIo, + EFI_SIZE_TO_PAGES (TableSize), + Trb->AdmaDesc + ); + return EFI_OUT_OF_RESOURCES; + } + + if ((UINT64)(UINTN)Trb->AdmaDescPhy > 0x100000000ul) { + // + // The ADMA doesn't support 64bit addressing. + // + PciIo->Unmap ( + PciIo, + Trb->AdmaMap + ); + PciIo->FreeBuffer ( + PciIo, + EFI_SIZE_TO_PAGES (TableSize), + Trb->AdmaDesc + ); + return EFI_DEVICE_ERROR; + } + + Remaining = DataLen; + Address = (UINT32)Data; + for (Index = 0; Index < Entries; Index++) { + if (Remaining <= ADMA_MAX_DATA_PER_LINE) { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = (UINT16)Remaining; + Trb->AdmaDesc[Index].Address = Address; + break; + } else { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = 0; + Trb->AdmaDesc[Index].Address = Address; + } + + Remaining -= ADMA_MAX_DATA_PER_LINE; + Address += ADMA_MAX_DATA_PER_LINE; + } + + // + // Set the last descriptor line as end of descriptor table + // + Trb->AdmaDesc[Index].End = 1; + return EFI_SUCCESS; +} + +/** + Create a new TRB for the SD/MMC cmd request. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If Event is + not NULL, then nonblocking I/O is performed, and Event + will be signaled when the Packet completes. + + @return Created Trb or NULL. + +**/ +SD_MMC_HC_TRB * +SdMmcCreateTrb ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event + ) +{ + SD_MMC_HC_TRB *Trb; + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_PCI_IO_PROTOCOL_OPERATION Flag; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN MapLength; + + Trb = AllocateZeroPool (sizeof (SD_MMC_HC_TRB)); + if (Trb == NULL) { + return NULL; + } + + Trb->Signature = SD_MMC_HC_TRB_SIG; + Trb->Slot = Slot; + Trb->BlockSize = 0x200; + Trb->Packet = Packet; + Trb->Event = Event; + Trb->Started = FALSE; + Trb->Timeout = Packet->Timeout; + Trb->Private = Private; + + if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { + Trb->Data = Packet->InDataBuffer; + Trb->DataLen = Packet->InTransferLength; + Trb->Read = TRUE; + } else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) { + Trb->Data = Packet->OutDataBuffer; + Trb->DataLen = Packet->OutTransferLength; + Trb->Read = FALSE; + } else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) { + Trb->Data = NULL; + Trb->DataLen = 0; + } else { + goto Error; + } + + if (Trb->Read) { + Flag = EfiPciIoOperationBusMasterWrite; + } else { + Flag = EfiPciIoOperationBusMasterRead; + } + + PciIo = Private->PciIo; + if (Trb->DataLen != 0) { + MapLength = Trb->DataLen; + Status = PciIo->Map ( + PciIo, + Flag, + Trb->Data, + &MapLength, + &Trb->DataPhy, + &Trb->DataMap + ); + if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + } + + if ((Trb->DataLen % Trb->BlockSize) != 0) { + if (Trb->DataLen < Trb->BlockSize) { + Trb->BlockSize = (UINT16)Trb->DataLen; + } + } + + if (Trb->DataLen == 0) { + Trb->Mode = SdMmcNoData; + } else if (Private->Capability[Slot].Adma2 != 0) { + Trb->Mode = SdMmcAdmaMode; + Status = BuildAdmaDescTable (Trb); + if (EFI_ERROR (Status)) { + PciIo->Unmap (PciIo, Trb->DataMap); + goto Error; + } + } else if (Private->Capability[Slot].Sdma != 0) { + Trb->Mode = SdMmcSdmaMode; + } else { + Trb->Mode = SdMmcPioMode; + } + + if (Event != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Private->Queue, &Trb->TrbList); + gBS->RestoreTPL (OldTpl); + } + + return Trb; + +Error: + SdMmcFreeTrb (Trb); + return NULL; +} + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + +**/ +VOID +SdMmcFreeTrb ( + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + + PciIo = Trb->Private->PciIo; + + if (Trb->AdmaMap != NULL) { + PciIo->Unmap ( + PciIo, + Trb->AdmaMap + ); + } + if (Trb->AdmaDesc != NULL) { + PciIo->FreeBuffer ( + PciIo, + Trb->AdmaPages, + Trb->AdmaDesc + ); + } + if (Trb->DataMap != NULL) { + PciIo->Unmap ( + PciIo, + Trb->DataMap + ); + } + FreePool (Trb); + return; +} + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdMmcCheckTrbEnv ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT32 PresentState; + + Packet = Trb->Packet; + + if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc) || + (Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR1b) || + (Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR5b)) { + // + // Wait Command Inhibit (CMD) and Command Inhibit (DAT) in + // the Present State register to be 0 + // + PresentState = BIT0 | BIT1; + // + // For Send Tuning Block cmd, just wait for Command Inhibit (CMD) to be 0 + // + if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || + ((Private->Slot[Trb->Slot].CardType == SdCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { + PresentState = BIT0; + } + } else { + // + // Wait Command Inhibit (CMD) in the Present State register + // to be 0 + // + PresentState = BIT0; + } + + PciIo = Private->PciIo; + Status = SdMmcHcCheckMmioSet ( + PciIo, + Trb->Slot, + SD_MMC_HC_PRESENT_STATE, + sizeof (PresentState), + PresentState, + 0 + ); + + return Status; +} + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdMmcWaitTrbEnv ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Packet = Trb->Packet; + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdMmcCheckTrbEnv (Private, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Execute the specified TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the host controller. + +**/ +EFI_STATUS +SdMmcExecTrb ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT16 Cmd; + UINT16 IntStatus; + UINT32 Argument; + UINT16 BlkCount; + UINT16 BlkSize; + UINT16 TransMode; + UINT8 HostCtrl1; + UINT32 SdmaAddr; + UINT64 AdmaAddr; + + Packet = Trb->Packet; + PciIo = Trb->Private->PciIo; + // + // Clear all bits in Error Interrupt Status Register + // + IntStatus = 0xFFFF; + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ERR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clear all bits in Normal Interrupt Status Register excepts for Card Removal & Card Insertion bits. + // + IntStatus = 0xFF3F; + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set Host Control 1 register DMA Select field + // + if (Trb->Mode == SdMmcAdmaMode) { + HostCtrl1 = BIT4; + Status = SdMmcHcOrMmio (PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + SdMmcHcLedOnOff (PciIo, Trb->Slot, TRUE); + + if (Trb->Mode == SdMmcSdmaMode) { + if ((UINT64)(UINTN)Trb->DataPhy >= 0x100000000ul) { + return EFI_INVALID_PARAMETER; + } + + SdmaAddr = (UINT32)(UINTN)Trb->DataPhy; + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (SdmaAddr), &SdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } else if (Trb->Mode == SdMmcAdmaMode) { + AdmaAddr = (UINT64)(UINTN)Trb->AdmaDescPhy; + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } + + BlkSize = Trb->BlockSize; + if (Trb->Mode == SdMmcSdmaMode) { + // + // Set SDMA boundary to be 512K bytes. + // + BlkSize |= 0x7000; + } + + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_SIZE, FALSE, sizeof (BlkSize), &BlkSize); + if (EFI_ERROR (Status)) { + return Status; + } + + BlkCount = (UINT16)(Trb->DataLen / Trb->BlockSize); + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_COUNT, FALSE, sizeof (BlkCount), &BlkCount); + if (EFI_ERROR (Status)) { + return Status; + } + + Argument = Packet->SdMmcCmdBlk->CommandArgument; + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ARG1, FALSE, sizeof (Argument), &Argument); + if (EFI_ERROR (Status)) { + return Status; + } + + TransMode = 0; + if (Trb->Mode != SdMmcNoData) { + if (Trb->Mode != SdMmcPioMode) { + TransMode |= BIT0; + } + if (Trb->Read) { + TransMode |= BIT4; + } + if (BlkCount != 0) { + TransMode |= BIT5 | BIT1; + } + // + // Only SD memory card needs to use AUTO CMD12 feature. + // + if (Private->Slot[Trb->Slot].CardType == SdCardType) { + if (BlkCount > 1) { + TransMode |= BIT2; + } + } + } + + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_TRANS_MOD, FALSE, sizeof (TransMode), &TransMode); + if (EFI_ERROR (Status)) { + return Status; + } + + Cmd = (UINT16)LShiftU64(Packet->SdMmcCmdBlk->CommandIndex, 8); + if (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc) { + Cmd |= BIT5; + } + // + // Convert ResponseType to value + // + if (Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeBc) { + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR1: + case SdMmcResponseTypeR5: + case SdMmcResponseTypeR6: + case SdMmcResponseTypeR7: + Cmd |= (BIT1 | BIT3 | BIT4); + break; + case SdMmcResponseTypeR2: + Cmd |= (BIT0 | BIT3); + break; + case SdMmcResponseTypeR3: + case SdMmcResponseTypeR4: + Cmd |= BIT1; + break; + case SdMmcResponseTypeR1b: + case SdMmcResponseTypeR5b: + Cmd |= (BIT0 | BIT1 | BIT3 | BIT4); + break; + default: + ASSERT (FALSE); + break; + } + } + // + // Execute cmd + // + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_COMMAND, FALSE, sizeof (Cmd), &Cmd); + return Status; +} + +/** + Check the TRB execution result. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdMmcCheckTrbResult ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT16 IntStatus; + UINT32 Response[4]; + UINT32 SdmaAddr; + UINT8 Index; + UINT8 SwReset; + + SwReset = 0; + Packet = Trb->Packet; + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Check Transfer Complete bit is set or not. + // + if ((IntStatus & BIT1) == BIT1) { + if ((IntStatus & BIT15) == BIT15) { + // + // Read Error Interrupt Status register to check if the error is + // Data Timeout Error. + // If yes, treat it as success as Transfer Complete has higher + // priority than Data Timeout Error. + // + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (!EFI_ERROR (Status)) { + if ((IntStatus & BIT4) == BIT4) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + } + } + } + + goto Done; + } + // + // Check if there is a error happened during cmd execution. + // If yes, then do error recovery procedure to follow SD Host Controller + // Simplified Spec 3.0 section 3.10.1. + // + if ((IntStatus & BIT15) == BIT15) { + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + if ((IntStatus & 0x0F) != 0) { + SwReset |= BIT1; + } + if ((IntStatus & 0xF0) != 0) { + SwReset |= BIT2; + } + + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_SW_RST, + FALSE, + sizeof (SwReset), + &SwReset + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Status = SdMmcHcWaitMmioSet ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EFI_DEVICE_ERROR; + goto Done; + } + // + // Check if DMA interrupt is signalled for the SDMA transfer. + // + if ((Trb->Mode == SdMmcSdmaMode) && ((IntStatus & BIT3) == BIT3)) { + // + // Clear DMA interrupt bit. + // + IntStatus = BIT3; + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Update SDMA Address register. + // + SdmaAddr = SD_MMC_SDMA_ROUND_UP ((UINT32)(UINTN)Trb->DataPhy, SD_MMC_SDMA_BOUNDARY); + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_SDMA_ADDR, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Trb->DataPhy = (UINT32)(UINTN)SdmaAddr; + } + + if ((Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeAdtc) && + (Packet->SdMmcCmdBlk->ResponseType != SdMmcResponseTypeR1b) && + (Packet->SdMmcCmdBlk->ResponseType != SdMmcResponseTypeR5b)) { + if ((IntStatus & BIT0) == BIT0) { + Status = EFI_SUCCESS; + goto Done; + } + } + + if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || + ((Private->Slot[Trb->Slot].CardType == SdCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { + // + // While performing tuning procedure (Execute Tuning is set to 1), + // Transfer Completeis not set to 1 + // Refer to SD Host Controller Simplified Specification 3.0 table 2-23 for details. + // + Status = EFI_SUCCESS; + goto Done; + } + + Status = EFI_NOT_READY; +Done: + // + // Get response data when the cmd is executed successfully. + // + if (!EFI_ERROR (Status)) { + if (Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeBc) { + for (Index = 0; Index < 4; Index++) { + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_RESPONSE + Index * 4, + TRUE, + sizeof (UINT32), + &Response[Index] + ); + if (EFI_ERROR (Status)) { + SdMmcHcLedOnOff (Private->PciIo, Trb->Slot, FALSE); + return Status; + } + } + CopyMem (Packet->SdMmcStatusBlk, Response, sizeof (Response)); + } + } + + if (Status != EFI_NOT_READY) { + SdMmcHcLedOnOff (Private->PciIo, Trb->Slot, FALSE); + } + + return Status; +} + +/** + Wait for the TRB execution result. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdMmcWaitTrbResult ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + Packet = Trb->Packet; + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdMmcCheckTrbResult (Private, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h new file mode 100644 index 0000000000..fb62758602 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.h @@ -0,0 +1,546 @@ +/** @file + + Provides some data structure definitions used by the SD/MMC host controller driver. + +Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_MMC_PCI_HCI_H_ +#define _SD_MMC_PCI_HCI_H_ + +// +// SD Host Controller SlotInfo Register Offset +// +#define SD_MMC_HC_SLOT_OFFSET 0x40 + +#define SD_MMC_HC_MAX_SLOT 6 + +// +// SD Host Controller MMIO Register Offset +// +#define SD_MMC_HC_SDMA_ADDR 0x00 +#define SD_MMC_HC_ARG2 0x00 +#define SD_MMC_HC_BLK_SIZE 0x04 +#define SD_MMC_HC_BLK_COUNT 0x06 +#define SD_MMC_HC_ARG1 0x08 +#define SD_MMC_HC_TRANS_MOD 0x0C +#define SD_MMC_HC_COMMAND 0x0E +#define SD_MMC_HC_RESPONSE 0x10 +#define SD_MMC_HC_BUF_DAT_PORT 0x20 +#define SD_MMC_HC_PRESENT_STATE 0x24 +#define SD_MMC_HC_HOST_CTRL1 0x28 +#define SD_MMC_HC_POWER_CTRL 0x29 +#define SD_MMC_HC_BLK_GAP_CTRL 0x2A +#define SD_MMC_HC_WAKEUP_CTRL 0x2B +#define SD_MMC_HC_CLOCK_CTRL 0x2C +#define SD_MMC_HC_TIMEOUT_CTRL 0x2E +#define SD_MMC_HC_SW_RST 0x2F +#define SD_MMC_HC_NOR_INT_STS 0x30 +#define SD_MMC_HC_ERR_INT_STS 0x32 +#define SD_MMC_HC_NOR_INT_STS_EN 0x34 +#define SD_MMC_HC_ERR_INT_STS_EN 0x36 +#define SD_MMC_HC_NOR_INT_SIG_EN 0x38 +#define SD_MMC_HC_ERR_INT_SIG_EN 0x3A +#define SD_MMC_HC_AUTO_CMD_ERR_STS 0x3C +#define SD_MMC_HC_HOST_CTRL2 0x3E +#define SD_MMC_HC_CAP 0x40 +#define SD_MMC_HC_MAX_CURRENT_CAP 0x48 +#define SD_MMC_HC_FORCE_EVT_AUTO_CMD 0x50 +#define SD_MMC_HC_FORCE_EVT_ERR_INT 0x52 +#define SD_MMC_HC_ADMA_ERR_STS 0x54 +#define SD_MMC_HC_ADMA_SYS_ADDR 0x58 +#define SD_MMC_HC_PRESET_VAL 0x60 +#define SD_MMC_HC_SHARED_BUS_CTRL 0xE0 +#define SD_MMC_HC_SLOT_INT_STS 0xFC +#define SD_MMC_HC_CTRL_VER 0xFE + +// +// The transfer modes supported by SD Host Controller +// Simplified Spec 3.0 Table 1-2 +// +typedef enum { + SdMmcNoData, + SdMmcPioMode, + SdMmcSdmaMode, + SdMmcAdmaMode +} SD_MMC_HC_TRANSFER_MODE; + +// +// The maximum data length of each descriptor line +// +#define ADMA_MAX_DATA_PER_LINE 0x10000 + +typedef struct { + UINT32 Valid:1; + UINT32 End:1; + UINT32 Int:1; + UINT32 Reserved:1; + UINT32 Act:2; + UINT32 Reserved1:10; + UINT32 Length:16; + UINT32 Address; +} SD_MMC_HC_ADMA_DESC_LINE; + +#define SD_MMC_SDMA_BOUNDARY 512 * 1024 +#define SD_MMC_SDMA_ROUND_UP(x, n) (((x) + n) & ~(n - 1)) + +typedef struct { + UINT8 FirstBar:3; // bit 0:2 + UINT8 Reserved:1; // bit 3 + UINT8 SlotNum:3; // bit 4:6 + UINT8 Reserved1:1; // bit 7 +} SD_MMC_HC_SLOT_INFO; + +typedef struct { + UINT32 TimeoutFreq:6; // bit 0:5 + UINT32 Reserved:1; // bit 6 + UINT32 TimeoutUnit:1; // bit 7 + UINT32 BaseClkFreq:8; // bit 8:15 + UINT32 MaxBlkLen:2; // bit 16:17 + UINT32 BusWidth8:1; // bit 18 + UINT32 Adma2:1; // bit 19 + UINT32 Reserved2:1; // bit 20 + UINT32 HighSpeed:1; // bit 21 + UINT32 Sdma:1; // bit 22 + UINT32 SuspRes:1; // bit 23 + UINT32 Voltage33:1; // bit 24 + UINT32 Voltage30:1; // bit 25 + UINT32 Voltage18:1; // bit 26 + UINT32 Reserved3:1; // bit 27 + UINT32 SysBus64:1; // bit 28 + UINT32 AsyncInt:1; // bit 29 + UINT32 SlotType:2; // bit 30:31 + UINT32 Sdr50:1; // bit 32 + UINT32 Sdr104:1; // bit 33 + UINT32 Ddr50:1; // bit 34 + UINT32 Reserved4:1; // bit 35 + UINT32 DriverTypeA:1; // bit 36 + UINT32 DriverTypeC:1; // bit 37 + UINT32 DriverTypeD:1; // bit 38 + UINT32 DriverType4:1; // bit 39 + UINT32 TimerCount:4; // bit 40:43 + UINT32 Reserved5:1; // bit 44 + UINT32 TuningSDR50:1; // bit 45 + UINT32 RetuningMod:2; // bit 46:47 + UINT32 ClkMultiplier:8; // bit 48:55 + UINT32 Reserved6:7; // bit 56:62 + UINT32 Hs400:1; // bit 63 +} SD_MMC_HC_SLOT_CAP; + +/** + Dump the content of SD/MMC host controller's Capability Register. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The buffer to store the capability data. + +**/ +VOID +DumpCapabilityReg ( + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP *Capability + ); + +/** + Read SlotInfo register from SD/MMC host controller pci config space. + + @param[in] PciIo The PCI IO protocol instance. + @param[out] FirstBar The buffer to store the first BAR value. + @param[out] SlotNum The buffer to store the supported slot number. + + @retval EFI_SUCCESS The operation succeeds. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcGetSlotInfo ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT UINT8 *FirstBar, + OUT UINT8 *SlotNum + ); + +/** + Read/Write specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcRwMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ); + +/** + Do OR operation with the value of the specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcOrMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ); + +/** + Do AND operation with the value of the specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcAndMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *AndData + ); + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +SdMmcHcWaitMmioSet ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ); + +/** + Software reset the specified SD/MMC host controller. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +SdMmcHcReset ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcEnableInterrupt ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); + +/** + Get the capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcGetCapability ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT SD_MMC_HC_SLOT_CAP *Capability + ); + +/** + Get the maximum current capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] MaxCurrent The buffer to store the maximum current capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcGetMaxCurrent ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT UINT64 *MaxCurrent + ); + +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +SdMmcHcCardDetect ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT BOOLEAN *MediaPresent + ); + +/** + Stop SD/MMC card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +SdMmcHcStopClock ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); + +/** + SD/MMC card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcClockSupply ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT64 ClockFreq, + IN SD_MMC_HC_SLOT_CAP Capability + ); + +/** + SD/MMC bus power control. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] PowerCtrl The value setting to the power control register. + + @retval TRUE There is a SD/MMC card attached. + @retval FALSE There is no a SD/MMC card attached. + +**/ +EFI_STATUS +SdMmcHcPowerControl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT8 PowerCtrl + ); + +/** + Set the SD/MMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +SdMmcHcSetBusWidth ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT16 BusWidth + ); + +/** + Supply SD/MMC card with lowest clock frequency at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcInitClockFreq ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ); + +/** + Supply SD/MMC card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +SdMmcHcInitPowerVoltage ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ); + +/** + Initialize the Timeout Control register with most conservative value at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +SdMmcHcInitTimeoutCtrl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); + +/** + Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +SdMmcHcInitHost ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_HC_SLOT_CAP Capability + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.c new file mode 100644 index 0000000000..3f4ebc40d8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.c @@ -0,0 +1,212 @@ +/** @file + SdMmcPciHcPei driver is used to provide platform-dependent info, mainly SD/MMC + host controller MMIO base, to upper layer SD/MMC drivers. + + Copyright (c) 2015, Intel Corporation. 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 "SdMmcPciHcPei.h" + +EDKII_SD_MMC_HOST_CONTROLLER_PPI mSdMmcHostControllerPpi = { GetSdMmcHcMmioBar }; + +EFI_PEI_PPI_DESCRIPTOR mPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiPeiSdMmcHostControllerPpiGuid, + &mSdMmcHostControllerPpi +}; + +/** + Get the MMIO base address of SD/MMC host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the SD/MMC host controller. + @param[in,out] MmioBar The pointer to store the array of available + SD/MMC host controller slot MMIO base addresses. + The entry number of the array is specified by BarNum. + @param[out] BarNum The pointer to store the supported bar number. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetSdMmcHcMmioBar ( + IN EDKII_SD_MMC_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + IN OUT UINTN **MmioBar, + OUT UINT8 *BarNum + ) +{ + SD_MMC_HC_PEI_PRIVATE_DATA *Private; + + if ((This == NULL) || (MmioBar == NULL) || (BarNum == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = SD_MMC_HC_PEI_PRIVATE_DATA_FROM_THIS (This); + + if (ControllerId >= Private->TotalSdMmcHcs) { + return EFI_INVALID_PARAMETER; + } + + *MmioBar = &Private->MmioBar[ControllerId].MmioBarAddr[0]; + *BarNum = (UINT8)Private->MmioBar[ControllerId].SlotNum; + return EFI_SUCCESS; +} + +/** + The user code starts with this function. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeSdMmcHcPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_BOOT_MODE BootMode; + EFI_STATUS Status; + UINT16 Bus; + UINT16 Device; + UINT16 Function; + UINT32 Size; + UINT64 MmioSize; + UINT8 SubClass; + UINT8 BaseClass; + UINT8 SlotInfo; + UINT8 SlotNum; + UINT8 FirstBar; + UINT8 Index; + UINT8 Slot; + UINT32 BarAddr; + SD_MMC_HC_PEI_PRIVATE_DATA *Private; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + Status = PeiServicesGetBootMode (&BootMode); + /// + /// We do not expose this in S3 boot path, because it is only for recovery. + /// + if (BootMode == BOOT_ON_S3_RESUME) { + return EFI_SUCCESS; + } + + Private = (SD_MMC_HC_PEI_PRIVATE_DATA *) AllocateZeroPool (sizeof (SD_MMC_HC_PEI_PRIVATE_DATA)); + if (Private == NULL) { + DEBUG ((EFI_D_ERROR, "Failed to allocate memory for SD_MMC_HC_PEI_PRIVATE_DATA! \n")); + return EFI_OUT_OF_RESOURCES; + } + + Private->Signature = SD_MMC_HC_PEI_SIGNATURE; + Private->SdMmcHostControllerPpi = mSdMmcHostControllerPpi; + Private->PpiList = mPpiList; + Private->PpiList.Ppi = &Private->SdMmcHostControllerPpi; + + BarAddr = PcdGet32 (PcdSdMmcPciHostControllerMmioBase); + for (Bus = 0; Bus < 256; Bus++) { + for (Device = 0; Device < 32; Device++) { + for (Function = 0; Function < 8; Function++) { + SubClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A)); + BaseClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B)); + + if ((SubClass == PCI_SUBCLASS_SD_HOST_CONTROLLER) && (BaseClass == PCI_CLASS_SYSTEM_PERIPHERAL)) { + // + // Get the SD/MMC Pci host controller's Slot Info. + // + SlotInfo = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, SD_MMC_HC_PEI_SLOT_OFFSET)); + FirstBar = (*(SD_MMC_HC_PEI_SLOT_INFO*)&SlotInfo).FirstBar; + SlotNum = (*(SD_MMC_HC_PEI_SLOT_INFO*)&SlotInfo).SlotNum + 1; + ASSERT ((FirstBar + SlotNum) < MAX_SD_MMC_SLOTS); + + for (Index = 0, Slot = FirstBar; Slot < (FirstBar + SlotNum); Index++, Slot++) { + // + // Get the SD/MMC Pci host controller's MMIO region size. + // + PciAnd16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (UINT16)~(EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE)); + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4 * Slot), 0xFFFFFFFF); + Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4 * Slot)); + + switch (Size & 0x07) { + case 0x0: + // + // Memory space: anywhere in 32 bit address space + // + MmioSize = (~(Size & 0xFFFFFFF0)) + 1; + break; + case 0x4: + // + // Memory space: anywhere in 64 bit address space + // + MmioSize = Size & 0xFFFFFFF0; + PciWrite32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4), 0xFFFFFFFF); + Size = PciRead32 (PCI_LIB_ADDRESS(Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4)); + // + // Fix the length to support some spefic 64 bit BAR + // + Size |= ((UINT32)(-1) << HighBitSet32 (Size)); + // + // Calculate the size of 64bit bar + // + MmioSize |= LShiftU64 ((UINT64) Size, 32); + MmioSize = (~(MmioSize)) + 1; + // + // Clean the high 32bits of this 64bit BAR to 0 as we only allow a 32bit BAR. + // + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4 * Slot + 4), 0); + break; + default: + // + // Unknown BAR type + // + ASSERT (FALSE); + continue; + }; + // + // Assign resource to the SdMmc Pci host controller's MMIO BAR. + // Enable the SdMmc Pci host controller by setting BME and MSE bits of PCI_CMD register. + // + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET + 4 * Slot), BarAddr); + PciOr16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE)); + // + // Record the allocated Mmio base address. + // + Private->MmioBar[Private->TotalSdMmcHcs].SlotNum++; + Private->MmioBar[Private->TotalSdMmcHcs].MmioBarAddr[Index] = BarAddr; + BarAddr += (UINT32)MmioSize; + } + Private->TotalSdMmcHcs++; + ASSERT (Private->TotalSdMmcHcs < MAX_SD_MMC_HCS); + } + } + } + } + + /// + /// Install SdMmc Host Controller PPI + /// + Status = PeiServicesInstallPpi (&Private->PpiList); + + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.h b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.h new file mode 100644 index 0000000000..1fbd2ca35b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.h @@ -0,0 +1,86 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_MMC_PCI_HOST_CONTROLLER_PEI_H_ +#define _SD_MMC_PCI_HOST_CONTROLLER_PEI_H_ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define SD_MMC_HC_PEI_SIGNATURE SIGNATURE_32 ('S', 'D', 'M', 'C') + +#define MAX_SD_MMC_HCS 8 +#define MAX_SD_MMC_SLOTS 6 + +// +// SD Host Controller SlotInfo Register Offset +// +#define SD_MMC_HC_PEI_SLOT_OFFSET 0x40 + +typedef struct { + UINT8 FirstBar:3; // bit 0:2 + UINT8 Reserved:1; // bit 3 + UINT8 SlotNum:3; // bit 4:6 + UINT8 Reserved1:1; // bit 7 +} SD_MMC_HC_PEI_SLOT_INFO; + +typedef struct { + UINTN SlotNum; + UINTN MmioBarAddr[MAX_SD_MMC_SLOTS]; +} SD_MMC_HC_PEI_BAR; + +typedef struct { + UINTN Signature; + EDKII_SD_MMC_HOST_CONTROLLER_PPI SdMmcHostControllerPpi; + EFI_PEI_PPI_DESCRIPTOR PpiList; + UINTN TotalSdMmcHcs; + SD_MMC_HC_PEI_BAR MmioBar[MAX_SD_MMC_HCS]; +} SD_MMC_HC_PEI_PRIVATE_DATA; + +#define SD_MMC_HC_PEI_PRIVATE_DATA_FROM_THIS(a) CR (a, SD_MMC_HC_PEI_PRIVATE_DATA, SdMmcHostControllerPpi, SD_MMC_HC_PEI_SIGNATURE) + +/** + Get the MMIO base address of SD/MMC host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the SD/MMC host controller. + @param[in,out] MmioBar The pointer to store the array of available + SD/MMC host controller slot MMIO base addresses. + The entry number of the array is specified by BarNum. + @param[out] BarNum The pointer to store the supported bar number. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetSdMmcHcMmioBar ( + IN EDKII_SD_MMC_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + IN OUT UINTN **MmioBar, + OUT UINT8 *BarNum + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf new file mode 100644 index 0000000000..bc6ea60d6b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf @@ -0,0 +1,56 @@ +## @file +# Component Description File For SD/MMC Pci Host Controller Pei Module. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SdMmcPciHcPei + MODULE_UNI_FILE = SdMmcPciHcPei.uni + FILE_GUID = 1BB737EF-427A-4144-8B3B-B76EF38515E6 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeSdMmcHcPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + SdMmcPciHcPei.c + SdMmcPciHcPei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PciLib + DebugLib + PeiServicesLib + MemoryAllocationLib + PeimEntryPoint + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdSdMmcPciHostControllerMmioBase ## CONSUMES + +[Ppis] + gEdkiiPeiSdMmcHostControllerPpiGuid ## PRODUCES + +[Depex] + gEfiPeiMasterBootModePpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SdMmcPciHcPeiExtra.uni \ No newline at end of file diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.uni b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.uni new file mode 100644 index 0000000000..0520cd2116 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.uni @@ -0,0 +1,22 @@ +// /** @file +// The SdMmcPciHcPei driver is used by upper layer to retrieve mmio base address +// of managed pci-based SD/MMC host controller at PEI phase. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Providing interface for upper layer to retrieve mmio base address of managed pci-based SD/MMC host controller at PEI phase." + +#string STR_MODULE_DESCRIPTION #language en-US "It implements the interface of getting mmio base address of managed pci-based SD/MMC host controller at PEI phase." + diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPeiExtra.uni b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPeiExtra.uni new file mode 100644 index 0000000000..71c4faed82 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPeiExtra.uni @@ -0,0 +1,21 @@ +// /** @file +// SdMmcPciHcPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SD/MMC PCI-Based HC Module for Recovery" + + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c new file mode 100644 index 0000000000..004670cb28 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c @@ -0,0 +1,807 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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 "EmmcBlockIoPei.h" + +// +// Template for EMMC HC Slot Data. +// +EMMC_PEIM_HC_SLOT gEmmcHcSlotTemplate = { + EMMC_PEIM_SLOT_SIG, // Signature + { // Media + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + { + MSG_EMMC_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + } + }, + 0, // MediaNum + { // PartitionType + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown, + EmmcPartitionUnknown + }, + 0, // EmmcHcBase + { // Capability + 0, + }, + { // Csd + 0, + }, + { // ExtCsd + {0}, + }, + TRUE, // SectorAddressing + NULL // Private +}; + +// +// Template for EMMC HC Private Data. +// +EMMC_PEIM_HC_PRIVATE_DATA gEmmcHcPrivateTemplate = { + EMMC_PEIM_SIG, // Signature + NULL, // Pool + { // BlkIoPpi + EmmcBlockIoPeimGetDeviceNo, + EmmcBlockIoPeimGetMediaInfo, + EmmcBlockIoPeimReadBlocks + }, + { // BlkIo2Ppi + EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION, + EmmcBlockIoPeimGetDeviceNo2, + EmmcBlockIoPeimGetMediaInfo2, + EmmcBlockIoPeimReadBlocks2 + }, + { // BlkIoPpiList + EFI_PEI_PPI_DESCRIPTOR_PPI, + &gEfiPeiVirtualBlockIoPpiGuid, + NULL + }, + { // BlkIo2PpiList + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEfiPeiVirtualBlockIo2PpiGuid, + NULL + }, + { // Slot + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + } + }, + 0, // SlotNum + 0 // TotalBlkIoDevices +}; +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + EMMC_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + *NumberBlockDevices = Private->TotalBlkIoDevices; + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ) +{ + EMMC_PEIM_HC_PRIVATE_DATA *Private; + UINT8 SlotNum; + UINT8 MediaNum; + UINT8 Location; + BOOLEAN Found; + + Found = FALSE; + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + + if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) { + return EFI_INVALID_PARAMETER; + } + + Location = 0; + MediaNum = 0; + for (SlotNum = 0; SlotNum < Private->SlotNum; SlotNum++) { + for (MediaNum = 0; MediaNum < Private->Slot[SlotNum].MediaNum; MediaNum++) { + Location ++; + if (Location == DeviceIndex) { + Found = TRUE; + break; + } + } + if (Found) { + break; + } + } + + MediaInfo->DeviceType = EMMC; + MediaInfo->MediaPresent = TRUE; + MediaInfo->LastBlock = (UINTN)Private->Slot[SlotNum].Media[MediaNum].LastBlock; + MediaInfo->BlockSize = Private->Slot[SlotNum].Media[MediaNum].BlockSize; + + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UINTN NumberOfBlocks; + EMMC_PEIM_HC_PRIVATE_DATA *Private; + UINT8 SlotNum; + UINT8 MediaNum; + UINT8 Location; + UINT8 PartitionConfig; + UINTN Remaining; + UINT32 MaxBlock; + BOOLEAN Found; + + Status = EFI_SUCCESS; + Found = FALSE; + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + + // + // Check parameters + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) { + return EFI_INVALID_PARAMETER; + } + + Location = 0; + MediaNum = 0; + for (SlotNum = 0; SlotNum < Private->SlotNum; SlotNum++) { + for (MediaNum = 0; MediaNum < Private->Slot[SlotNum].MediaNum; MediaNum++) { + Location ++; + if (Location == DeviceIndex) { + Found = TRUE; + break; + } + } + if (Found) { + break; + } + } + + BlockSize = Private->Slot[SlotNum].Media[MediaNum].BlockSize; + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > Private->Slot[SlotNum].Media[MediaNum].LastBlock) { + return EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / BlockSize; + + // + // Check if needs to switch partition access. + // + PartitionConfig = Private->Slot[SlotNum].ExtCsd.PartitionConfig; + if ((PartitionConfig & 0x7) != Private->Slot[SlotNum].PartitionType[MediaNum]) { + PartitionConfig &= (UINT8)~0x7; + PartitionConfig |= Private->Slot[SlotNum].PartitionType[MediaNum]; + Status = EmmcPeimSwitch ( + &Private->Slot[SlotNum], + 0x3, + OFFSET_OF (EMMC_EXT_CSD, PartitionConfig), + PartitionConfig, + 0x0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Private->Slot[SlotNum].ExtCsd.PartitionConfig = PartitionConfig; + } + // + // Start to execute data transfer. The max block number in single cmd is 65535 blocks. + // + Remaining = NumberOfBlocks; + MaxBlock = 0xFFFF; + + while (Remaining > 0) { + if (Remaining <= MaxBlock) { + NumberOfBlocks = Remaining; + } else { + NumberOfBlocks = MaxBlock; + } + + Status = EmmcPeimSetBlkCount (&Private->Slot[SlotNum], (UINT16)NumberOfBlocks); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferSize = NumberOfBlocks * BlockSize; + Status = EmmcPeimRwMultiBlocks (&Private->Slot[SlotNum], StartLBA, BlockSize, Buffer, BufferSize, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + StartLBA += NumberOfBlocks; + Buffer = (UINT8*)Buffer + BufferSize; + Remaining -= NumberOfBlocks; + } + return Status; +} + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + EMMC_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + *NumberBlockDevices = Private->TotalBlkIoDevices; + + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetMediaInfo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo + ) +{ + EFI_STATUS Status; + EMMC_PEIM_HC_PRIVATE_DATA *Private; + EFI_PEI_BLOCK_IO_MEDIA Media; + UINT8 SlotNum; + UINT8 MediaNum; + UINT8 Location; + BOOLEAN Found; + + Found = FALSE; + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + + Status = EmmcBlockIoPeimGetMediaInfo ( + PeiServices, + &Private->BlkIoPpi, + DeviceIndex, + &Media + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Location = 0; + MediaNum = 0; + for (SlotNum = 0; SlotNum < Private->SlotNum; SlotNum++) { + for (MediaNum = 0; MediaNum < Private->Slot[SlotNum].MediaNum; MediaNum++) { + Location ++; + if (Location == DeviceIndex) { + Found = TRUE; + break; + } + } + if (Found) { + break; + } + } + + CopyMem (MediaInfo, &(Private->Slot[SlotNum].Media[MediaNum]), sizeof (EFI_PEI_BLOCK_IO2_MEDIA)); + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + EMMC_PEIM_HC_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + + Status = EmmcBlockIoPeimReadBlocks ( + PeiServices, + &Private->BlkIoPpi, + DeviceIndex, + StartLBA, + BufferSize, + Buffer + ); + return Status; +} + +/** + The user code starts with this function. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeEmmcBlockIoPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + EMMC_PEIM_HC_PRIVATE_DATA *Private; + EDKII_SD_MMC_HOST_CONTROLLER_PPI *SdMmcHcPpi; + UINT32 Index; + UINT32 PartitionIndex; + UINTN *MmioBase; + UINT8 BarNum; + UINT8 SlotNum; + UINT8 MediaNum; + UINT8 Controller; + UINT64 Capacity; + EMMC_EXT_CSD *ExtCsd; + EMMC_HC_SLOT_CAP Capability; + EMMC_PEIM_HC_SLOT *Slot; + UINT32 SecCount; + UINT32 GpSizeMult; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + // + // locate Emmc host controller PPI + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiSdMmcHostControllerPpiGuid, + 0, + NULL, + (VOID **) &SdMmcHcPpi + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Controller = 0; + MmioBase = NULL; + while (TRUE) { + Status = SdMmcHcPpi->GetSdMmcHcMmioBar (SdMmcHcPpi, Controller, &MmioBase, &BarNum); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR (Status)) { + break; + } + + if (BarNum == 0) { + Controller++; + continue; + } + + Private = AllocateCopyPool (sizeof (EMMC_PEIM_HC_PRIVATE_DATA), &gEmmcHcPrivateTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + Private->BlkIoPpiList.Ppi = (VOID*)&Private->BlkIoPpi; + Private->BlkIo2PpiList.Ppi = (VOID*)&Private->BlkIo2Ppi; + // + // Initialize the memory pool which will be used in all transactions. + // + Status = EmmcPeimInitMemPool (Private); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + for (Index = 0; Index < BarNum; Index++) { + Status = EmmcPeimHcGetCapability (MmioBase[Index], &Capability); + if (EFI_ERROR (Status)) { + continue; + } + if (Capability.SlotType != 0x1) { + DEBUG ((EFI_D_INFO, "The slot at 0x%x is not embedded slot type\n", MmioBase[Index])); + Status = EFI_UNSUPPORTED; + continue; + } + + Status = EmmcPeimHcReset (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + Status = EmmcPeimHcCardDetect (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + Status = EmmcPeimHcInitHost (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + + SlotNum = Private->SlotNum; + Slot = &Private->Slot[SlotNum]; + CopyMem (Slot, &gEmmcHcSlotTemplate, sizeof (EMMC_PEIM_HC_SLOT)); + Slot->Private = Private; + Slot->EmmcHcBase = MmioBase[Index]; + CopyMem (&Slot->Capability, &Capability, sizeof (Capability)); + + Status = EmmcPeimIdentification (Slot); + if (EFI_ERROR (Status)) { + continue; + } + + ExtCsd = &Slot->ExtCsd; + if (ExtCsd->ExtCsdRev < 5) { + DEBUG ((EFI_D_ERROR, "The EMMC device version is too low, we don't support!!!\n")); + Status = EFI_UNSUPPORTED; + continue; + } + if ((ExtCsd->PartitioningSupport & BIT0) != BIT0) { + DEBUG ((EFI_D_ERROR, "The EMMC device doesn't support Partition Feature!!!\n")); + Status = EFI_UNSUPPORTED; + continue; + } + + for (PartitionIndex = 0; PartitionIndex < EMMC_PEIM_MAX_PARTITIONS; PartitionIndex++) { + switch (PartitionIndex) { + case EmmcPartitionUserData: + SecCount = *(UINT32*)&ExtCsd->SecCount; + Capacity = MultU64x32 ((UINT64)SecCount, 0x200); + break; + case EmmcPartitionBoot1: + case EmmcPartitionBoot2: + Capacity = ExtCsd->BootSizeMult * SIZE_128KB; + break; + case EmmcPartitionRPMB: + Capacity = ExtCsd->RpmbSizeMult * SIZE_128KB; + break; + case EmmcPartitionGP1: + GpSizeMult = (ExtCsd->GpSizeMult[0] | (ExtCsd->GpSizeMult[1] << 8) | (ExtCsd->GpSizeMult[2] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP2: + GpSizeMult = (ExtCsd->GpSizeMult[3] | (ExtCsd->GpSizeMult[4] << 8) | (ExtCsd->GpSizeMult[5] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP3: + GpSizeMult = (ExtCsd->GpSizeMult[6] | (ExtCsd->GpSizeMult[7] << 8) | (ExtCsd->GpSizeMult[8] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP4: + GpSizeMult = (ExtCsd->GpSizeMult[9] | (ExtCsd->GpSizeMult[10] << 8) | (ExtCsd->GpSizeMult[11] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + default: + ASSERT (FALSE); + continue; + } + + MediaNum = Slot->MediaNum; + if (Capacity != 0) { + Slot->Media[MediaNum].LastBlock = DivU64x32 (Capacity, Slot->Media[MediaNum].BlockSize) - 1; + Slot->PartitionType[MediaNum] = PartitionIndex; + Private->TotalBlkIoDevices++; + Slot->MediaNum++; + } + } + Private->SlotNum++; + } + Controller++; + + if (!EFI_ERROR (Status)) { + PeiServicesInstallPpi (&Private->BlkIoPpiList); + } + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h new file mode 100644 index 0000000000..5c8b740e4f --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h @@ -0,0 +1,381 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EMMC_BLOCK_IO_PEI_H_ +#define _EMMC_BLOCK_IO_PEI_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct _EMMC_PEIM_HC_PRIVATE_DATA EMMC_PEIM_HC_PRIVATE_DATA; +typedef struct _EMMC_PEIM_HC_SLOT EMMC_PEIM_HC_SLOT; +typedef struct _EMMC_TRB EMMC_TRB; + +#include "EmmcHci.h" +#include "EmmcHcMem.h" + +#define EMMC_PEIM_SIG SIGNATURE_32 ('E', 'M', 'C', 'P') +#define EMMC_PEIM_SLOT_SIG SIGNATURE_32 ('E', 'M', 'C', 'S') + +#define EMMC_PEIM_MAX_SLOTS 6 +#define EMMC_PEIM_MAX_PARTITIONS 8 + +struct _EMMC_PEIM_HC_SLOT { + UINT32 Signature; + EFI_PEI_BLOCK_IO2_MEDIA Media[EMMC_PEIM_MAX_PARTITIONS]; + UINT8 MediaNum; + EMMC_PARTITION_TYPE PartitionType[EMMC_PEIM_MAX_PARTITIONS]; + + UINTN EmmcHcBase; + EMMC_HC_SLOT_CAP Capability; + EMMC_CSD Csd; + EMMC_EXT_CSD ExtCsd; + BOOLEAN SectorAddressing; + EMMC_PEIM_HC_PRIVATE_DATA *Private; +}; + +struct _EMMC_PEIM_HC_PRIVATE_DATA { + UINT32 Signature; + EMMC_PEIM_MEM_POOL *Pool; + EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI BlkIo2Ppi; + EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList; + EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList; + EMMC_PEIM_HC_SLOT Slot[EMMC_PEIM_MAX_SLOTS]; + UINT8 SlotNum; + UINT8 TotalBlkIoDevices; +}; + +#define EMMC_TIMEOUT MultU64x32((UINT64)(3), 1000000) +#define GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS(a) CR (a, EMMC_PEIM_HC_PRIVATE_DATA, BlkIoPpi, EMMC_PEIM_SIG) +#define GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS2(a) CR (a, EMMC_PEIM_HC_PRIVATE_DATA, BlkIo2Ppi, EMMC_PEIM_SIG) + +struct _EMMC_TRB { + EMMC_PEIM_HC_SLOT *Slot; + UINT16 BlockSize; + + EMMC_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + EMMC_HC_TRANSFER_MODE Mode; + + UINT64 Timeout; + + EMMC_HC_ADMA_DESC_LINE *AdmaDesc; + UINTN AdmaDescSize; +}; + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimGetMediaInfo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Initialize the memory management pool for the host controller. + + @param Private The Emmc Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +EmmcPeimInitMemPool ( + IN EMMC_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +EmmcPeimAllocateMem ( + IN EMMC_PEIM_MEM_POOL *Pool, + IN UINTN Size + ); + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +EmmcPeimFreeMem ( + IN EMMC_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +#endif diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf new file mode 100644 index 0000000000..4163b528b8 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf @@ -0,0 +1,62 @@ +## @file +# Description file for the Embedded MMC (eMMC) Peim driver. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EmmcBlockIoPei + MODULE_UNI_FILE = EmmcBlockIoPei.uni + FILE_GUID = 7F06A90F-AE0D-4887-82C0-FEC7F4F68B29 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeEmmcBlockIoPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + EmmcBlockIoPei.c + EmmcBlockIoPei.h + EmmcHci.c + EmmcHci.h + EmmcHcMem.c + EmmcHcMem.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES + gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES + gEdkiiPeiSdMmcHostControllerPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiSdMmcHostControllerPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + EmmcBlockIoPeiExtra.uni + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni new file mode 100644 index 0000000000..f90e831019 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni @@ -0,0 +1,21 @@ +// /** @file +// The EmmcBlockIoPei driver is used to support recovery from EMMC device. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Support recovery from EMMC devices" + +#string STR_MODULE_DESCRIPTION #language en-US "The EmmcBlockIoPei driver is used to support recovery from EMMC device." + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni new file mode 100644 index 0000000000..9299c1b5c1 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni @@ -0,0 +1,21 @@ +// /** @file +// EmmcBlockIoPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EMMC BlockIo Peim for Recovery" + + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c new file mode 100644 index 0000000000..073af16dcb --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c @@ -0,0 +1,455 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. 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 "EmmcBlockIoPei.h" + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Pages How many pages to allocate. + + @return The allocated memory block or NULL if failed. + +**/ +EMMC_PEIM_MEM_BLOCK * +EmmcPeimAllocMemBlock ( + IN UINTN Pages + ) +{ + EMMC_PEIM_MEM_BLOCK *Block; + EFI_STATUS Status; + VOID *TempPtr; + EFI_PHYSICAL_ADDRESS Address; + + TempPtr = NULL; + Block = NULL; + + Status = PeiServicesAllocatePool (sizeof(EMMC_PEIM_MEM_BLOCK), &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(EMMC_PEIM_MEM_BLOCK)); + + // + // each bit in the bit array represents EMMC_PEIM_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (EMMC_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (EMMC_PEIM_MEM_BLOCK*)(UINTN)TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (EMMC_PEIM_MEM_UNIT * 8); + + Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen); + + Block->Bits = (UINT8*)(UINTN)TempPtr; + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + Pages, + &Address + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages)); + + Block->Buf = (UINT8*)((UINTN)Address); + Block->Next = NULL; + + return Block; +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +EmmcPeimFreeMemBlock ( + IN EMMC_PEIM_MEM_POOL *Pool, + IN EMMC_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT ((Pool != NULL) && (Block != NULL)); +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. If couldn't allocate the needed memory, + the return value is NULL. + +**/ +VOID * +EmmcPeimAllocMemFromBlock ( + IN EMMC_PEIM_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + EMMC_PEIM_NEXT_BIT (Byte, Bit); + + } else { + EMMC_PEIM_NEXT_BIT (Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT (!EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) EMMC_PEIM_MEM_BIT (Bit)); + EMMC_PEIM_NEXT_BIT (Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * EMMC_PEIM_MEM_UNIT; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +EmmcPeimInsertMemBlockToPool ( + IN EMMC_PEIM_MEM_BLOCK *Head, + IN EMMC_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT ((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @retval TRUE The memory block is empty. + @retval FALSE The memory block isn't empty. + +**/ +BOOLEAN +EmmcPeimIsMemBlockEmpty ( + IN EMMC_PEIM_MEM_BLOCK *Block + ) +{ + UINTN Index; + + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +EmmcPeimUnlinkMemBlock ( + IN EMMC_PEIM_MEM_BLOCK *Head, + IN EMMC_PEIM_MEM_BLOCK *BlockToUnlink + ) +{ + EMMC_PEIM_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @param Private The Emmc Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +EmmcPeimInitMemPool ( + IN EMMC_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EMMC_PEIM_MEM_POOL *Pool; + EFI_STATUS Status; + VOID *TempPtr; + + TempPtr = NULL; + Pool = NULL; + + Status = PeiServicesAllocatePool (sizeof (EMMC_PEIM_MEM_POOL), &TempPtr); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (EMMC_PEIM_MEM_POOL)); + + Pool = (EMMC_PEIM_MEM_POOL *)((UINTN)TempPtr); + + Pool->Head = EmmcPeimAllocMemBlock (EMMC_PEIM_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Pool = Pool; + return EFI_SUCCESS; +} + +/** + Release the memory management pool. + + @param Pool The memory pool to free. + + @retval EFI_DEVICE_ERROR Fail to free the memory pool. + @retval EFI_SUCCESS The memory pool is freed. + +**/ +EFI_STATUS +EmmcPeimFreeMemPool ( + IN EMMC_PEIM_MEM_POOL *Pool + ) +{ + EMMC_PEIM_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // EmmcPeimUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + EmmcPeimFreeMemBlock (Pool, Block); + } + + EmmcPeimFreeMemBlock (Pool, Pool->Head); + + return EFI_SUCCESS; +} + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +EmmcPeimAllocateMem ( + IN EMMC_PEIM_MEM_POOL *Pool, + IN UINTN Size + ) +{ + EMMC_PEIM_MEM_BLOCK *Head; + EMMC_PEIM_MEM_BLOCK *Block; + EMMC_PEIM_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = EMMC_PEIM_MEM_ROUND (Size); + Head = Pool->Head; + ASSERT (Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = EmmcPeimAllocMemFromBlock (Block, AllocSize / EMMC_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE (EMMC_PEIM_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1; + } else { + Pages = EMMC_PEIM_MEM_DEFAULT_PAGES; + } + + NewBlock = EmmcPeimAllocMemBlock (Pages); + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + EmmcPeimInsertMemBlockToPool (Head, NewBlock); + Mem = EmmcPeimAllocMemFromBlock (NewBlock, AllocSize / EMMC_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + } + + return Mem; +} + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +EmmcPeimFreeMem ( + IN EMMC_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + EMMC_PEIM_MEM_BLOCK *Head; + EMMC_PEIM_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = EMMC_PEIM_MEM_ROUND (Size); + ToFree = (UINT8 *) Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->Buf) / EMMC_PEIM_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / EMMC_PEIM_MEM_UNIT) % 8; + + // + // reset associated bits in bit arry + // + for (Count = 0; Count < (AllocSize / EMMC_PEIM_MEM_UNIT); Count++) { + ASSERT (EMMC_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ EMMC_PEIM_MEM_BIT (Bit)); + EMMC_PEIM_NEXT_BIT (Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory point + // + ASSERT (Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && EmmcPeimIsMemBlockEmpty (Block)) { + EmmcPeimFreeMemBlock (Pool, Block); + } + + return ; +} diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h new file mode 100644 index 0000000000..af0c93c610 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h @@ -0,0 +1,61 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EMMC_PEIM_MEM_H_ +#define _EMMC_PEIM_MEM_H_ + +#define EMMC_PEIM_MEM_BIT(a) ((UINTN)(1 << (a))) + +#define EMMC_PEIM_MEM_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & EMMC_PEIM_MEM_BIT(Bit)) == EMMC_PEIM_MEM_BIT(Bit))) + +typedef struct _EMMC_PEIM_MEM_BLOCK EMMC_PEIM_MEM_BLOCK; + +struct _EMMC_PEIM_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINTN BufLen; // Memory size in bytes + EMMC_PEIM_MEM_BLOCK *Next; +}; + +typedef struct _EMMC_PEIM_MEM_POOL { + EMMC_PEIM_MEM_BLOCK *Head; +} EMMC_PEIM_MEM_POOL; + +// +// Memory allocation unit, note that the value must meet EMMC spec alignment requirement. +// +#define EMMC_PEIM_MEM_UNIT 128 + +#define EMMC_PEIM_MEM_UNIT_MASK (EMMC_PEIM_MEM_UNIT - 1) +#define EMMC_PEIM_MEM_DEFAULT_PAGES 16 + +#define EMMC_PEIM_MEM_ROUND(Len) (((Len) + EMMC_PEIM_MEM_UNIT_MASK) & (~EMMC_PEIM_MEM_UNIT_MASK)) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define EMMC_PEIM_NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + +#endif + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c new file mode 100644 index 0000000000..050d843176 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c @@ -0,0 +1,2833 @@ +/** @file + + Copyright (c) 2015 - 2016, Intel Corporation. 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 "EmmcBlockIoPei.h" + +/** + Read/Write specified EMMC host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the Data or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +EmmcPeimHcRwMmio ( + IN UINTN Address, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ) +{ + if ((Address == 0) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) { + return EFI_INVALID_PARAMETER; + } + + switch (Count) { + case 1: + if (Read) { + *(UINT8*)Data = MmioRead8 (Address); + } else { + MmioWrite8 (Address, *(UINT8*)Data); + } + break; + case 2: + if (Read) { + *(UINT16*)Data = MmioRead16 (Address); + } else { + MmioWrite16 (Address, *(UINT16*)Data); + } + break; + case 4: + if (Read) { + *(UINT32*)Data = MmioRead32 (Address); + } else { + MmioWrite32 (Address, *(UINT32*)Data); + } + break; + case 8: + if (Read) { + *(UINT64*)Data = MmioRead64 (Address); + } else { + MmioWrite64 (Address, *(UINT64*)Data); + } + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Do OR operation with the value of the specified EMMC host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the OrData or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +EmmcPeimHcOrMmio ( + IN UINTN Address, + IN UINT8 Count, + IN VOID *OrData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 Or; + + Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + Or = *(UINT8*) OrData; + } else if (Count == 2) { + Or = *(UINT16*) OrData; + } else if (Count == 4) { + Or = *(UINT32*) OrData; + } else if (Count == 8) { + Or = *(UINT64*) OrData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data |= Or; + Status = EmmcPeimHcRwMmio (Address, FALSE, Count, &Data); + + return Status; +} + +/** + Do AND operation with the value of the specified EMMC host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the AndData or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +EmmcPeimHcAndMmio ( + IN UINTN Address, + IN UINT8 Count, + IN VOID *AndData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 And; + + Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + And = *(UINT8*) AndData; + } else if (Count == 2) { + And = *(UINT16*) AndData; + } else if (Count == 4) { + And = *(UINT32*) AndData; + } else if (Count == 8) { + And = *(UINT64*) AndData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data &= And; + Status = EmmcPeimHcRwMmio (Address, FALSE, Count, &Data); + + return Status; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] Address The address of the mmio register to be checked. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + + @retval EFI_NOT_READY The MMIO register hasn't set to the expected value. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +EmmcPeimHcCheckMmioSet ( + IN UINTN Address, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue + ) +{ + EFI_STATUS Status; + UINT64 Value; + + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = 0; + Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] Address The address of the mmio register to wait. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +EmmcPeimHcWaitMmioSet ( + IN UINTN Address, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + Status = EmmcPeimHcCheckMmioSet ( + Address, + Count, + MaskValue, + TestValue + ); + if (Status != EFI_NOT_READY) { + return Status; + } + + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Software reset the specified EMMC host controller and enable all interrupts. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +EmmcPeimHcReset ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT8 SwReset; + + SwReset = 0xFF; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_SW_RST, FALSE, sizeof (SwReset), &SwReset); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimHcReset: write full 1 fails: %r\n", Status)); + return Status; + } + + Status = EmmcPeimHcWaitMmioSet ( + Bar + EMMC_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0x00, + EMMC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "EmmcPeimHcReset: reset done with %r\n", Status)); + return Status; + } + // + // Enable all interrupt after reset all. + // + Status = EmmcPeimHcEnableInterrupt (Bar); + + return Status; +} + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimHcEnableInterrupt ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT16 IntStatus; + + // + // Enable all bits in Error Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_ERR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Enable all bits in Normal Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + + return Status; +} + +/** + Get the capability data from the specified slot. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimHcGetCapability ( + IN UINTN Bar, + OUT EMMC_HC_SLOT_CAP *Capability + ) +{ + EFI_STATUS Status; + UINT64 Cap; + + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_CAP, TRUE, sizeof (Cap), &Cap); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Capability, &Cap, sizeof (Cap)); + + return EFI_SUCCESS; +} + +/** + Detect whether there is a EMMC card attached at the specified EMMC host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS There is a EMMC card attached. + @retval EFI_NO_MEDIA There is not a EMMC card attached. + @retval Others The detection fails. + +**/ +EFI_STATUS +EmmcPeimHcCardDetect ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT16 Data; + UINT32 PresentState; + + // + // Check Normal Interrupt Status Register + // + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Data & (BIT6 | BIT7)) != 0) { + // + // Clear BIT6 and BIT7 by writing 1 to these two bits if set. + // + Data &= BIT6 | BIT7; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Check Present State Register to see if there is a card presented. + // + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PresentState & BIT16) != 0) { + return EFI_SUCCESS; + } else { + return EFI_NO_MEDIA; + } +} + +/** + Stop EMMC card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS Succeed to stop EMMC clock. + @retval Others Fail to stop EMMC clock. + +**/ +EFI_STATUS +EmmcPeimHcStopClock ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT32 PresentState; + UINT16 ClockCtrl; + + // + // Ensure no SD transactions are occurring on the SD Bus by + // waiting for Command Inhibit (DAT) and Command Inhibit (CMD) + // in the Present State register to be 0. + // + Status = EmmcPeimHcWaitMmioSet ( + Bar + EMMC_HC_PRESENT_STATE, + sizeof (PresentState), + BIT0 | BIT1, + 0, + EMMC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 0 + // + ClockCtrl = (UINT16)~BIT2; + Status = EmmcPeimHcAndMmio (Bar + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + EMMC card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +EmmcPeimHcClockSupply ( + IN UINTN Bar, + IN UINT64 ClockFreq + ) +{ + EFI_STATUS Status; + EMMC_HC_SLOT_CAP Capability; + UINT32 BaseClkFreq; + UINT32 SettingFreq; + UINT32 Divisor; + UINT32 Remainder; + UINT16 ControllerVer; + UINT16 ClockCtrl; + + // + // Calculate a divisor for SD clock frequency + // + Status = EmmcPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Capability.BaseClkFreq != 0); + + BaseClkFreq = Capability.BaseClkFreq; + if ((ClockFreq > (BaseClkFreq * 1000)) || (ClockFreq == 0)) { + return EFI_INVALID_PARAMETER; + } + // + // Calculate the divisor of base frequency. + // + Divisor = 0; + SettingFreq = BaseClkFreq * 1000; + while (ClockFreq < SettingFreq) { + Divisor++; + + SettingFreq = (BaseClkFreq * 1000) / (2 * Divisor); + Remainder = (BaseClkFreq * 1000) % (2 * Divisor); + if ((ClockFreq == SettingFreq) && (Remainder == 0)) { + break; + } + if ((ClockFreq == SettingFreq) && (Remainder != 0)) { + SettingFreq ++; + } + } + + DEBUG ((EFI_D_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); + + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set SDCLK Frequency Select and Internal Clock Enable fields in Clock Control register. + // + if ((ControllerVer & 0xFF) == 2) { + ASSERT (Divisor <= 0x3FF); + ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2); + } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + // + // Only the most significant bit can be used as divisor. + // + if (((Divisor - 1) & Divisor) != 0) { + Divisor = 1 << (HighBitSet32 (Divisor) + 1); + } + ASSERT (Divisor <= 0x80); + ClockCtrl = (Divisor & 0xFF) << 8; + } else { + DEBUG ((EFI_D_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); + return EFI_UNSUPPORTED; + } + + // + // Stop bus clock at first + // + Status = EmmcPeimHcStopClock (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Supply clock frequency with specified divisor + // + ClockCtrl |= BIT0; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); + return Status; + } + + // + // Wait Internal Clock Stable in the Clock Control register to be 1 + // + Status = EmmcPeimHcWaitMmioSet ( + Bar + EMMC_HC_CLOCK_CTRL, + sizeof (ClockCtrl), + BIT1, + BIT1, + EMMC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + Status = EmmcPeimHcOrMmio (Bar + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + EMMC bus power control. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] PowerCtrl The value setting to the power control register. + + @retval TRUE There is a EMMC card attached. + @retval FALSE There is no a EMMC card attached. + +**/ +EFI_STATUS +EmmcPeimHcPowerControl ( + IN UINTN Bar, + IN UINT8 PowerCtrl + ) +{ + EFI_STATUS Status; + + // + // Clr SD Bus Power + // + PowerCtrl &= (UINT8)~BIT0; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + PowerCtrl |= BIT0; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + + return Status; +} + +/** + Set the EMMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] BusWidth The bus width used by the EMMC device, it must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +EmmcPeimHcSetBusWidth ( + IN UINTN Bar, + IN UINT16 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (BusWidth == 1) { + HostCtrl1 = (UINT8)~(BIT5 | BIT1); + Status = EmmcPeimHcAndMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 4) { + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 |= BIT1; + HostCtrl1 &= (UINT8)~BIT5; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 8) { + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 &= (UINT8)~BIT1; + HostCtrl1 |= BIT5; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** + Supply EMMC card with lowest clock frequency at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +EmmcPeimHcInitClockFreq ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + EMMC_HC_SLOT_CAP Capability; + UINT32 InitFreq; + + // + // Calculate a divisor for SD clock frequency + // + Status = EmmcPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Capability.BaseClkFreq == 0) { + // + // Don't support get Base Clock Frequency information via another method + // + return EFI_UNSUPPORTED; + } + // + // Supply 400KHz clock frequency at initialization phase. + // + InitFreq = 400; + Status = EmmcPeimHcClockSupply (Bar, InitFreq); + return Status; +} + +/** + Supply EMMC card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +EmmcPeimHcInitPowerVoltage ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + EMMC_HC_SLOT_CAP Capability; + UINT8 MaxVoltage; + UINT8 HostCtrl2; + + // + // Get the support voltage of the Host Controller + // + Status = EmmcPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Calculate supported maximum voltage according to SD Bus Voltage Select + // + if (Capability.Voltage33 != 0) { + // + // Support 3.3V + // + MaxVoltage = 0x0E; + } else if (Capability.Voltage30 != 0) { + // + // Support 3.0V + // + MaxVoltage = 0x0C; + } else if (Capability.Voltage18 != 0) { + // + // Support 1.8V + // + MaxVoltage = 0x0A; + HostCtrl2 = BIT3; + Status = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + MicroSecondDelay (5000); + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + Status = EmmcPeimHcPowerControl (Bar, MaxVoltage); + + return Status; +} + +/** + Initialize the Timeout Control register with most conservative value at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +EmmcPeimHcInitTimeoutCtrl ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT8 Timeout; + + Timeout = 0x0E; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_TIMEOUT_CTRL, FALSE, sizeof (Timeout), &Timeout); + + return Status; +} + +/** + Initial EMMC host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +EmmcPeimHcInitHost ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + + Status = EmmcPeimHcInitClockFreq (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimHcInitPowerVoltage (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimHcInitTimeoutCtrl (Bar); + return Status; +} + +/** + Turn on/off LED. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] On The boolean to turn on/off LED. + + @retval EFI_SUCCESS The LED is turned on/off successfully. + @retval Others The LED isn't turned on/off successfully. + +**/ +EFI_STATUS +EmmcPeimHcLedOnOff ( + IN UINTN Bar, + IN BOOLEAN On + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (On) { + HostCtrl1 = BIT0; + Status = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else { + HostCtrl1 = (UINT8)~BIT0; + Status = EmmcPeimHcAndMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } + + return Status; +} + +/** + Build ADMA descriptor table for transfer. + + Refer to SD Host Controller Simplified spec 3.0 Section 1.13 for details. + + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The ADMA descriptor table is created successfully. + @retval Others The ADMA descriptor table isn't created successfully. + +**/ +EFI_STATUS +BuildAdmaDescTable ( + IN EMMC_TRB *Trb + ) +{ + EFI_PHYSICAL_ADDRESS Data; + UINT64 DataLen; + UINT64 Entries; + UINT32 Index; + UINT64 Remaining; + UINT32 Address; + + Data = (EFI_PHYSICAL_ADDRESS)(UINTN)Trb->Data; + DataLen = Trb->DataLen; + // + // Only support 32bit ADMA Descriptor Table + // + if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + return EFI_INVALID_PARAMETER; + } + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) + // for 32-bit address descriptor table. + // + if ((Data & (BIT0 | BIT1)) != 0) { + DEBUG ((EFI_D_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data)); + } + + Entries = DivU64x32 ((DataLen + ADMA_MAX_DATA_PER_LINE - 1), ADMA_MAX_DATA_PER_LINE); + + Trb->AdmaDescSize = (UINTN)MultU64x32 (Entries, sizeof (EMMC_HC_ADMA_DESC_LINE)); + Trb->AdmaDesc = EmmcPeimAllocateMem (Trb->Slot->Private->Pool, Trb->AdmaDescSize); + if (Trb->AdmaDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Remaining = DataLen; + Address = (UINT32)Data; + for (Index = 0; Index < Entries; Index++) { + if (Remaining <= ADMA_MAX_DATA_PER_LINE) { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = (UINT16)Remaining; + Trb->AdmaDesc[Index].Address = Address; + break; + } else { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = 0; + Trb->AdmaDesc[Index].Address = Address; + } + + Remaining -= ADMA_MAX_DATA_PER_LINE; + Address += ADMA_MAX_DATA_PER_LINE; + } + + // + // Set the last descriptor line as end of descriptor table + // + Trb->AdmaDesc[Index].End = 1; + return EFI_SUCCESS; +} + +/** + Create a new TRB for the EMMC cmd request. + + @param[in] Slot The slot number of the EMMC card to send the command to. + @param[in] Packet A pointer to the SD command data structure. + + @return Created Trb or NULL. + +**/ +EMMC_TRB * +EmmcPeimCreateTrb ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN EMMC_COMMAND_PACKET *Packet + ) +{ + EMMC_TRB *Trb; + EFI_STATUS Status; + EMMC_HC_SLOT_CAP Capability; + + // + // Calculate a divisor for SD clock frequency + // + Status = EmmcPeimHcGetCapability (Slot->EmmcHcBase, &Capability); + if (EFI_ERROR (Status)) { + return NULL; + } + + Trb = EmmcPeimAllocateMem (Slot->Private->Pool, sizeof (EMMC_TRB)); + if (Trb == NULL) { + return NULL; + } + + Trb->Slot = Slot; + Trb->BlockSize = 0x200; + Trb->Packet = Packet; + Trb->Timeout = Packet->Timeout; + + if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { + Trb->Data = Packet->InDataBuffer; + Trb->DataLen = Packet->InTransferLength; + Trb->Read = TRUE; + } else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) { + Trb->Data = Packet->OutDataBuffer; + Trb->DataLen = Packet->OutTransferLength; + Trb->Read = FALSE; + } else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) { + Trb->Data = NULL; + Trb->DataLen = 0; + } else { + goto Error; + } + + if ((Trb->DataLen % Trb->BlockSize) != 0) { + if (Trb->DataLen < Trb->BlockSize) { + Trb->BlockSize = (UINT16)Trb->DataLen; + } + } + + if (Trb->DataLen == 0) { + Trb->Mode = EmmcNoData; + } else if (Capability.Adma2 != 0) { + Trb->Mode = EmmcAdmaMode; + Status = BuildAdmaDescTable (Trb); + if (EFI_ERROR (Status)) { + goto Error; + } + } else if (Capability.Sdma != 0) { + Trb->Mode = EmmcSdmaMode; + } else { + Trb->Mode = EmmcPioMode; + } + + return Trb; + +Error: + EmmcPeimFreeTrb (Trb); + return NULL; +} + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the EMMC_TRB instance. + +**/ +VOID +EmmcPeimFreeTrb ( + IN EMMC_TRB *Trb + ) +{ + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + EmmcPeimFreeMem (Trb->Slot->Private->Pool, Trb->AdmaDesc, Trb->AdmaDescSize); + } + + if (Trb != NULL) { + EmmcPeimFreeMem (Trb->Slot->Private->Pool, Trb, sizeof (EMMC_TRB)); + } + return; +} + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +EmmcPeimCheckTrbEnv ( + IN UINTN Bar, + IN EMMC_TRB *Trb + ) +{ + EFI_STATUS Status; + EMMC_COMMAND_PACKET *Packet; + UINT32 PresentState; + + Packet = Trb->Packet; + + if ((Packet->EmmcCmdBlk->CommandType == EmmcCommandTypeAdtc) || + (Packet->EmmcCmdBlk->ResponseType == EmmcResponceTypeR1b) || + (Packet->EmmcCmdBlk->ResponseType == EmmcResponceTypeR5b)) { + // + // Wait Command Inhibit (CMD) and Command Inhibit (DAT) in + // the Present State register to be 0 + // + PresentState = BIT0 | BIT1; + if (Packet->EmmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK) { + PresentState = BIT0; + } + } else { + // + // Wait Command Inhibit (CMD) in the Present State register + // to be 0 + // + PresentState = BIT0; + } + + Status = EmmcPeimHcCheckMmioSet ( + Bar + EMMC_HC_PRESENT_STATE, + sizeof (PresentState), + PresentState, + 0 + ); + + return Status; +} + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +EmmcPeimWaitTrbEnv ( + IN UINTN Bar, + IN EMMC_TRB *Trb + ) +{ + EFI_STATUS Status; + EMMC_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Packet = Trb->Packet; + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = EmmcPeimCheckTrbEnv (Bar, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Execute the specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the host controller. + +**/ +EFI_STATUS +EmmcPeimExecTrb ( + IN UINTN Bar, + IN EMMC_TRB *Trb + ) +{ + EFI_STATUS Status; + EMMC_COMMAND_PACKET *Packet; + UINT16 Cmd; + UINT16 IntStatus; + UINT32 Argument; + UINT16 BlkCount; + UINT16 BlkSize; + UINT16 TransMode; + UINT8 HostCtrl1; + UINT32 SdmaAddr; + UINT64 AdmaAddr; + + Packet = Trb->Packet; + // + // Clear all bits in Error Interrupt Status Register + // + IntStatus = 0xFFFF; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_ERR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clear all bits in Normal Interrupt Status Register + // + IntStatus = 0xFFFF; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set Host Control 1 register DMA Select field + // + if (Trb->Mode == EmmcAdmaMode) { + HostCtrl1 = BIT4; + Status = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + EmmcPeimHcLedOnOff (Bar, TRUE); + + if (Trb->Mode == EmmcSdmaMode) { + if ((UINT64)(UINTN)Trb->Data >= 0x100000000ul) { + return EFI_INVALID_PARAMETER; + } + + SdmaAddr = (UINT32)(UINTN)Trb->Data; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_SDMA_ADDR, FALSE, sizeof (SdmaAddr), &SdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } else if (Trb->Mode == EmmcAdmaMode) { + AdmaAddr = (UINT64)(UINTN)Trb->AdmaDesc; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } + + BlkSize = Trb->BlockSize; + if (Trb->Mode == EmmcSdmaMode) { + // + // Set SDMA boundary to be 512K bytes. + // + BlkSize |= 0x7000; + } + + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_BLK_SIZE, FALSE, sizeof (BlkSize), &BlkSize); + if (EFI_ERROR (Status)) { + return Status; + } + + BlkCount = (UINT16)(Trb->DataLen / Trb->BlockSize); + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_BLK_COUNT, FALSE, sizeof (BlkCount), &BlkCount); + if (EFI_ERROR (Status)) { + return Status; + } + + Argument = Packet->EmmcCmdBlk->CommandArgument; + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_ARG1, FALSE, sizeof (Argument), &Argument); + if (EFI_ERROR (Status)) { + return Status; + } + + TransMode = 0; + if (Trb->Mode != EmmcNoData) { + if (Trb->Mode != EmmcPioMode) { + TransMode |= BIT0; + } + if (Trb->Read) { + TransMode |= BIT4; + } + if (BlkCount != 0) { + TransMode |= BIT5 | BIT1; + } + } + + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_TRANS_MOD, FALSE, sizeof (TransMode), &TransMode); + if (EFI_ERROR (Status)) { + return Status; + } + + Cmd = (UINT16)LShiftU64(Packet->EmmcCmdBlk->CommandIndex, 8); + if (Packet->EmmcCmdBlk->CommandType == EmmcCommandTypeAdtc) { + Cmd |= BIT5; + } + // + // Convert ResponseType to value + // + if (Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeBc) { + switch (Packet->EmmcCmdBlk->ResponseType) { + case EmmcResponceTypeR1: + case EmmcResponceTypeR5: + case EmmcResponceTypeR6: + case EmmcResponceTypeR7: + Cmd |= (BIT1 | BIT3 | BIT4); + break; + case EmmcResponceTypeR2: + Cmd |= (BIT0 | BIT3); + break; + case EmmcResponceTypeR3: + case EmmcResponceTypeR4: + Cmd |= BIT1; + break; + case EmmcResponceTypeR1b: + case EmmcResponceTypeR5b: + Cmd |= (BIT0 | BIT1 | BIT3 | BIT4); + break; + default: + ASSERT (FALSE); + break; + } + } + // + // Execute cmd + // + Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_COMMAND, FALSE, sizeof (Cmd), &Cmd); + return Status; +} + +/** + Check the TRB execution result. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +EmmcPeimCheckTrbResult ( + IN UINTN Bar, + IN EMMC_TRB *Trb + ) +{ + EFI_STATUS Status; + EMMC_COMMAND_PACKET *Packet; + UINT16 IntStatus; + UINT32 Response[4]; + UINT32 SdmaAddr; + UINT8 Index; + UINT8 SwReset; + + SwReset = 0; + Packet = Trb->Packet; + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_NOR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Check Transfer Complete bit is set or not. + // + if ((IntStatus & BIT1) == BIT1) { + if ((IntStatus & BIT15) == BIT15) { + // + // Read Error Interrupt Status register to check if the error is + // Data Timeout Error. + // If yes, treat it as success as Transfer Complete has higher + // priority than Data Timeout Error. + // + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (!EFI_ERROR (Status)) { + if ((IntStatus & BIT4) == BIT4) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + } + } + } + + goto Done; + } + // + // Check if there is a error happened during cmd execution. + // If yes, then do error recovery procedure to follow SD Host Controller + // Simplified Spec 3.0 section 3.10.1. + // + if ((IntStatus & BIT15) == BIT15) { + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if ((IntStatus & 0x0F) != 0) { + SwReset |= BIT1; + } + if ((IntStatus & 0xF0) != 0) { + SwReset |= BIT2; + } + + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_SW_RST, + FALSE, + sizeof (SwReset), + &SwReset + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Status = EmmcPeimHcWaitMmioSet ( + Bar + EMMC_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0, + EMMC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EFI_DEVICE_ERROR; + goto Done; + } + // + // Check if DMA interrupt is signalled for the SDMA transfer. + // + if ((Trb->Mode == EmmcSdmaMode) && ((IntStatus & BIT3) == BIT3)) { + // + // Clear DMA interrupt bit. + // + IntStatus = BIT3; + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_NOR_INT_STS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Update SDMA Address register. + // + SdmaAddr = EMMC_SDMA_ROUND_UP ((UINT32)(UINTN)Trb->Data, EMMC_SDMA_BOUNDARY); + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_SDMA_ADDR, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Trb->Data = (VOID*)(UINTN)SdmaAddr; + } + + if ((Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeAdtc) && + (Packet->EmmcCmdBlk->ResponseType != EmmcResponceTypeR1b) && + (Packet->EmmcCmdBlk->ResponseType != EmmcResponceTypeR5b)) { + if ((IntStatus & BIT0) == BIT0) { + Status = EFI_SUCCESS; + goto Done; + } + } + + if (Packet->EmmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK) { + Status = EFI_SUCCESS; + goto Done; + } + + Status = EFI_NOT_READY; +Done: + // + // Get response data when the cmd is executed successfully. + // + if (!EFI_ERROR (Status)) { + if (Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeBc) { + for (Index = 0; Index < 4; Index++) { + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_RESPONSE + Index * 4, + TRUE, + sizeof (UINT32), + &Response[Index] + ); + if (EFI_ERROR (Status)) { + EmmcPeimHcLedOnOff (Bar, FALSE); + return Status; + } + } + CopyMem (Packet->EmmcStatusBlk, Response, sizeof (Response)); + } + } + + if (Status != EFI_NOT_READY) { + EmmcPeimHcLedOnOff (Bar, FALSE); + } + + return Status; +} + +/** + Wait for the TRB execution result. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the EMMC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +EmmcPeimWaitTrbResult ( + IN UINTN Bar, + IN EMMC_TRB *Trb + ) +{ + EFI_STATUS Status; + EMMC_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + Packet = Trb->Packet; + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = EmmcPeimCheckTrbResult (Bar, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Sends EMMC command to an EMMC card that is attached to the EMMC controller. + + If Packet is successfully sent to the EMMC card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the EMMC controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in,out] Packet A pointer to the EMMC command data structure. + + @retval EFI_SUCCESS The EMMC Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the EMMC Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by EMMC card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +EmmcPeimExecCmd ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN OUT EMMC_COMMAND_PACKET *Packet + ) +{ + EFI_STATUS Status; + EMMC_TRB *Trb; + + if (Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->EmmcCmdBlk == NULL) || (Packet->EmmcStatusBlk == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + Trb = EmmcPeimCreateTrb (Slot, Packet); + if (Trb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EmmcPeimWaitTrbEnv (Slot->EmmcHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EmmcPeimExecTrb (Slot->EmmcHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EmmcPeimWaitTrbResult (Slot->EmmcHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + EmmcPeimFreeTrb (Trb); + + return Status; +} + +/** + Send command GO_IDLE_STATE (CMD0 with argument of 0x00000000) to the device to + make it go to Idle State. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + + @retval EFI_SUCCESS The EMMC device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +EmmcPeimReset ( + IN EMMC_PEIM_HC_SLOT *Slot + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_GO_IDLE_STATE; + EmmcCmdBlk.CommandType = EmmcCommandTypeBc; + EmmcCmdBlk.ResponseType = 0; + EmmcCmdBlk.CommandArgument = 0; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_OP_COND to the EMMC device to get the data of the OCR register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in, out] Argument On input, the argument of SEND_OP_COND is to send to the device. + On output, the argument is the value of OCR register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimGetOcr ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN OUT UINT32 *Argument + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SEND_OP_COND; + EmmcCmdBlk.CommandType = EmmcCommandTypeBcr; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR3; + EmmcCmdBlk.CommandArgument = *Argument; + + Status = EmmcPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + *Argument = EmmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to send the + data of their CID registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimGetAllCid ( + IN EMMC_PEIM_HC_SLOT *Slot + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_ALL_SEND_CID; + EmmcCmdBlk.CommandType = EmmcCommandTypeBcr; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR2; + EmmcCmdBlk.CommandArgument = 0; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative device + Address (RCA). + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSetRca ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SET_RELATIVE_ADDR; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = Rca << 16; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_CSD to the EMMC device to get the data of the CSD register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of this + buffer as the content of this byte is meaningless even + if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimGetCsd ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + OUT EMMC_CSD *Csd + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SEND_CSD; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR2; + EmmcCmdBlk.CommandArgument = Rca << 16; + + Status = EmmcPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &EmmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1); + } + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the EMMC device to select/deselect it. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSelect ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = Rca << 16; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_EXT_CSD to the EMMC device to get the data of the EXT_CSD register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[out] ExtCsd The buffer to store the content of the EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimGetExtCsd ( + IN EMMC_PEIM_HC_SLOT *Slot, + OUT EMMC_EXT_CSD *ExtCsd + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD; + EmmcCmdBlk.CommandType = EmmcCommandTypeAdtc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = 0x00000000; + + Packet.InDataBuffer = ExtCsd; + Packet.InTransferLength = sizeof (EMMC_EXT_CSD); + + Status = EmmcPeimExecCmd (Slot, &Packet); + return Status; +} + +/** + Send command SWITCH to the EMMC device to switch the mode of operation of the + selected Device or modifies the EXT_CSD registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Access The access mode of SWTICH command. + @param[in] Index The offset of the field to be access. + @param[in] Value The value to be set to the specified field of EXT_CSD register. + @param[in] CmdSet The value of CmdSet field of EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitch ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT8 Access, + IN UINT8 Index, + IN UINT8 Value, + IN UINT8 CmdSet + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SWITCH; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1b; + EmmcCmdBlk.CommandArgument = (Access << 24) | (Index << 16) | (Value << 8) | CmdSet; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed EMMC device to get its status register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSendStatus ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + OUT UINT32 *DevStatus + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SEND_STATUS; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = Rca << 16; + + Status = EmmcPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + *DevStatus = EmmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SET_BLOCK_COUNT to the addressed EMMC device to set the number of + blocks for the following block read/write cmd. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] BlockCount The number of the logical block to access. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSetBlkCount ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT16 BlockCount + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SET_BLOCK_COUNT; + EmmcCmdBlk.CommandType = EmmcCommandTypeAc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = BlockCount; + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command READ_MULTIPLE_BLOCK/WRITE_MULTIPLE_BLOCK to the addressed EMMC device + to read/write the specified number of blocks. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified EMMC device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimRwMultiBlocks ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest + // transfer speed (2.4MB/s). + // Refer to eMMC 5.0 spec section 6.9.1 for details. + // + Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;; + + if (IsRead) { + Packet.InDataBuffer = Buffer; + Packet.InTransferLength = (UINT32)BufferSize; + + EmmcCmdBlk.CommandIndex = EMMC_READ_MULTIPLE_BLOCK; + EmmcCmdBlk.CommandType = EmmcCommandTypeAdtc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + } else { + Packet.OutDataBuffer = Buffer; + Packet.OutTransferLength = (UINT32)BufferSize; + + EmmcCmdBlk.CommandIndex = EMMC_WRITE_MULTIPLE_BLOCK; + EmmcCmdBlk.CommandType = EmmcCommandTypeAdtc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + } + + if (Slot->SectorAddressing) { + EmmcCmdBlk.CommandArgument = (UINT32)Lba; + } else { + EmmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, BlockSize); + } + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal sampling point + detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSendTuningBlk ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT8 BusWidth + ) +{ + EMMC_COMMAND_BLOCK EmmcCmdBlk; + EMMC_STATUS_BLOCK EmmcStatusBlk; + EMMC_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[128]; + + ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk)); + ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.EmmcCmdBlk = &EmmcCmdBlk; + Packet.EmmcStatusBlk = &EmmcStatusBlk; + Packet.Timeout = EMMC_TIMEOUT; + + EmmcCmdBlk.CommandIndex = EMMC_SEND_TUNING_BLOCK; + EmmcCmdBlk.CommandType = EmmcCommandTypeAdtc; + EmmcCmdBlk.ResponseType = EmmcResponceTypeR1; + EmmcCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + if (BusWidth == 8) { + Packet.InTransferLength = sizeof (TuningBlock); + } else { + Packet.InTransferLength = 64; + } + + Status = EmmcPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Tunning the clock to get HS200 optimal sampling point. + + Command SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes the + tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimTuningClkForHs200 ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl2; + UINT8 Retry; + + // + // Notify the host that the sampling clock tuning procedure starts. + // + HostCtrl2 = BIT6; + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Ask the device to send a sequence of tuning blocks till the tuning procedure is done. + // + Retry = 0; + do { + Status = EmmcPeimSendTuningBlk (Slot, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimHcRwMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + break; + } + } while (++Retry < 40); + + if (Retry == 40) { + Status = EFI_TIMEOUT; + } + return Status; +} + +/** + Switch the bus width to specified width. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9 and SD Host Controller + Simplified Spec 3.0 section Figure 3-7 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] IsDdr If TRUE, use dual data rate data simpling method. Otherwise + use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitchBusWidth ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, BusWidth); + if (BusWidth == 4) { + Value = 1; + } else if (BusWidth == 8) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + + if (IsDdr) { + Value += 4; + } + + CmdSet = 0; + Status = EmmcPeimSwitch (Slot, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimSendStatus (Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus & BIT7) != 0) { + return EFI_DEVICE_ERROR; + } + + Status = EmmcPeimHcSetBusWidth (Slot->EmmcHcBase, BusWidth); + + return Status; +} + +/** + Switch the clock frequency to the specified value. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6 and SD Host Controller + Simplified Spec 3.0 section Figure 3-3 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] HsTiming The value to be written to HS_TIMING field of EXT_CSD register. + @param[in] ClockFreq The max clock frequency to be set, the unit is MHz. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitchClockFreq ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + IN UINT8 HsTiming, + IN UINT32 ClockFreq + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, HsTiming); + Value = HsTiming; + CmdSet = 0; + + Status = EmmcPeimSwitch (Slot, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimSendStatus (Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus & BIT7) != 0) { + return EFI_DEVICE_ERROR; + } + // + // Convert the clock freq unit from MHz to KHz. + // + Status = EmmcPeimHcClockSupply (Slot->EmmcHcBase, ClockFreq * 1000); + + return Status; +} + +/** + Switch to the High Speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] IsDdr If TRUE, use dual data rate data simpling method. Otherwise + use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitchToHighSpeed ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + IN UINT32 ClockFreq, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl1; + UINT8 HostCtrl2; + + Status = EmmcPeimSwitchBusWidth (Slot, Rca, IsDdr, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to Hight Speed timing + // + HostCtrl1 = BIT2; + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + + HostCtrl2 = (UINT8)~0x7; + Status = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + if (IsDdr) { + HostCtrl2 = BIT2; + } else if (ClockFreq == 52) { + HostCtrl2 = BIT0; + } else { + HostCtrl2 = 0; + } + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + HsTiming = 1; + Status = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + Switch to the HS200 timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitchToHS200 ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + IN UINT32 ClockFreq, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl2; + UINT16 ClockCtrl; + + if ((BusWidth != 4) && (BusWidth != 8)) { + return EFI_INVALID_PARAMETER; + } + + Status = EmmcPeimSwitchBusWidth (Slot, Rca, FALSE, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to HS200/SDR104 timing + // + // + // Stop bus clock at first + // + Status = EmmcPeimHcStopClock (Slot->EmmcHcBase); + if (EFI_ERROR (Status)) { + return Status; + } + + HostCtrl2 = (UINT8)~0x7; + Status = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl2 = BIT0 | BIT1; + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Wait Internal Clock Stable in the Clock Control register to be 1 before set SD Clock Enable bit + // + Status = EmmcPeimHcWaitMmioSet ( + Slot->EmmcHcBase + EMMC_HC_CLOCK_CTRL, + sizeof (ClockCtrl), + BIT1, + BIT1, + EMMC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + HsTiming = 2; + Status = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcPeimTuningClkForHs200 (Slot, BusWidth); + + return Status; +} + +/** + Switch to the HS400 timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitchToHS400 ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca, + IN UINT32 ClockFreq + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + UINT8 HostCtrl2; + + Status = EmmcPeimSwitchToHS200 (Slot, Rca, ClockFreq, 8); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to Hight Speed timing and set the clock frequency to a value less than 52MHz. + // + HsTiming = 1; + Status = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, 52); + if (EFI_ERROR (Status)) { + return Status; + } + // + // HS400 mode must use 8 data lines. + // + Status = EmmcPeimSwitchBusWidth (Slot, Rca, TRUE, 8); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set to HS400 timing + // + HostCtrl2 = (UINT8)~0x7; + Status = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl2 = BIT0 | BIT2; + Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + HsTiming = 3; + Status = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq); + + return Status; +} + +/** + Switch the high speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller + Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSetBusMode ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT32 Rca + ) +{ + EFI_STATUS Status; + EMMC_HC_SLOT_CAP Capability; + UINT8 HsTiming; + BOOLEAN IsDdr; + UINT32 ClockFreq; + UINT8 BusWidth; + + Status = EmmcPeimGetCsd (Slot, Rca, &Slot->Csd); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimSetBusMode: EmmcPeimGetCsd fails with %r\n", Status)); + return Status; + } + + if ((Slot->Csd.CSizeLow | Slot->Csd.CSizeHigh << 2) == 0xFFF) { + Slot->SectorAddressing = TRUE; + } else { + Slot->SectorAddressing = FALSE; + } + + Status = EmmcPeimSelect (Slot, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimSetBusMode: EmmcPeimSelect fails with %r\n", Status)); + return Status; + } + + Status = EmmcPeimHcGetCapability (Slot->EmmcHcBase, &Capability); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimSetBusMode: EmmcPeimHcGetCapability fails with %r\n", Status)); + return Status; + } + + ASSERT (Capability.BaseClkFreq != 0); + // + // Check if the Host Controller support 8bits bus width. + // + if (Capability.BusWidth8 != 0) { + BusWidth = 8; + } else { + BusWidth = 4; + } + // + // Get Deivce_Type from EXT_CSD register. + // + Status = EmmcPeimGetExtCsd (Slot, &Slot->ExtCsd); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimSetBusMode: EmmcPeimGetExtCsd fails with %r\n", Status)); + return Status; + } + // + // Calculate supported bus speed/bus width/clock frequency. + // + HsTiming = 0; + IsDdr = FALSE; + ClockFreq = 0; + if (((Slot->ExtCsd.DeviceType & (BIT4 | BIT5)) != 0) && (Capability.Sdr104 != 0)) { + HsTiming = 2; + IsDdr = FALSE; + ClockFreq = 200; + } else if (((Slot->ExtCsd.DeviceType & (BIT2 | BIT3)) != 0) && (Capability.Ddr50 != 0)) { + HsTiming = 1; + IsDdr = TRUE; + ClockFreq = 52; + } else if (((Slot->ExtCsd.DeviceType & BIT1) != 0) && (Capability.HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 52; + } else if (((Slot->ExtCsd.DeviceType & BIT0) != 0) && (Capability.HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 26; + } + // + // Check if both of the device and the host controller support HS400 DDR mode. + // + if (((Slot->ExtCsd.DeviceType & (BIT6 | BIT7)) != 0) && (Capability.Hs400 != 0)) { + // + // The host controller supports 8bits bus. + // + ASSERT (BusWidth == 8); + HsTiming = 3; + IsDdr = TRUE; + ClockFreq = 200; + } + + if ((ClockFreq == 0) || (HsTiming == 0)) { + // + // Continue using default setting. + // + return EFI_SUCCESS; + } + + DEBUG ((EFI_D_INFO, "HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n", HsTiming, ClockFreq, BusWidth, IsDdr ? "TRUE":"FALSE")); + + if (HsTiming == 3) { + // + // Execute HS400 timing switch procedure + // + Status = EmmcPeimSwitchToHS400 (Slot, Rca, ClockFreq); + } else if (HsTiming == 2) { + // + // Execute HS200 timing switch procedure + // + Status = EmmcPeimSwitchToHS200 (Slot, Rca, ClockFreq, BusWidth); + } else { + // + // Execute High Speed timing switch procedure + // + Status = EmmcPeimSwitchToHighSpeed (Slot, Rca, ClockFreq, BusWidth, IsDdr); + } + + return Status; +} + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcPeimIdentification ( + IN EMMC_PEIM_HC_SLOT *Slot + ) +{ + EFI_STATUS Status; + UINT32 Ocr; + UINT32 Rca; + + Status = EmmcPeimReset (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimReset fails with %r\n", Status)); + return Status; + } + + Ocr = 0; + do { + Status = EmmcPeimGetOcr (Slot, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimGetOcr fails with %r\n", Status)); + return Status; + } + } while ((Ocr & BIT31) == 0); + + Status = EmmcPeimGetAllCid (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimGetAllCid fails with %r\n", Status)); + return Status; + } + // + // Don't support multiple devices on the slot, that is + // shared bus slot feature. + // + Rca = 1; + Status = EmmcPeimSetRca (Slot, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimSetRca fails with %r\n", Status)); + return Status; + } + // + // Enter Data Tranfer Mode. + // + DEBUG ((EFI_D_INFO, "Found a EMMC device at slot [%d], RCA [%d]\n", Slot, Rca)); + + Status = EmmcPeimSetBusMode (Slot, Rca); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h new file mode 100644 index 0000000000..a74f1c6b99 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h @@ -0,0 +1,345 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EMMC_HCI_H_ +#define _EMMC_HCI_H_ + +// +// EMMC Host Controller MMIO Register Offset +// +#define EMMC_HC_SDMA_ADDR 0x00 +#define EMMC_HC_ARG2 0x00 +#define EMMC_HC_BLK_SIZE 0x04 +#define EMMC_HC_BLK_COUNT 0x06 +#define EMMC_HC_ARG1 0x08 +#define EMMC_HC_TRANS_MOD 0x0C +#define EMMC_HC_COMMAND 0x0E +#define EMMC_HC_RESPONSE 0x10 +#define EMMC_HC_BUF_DAT_PORT 0x20 +#define EMMC_HC_PRESENT_STATE 0x24 +#define EMMC_HC_HOST_CTRL1 0x28 +#define EMMC_HC_POWER_CTRL 0x29 +#define EMMC_HC_BLK_GAP_CTRL 0x2A +#define EMMC_HC_WAKEUP_CTRL 0x2B +#define EMMC_HC_CLOCK_CTRL 0x2C +#define EMMC_HC_TIMEOUT_CTRL 0x2E +#define EMMC_HC_SW_RST 0x2F +#define EMMC_HC_NOR_INT_STS 0x30 +#define EMMC_HC_ERR_INT_STS 0x32 +#define EMMC_HC_NOR_INT_STS_EN 0x34 +#define EMMC_HC_ERR_INT_STS_EN 0x36 +#define EMMC_HC_NOR_INT_SIG_EN 0x38 +#define EMMC_HC_ERR_INT_SIG_EN 0x3A +#define EMMC_HC_AUTO_CMD_ERR_STS 0x3C +#define EMMC_HC_HOST_CTRL2 0x3E +#define EMMC_HC_CAP 0x40 +#define EMMC_HC_MAX_CURRENT_CAP 0x48 +#define EMMC_HC_FORCE_EVT_AUTO_CMD 0x50 +#define EMMC_HC_FORCE_EVT_ERR_INT 0x52 +#define EMMC_HC_ADMA_ERR_STS 0x54 +#define EMMC_HC_ADMA_SYS_ADDR 0x58 +#define EMMC_HC_PRESET_VAL 0x60 +#define EMMC_HC_SHARED_BUS_CTRL 0xE0 +#define EMMC_HC_SLOT_INT_STS 0xFC +#define EMMC_HC_CTRL_VER 0xFE + +// +// The transfer modes supported by SD Host Controller +// Simplified Spec 3.0 Table 1-2 +// +typedef enum { + EmmcNoData, + EmmcPioMode, + EmmcSdmaMode, + EmmcAdmaMode +} EMMC_HC_TRANSFER_MODE; + +// +// The maximum data length of each descriptor line +// +#define ADMA_MAX_DATA_PER_LINE 0x10000 +#define EMMC_SDMA_BOUNDARY 512 * 1024 +#define EMMC_SDMA_ROUND_UP(x, n) (((x) + n) & ~(n - 1)) + +typedef enum { + EmmcCommandTypeBc, // Broadcast commands, no response + EmmcCommandTypeBcr, // Broadcast commands with response + EmmcCommandTypeAc, // Addressed(point-to-point) commands + EmmcCommandTypeAdtc // Addressed(point-to-point) data transfer commands +} EMMC_COMMAND_TYPE; + +typedef enum { + EmmcResponceTypeR1, + EmmcResponceTypeR1b, + EmmcResponceTypeR2, + EmmcResponceTypeR3, + EmmcResponceTypeR4, + EmmcResponceTypeR5, + EmmcResponceTypeR5b, + EmmcResponceTypeR6, + EmmcResponceTypeR7 +} EMMC_RESPONSE_TYPE; + +typedef struct _EMMC_COMMAND_BLOCK { + UINT16 CommandIndex; + UINT32 CommandArgument; + UINT32 CommandType; // One of the EMMC_COMMAND_TYPE values + UINT32 ResponseType; // One of the EMMC_RESPONSE_TYPE values +} EMMC_COMMAND_BLOCK; + +typedef struct _EMMC_STATUS_BLOCK { + UINT32 Resp0; + UINT32 Resp1; + UINT32 Resp2; + UINT32 Resp3; +} EMMC_STATUS_BLOCK; + +typedef struct _EMMC_COMMAND_PACKET { + UINT64 Timeout; + EMMC_COMMAND_BLOCK *EmmcCmdBlk; + EMMC_STATUS_BLOCK *EmmcStatusBlk; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT32 InTransferLength; + UINT32 OutTransferLength; +} EMMC_COMMAND_PACKET; + +#pragma pack(1) + +typedef struct { + UINT32 Valid:1; + UINT32 End:1; + UINT32 Int:1; + UINT32 Reserved:1; + UINT32 Act:2; + UINT32 Reserved1:10; + UINT32 Length:16; + UINT32 Address; +} EMMC_HC_ADMA_DESC_LINE; + +typedef struct { + UINT32 TimeoutFreq:6; // bit 0:5 + UINT32 Reserved:1; // bit 6 + UINT32 TimeoutUnit:1; // bit 7 + UINT32 BaseClkFreq:8; // bit 8:15 + UINT32 MaxBlkLen:2; // bit 16:17 + UINT32 BusWidth8:1; // bit 18 + UINT32 Adma2:1; // bit 19 + UINT32 Reserved2:1; // bit 20 + UINT32 HighSpeed:1; // bit 21 + UINT32 Sdma:1; // bit 22 + UINT32 SuspRes:1; // bit 23 + UINT32 Voltage33:1; // bit 24 + UINT32 Voltage30:1; // bit 25 + UINT32 Voltage18:1; // bit 26 + UINT32 Reserved3:1; // bit 27 + UINT32 SysBus64:1; // bit 28 + UINT32 AsyncInt:1; // bit 29 + UINT32 SlotType:2; // bit 30:31 + UINT32 Sdr50:1; // bit 32 + UINT32 Sdr104:1; // bit 33 + UINT32 Ddr50:1; // bit 34 + UINT32 Reserved4:1; // bit 35 + UINT32 DriverTypeA:1; // bit 36 + UINT32 DriverTypeC:1; // bit 37 + UINT32 DriverTypeD:1; // bit 38 + UINT32 DriverType4:1; // bit 39 + UINT32 TimerCount:4; // bit 40:43 + UINT32 Reserved5:1; // bit 44 + UINT32 TuningSDR50:1; // bit 45 + UINT32 RetuningMod:2; // bit 46:47 + UINT32 ClkMultiplier:8; // bit 48:55 + UINT32 Reserved6:7; // bit 56:62 + UINT32 Hs400:1; // bit 63 +} EMMC_HC_SLOT_CAP; + +#pragma pack() + +/** + Software reset the specified EMMC host controller and enable all interrupts. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +EmmcPeimHcReset ( + IN UINTN Bar + ); + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimHcEnableInterrupt ( + IN UINTN Bar + ); + +/** + Get the capability data from the specified slot. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimHcGetCapability ( + IN UINTN Bar, + OUT EMMC_HC_SLOT_CAP *Capability + ); + +/** + Detect whether there is a EMMC card attached at the specified EMMC host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS There is a EMMC card attached. + @retval EFI_NO_MEDIA There is not a EMMC card attached. + @retval Others The detection fails. + +**/ +EFI_STATUS +EmmcPeimHcCardDetect ( + IN UINTN Bar + ); + +/** + Initial EMMC host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +EmmcPeimHcInitHost ( + IN UINTN Bar + ); + +/** + Send command SWITCH to the EMMC device to switch the mode of operation of the + selected Device or modifies the EXT_CSD registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Access The access mode of SWTICH command. + @param[in] Index The offset of the field to be access. + @param[in] Value The value to be set to the specified field of EXT_CSD register. + @param[in] CmdSet The value of CmdSet field of EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSwitch ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT8 Access, + IN UINT8 Index, + IN UINT8 Value, + IN UINT8 CmdSet + ); + +/** + Send command SET_BLOCK_COUNT to the addressed EMMC device to set the number of + blocks for the following block read/write cmd. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] BlockCount The number of the logical block to access. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimSetBlkCount ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN UINT16 BlockCount + ); + +/** + Send command READ_MULTIPLE_BLOCK/WRITE_MULTIPLE_BLOCK to the addressed EMMC device + to read/write the specified number of blocks. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified EMMC device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcPeimRwMultiBlocks ( + IN EMMC_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ); + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Emmc card to send the command to. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcPeimIdentification ( + IN EMMC_PEIM_HC_SLOT *Slot + ); + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the EMMC_TRB instance. + +**/ +VOID +EmmcPeimFreeTrb ( + IN EMMC_TRB *Trb + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c b/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c new file mode 100644 index 0000000000..c9e9d081bc --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c @@ -0,0 +1,241 @@ +/** @file + UEFI Component Name(2) protocol implementation for EmmcDxe driver. + + Copyright (c) 2015, Intel Corporation. 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 "EmmcDxe.h" + +// +// Driver name table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mEmmcDxeDriverNameTable[] = { + { "eng;en", L"Edkii Emmc Device Driver" }, + { NULL , NULL } +}; + +// +// Controller name table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mEmmcDxeControllerNameTable[] = { + { "eng;en", L"Edkii Emmc Host Controller" }, + { NULL , NULL } +}; + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gEmmcDxeComponentName = { + EmmcDxeComponentNameGetDriverName, + EmmcDxeComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gEmmcDxeComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) EmmcDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) EmmcDxeComponentNameGetControllerName, + "en" +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mEmmcDxeDriverNameTable, + DriverName, + (BOOLEAN)(This == &gEmmcDxeComponentName) + ); + +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EMMC_DEVICE *Device; + EMMC_PARTITION *Partition; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gEmmcDxeDriverBinding.DriverBindingHandle, + &gEfiSdMmcPassThruProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ControllerNameTable = mEmmcDxeControllerNameTable; + if (ChildHandle != NULL) { + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiSdMmcPassThruProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the child context + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + gEmmcDxeDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO (BlockIo); + Device = Partition->Device; + ControllerNameTable = Device->ControllerNameTable; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gEmmcDxeComponentName) + );} + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c new file mode 100644 index 0000000000..edb438b09b --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c @@ -0,0 +1,1591 @@ +/** @file + The helper functions for BlockIo and BlockIo2 protocol. + + Copyright (c) 2015, Intel Corporation. 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 "EmmcDxe.h" + +/** + Nonblocking I/O callback funtion when the event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +AsyncIoCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EMMC_REQUEST *Request; + EFI_STATUS Status; + + Status = gBS->CloseEvent (Event); + if (EFI_ERROR (Status)) { + return; + } + + Request = (EMMC_REQUEST *) Context; + + DEBUG_CODE_BEGIN (); + DEBUG ((EFI_D_INFO, "Emmc Async Request: CmdIndex[%d] Arg[%08x] %r\n", + Request->SdMmcCmdBlk.CommandIndex, Request->SdMmcCmdBlk.CommandArgument, + Request->Packet.TransactionStatus)); + DEBUG_CODE_END (); + + if (EFI_ERROR (Request->Packet.TransactionStatus)) { + Request->Token->TransactionStatus = Request->Packet.TransactionStatus; + } + + RemoveEntryList (&Request->Link); + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); +} + +/** + Send command SELECT to the device to select/deselect the device. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSelect ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the device to get device status. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] DevStatus The buffer to store the device status. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSendStatus ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + CopyMem (DevStatus, &SdMmcStatusBlk.Resp0, sizeof (UINT32)); + } + + return Status; +} + +/** + Send command SEND_CSD to the device to get the CSD register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Csd The buffer to store the EMMC_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetCsd ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT EMMC_CSD *Csd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + ZeroMem (Csd, sizeof (EMMC_CSD)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1); + } + + return Status; +} + +/** + Send command SEND_CID to the device to get the CID register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Cid The buffer to store the EMMC_CID register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetCid ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT EMMC_CID *Cid + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + ZeroMem (Cid, sizeof (EMMC_CID)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Cid) + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CID) - 1); + } + + return Status; +} + +/** + Send command SEND_EXT_CSD to the device to get the EXT_CSD register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[out] ExtCsd The buffer to store the EXT_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetExtCsd ( + IN EMMC_DEVICE *Device, + OUT EMMC_EXT_CSD *ExtCsd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + ZeroMem (ExtCsd, sizeof (EMMC_EXT_CSD)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0x00000000; + Packet.InDataBuffer = ExtCsd; + Packet.InTransferLength = sizeof (EMMC_EXT_CSD); + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + + return Status; +} + +/** + Set the specified EXT_CSD register field through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] Offset The offset of the specified field in EXT_CSD register. + @param[in] Value The byte value written to the field specified by Offset. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSetExtCsd ( + IN EMMC_PARTITION *Partition, + IN UINT8 Offset, + IN UINT8 Value, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EMMC_REQUEST *SetExtCsdReq; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT32 CommandArgument; + EFI_TPL OldTpl; + + SetExtCsdReq = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + SetExtCsdReq = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (SetExtCsdReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + SetExtCsdReq->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Partition->Queue, &SetExtCsdReq->Link); + gBS->RestoreTPL (OldTpl); + SetExtCsdReq->Packet.SdMmcCmdBlk = &SetExtCsdReq->SdMmcCmdBlk; + SetExtCsdReq->Packet.SdMmcStatusBlk = &SetExtCsdReq->SdMmcStatusBlk; + SetExtCsdReq->Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SetExtCsdReq->SdMmcCmdBlk.CommandIndex = EMMC_SWITCH; + SetExtCsdReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SetExtCsdReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + // + // Write the Value to the field specified by Offset. + // + CommandArgument = (Value << 8) | (Offset << 16) | BIT24 | BIT25; + SetExtCsdReq->SdMmcCmdBlk.CommandArgument = CommandArgument; + + SetExtCsdReq->IsEnd = IsEnd; + SetExtCsdReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + SetExtCsdReq, + &SetExtCsdReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + SetExtCsdReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &SetExtCsdReq->Packet, SetExtCsdReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (SetExtCsdReq != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&SetExtCsdReq->Link); + gBS->RestoreTPL (OldTpl); + if (SetExtCsdReq->Event != NULL) { + gBS->CloseEvent (SetExtCsdReq->Event); + } + FreePool (SetExtCsdReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (SetExtCsdReq != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&SetExtCsdReq->Link); + gBS->RestoreTPL (OldTpl); + FreePool (SetExtCsdReq); + } + } + + return Status; +} + +/** + Set the number of blocks for a block read/write cmd through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] BlockNum The number of blocks for transfer. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSetBlkCount ( + IN EMMC_PARTITION *Partition, + IN UINT16 BlockNum, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EMMC_REQUEST *SetBlkCntReq; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_TPL OldTpl; + + SetBlkCntReq = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + SetBlkCntReq = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (SetBlkCntReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + SetBlkCntReq->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Partition->Queue, &SetBlkCntReq->Link); + gBS->RestoreTPL (OldTpl); + SetBlkCntReq->Packet.SdMmcCmdBlk = &SetBlkCntReq->SdMmcCmdBlk; + SetBlkCntReq->Packet.SdMmcStatusBlk = &SetBlkCntReq->SdMmcStatusBlk; + SetBlkCntReq->Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + SetBlkCntReq->SdMmcCmdBlk.CommandIndex = EMMC_SET_BLOCK_COUNT; + SetBlkCntReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SetBlkCntReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SetBlkCntReq->SdMmcCmdBlk.CommandArgument = BlockNum; + + SetBlkCntReq->IsEnd = IsEnd; + SetBlkCntReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + SetBlkCntReq, + &SetBlkCntReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + SetBlkCntReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &SetBlkCntReq->Packet, SetBlkCntReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (SetBlkCntReq != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&SetBlkCntReq->Link); + gBS->RestoreTPL (OldTpl); + if (SetBlkCntReq->Event != NULL) { + gBS->CloseEvent (SetBlkCntReq->Event); + } + FreePool (SetBlkCntReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (SetBlkCntReq != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&SetBlkCntReq->Link); + gBS->RestoreTPL (OldTpl); + FreePool (SetBlkCntReq); + } + } + + return Status; +} + +/** + Read blocks through security protocol cmds with the way of sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param[in] SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param[in] PayloadBufferSize Size in bytes of the payload data buffer. + @param[out] PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param[in] IsRead Indicates it is a read or write operation. + @param[in] Timeout The timeout value, in 100ns units. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcProtocolInOut ( + IN EMMC_PARTITION *Partition, + IN UINT8 SecurityProtocol, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + IN BOOLEAN IsRead, + IN UINT64 Timeout, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EMMC_REQUEST *ProtocolReq; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_TPL OldTpl; + + ProtocolReq = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + ProtocolReq = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (ProtocolReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + ProtocolReq->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Partition->Queue, &ProtocolReq->Link); + gBS->RestoreTPL (OldTpl); + ProtocolReq->Packet.SdMmcCmdBlk = &ProtocolReq->SdMmcCmdBlk; + ProtocolReq->Packet.SdMmcStatusBlk = &ProtocolReq->SdMmcStatusBlk; + + if (IsRead) { + ProtocolReq->Packet.InDataBuffer = PayloadBuffer; + ProtocolReq->Packet.InTransferLength = (UINT32)PayloadBufferSize; + + ProtocolReq->SdMmcCmdBlk.CommandIndex = EMMC_PROTOCOL_RD; + ProtocolReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + ProtocolReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } else { + ProtocolReq->Packet.OutDataBuffer = PayloadBuffer; + ProtocolReq->Packet.OutTransferLength = (UINT32)PayloadBufferSize; + + ProtocolReq->SdMmcCmdBlk.CommandIndex = EMMC_PROTOCOL_WR; + ProtocolReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + ProtocolReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } + + ProtocolReq->SdMmcCmdBlk.CommandArgument = (SecurityProtocol << 8) | (SecurityProtocolSpecificData << 16); + // + // Convert to 1 microsecond unit. + // + ProtocolReq->Packet.Timeout = DivU64x32 (Timeout, 10) + 1; + + ProtocolReq->IsEnd = IsEnd; + ProtocolReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + ProtocolReq, + &ProtocolReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + ProtocolReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &ProtocolReq->Packet, ProtocolReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (ProtocolReq != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&ProtocolReq->Link); + gBS->RestoreTPL (OldTpl); + if (ProtocolReq->Event != NULL) { + gBS->CloseEvent (ProtocolReq->Event); + } + FreePool (ProtocolReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (ProtocolReq != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&ProtocolReq->Link); + gBS->RestoreTPL (OldTpl); + FreePool (ProtocolReq); + } + } + + return Status; +} + +/** + Read/write multiple blocks through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] Lba The starting logical block address to be read/written. + The caller is responsible for reading/writing to only + legitimate locations. + @param[in] Buffer A pointer to the destination/source buffer for the data. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] IsRead Indicates it is a read or write operation. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcRwMultiBlocks ( + IN EMMC_PARTITION *Partition, + IN EFI_LBA Lba, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EMMC_REQUEST *RwMultiBlkReq; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_TPL OldTpl; + + RwMultiBlkReq = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + RwMultiBlkReq = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (RwMultiBlkReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + RwMultiBlkReq->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Partition->Queue, &RwMultiBlkReq->Link); + gBS->RestoreTPL (OldTpl); + RwMultiBlkReq->Packet.SdMmcCmdBlk = &RwMultiBlkReq->SdMmcCmdBlk; + RwMultiBlkReq->Packet.SdMmcStatusBlk = &RwMultiBlkReq->SdMmcStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest + // transfer speed (2.4MB/s). + // Refer to eMMC 5.0 spec section 6.9.1 for details. + // + RwMultiBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000; + + if (IsRead) { + RwMultiBlkReq->Packet.InDataBuffer = Buffer; + RwMultiBlkReq->Packet.InTransferLength = (UINT32)BufferSize; + + RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = EMMC_READ_MULTIPLE_BLOCK; + RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } else { + RwMultiBlkReq->Packet.OutDataBuffer = Buffer; + RwMultiBlkReq->Packet.OutTransferLength = (UINT32)BufferSize; + + RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = EMMC_WRITE_MULTIPLE_BLOCK; + RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } + + if (Partition->Device->SectorAddressing) { + RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba; + } else { + RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Partition->BlockMedia.BlockSize); + } + + RwMultiBlkReq->IsEnd = IsEnd; + RwMultiBlkReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + RwMultiBlkReq, + &RwMultiBlkReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + RwMultiBlkReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &RwMultiBlkReq->Packet, RwMultiBlkReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (RwMultiBlkReq != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&RwMultiBlkReq->Link); + gBS->RestoreTPL (OldTpl); + if (RwMultiBlkReq->Event != NULL) { + gBS->CloseEvent (RwMultiBlkReq->Event); + } + FreePool (RwMultiBlkReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (RwMultiBlkReq != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + RemoveEntryList (&RwMultiBlkReq->Link); + gBS->RestoreTPL (OldTpl); + FreePool (RwMultiBlkReq); + } + } + + return Status; +} + +/** + This function transfers data from/to EMMC device. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] MediaId The media ID that the read/write request is for. + @param[in] Lba The starting logical block address to be read/written. + The caller is responsible for reading/writing to only + legitimate locations. + @param[in, out] Buffer A pointer to the destination/source buffer for the data. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] IsRead Indicates it is a read or write operation. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS The data was read/written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be read/written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read/write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read/write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EmmcReadWrite ( + IN EMMC_PARTITION *Partition, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN BlockNum; + UINTN IoAlign; + UINT8 PartitionConfig; + UINTN Remaining; + UINT32 MaxBlock; + BOOLEAN LastRw; + + Status = EFI_SUCCESS; + Device = Partition->Device; + Media = &Partition->BlockMedia; + LastRw = FALSE; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (!IsRead && Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + // + // Check parameters. + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + return EFI_SUCCESS; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + BlockNum = BufferSize / BlockSize; + if ((Lba + BlockNum - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + } + // + // Check if needs to switch partition access. + // + PartitionConfig = Device->ExtCsd.PartitionConfig; + if ((PartitionConfig & 0x7) != Partition->PartitionType) { + PartitionConfig &= (UINT8)~0x7; + PartitionConfig |= Partition->PartitionType; + Status = EmmcSetExtCsd (Partition, OFFSET_OF (EMMC_EXT_CSD, PartitionConfig), PartitionConfig, Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + Device->ExtCsd.PartitionConfig = PartitionConfig; + } + // + // Start to execute data transfer. The max block number in single cmd is 65535 blocks. + // + Remaining = BlockNum; + MaxBlock = 0xFFFF; + + while (Remaining > 0) { + if (Remaining <= MaxBlock) { + BlockNum = Remaining; + LastRw = TRUE; + } else { + BlockNum = MaxBlock; + } + Status = EmmcSetBlkCount (Partition, (UINT16)BlockNum, Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + BufferSize = BlockNum * BlockSize; + Status = EmmcRwMultiBlocks (Partition, Lba, Buffer, BufferSize, IsRead, Token, LastRw); + if (EFI_ERROR (Status)) { + return Status; + } + DEBUG ((EFI_D_INFO, "Emmc%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", IsRead ? "Read" : "Write", Lba, BlockNum, Token->Event, Status)); + + Lba += BlockNum; + Buffer = (UINT8*)Buffer + BufferSize; + Remaining -= BlockNum; + } + + return Status; +} + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +EmmcReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This); + + PassThru = Partition->Device->Private->PassThru; + Status = PassThru->ResetDevice (PassThru, Partition->Device->Slot); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This); + + Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, TRUE, NULL); + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO (This); + + Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, FALSE, NULL); + return Status; +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +EmmcFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + // + // return directly + // + return EFI_SUCCESS; +} + +/** + Reset the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +EmmcResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EMMC_PARTITION *Partition; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + EMMC_REQUEST *Request; + EFI_TPL OldTpl; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + for (Link = GetFirstNode (&Partition->Queue); + !IsNull (&Partition->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Partition->Queue, Link); + RemoveEntryList (Link); + + Request = EMMC_REQUEST_FROM_LINK (Link); + + gBS->CloseEvent (Request->Event); + Request->Token->TransactionStatus = EFI_ABORTED; + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); + } + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Event is not NULL. + The data was read correctly from the device if + the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +EmmcReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This); + + Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, TRUE, Token); + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + + Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (This); + + Status = EmmcReadWrite (Partition, MediaId, Lba, Buffer, BufferSize, FALSE, Token); + return Status; +} + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +EmmcFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + // + // Signal event and return directly. + // + if (Token != NULL && Token->Event != NULL) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId ID of the medium to receive data from. + @param[in] Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param[in] SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param[in] SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param[in] PayloadBufferSize Size in bytes of the payload data buffer. + @param[out] PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param[out] PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + @param[in] IsRead Indicates it is a read or write operation. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolInOut ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize, + IN BOOLEAN IsRead + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + EMMC_DEVICE *Device; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN BlockNum; + UINTN IoAlign; + UINTN Remaining; + UINT32 MaxBlock; + UINT8 PartitionConfig; + + Status = EFI_SUCCESS; + Partition = EMMC_PARTITION_DATA_FROM_SSP (This); + Device = Partition->Device; + Media = &Partition->BlockMedia; + + if (PayloadTransferSize != NULL) { + *PayloadTransferSize = 0; + } + + if ((PayloadBuffer == NULL) && (PayloadBufferSize != 0)) { + return EFI_INVALID_PARAMETER; + } + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (PayloadBufferSize == 0) { + return EFI_SUCCESS; + } + + BlockSize = Media->BlockSize; + if ((PayloadBufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + BlockNum = PayloadBufferSize / BlockSize; + + IoAlign = Media->IoAlign; + if (IoAlign > 0 && (((UINTN) PayloadBuffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Security protocol interface is synchronous transfer. + // Waiting for async I/O list to be empty before any operation. + // + while (!IsListEmpty (&Partition->Queue)); + + // + // Check if needs to switch partition access. + // + PartitionConfig = Device->ExtCsd.PartitionConfig; + if ((PartitionConfig & 0x7) != Partition->PartitionType) { + PartitionConfig &= (UINT8)~0x7; + PartitionConfig |= Partition->PartitionType; + Status = EmmcSetExtCsd (Partition, OFFSET_OF (EMMC_EXT_CSD, PartitionConfig), PartitionConfig, NULL, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + Device->ExtCsd.PartitionConfig = PartitionConfig; + } + // + // Start to execute data transfer. The max block number in single cmd is 65535 blocks. + // + Remaining = BlockNum; + MaxBlock = 0xFFFF; + + while (Remaining > 0) { + if (Remaining <= MaxBlock) { + BlockNum = Remaining; + } else { + BlockNum = MaxBlock; + } + + Status = EmmcSetBlkCount (Partition, (UINT16)BlockNum, NULL, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + PayloadBufferSize = BlockNum * BlockSize; + Status = EmmcProtocolInOut (Partition, SecurityProtocolId, SecurityProtocolSpecificData, PayloadBufferSize, PayloadBuffer, IsRead, Timeout, NULL, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + PayloadBuffer = (UINT8*)PayloadBuffer + PayloadBufferSize; + Remaining -= BlockNum; + if (PayloadTransferSize != NULL) { + *PayloadTransferSize += PayloadBufferSize; + } + } + + return Status; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolIn ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ) +{ + EFI_STATUS Status; + + if ((PayloadTransferSize == NULL) && PayloadBufferSize != 0) { + return EFI_INVALID_PARAMETER; + } + + Status = EmmcSecurityProtocolInOut ( + This, + MediaId, + Timeout, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer, + PayloadTransferSize, + TRUE + ); + + return Status; +} + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolOut ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer + ) +{ + EFI_STATUS Status; + + Status = EmmcSecurityProtocolInOut ( + This, + MediaId, + Timeout, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer, + NULL, + FALSE + ); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h new file mode 100644 index 0000000000..f0e1312b25 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h @@ -0,0 +1,466 @@ +/** @file + Header file for EmmcDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EMMC_BLOCK_IO_H_ +#define _EMMC_BLOCK_IO_H_ + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +EmmcReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +EmmcFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +/** + Reset the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +EmmcResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Event is not NULL. + The data was read correctly from the device if + the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +EmmcReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +EmmcWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +EmmcFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId ID of the medium to receive data from. + @param[in] Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param[in] SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param[in] SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param[in] PayloadBufferSize Size in bytes of the payload data buffer. + @param[out] PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param[out] PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + @param[in] IsRead Indicates it is a read or write operation. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolInOut ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize, + IN BOOLEAN IsRead + ); + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolIn ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ); + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +EmmcSecurityProtocolOut ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c new file mode 100644 index 0000000000..f07540fd36 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c @@ -0,0 +1,1175 @@ +/** @file + The EmmcDxe driver is used to manage the EMMC device. + + It produces BlockIo, BlockIo2 and StorageSecurity protocols to allow upper layer + access the EMMC device. + + Copyright (c) 2015, Intel Corporation. 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 "EmmcDxe.h" + +// +// EmmcDxe Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gEmmcDxeDriverBinding = { + EmmcDxeDriverBindingSupported, + EmmcDxeDriverBindingStart, + EmmcDxeDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for Emmc Partitions. +// +EMMC_PARTITION mEmmcPartitionTemplate = { + EMMC_PARTITION_SIGNATURE, // Signature + FALSE, // Enable + EmmcPartitionUnknown, // PartitionType + NULL, // Handle + NULL, // DevicePath + { // BlockIo + EFI_BLOCK_IO_PROTOCOL_REVISION, + NULL, + EmmcReset, + EmmcReadBlocks, + EmmcWriteBlocks, + EmmcFlushBlocks + }, + { // BlockIo2 + NULL, + EmmcResetEx, + EmmcReadBlocksEx, + EmmcWriteBlocksEx, + EmmcFlushBlocksEx + }, + { // BlockMedia + 0, // MediaId + FALSE, // RemovableMedia + TRUE, // MediaPresent + FALSE, // LogicPartition + FALSE, // ReadOnly + FALSE, // WritingCache + 0x200, // BlockSize + 0, // IoAlign + 0 // LastBlock + }, + { // StorageSecurity + EmmcSecurityProtocolIn, + EmmcSecurityProtocolOut + }, + { + NULL, + NULL + }, + NULL // Device +}; + +/** + Decode and print EMMC CSD Register content. + + @param[in] Csd Pointer to EMMC_CSD data structure. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +DumpCsd ( + IN EMMC_CSD *Csd + ) +{ + DEBUG((DEBUG_INFO, "== Dump Emmc Csd Register==\n")); + DEBUG((DEBUG_INFO, " CSD structure 0x%x\n", Csd->CsdStructure)); + DEBUG((DEBUG_INFO, " System specification version 0x%x\n", Csd->SpecVers)); + DEBUG((DEBUG_INFO, " Data read access-time 1 0x%x\n", Csd->Taac)); + DEBUG((DEBUG_INFO, " Data read access-time 2 0x%x\n", Csd->Nsac)); + DEBUG((DEBUG_INFO, " Max. bus clock frequency 0x%x\n", Csd->TranSpeed)); + DEBUG((DEBUG_INFO, " Device command classes 0x%x\n", Csd->Ccc)); + DEBUG((DEBUG_INFO, " Max. read data block length 0x%x\n", Csd->ReadBlLen)); + DEBUG((DEBUG_INFO, " Partial blocks for read allowed 0x%x\n", Csd->ReadBlPartial)); + DEBUG((DEBUG_INFO, " Write block misalignment 0x%x\n", Csd->WriteBlkMisalign)); + DEBUG((DEBUG_INFO, " Read block misalignment 0x%x\n", Csd->ReadBlkMisalign)); + DEBUG((DEBUG_INFO, " DSR implemented 0x%x\n", Csd->DsrImp)); + DEBUG((DEBUG_INFO, " Device size 0x%x\n", Csd->CSizeLow | (Csd->CSizeHigh << 2))); + DEBUG((DEBUG_INFO, " Max. read current @ VDD min 0x%x\n", Csd->VddRCurrMin)); + DEBUG((DEBUG_INFO, " Max. read current @ VDD max 0x%x\n", Csd->VddRCurrMax)); + DEBUG((DEBUG_INFO, " Max. write current @ VDD min 0x%x\n", Csd->VddWCurrMin)); + DEBUG((DEBUG_INFO, " Max. write current @ VDD max 0x%x\n", Csd->VddWCurrMax)); + DEBUG((DEBUG_INFO, " Device size multiplier 0x%x\n", Csd->CSizeMult)); + DEBUG((DEBUG_INFO, " Erase group size 0x%x\n", Csd->EraseGrpSize)); + DEBUG((DEBUG_INFO, " Erase group size multiplier 0x%x\n", Csd->EraseGrpMult)); + DEBUG((DEBUG_INFO, " Write protect group size 0x%x\n", Csd->WpGrpSize)); + DEBUG((DEBUG_INFO, " Write protect group enable 0x%x\n", Csd->WpGrpEnable)); + DEBUG((DEBUG_INFO, " Manufacturer default ECC 0x%x\n", Csd->DefaultEcc)); + DEBUG((DEBUG_INFO, " Write speed factor 0x%x\n", Csd->R2WFactor)); + DEBUG((DEBUG_INFO, " Max. write data block length 0x%x\n", Csd->WriteBlLen)); + DEBUG((DEBUG_INFO, " Partial blocks for write allowed 0x%x\n", Csd->WriteBlPartial)); + DEBUG((DEBUG_INFO, " Content protection application 0x%x\n", Csd->ContentProtApp)); + DEBUG((DEBUG_INFO, " File format group 0x%x\n", Csd->FileFormatGrp)); + DEBUG((DEBUG_INFO, " Copy flag (OTP) 0x%x\n", Csd->Copy)); + DEBUG((DEBUG_INFO, " Permanent write protection 0x%x\n", Csd->PermWriteProtect)); + DEBUG((DEBUG_INFO, " Temporary write protection 0x%x\n", Csd->TmpWriteProtect)); + DEBUG((DEBUG_INFO, " File format 0x%x\n", Csd->FileFormat)); + DEBUG((DEBUG_INFO, " ECC code 0x%x\n", Csd->Ecc)); + + return EFI_SUCCESS; +} + +/** + Decode and print EMMC EXT_CSD Register content. + + @param[in] ExtCsd Pointer to the EMMC_EXT_CSD data structure. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +DumpExtCsd ( + IN EMMC_EXT_CSD *ExtCsd + ) +{ + DEBUG((DEBUG_INFO, "==Dump Emmc ExtCsd Register==\n")); + DEBUG((DEBUG_INFO, " Supported Command Sets 0x%x\n", ExtCsd->CmdSet)); + DEBUG((DEBUG_INFO, " HPI features 0x%x\n", ExtCsd->HpiFeatures)); + DEBUG((DEBUG_INFO, " Background operations support 0x%x\n", ExtCsd->BkOpsSupport)); + DEBUG((DEBUG_INFO, " Background operations status 0x%x\n", ExtCsd->BkopsStatus)); + DEBUG((DEBUG_INFO, " Number of correctly programmed sectors 0x%x\n", *((UINT32*)&ExtCsd->CorrectlyPrgSectorsNum[0]))); + DEBUG((DEBUG_INFO, " Initialization time after partitioning 0x%x\n", ExtCsd->IniTimeoutAp)); + DEBUG((DEBUG_INFO, " TRIM Multiplier 0x%x\n", ExtCsd->TrimMult)); + DEBUG((DEBUG_INFO, " Secure Feature support 0x%x\n", ExtCsd->SecFeatureSupport)); + DEBUG((DEBUG_INFO, " Secure Erase Multiplier 0x%x\n", ExtCsd->SecEraseMult)); + DEBUG((DEBUG_INFO, " Secure TRIM Multiplier 0x%x\n", ExtCsd->SecTrimMult)); + DEBUG((DEBUG_INFO, " Boot information 0x%x\n", ExtCsd->BootInfo)); + DEBUG((DEBUG_INFO, " Boot partition size 0x%x\n", ExtCsd->BootSizeMult)); + DEBUG((DEBUG_INFO, " Access size 0x%x\n", ExtCsd->AccSize)); + DEBUG((DEBUG_INFO, " High-capacity erase unit size 0x%x\n", ExtCsd->HcEraseGrpSize)); + DEBUG((DEBUG_INFO, " High-capacity erase timeout 0x%x\n", ExtCsd->EraseTimeoutMult)); + DEBUG((DEBUG_INFO, " Reliable write sector count 0x%x\n", ExtCsd->RelWrSecC)); + DEBUG((DEBUG_INFO, " High-capacity write protect group size 0x%x\n", ExtCsd->HcWpGrpSize)); + DEBUG((DEBUG_INFO, " Sleep/awake timeout 0x%x\n", ExtCsd->SATimeout)); + DEBUG((DEBUG_INFO, " Sector Count 0x%x\n", *((UINT32*)&ExtCsd->SecCount[0]))); + DEBUG((DEBUG_INFO, " Partition switching timing 0x%x\n", ExtCsd->PartitionSwitchTime)); + DEBUG((DEBUG_INFO, " Out-of-interrupt busy timing 0x%x\n", ExtCsd->OutOfInterruptTime)); + DEBUG((DEBUG_INFO, " I/O Driver Strength 0x%x\n", ExtCsd->DriverStrength)); + DEBUG((DEBUG_INFO, " Device type 0x%x\n", ExtCsd->DeviceType)); + DEBUG((DEBUG_INFO, " CSD STRUCTURE 0x%x\n", ExtCsd->CsdStructure)); + DEBUG((DEBUG_INFO, " Extended CSD revision 0x%x\n", ExtCsd->ExtCsdRev)); + DEBUG((DEBUG_INFO, " Command set 0x%x\n", ExtCsd->CmdSet)); + DEBUG((DEBUG_INFO, " Command set revision 0x%x\n", ExtCsd->CmdSetRev)); + DEBUG((DEBUG_INFO, " Power class 0x%x\n", ExtCsd->PowerClass)); + DEBUG((DEBUG_INFO, " High-speed interface timing 0x%x\n", ExtCsd->HsTiming)); + DEBUG((DEBUG_INFO, " Bus width mode 0x%x\n", ExtCsd->BusWidth)); + DEBUG((DEBUG_INFO, " Erased memory content 0x%x\n", ExtCsd->ErasedMemCont)); + DEBUG((DEBUG_INFO, " Partition configuration 0x%x\n", ExtCsd->PartitionConfig)); + DEBUG((DEBUG_INFO, " Boot config protection 0x%x\n", ExtCsd->BootConfigProt)); + DEBUG((DEBUG_INFO, " Boot bus Conditions 0x%x\n", ExtCsd->BootBusConditions)); + DEBUG((DEBUG_INFO, " High-density erase group definition 0x%x\n", ExtCsd->EraseGroupDef)); + DEBUG((DEBUG_INFO, " Boot write protection status register 0x%x\n", ExtCsd->BootWpStatus)); + DEBUG((DEBUG_INFO, " Boot area write protection register 0x%x\n", ExtCsd->BootWp)); + DEBUG((DEBUG_INFO, " User area write protection register 0x%x\n", ExtCsd->UserWp)); + DEBUG((DEBUG_INFO, " FW configuration 0x%x\n", ExtCsd->FwConfig)); + DEBUG((DEBUG_INFO, " RPMB Size 0x%x\n", ExtCsd->RpmbSizeMult)); + DEBUG((DEBUG_INFO, " H/W reset function 0x%x\n", ExtCsd->RstFunction)); + DEBUG((DEBUG_INFO, " Partitioning Support 0x%x\n", ExtCsd->PartitioningSupport)); + DEBUG((DEBUG_INFO, " Max Enhanced Area Size 0x%02x%02x%02x\n", \ + ExtCsd->MaxEnhSizeMult[2], ExtCsd->MaxEnhSizeMult[1], ExtCsd->MaxEnhSizeMult[0])); + DEBUG((DEBUG_INFO, " Partitions attribute 0x%x\n", ExtCsd->PartitionsAttribute)); + DEBUG((DEBUG_INFO, " Partitioning Setting 0x%x\n", ExtCsd->PartitionSettingCompleted)); + DEBUG((DEBUG_INFO, " General Purpose Partition 1 Size 0x%02x%02x%02x\n", \ + ExtCsd->GpSizeMult[2], ExtCsd->GpSizeMult[1], ExtCsd->GpSizeMult[0])); + DEBUG((DEBUG_INFO, " General Purpose Partition 2 Size 0x%02x%02x%02x\n", \ + ExtCsd->GpSizeMult[5], ExtCsd->GpSizeMult[4], ExtCsd->GpSizeMult[3])); + DEBUG((DEBUG_INFO, " General Purpose Partition 3 Size 0x%02x%02x%02x\n", \ + ExtCsd->GpSizeMult[8], ExtCsd->GpSizeMult[7], ExtCsd->GpSizeMult[6])); + DEBUG((DEBUG_INFO, " General Purpose Partition 4 Size 0x%02x%02x%02x\n", \ + ExtCsd->GpSizeMult[11], ExtCsd->GpSizeMult[10], ExtCsd->GpSizeMult[9])); + DEBUG((DEBUG_INFO, " Enhanced User Data Area Size 0x%02x%02x%02x\n", \ + ExtCsd->EnhSizeMult[2], ExtCsd->EnhSizeMult[1], ExtCsd->EnhSizeMult[0])); + DEBUG((DEBUG_INFO, " Enhanced User Data Start Address 0x%x\n", *((UINT32*)&ExtCsd->EnhStartAddr[0]))); + DEBUG((DEBUG_INFO, " Bad Block Management mode 0x%x\n", ExtCsd->SecBadBlkMgmnt)); + DEBUG((DEBUG_INFO, " Native sector size 0x%x\n", ExtCsd->NativeSectorSize)); + DEBUG((DEBUG_INFO, " Sector size emulation 0x%x\n", ExtCsd->UseNativeSector)); + DEBUG((DEBUG_INFO, " Sector size 0x%x\n", ExtCsd->DataSectorSize)); + + return EFI_SUCCESS; +} + +/** + Get EMMC device model name. + + @param[in, out] Device The pointer to the EMMC_DEVICE data structure. + @param[in] Cid Pointer to EMMC_CID data structure. + + @retval EFI_SUCCESS The function completed successfully + +**/ +EFI_STATUS +GetEmmcModelName ( + IN OUT EMMC_DEVICE *Device, + IN EMMC_CID *Cid + ) +{ + CHAR8 String[EMMC_MODEL_NAME_MAX_LEN]; + + ZeroMem (String, sizeof (String)); + CopyMem (String, &Cid->OemId, sizeof (Cid->OemId)); + String[sizeof (Cid->OemId)] = ' '; + CopyMem (String + sizeof (Cid->OemId) + 1, Cid->ProductName, sizeof (Cid->ProductName)); + String[sizeof (Cid->OemId) + sizeof (Cid->ProductName)] = ' '; + CopyMem (String + sizeof (Cid->OemId) + sizeof (Cid->ProductName) + 1, Cid->ProductSerialNumber, sizeof (Cid->ProductSerialNumber)); + + AsciiStrToUnicodeStr (String, Device->ModelName); + + return EFI_SUCCESS; +} + +/** + Discover all partitions in the EMMC device. + + @param[in] Device The pointer to the EMMC_DEVICE data structure. + + @retval EFI_SUCCESS All the partitions in the device are successfully enumerated. + @return Others Some error occurs when enumerating the partitions. + +**/ +EFI_STATUS +DiscoverAllPartitions ( + IN EMMC_DEVICE *Device + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EMMC_PARTITION *Partition; + EMMC_CSD *Csd; + EMMC_CID *Cid; + EMMC_EXT_CSD *ExtCsd; + UINT8 Slot; + UINT64 Capacity; + UINT32 DevStatus; + UINT8 Index; + UINT32 SecCount; + UINT32 GpSizeMult; + + PassThru = Device->Private->PassThru; + Slot = Device->Slot; + + Status = EmmcSendStatus (Device, Slot + 1, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Deselect the device to force it enter stby mode before getting CSD + // register content. + // Note here we don't judge return status as some EMMC devices return + // error but the state has been stby. + // + EmmcSelect (Device, 0); + + Status = EmmcSendStatus (Device, Slot + 1, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + Csd = &Device->Csd; + Status = EmmcGetCsd (Device, Slot + 1, Csd); + if (EFI_ERROR (Status)) { + return Status; + } + DumpCsd (Csd); + + if ((Csd->CSizeLow | Csd->CSizeHigh << 2) == 0xFFF) { + Device->SectorAddressing = TRUE; + } else { + Device->SectorAddressing = FALSE; + } + + Cid = &Device->Cid; + Status = EmmcGetCid (Device, Slot + 1, Cid); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcSelect (Device, Slot + 1); + if (EFI_ERROR (Status)) { + return Status; + } + + ExtCsd = &Device->ExtCsd; + Status = EmmcGetExtCsd (Device, ExtCsd); + if (EFI_ERROR (Status)) { + return Status; + } + DumpExtCsd (ExtCsd); + + if (ExtCsd->ExtCsdRev < 5) { + DEBUG ((EFI_D_ERROR, "The EMMC device version is too low, we don't support!!!\n")); + return EFI_UNSUPPORTED; + } + + if ((ExtCsd->PartitioningSupport & BIT0) != BIT0) { + DEBUG ((EFI_D_ERROR, "The EMMC device doesn't support Partition Feature!!!\n")); + return EFI_UNSUPPORTED; + } + + for (Index = 0; Index < EMMC_MAX_PARTITIONS; Index++) { + Partition = &Device->Partition[Index]; + CopyMem (Partition, &mEmmcPartitionTemplate, sizeof (EMMC_PARTITION)); + Partition->Device = Device; + InitializeListHead (&Partition->Queue); + Partition->BlockIo.Media = &Partition->BlockMedia; + Partition->BlockIo2.Media = &Partition->BlockMedia; + Partition->PartitionType = Index; + Partition->BlockMedia.IoAlign = Device->Private->PassThru->IoAlign; + Partition->BlockMedia.BlockSize = 0x200; + Partition->BlockMedia.LastBlock = 0x00; + Partition->BlockMedia.RemovableMedia = FALSE; + Partition->BlockMedia.MediaPresent = TRUE; + Partition->BlockMedia.LogicalPartition = FALSE; + + switch (Index) { + case EmmcPartitionUserData: + SecCount = *(UINT32*)&ExtCsd->SecCount; + Capacity = MultU64x32 ((UINT64) SecCount, 0x200); + break; + case EmmcPartitionBoot1: + case EmmcPartitionBoot2: + Capacity = ExtCsd->BootSizeMult * SIZE_128KB; + break; + case EmmcPartitionRPMB: + Capacity = ExtCsd->RpmbSizeMult * SIZE_128KB; + break; + case EmmcPartitionGP1: + GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[0] | (ExtCsd->GpSizeMult[1] << 8) | (ExtCsd->GpSizeMult[2] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP2: + GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[3] | (ExtCsd->GpSizeMult[4] << 8) | (ExtCsd->GpSizeMult[5] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP3: + GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[6] | (ExtCsd->GpSizeMult[7] << 8) | (ExtCsd->GpSizeMult[8] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + case EmmcPartitionGP4: + GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[9] | (ExtCsd->GpSizeMult[10] << 8) | (ExtCsd->GpSizeMult[11] << 16)); + Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB); + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + if (Capacity != 0) { + Partition->Enable = TRUE; + Partition->BlockMedia.LastBlock = DivU64x32 (Capacity, Partition->BlockMedia.BlockSize) - 1; + } + } + + return EFI_SUCCESS; +} + +/** + Install BlkIo, BlkIo2 and Ssp protocols for the specified partition in the EMMC device. + + @param[in] Device The pointer to the EMMC_DEVICE data structure. + @param[in] Index The index of the partition. + + @retval EFI_SUCCESS The protocols are installed successfully. + @retval Others Some error occurs when installing the protocols. + +**/ +EFI_STATUS +InstallProtocolOnPartition ( + IN EMMC_DEVICE *Device, + IN UINT8 Index + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + CONTROLLER_DEVICE_PATH ControlNode; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_HANDLE DeviceHandle; + + // + // Build device path + // + ParentDevicePath = Device->DevicePath; + + ControlNode.Header.Type = HARDWARE_DEVICE_PATH; + ControlNode.Header.SubType = HW_CONTROLLER_DP; + SetDevicePathNodeLength (&ControlNode.Header, sizeof (CONTROLLER_DEVICE_PATH)); + ControlNode.ControllerNumber = Index; + + DevicePath = AppendDevicePathNode (ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*)&ControlNode); + if (DevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + DeviceHandle = NULL; + RemainingDevicePath = DevicePath; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle); + if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(RemainingDevicePath)) { + Status = EFI_ALREADY_STARTED; + goto Error; + } + + Partition = &Device->Partition[Index]; + Partition->DevicePath = DevicePath; + if (Partition->Enable) { + // + // Install BlkIo/BlkIo2/Ssp for the specified partition + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Partition->Handle, + &gEfiDevicePathProtocolGuid, + Partition->DevicePath, + &gEfiBlockIoProtocolGuid, + &Partition->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Partition->BlockIo2, + NULL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (((Partition->PartitionType == EmmcPartitionUserData) || + (Partition->PartitionType == EmmcPartitionBoot1) || + (Partition->PartitionType == EmmcPartitionBoot2)) && + ((Device->Csd.Ccc & BIT10) != 0)) { + Status = gBS->InstallProtocolInterface ( + &Partition->Handle, + &gEfiStorageSecurityCommandProtocolGuid, + EFI_NATIVE_INTERFACE, + &Partition->StorageSecurity + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + &Partition->Handle, + &gEfiDevicePathProtocolGuid, + Partition->DevicePath, + &gEfiBlockIoProtocolGuid, + &Partition->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Partition->BlockIo2, + NULL + ); + goto Error; + } + } + + gBS->OpenProtocol ( + Device->Private->Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **) &(Device->Private->PassThru), + Device->Private->DriverBindingHandle, + Partition->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + Status = EFI_INVALID_PARAMETER; + } + +Error: + if (EFI_ERROR (Status) && (DevicePath != NULL)) { + FreePool (DevicePath); + } + + return Status; +} + +/** + Scan EMMC Bus to discover the device. + + @param[in] Private The EMMC driver private data structure. + @param[in] Slot The slot number to check device present. + + @retval EFI_SUCCESS Successfully to discover the device and attach + SdMmcIoProtocol to it. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + @retval EFI_ALREADY_STARTED The device was discovered before. + @retval Others Fail to discover the device. + +**/ +EFI_STATUS +EFIAPI +DiscoverEmmcDevice ( + IN EMMC_DRIVER_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EMMC_DEVICE *Device; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *RemainingEmmcDevPath; + EFI_DEV_PATH *Node; + EFI_HANDLE DeviceHandle; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT8 Index; + + Device = NULL; + DevicePath = NULL; + NewDevicePath = NULL; + RemainingDevicePath = NULL; + PassThru = Private->PassThru; + Device = &Private->Device[Slot]; + + // + // Build Device Path to check if the EMMC device present at the slot. + // + Status = PassThru->BuildDevicePath ( + PassThru, + Slot, + &DevicePath + ); + if (EFI_ERROR(Status)) { + return Status; + } + + if (DevicePath->SubType != MSG_EMMC_DP) { + Status = EFI_UNSUPPORTED; + goto Error; + } + + NewDevicePath = AppendDevicePathNode ( + Private->ParentDevicePath, + DevicePath + ); + if (NewDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + DeviceHandle = NULL; + RemainingEmmcDevPath = NewDevicePath; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingEmmcDevPath, &DeviceHandle); + // + // The device path to the EMMC device doesn't exist. It means the corresponding device private data hasn't been initialized. + // + if (EFI_ERROR (Status) || (DeviceHandle == NULL) || !IsDevicePathEnd (RemainingEmmcDevPath)) { + Device->DevicePath = NewDevicePath; + Device->Slot = Slot; + Device->Private = Private; + // + // Expose user area in the Sd memory card to upper layer. + // + Status = DiscoverAllPartitions (Device); + if (EFI_ERROR(Status)) { + FreePool (NewDevicePath); + goto Error; + } + + Status = gBS->InstallProtocolInterface ( + &Device->Handle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + Device->DevicePath + ); + if (EFI_ERROR(Status)) { + FreePool (NewDevicePath); + goto Error; + } + + Device->ControllerNameTable = NULL; + GetEmmcModelName (Device, &Device->Cid); + AddUnicodeString2 ( + "eng", + gEmmcDxeComponentName.SupportedLanguages, + &Device->ControllerNameTable, + Device->ModelName, + TRUE + ); + AddUnicodeString2 ( + "en", + gEmmcDxeComponentName.SupportedLanguages, + &Device->ControllerNameTable, + Device->ModelName, + FALSE + ); + } + + if (RemainingDevicePath == NULL) { + // + // Expose all partitions in the Emmc device to upper layer. + // + for (Index = 0; Index < EMMC_MAX_PARTITIONS; Index++) { + InstallProtocolOnPartition (Device, Index); + } + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // Enumerate the specified partition + // + Node = (EFI_DEV_PATH *) RemainingDevicePath; + if ((DevicePathType (&Node->DevPath) != HARDWARE_DEVICE_PATH) || + (DevicePathSubType (&Node->DevPath) != HW_CONTROLLER_DP) || + (DevicePathNodeLength (&Node->DevPath) != sizeof (CONTROLLER_DEVICE_PATH))) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Index = (UINT8)Node->Controller.ControllerNumber; + if (Index >= EMMC_MAX_PARTITIONS) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Status = InstallProtocolOnPartition (Device, Index); + } + +Error: + FreePool (DevicePath); + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT8 Slot; + + // + // Test EFI_SD_MMC_PASS_THRU_PROTOCOL on the controller handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Test RemainingDevicePath is valid or not. + // + if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { + Status = PassThru->GetSlotNumber (PassThru, RemainingDevicePath, &Slot); + if (EFI_ERROR (Status)) { + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + return Status; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EMMC_DRIVER_PRIVATE_DATA *Private; + UINT8 Slot; + + Private = NULL; + PassThru = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + // + // Check EFI_ALREADY_STARTED to reuse the original EMMC_DRIVER_PRIVATE_DATA. + // + if (Status != EFI_ALREADY_STARTED) { + Private = AllocateZeroPool (sizeof (EMMC_DRIVER_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + Private->PassThru = PassThru; + Private->Controller = Controller; + Private->ParentDevicePath = ParentDevicePath; + Private->DriverBindingHandle = This->DriverBindingHandle; + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + Private + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &Private, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + if (RemainingDevicePath == NULL) { + Slot = 0xFF; + while (TRUE) { + Status = PassThru->GetNextSlot (PassThru, &Slot); + if (EFI_ERROR (Status)) { + // + // Cannot find more legal slots. + // + Status = EFI_SUCCESS; + break; + } + + Status = DiscoverEmmcDevice (Private, Slot, NULL); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + break; + } + } + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + Status = PassThru->GetSlotNumber (PassThru, RemainingDevicePath, &Slot); + if (!EFI_ERROR (Status)) { + Status = DiscoverEmmcDevice (Private, Slot, NextDevicePathNode (RemainingDevicePath)); + } + } + +Error: + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if (Private != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + Private, + NULL + ); + FreePool (Private); + } + } + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EMMC_DRIVER_PRIVATE_DATA *Private; + EMMC_DEVICE *Device; + EMMC_PARTITION *Partition; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *StorageSecurity; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + EMMC_REQUEST *Request; + + BlockIo = NULL; + BlockIo2 = NULL; + if (NumberOfChildren == 0) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &Private, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + for (Index = 0; Index < EMMC_MAX_DEVICES; Index++) { + Device = &Private->Device[Index]; + Status = gBS->OpenProtocol ( + Device->Handle, + &gEfiDevicePathProtocolGuid, + (VOID **) &DevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + continue; + } + ASSERT (DevicePath == Device->DevicePath); + gBS->UninstallProtocolInterface ( + Device->Handle, + &gEfiDevicePathProtocolGuid, + DevicePath + ); + FreePool (Device->DevicePath); + } + + gBS->UninstallProtocolInterface ( + Controller, + &gEfiCallerIdGuid, + Private + ); + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + FreePool (Private); + + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + continue; + } + } + + if (BlockIo != NULL) { + Partition = EMMC_PARTITION_DATA_FROM_BLKIO (BlockIo); + } else { + ASSERT (BlockIo2 != NULL); + Partition = EMMC_PARTITION_DATA_FROM_BLKIO2 (BlockIo2); + } + + for (Link = GetFirstNode (&Partition->Queue); + !IsNull (&Partition->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Partition->Queue, Link); + + RemoveEntryList (Link); + Request = EMMC_REQUEST_FROM_LINK (Link); + + gBS->CloseEvent (Request->Event); + Request->Token->TransactionStatus = EFI_ABORTED; + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); + } + + // + // Close the child handle + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Partition->DevicePath, + &gEfiBlockIoProtocolGuid, + &Partition->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Partition->BlockIo2, + NULL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **)&Partition->Device->Private->PassThru, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + continue; + } + + // + // If Storage Security Command Protocol is installed, then uninstall this protocol. + // + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiStorageSecurityCommandProtocolGuid, + (VOID **) &StorageSecurity, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + Status = gBS->UninstallProtocolInterface ( + ChildHandleBuffer[Index], + &gEfiStorageSecurityCommandProtocolGuid, + &Partition->StorageSecurity + ); + if (EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **) &Partition->Device->Private->PassThru, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + AllChildrenStopped = FALSE; + continue; + } + } + + FreePool (Partition->DevicePath); + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + The user Entry Point for module EmmcDxe. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some errors occur when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeEmmcDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gEmmcDxeDriverBinding, + ImageHandle, + &gEmmcDxeComponentName, + &gEmmcDxeComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h new file mode 100644 index 0000000000..a463e342ef --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h @@ -0,0 +1,495 @@ +/** @file + Header file for EmmcDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EMMC_DXE_H_ +#define _EMMC_DXE_H_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EmmcBlockIo.h" +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gEmmcDxeDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gEmmcDxeComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gEmmcDxeComponentName2; + +#define EMMC_PARTITION_SIGNATURE SIGNATURE_32 ('E', 'm', 'm', 'P') + +#define EMMC_PARTITION_DATA_FROM_BLKIO(a) \ + CR(a, EMMC_PARTITION, BlockIo, EMMC_PARTITION_SIGNATURE) + +#define EMMC_PARTITION_DATA_FROM_BLKIO2(a) \ + CR(a, EMMC_PARTITION, BlockIo2, EMMC_PARTITION_SIGNATURE) + +#define EMMC_PARTITION_DATA_FROM_SSP(a) \ + CR(a, EMMC_PARTITION, StorageSecurity, EMMC_PARTITION_SIGNATURE) + +// +// Take 2.5 seconds as generic time out value, 1 microsecond as unit. +// +#define EMMC_GENERIC_TIMEOUT 2500 * 1000 + +#define EMMC_REQUEST_SIGNATURE SIGNATURE_32 ('E', 'm', 'R', 'e') + +typedef struct _EMMC_DEVICE EMMC_DEVICE; +typedef struct _EMMC_DRIVER_PRIVATE_DATA EMMC_DRIVER_PRIVATE_DATA; + +// +// Asynchronous I/O request. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + BOOLEAN IsEnd; + + EFI_BLOCK_IO2_TOKEN *Token; + EFI_EVENT Event; +} EMMC_REQUEST; + +#define EMMC_REQUEST_FROM_LINK(a) \ + CR(a, EMMC_REQUEST, Link, EMMC_REQUEST_SIGNATURE) + +typedef struct { + UINT32 Signature; + BOOLEAN Enable; + EMMC_PARTITION_TYPE PartitionType; + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO2_PROTOCOL BlockIo2; + EFI_BLOCK_IO_MEDIA BlockMedia; + EFI_STORAGE_SECURITY_COMMAND_PROTOCOL StorageSecurity; + + LIST_ENTRY Queue; + + EMMC_DEVICE *Device; +} EMMC_PARTITION; + +// +// Up to 6 slots per EMMC PCI host controller +// +#define EMMC_MAX_DEVICES 6 +// +// Up to 8 partitions per EMMC device. +// +#define EMMC_MAX_PARTITIONS 8 +#define EMMC_MODEL_NAME_MAX_LEN 32 + +struct _EMMC_DEVICE { + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT8 Slot; + BOOLEAN SectorAddressing; + + EMMC_PARTITION Partition[EMMC_MAX_PARTITIONS]; + EMMC_CSD Csd; + EMMC_CID Cid; + EMMC_EXT_CSD ExtCsd; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + // + // The model name consists of three fields in CID register + // 1) OEM/Application ID (2 bytes) + // 2) Product Name (5 bytes) + // 3) Product Serial Number (4 bytes) + // The delimiters of these fields are whitespace. + // + CHAR16 ModelName[EMMC_MODEL_NAME_MAX_LEN]; + EMMC_DRIVER_PRIVATE_DATA *Private; +} ; + +// +// EMMC DXE driver private data structure +// +struct _EMMC_DRIVER_PRIVATE_DATA { + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_HANDLE Controller; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_HANDLE DriverBindingHandle; + + EMMC_DEVICE Device[EMMC_MAX_DEVICES]; +} ; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +EmmcDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Send command SELECT to the device to select/deselect the device. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSelect ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca + ); + +/** + Send command SEND_STATUS to the device to get device status. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] DevStatus The buffer to store the device status. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcSendStatus ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ); + +/** + Send command SEND_CSD to the device to get the CSD register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Csd The buffer to store the EMMC_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetCsd ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT EMMC_CSD *Csd + ); + +/** + Send command SEND_CID to the device to get the CID register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Csd The buffer to store the EMMC_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetCid ( + IN EMMC_DEVICE *Device, + IN UINT16 Rca, + OUT EMMC_CID *Cid + ); + +/** + Send command SEND_EXT_CSD to the device to get the EXT_CSD register data. + + @param[in] Device A pointer to the EMMC_DEVICE instance. + @param[out] ExtCsd The buffer to store the EXT_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +EmmcGetExtCsd ( + IN EMMC_DEVICE *Device, + OUT EMMC_EXT_CSD *ExtCsd + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf new file mode 100644 index 0000000000..a10bcd2343 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf @@ -0,0 +1,66 @@ +## @file +# EmmcDxe driver is used to manage the EMMC device. +# +# It produces BlockIo, BlockIo2 and StorageSecurity protocols to allow upper layer +# access the EMMC device. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = EmmcDxe + MODULE_UNI_FILE = EmmcDxe.uni + FILE_GUID = 2145F72F-E6F1-4440-A828-59DC9AAB5F89 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeEmmcDxe + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gEmmcDxeDriverBinding +# COMPONENT_NAME = gEmmcDxeComponentName +# COMPONENT_NAME2 = gEmmcDxeComponentName2 +# + +[Sources.common] + ComponentName.c + EmmcDxe.c + EmmcDxe.h + EmmcBlockIo.c + EmmcBlockIo.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiSdMmcPassThruProtocolGuid ## TO_START + gEfiBlockIoProtocolGuid ## BY_START + gEfiBlockIo2ProtocolGuid ## BY_START + gEfiStorageSecurityCommandProtocolGuid ## SOMETIMES_PRODUCES + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni new file mode 100644 index 0000000000..a0c9cf2935 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni @@ -0,0 +1,20 @@ +// /** @file +// EMMC device driver to manage the EMMC device and provide interface for upper layer +// access. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "EMMC device driver to manage the EMMC device and provide interface for upper layer access" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver follows the UEFI driver model and layers on the SdMmcPassThru protocol. It installs BlockIo/BlockIo2/StorageSecurity protocols for the EMMC device partitions." + diff --git a/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni new file mode 100644 index 0000000000..083b4f67ae --- /dev/null +++ b/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni @@ -0,0 +1,19 @@ +// /** @file +// EmmcDxe Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EMMC Device Driver" + + diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c new file mode 100644 index 0000000000..6dc343edbd --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c @@ -0,0 +1,617 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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 "SdBlockIoPei.h" + +// +// Template for SD HC Slot Data. +// +SD_PEIM_HC_SLOT gSdHcSlotTemplate = { + SD_PEIM_SLOT_SIG, // Signature + { // Media + MSG_SD_DP, + FALSE, + TRUE, + FALSE, + 0x200, + 0 + }, + 0, // SdHcBase + { // Capability + 0, + }, + { // Csd + 0, + }, + TRUE, // SectorAddressing + NULL // Private +}; + +// +// Template for SD HC Private Data. +// +SD_PEIM_HC_PRIVATE_DATA gSdHcPrivateTemplate = { + SD_PEIM_SIG, // Signature + NULL, // Pool + { // BlkIoPpi + SdBlockIoPeimGetDeviceNo, + SdBlockIoPeimGetMediaInfo, + SdBlockIoPeimReadBlocks + }, + { // BlkIo2Ppi + EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION, + SdBlockIoPeimGetDeviceNo2, + SdBlockIoPeimGetMediaInfo2, + SdBlockIoPeimReadBlocks2 + }, + { // BlkIoPpiList + EFI_PEI_PPI_DESCRIPTOR_PPI, + &gEfiPeiVirtualBlockIoPpiGuid, + NULL + }, + { // BlkIo2PpiList + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEfiPeiVirtualBlockIo2PpiGuid, + NULL + }, + { // Slot + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + }, + { + 0, + } + }, + 0, // SlotNum + 0 // TotalBlkIoDevices +}; +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + SD_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + *NumberBlockDevices = Private->TotalBlkIoDevices; + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ) +{ + SD_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + + if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) { + return EFI_INVALID_PARAMETER; + } + + MediaInfo->DeviceType = SD; + MediaInfo->MediaPresent = TRUE; + MediaInfo->LastBlock = (UINTN)Private->Slot[DeviceIndex - 1].Media.LastBlock; + MediaInfo->BlockSize = Private->Slot[DeviceIndex - 1].Media.BlockSize; + + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT32 BlockSize; + UINTN NumberOfBlocks; + SD_PEIM_HC_PRIVATE_DATA *Private; + UINTN Remaining; + UINT32 MaxBlock; + + Status = EFI_SUCCESS; + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + + // + // Check parameters + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if ((DeviceIndex == 0) || (DeviceIndex > Private->TotalBlkIoDevices)) { + return EFI_INVALID_PARAMETER; + } + + BlockSize = Private->Slot[DeviceIndex - 1].Media.BlockSize; + if (BufferSize % BlockSize != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > Private->Slot[DeviceIndex - 1].Media.LastBlock) { + return EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / BlockSize; + + // + // Start to execute data transfer. The max block number in single cmd is 65535 blocks. + // + Remaining = NumberOfBlocks; + MaxBlock = 0xFFFF; + + while (Remaining > 0) { + if (Remaining <= MaxBlock) { + NumberOfBlocks = Remaining; + } else { + NumberOfBlocks = MaxBlock; + } + + BufferSize = NumberOfBlocks * BlockSize; + if (NumberOfBlocks != 1) { + Status = SdPeimRwMultiBlocks (&Private->Slot[DeviceIndex - 1], StartLBA, BlockSize, Buffer, BufferSize, TRUE); + } else { + Status = SdPeimRwSingleBlock (&Private->Slot[DeviceIndex - 1], StartLBA, BlockSize, Buffer, BufferSize, TRUE); + } + if (EFI_ERROR (Status)) { + return Status; + } + + StartLBA += NumberOfBlocks; + Buffer = (UINT8*)Buffer + BufferSize; + Remaining -= NumberOfBlocks; + } + return Status; +} + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + SD_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + *NumberBlockDevices = Private->TotalBlkIoDevices; + + return EFI_SUCCESS; +} + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetMediaInfo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo + ) +{ + EFI_STATUS Status; + SD_PEIM_HC_PRIVATE_DATA *Private; + EFI_PEI_BLOCK_IO_MEDIA Media; + + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + + Status = SdBlockIoPeimGetMediaInfo ( + PeiServices, + &Private->BlkIoPpi, + DeviceIndex, + &Media + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (MediaInfo, &(Private->Slot[DeviceIndex - 1].Media), sizeof (EFI_PEI_BLOCK_IO2_MEDIA)); + return EFI_SUCCESS; +} + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + SD_PEIM_HC_PRIVATE_DATA *Private; + + Status = EFI_SUCCESS; + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2 (This); + + Status = SdBlockIoPeimReadBlocks ( + PeiServices, + &Private->BlkIoPpi, + DeviceIndex, + StartLBA, + BufferSize, + Buffer + ); + return Status; +} + +/** + The user code starts with this function. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeSdBlockIoPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + SD_PEIM_HC_PRIVATE_DATA *Private; + EDKII_SD_MMC_HOST_CONTROLLER_PPI *SdMmcHcPpi; + UINT32 Index; + UINTN *MmioBase; + UINT8 BarNum; + UINT8 SlotNum; + UINT8 Controller; + UINT64 Capacity; + SD_HC_SLOT_CAP Capability; + SD_PEIM_HC_SLOT *Slot; + SD_CSD *Csd; + SD_CSD2 *Csd2; + UINT32 CSize; + UINT32 CSizeMul; + UINT32 ReadBlLen; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + // + // locate Sd host controller PPI + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiSdMmcHostControllerPpiGuid, + 0, + NULL, + (VOID **) &SdMmcHcPpi + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Controller = 0; + MmioBase = NULL; + while (TRUE) { + Status = SdMmcHcPpi->GetSdMmcHcMmioBar (SdMmcHcPpi, Controller, &MmioBase, &BarNum); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR (Status)) { + break; + } + + if (BarNum == 0) { + Controller++; + continue; + } + + Private = AllocateCopyPool (sizeof (SD_PEIM_HC_PRIVATE_DATA), &gSdHcPrivateTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + Private->BlkIoPpiList.Ppi = (VOID*)&Private->BlkIoPpi; + Private->BlkIo2PpiList.Ppi = (VOID*)&Private->BlkIo2Ppi; + // + // Initialize the memory pool which will be used in all transactions. + // + Status = SdPeimInitMemPool (Private); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + for (Index = 0; Index < BarNum; Index++) { + Status = SdPeimHcGetCapability (MmioBase[Index], &Capability); + if (EFI_ERROR (Status)) { + continue; + } + if (Capability.SlotType != 0x1) { + DEBUG ((EFI_D_INFO, "The slot at 0x%x is not embedded slot type\n", MmioBase[Index])); + Status = EFI_UNSUPPORTED; + continue; + } + + Status = SdPeimHcReset (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + Status = SdPeimHcCardDetect (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + Status = SdPeimHcInitHost (MmioBase[Index]); + if (EFI_ERROR (Status)) { + continue; + } + + SlotNum = Private->SlotNum; + Slot = &Private->Slot[SlotNum]; + CopyMem (Slot, &gSdHcSlotTemplate, sizeof (SD_PEIM_HC_SLOT)); + Slot->Private = Private; + Slot->SdHcBase = MmioBase[Index]; + CopyMem (&Slot->Capability, &Capability, sizeof (Capability)); + + Status = SdPeimIdentification (Slot); + if (EFI_ERROR (Status)) { + continue; + } + + Csd = &Slot->Csd; + if (Csd->CsdStructure == 0) { + Slot->SectorAddressing = FALSE; + CSize = (Csd->CSizeHigh << 2 | Csd->CSizeLow) + 1; + CSizeMul = (1 << (Csd->CSizeMul + 2)); + ReadBlLen = (1 << (Csd->ReadBlLen)); + Capacity = MultU64x32 (MultU64x32 ((UINT64)CSize, CSizeMul), ReadBlLen); + } else { + Slot->SectorAddressing = TRUE; + Csd2 = (SD_CSD2*)(VOID*)Csd; + CSize = (Csd2->CSizeHigh << 16 | Csd2->CSizeLow) + 1; + Capacity = MultU64x32 ((UINT64)CSize, SIZE_512KB); + } + + Slot->Media.LastBlock = DivU64x32 (Capacity, Slot->Media.BlockSize) - 1; + + Private->TotalBlkIoDevices++; + Private->SlotNum++; + } + + Controller++; + if (!EFI_ERROR (Status)) { + PeiServicesInstallPpi (&Private->BlkIoPpiList); + } + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h new file mode 100644 index 0000000000..b2491bb3cb --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h @@ -0,0 +1,377 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_BLOCK_IO_PEI_H_ +#define _SD_BLOCK_IO_PEI_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef struct _SD_PEIM_HC_PRIVATE_DATA SD_PEIM_HC_PRIVATE_DATA; +typedef struct _SD_PEIM_HC_SLOT SD_PEIM_HC_SLOT; +typedef struct _SD_TRB SD_TRB; + +#include "SdHci.h" +#include "SdHcMem.h" + +#define SD_PEIM_SIG SIGNATURE_32 ('S', 'D', 'C', 'P') +#define SD_PEIM_SLOT_SIG SIGNATURE_32 ('S', 'D', 'C', 'S') + +#define SD_PEIM_MAX_SLOTS 6 + +struct _SD_PEIM_HC_SLOT { + UINT32 Signature; + EFI_PEI_BLOCK_IO2_MEDIA Media; + + UINTN SdHcBase; + SD_HC_SLOT_CAP Capability; + SD_CSD Csd; + BOOLEAN SectorAddressing; + SD_PEIM_HC_PRIVATE_DATA *Private; +}; + +struct _SD_PEIM_HC_PRIVATE_DATA { + UINT32 Signature; + SD_PEIM_MEM_POOL *Pool; + EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi; + EFI_PEI_RECOVERY_BLOCK_IO2_PPI BlkIo2Ppi; + EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList; + EFI_PEI_PPI_DESCRIPTOR BlkIo2PpiList; + SD_PEIM_HC_SLOT Slot[SD_PEIM_MAX_SLOTS]; + UINT8 SlotNum; + UINT8 TotalBlkIoDevices; +}; + +#define SD_TIMEOUT MultU64x32((UINT64)(3), 1000000) +#define GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS(a) CR (a, SD_PEIM_HC_PRIVATE_DATA, BlkIoPpi, SD_PEIM_SIG) +#define GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS2(a) CR (a, SD_PEIM_HC_PRIVATE_DATA, BlkIo2Ppi, SD_PEIM_SIG) + +struct _SD_TRB { + SD_PEIM_HC_SLOT *Slot; + UINT16 BlockSize; + + SD_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + SD_HC_TRANSFER_MODE Mode; + + UINT64 Timeout; + + SD_HC_ADMA_DESC_LINE *AdmaDesc; + UINTN AdmaDescSize; +}; + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimReadBlocks ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Gets the count of block I/O devices that one specific block driver detects. + + This function is used for getting the count of block I/O devices that one + specific block driver detects. To the PEI ATAPI driver, it returns the number + of all the detected ATAPI devices it detects during the enumeration process. + To the PEI legacy floppy driver, it returns the number of all the legacy + devices it finds during its enumeration process. If no device is detected, + then the function will return zero. + + @param[in] PeiServices General-purpose services that are available + to every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI + instance. + @param[out] NumberBlockDevices The number of block I/O devices discovered. + + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetDeviceNo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + OUT UINTN *NumberBlockDevices + ); + +/** + Gets a block device's media information. + + This function will provide the caller with the specified block device's media + information. If the media changes, calling this function will update the media + information accordingly. + + @param[in] PeiServices General-purpose services that are available to every + PEIM + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, the PPIs that + want to talk to a single device must specify the + device index that was assigned during the enumeration + process. This index is a number from one to + NumberBlockDevices. + @param[out] MediaInfo The media information of the specified block media. + The caller is responsible for the ownership of this + data structure. + + @par Note: + The MediaInfo structure describes an enumeration of possible block device + types. This enumeration exists because no device paths are actually passed + across interfaces that describe the type or class of hardware that is publishing + the block I/O interface. This enumeration will allow for policy decisions + in the Recovery PEIM, such as "Try to recover from legacy floppy first, + LS-120 second, CD-ROM third." If there are multiple partitions abstracted + by a given device type, they should be reported in ascending order; this + order also applies to nested partitions, such as legacy MBR, where the + outermost partitions would have precedence in the reporting order. The + same logic applies to systems such as IDE that have precedence relationships + like "Master/Slave" or "Primary/Secondary". The master device should be + reported first, the slave second. + + @retval EFI_SUCCESS Media information about the specified block device + was obtained successfully. + @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware + error. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimGetMediaInfo2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo + ); + +/** + Reads the requested number of blocks from the specified block device. + + The function reads the requested number of blocks from the device. All the + blocks are read, or an error is returned. If there is no media in the device, + the function returns EFI_NO_MEDIA. + + @param[in] PeiServices General-purpose services that are available to + every PEIM. + @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance. + @param[in] DeviceIndex Specifies the block device to which the function wants + to talk. Because the driver that implements Block I/O + PPIs will manage multiple block devices, PPIs that + want to talk to a single device must specify the device + index that was assigned during the enumeration process. + This index is a number from one to NumberBlockDevices. + @param[in] StartLBA The starting logical block address (LBA) to read from + on the device + @param[in] BufferSize The size of the Buffer in bytes. This number must be + a multiple of the intrinsic block size of the device. + @param[out] Buffer A pointer to the destination buffer for the data. + The caller is responsible for the ownership of the + buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the read operation. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not + valid, or the buffer is not properly aligned. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of + the intrinsic block size of the device. + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimReadBlocks2 ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This, + IN UINTN DeviceIndex, + IN EFI_PEI_LBA StartLBA, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Initialize the memory management pool for the host controller. + + @param Private The Sd Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +SdPeimInitMemPool ( + IN SD_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +SdPeimAllocateMem ( + IN SD_PEIM_MEM_POOL *Pool, + IN UINTN Size + ); + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +SdPeimFreeMem ( + IN SD_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +#endif diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf new file mode 100644 index 0000000000..ca4c39b65f --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf @@ -0,0 +1,62 @@ +## @file +# Description file for the SD memory card Peim driver. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SdBlockIoPei + MODULE_UNI_FILE = SdBlockIoPei.uni + FILE_GUID = 17851FBF-45C4-4ff7-A2A0-C3B12D63C27E + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeSdBlockIoPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + SdBlockIoPei.c + SdBlockIoPei.h + SdHci.c + SdHci.h + SdHcMem.c + SdHcMem.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES + gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES + gEdkiiPeiSdMmcHostControllerPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiSdMmcHostControllerPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SdBlockIoPeiExtra.uni + diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni new file mode 100644 index 0000000000..30c9f0e92c --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni @@ -0,0 +1,21 @@ +// /** @file +// The SdBlockIoPei driver is used to support recovery from SD memory card device. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Support recovery from SD memory card devices" + +#string STR_MODULE_DESCRIPTION #language en-US "The SdBlockIoPei driver is used to support recovery from SD memory card device." + diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni new file mode 100644 index 0000000000..141e467233 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni @@ -0,0 +1,21 @@ +// /** @file +// SdBlockIoPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SD BlockIo Peim for Recovery" + + diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c new file mode 100644 index 0000000000..8bfe18cfd6 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c @@ -0,0 +1,455 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. 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 "SdBlockIoPei.h" + +/** + Allocate a block of memory to be used by the buffer pool. + + @param Pages How many pages to allocate. + + @return The allocated memory block or NULL if failed. + +**/ +SD_PEIM_MEM_BLOCK * +SdPeimAllocMemBlock ( + IN UINTN Pages + ) +{ + SD_PEIM_MEM_BLOCK *Block; + EFI_STATUS Status; + VOID *TempPtr; + EFI_PHYSICAL_ADDRESS Address; + + TempPtr = NULL; + Block = NULL; + + Status = PeiServicesAllocatePool (sizeof(SD_PEIM_MEM_BLOCK), &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(SD_PEIM_MEM_BLOCK)); + + // + // each bit in the bit array represents SD_PEIM_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (SD_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (SD_PEIM_MEM_BLOCK*)(UINTN)TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (SD_PEIM_MEM_UNIT * 8); + + Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen); + + Block->Bits = (UINT8*)(UINTN)TempPtr; + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + Pages, + &Address + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages)); + + Block->Buf = (UINT8*)((UINTN)Address); + Block->Next = NULL; + + return Block; +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +SdPeimFreeMemBlock ( + IN SD_PEIM_MEM_POOL *Pool, + IN SD_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT ((Pool != NULL) && (Block != NULL)); +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. If couldn't allocate the needed memory, + the return value is NULL. + +**/ +VOID * +SdPeimAllocMemFromBlock ( + IN SD_PEIM_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + SD_PEIM_NEXT_BIT (Byte, Bit); + + } else { + SD_PEIM_NEXT_BIT (Byte, Bit); + + Available = 0; + StartByte = Byte; + StartBit = Bit; + } + } + + if (Available < Units) { + return NULL; + } + + // + // Mark the memory as allocated + // + Byte = StartByte; + Bit = StartBit; + + for (Count = 0; Count < Units; Count++) { + ASSERT (!SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) SD_PEIM_MEM_BIT (Bit)); + SD_PEIM_NEXT_BIT (Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * SD_PEIM_MEM_UNIT; +} + +/** + Insert the memory block to the pool's list of the blocks. + + @param Head The head of the memory pool's block list. + @param Block The memory block to insert. + +**/ +VOID +SdPeimInsertMemBlockToPool ( + IN SD_PEIM_MEM_BLOCK *Head, + IN SD_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT ((Head != NULL) && (Block != NULL)); + Block->Next = Head->Next; + Head->Next = Block; +} + +/** + Is the memory block empty? + + @param Block The memory block to check. + + @retval TRUE The memory block is empty. + @retval FALSE The memory block isn't empty. + +**/ +BOOLEAN +SdPeimIsMemBlockEmpty ( + IN SD_PEIM_MEM_BLOCK *Block + ) +{ + UINTN Index; + + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +SdPeimUnlinkMemBlock ( + IN SD_PEIM_MEM_BLOCK *Head, + IN SD_PEIM_MEM_BLOCK *BlockToUnlink + ) +{ + SD_PEIM_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @param Private The Sd Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +SdPeimInitMemPool ( + IN SD_PEIM_HC_PRIVATE_DATA *Private + ) +{ + SD_PEIM_MEM_POOL *Pool; + EFI_STATUS Status; + VOID *TempPtr; + + TempPtr = NULL; + Pool = NULL; + + Status = PeiServicesAllocatePool (sizeof (SD_PEIM_MEM_POOL), &TempPtr); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (SD_PEIM_MEM_POOL)); + + Pool = (SD_PEIM_MEM_POOL *)((UINTN)TempPtr); + + Pool->Head = SdPeimAllocMemBlock (SD_PEIM_MEM_DEFAULT_PAGES); + + if (Pool->Head == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Private->Pool = Pool; + return EFI_SUCCESS; +} + +/** + Release the memory management pool. + + @param Pool The memory pool to free. + + @retval EFI_DEVICE_ERROR Fail to free the memory pool. + @retval EFI_SUCCESS The memory pool is freed. + +**/ +EFI_STATUS +SdPeimFreeMemPool ( + IN SD_PEIM_MEM_POOL *Pool + ) +{ + SD_PEIM_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // SdPeimUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + SdPeimFreeMemBlock (Pool, Block); + } + + SdPeimFreeMemBlock (Pool, Pool->Head); + + return EFI_SUCCESS; +} + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +SdPeimAllocateMem ( + IN SD_PEIM_MEM_POOL *Pool, + IN UINTN Size + ) +{ + SD_PEIM_MEM_BLOCK *Head; + SD_PEIM_MEM_BLOCK *Block; + SD_PEIM_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = SD_PEIM_MEM_ROUND (Size); + Head = Pool->Head; + ASSERT (Head != NULL); + + // + // First check whether current memory blocks can satisfy the allocation. + // + for (Block = Head; Block != NULL; Block = Block->Next) { + Mem = SdPeimAllocMemFromBlock (Block, AllocSize / SD_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + break; + } + } + + if (Mem != NULL) { + return Mem; + } + + // + // Create a new memory block if there is not enough memory + // in the pool. If the allocation size is larger than the + // default page number, just allocate a large enough memory + // block. Otherwise allocate default pages. + // + if (AllocSize > EFI_PAGES_TO_SIZE (SD_PEIM_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1; + } else { + Pages = SD_PEIM_MEM_DEFAULT_PAGES; + } + + NewBlock = SdPeimAllocMemBlock (Pages); + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + SdPeimInsertMemBlockToPool (Head, NewBlock); + Mem = SdPeimAllocMemFromBlock (NewBlock, AllocSize / SD_PEIM_MEM_UNIT); + + if (Mem != NULL) { + ZeroMem (Mem, Size); + } + + return Mem; +} + +/** + Free the allocated memory back to the memory pool. + + @param Pool The memory pool of the host controller. + @param Mem The memory to free. + @param Size The size of the memory to free. + +**/ +VOID +SdPeimFreeMem ( + IN SD_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + SD_PEIM_MEM_BLOCK *Head; + SD_PEIM_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = SD_PEIM_MEM_ROUND (Size); + ToFree = (UINT8 *) Mem; + + for (Block = Head; Block != NULL; Block = Block->Next) { + // + // scan the memory block list for the memory block that + // completely contains the memory to free. + // + if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) { + // + // compute the start byte and bit in the bit array + // + Byte = ((ToFree - Block->Buf) / SD_PEIM_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / SD_PEIM_MEM_UNIT) % 8; + + // + // reset associated bits in bit arry + // + for (Count = 0; Count < (AllocSize / SD_PEIM_MEM_UNIT); Count++) { + ASSERT (SD_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ SD_PEIM_MEM_BIT (Bit)); + SD_PEIM_NEXT_BIT (Byte, Bit); + } + + break; + } + } + + // + // If Block == NULL, it means that the current memory isn't + // in the host controller's pool. This is critical because + // the caller has passed in a wrong memory point + // + ASSERT (Block != NULL); + + // + // Release the current memory block if it is empty and not the head + // + if ((Block != Head) && SdPeimIsMemBlockEmpty (Block)) { + SdPeimFreeMemBlock (Pool, Block); + } + + return ; +} diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h new file mode 100644 index 0000000000..096b625f98 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h @@ -0,0 +1,61 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_PEIM_MEM_H_ +#define _SD_PEIM_MEM_H_ + +#define SD_PEIM_MEM_BIT(a) ((UINTN)(1 << (a))) + +#define SD_PEIM_MEM_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & SD_PEIM_MEM_BIT(Bit)) == SD_PEIM_MEM_BIT(Bit))) + +typedef struct _SD_PEIM_MEM_BLOCK SD_PEIM_MEM_BLOCK; + +struct _SD_PEIM_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINTN BufLen; // Memory size in bytes + SD_PEIM_MEM_BLOCK *Next; +}; + +typedef struct _SD_PEIM_MEM_POOL { + SD_PEIM_MEM_BLOCK *Head; +} SD_PEIM_MEM_POOL; + +// +// Memory allocation unit, note that the value must meet SD spec alignment requirement. +// +#define SD_PEIM_MEM_UNIT 128 + +#define SD_PEIM_MEM_UNIT_MASK (SD_PEIM_MEM_UNIT - 1) +#define SD_PEIM_MEM_DEFAULT_PAGES 16 + +#define SD_PEIM_MEM_ROUND(Len) (((Len) + SD_PEIM_MEM_UNIT_MASK) & (~SD_PEIM_MEM_UNIT_MASK)) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define SD_PEIM_NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + +#endif + diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c new file mode 100644 index 0000000000..8f7ecf9395 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c @@ -0,0 +1,2874 @@ +/** @file + + Copyright (c) 2015 - 2016, Intel Corporation. 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 "SdBlockIoPei.h" + +/** + Read/Write specified SD host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the Data or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +SdPeimHcRwMmio ( + IN UINTN Address, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ) +{ + if ((Address == 0) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) { + return EFI_INVALID_PARAMETER; + } + + switch (Count) { + case 1: + if (Read) { + *(UINT8*)Data = MmioRead8 (Address); + } else { + MmioWrite8 (Address, *(UINT8*)Data); + } + break; + case 2: + if (Read) { + *(UINT16*)Data = MmioRead16 (Address); + } else { + MmioWrite16 (Address, *(UINT16*)Data); + } + break; + case 4: + if (Read) { + *(UINT32*)Data = MmioRead32 (Address); + } else { + MmioWrite32 (Address, *(UINT32*)Data); + } + break; + case 8: + if (Read) { + *(UINT64*)Data = MmioRead64 (Address); + } else { + MmioWrite64 (Address, *(UINT64*)Data); + } + break; + default: + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Do OR operation with the value of the specified SD host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the OrData or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +SdPeimHcOrMmio ( + IN UINTN Address, + IN UINT8 Count, + IN VOID *OrData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 Or; + + Status = SdPeimHcRwMmio (Address, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + Or = *(UINT8*) OrData; + } else if (Count == 2) { + Or = *(UINT16*) OrData; + } else if (Count == 4) { + Or = *(UINT32*) OrData; + } else if (Count == 8) { + Or = *(UINT64*) OrData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data |= Or; + Status = SdPeimHcRwMmio (Address, FALSE, Count, &Data); + + return Status; +} + +/** + Do AND operation with the value of the specified SD host controller mmio register. + + @param[in] Address The address of the mmio register to be read/written. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The Address or the AndData or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +SdPeimHcAndMmio ( + IN UINTN Address, + IN UINT8 Count, + IN VOID *AndData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 And; + + Status = SdPeimHcRwMmio (Address, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + And = *(UINT8*) AndData; + } else if (Count == 2) { + And = *(UINT16*) AndData; + } else if (Count == 4) { + And = *(UINT32*) AndData; + } else if (Count == 8) { + And = *(UINT64*) AndData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data &= And; + Status = SdPeimHcRwMmio (Address, FALSE, Count, &Data); + + return Status; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] Address The address of the mmio register to be checked. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + + @retval EFI_NOT_READY The MMIO register hasn't set to the expected value. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +SdPeimHcCheckMmioSet ( + IN UINTN Address, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue + ) +{ + EFI_STATUS Status; + UINT64 Value; + + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = 0; + Status = SdPeimHcRwMmio (Address, TRUE, Count, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] Address The address of the mmio register to wait. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +SdPeimHcWaitMmioSet ( + IN UINTN Address, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + Status = SdPeimHcCheckMmioSet ( + Address, + Count, + MaskValue, + TestValue + ); + if (Status != EFI_NOT_READY) { + return Status; + } + + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Software reset the specified SD host controller and enable all interrupts. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +SdPeimHcReset ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT8 SwReset; + + SwReset = 0xFF; + Status = SdPeimHcRwMmio (Bar + SD_HC_SW_RST, FALSE, sizeof (SwReset), &SwReset); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimHcReset: write full 1 fails: %r\n", Status)); + return Status; + } + + Status = SdPeimHcWaitMmioSet ( + Bar + SD_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0x00, + SD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "SdPeimHcReset: reset done with %r\n", Status)); + return Status; + } + // + // Enable all interrupt after reset all. + // + Status = SdPeimHcEnableInterrupt (Bar); + + return Status; +} + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimHcEnableInterrupt ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT16 IntStatus; + + // + // Enable all bits in Error Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = SdPeimHcRwMmio (Bar + SD_HC_ERR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Enable all bits in Normal Interrupt Status Enable Register + // + IntStatus = 0xFFFF; + Status = SdPeimHcRwMmio (Bar + SD_HC_NOR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus); + + return Status; +} + +/** + Get the capability data from the specified slot. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimHcGetCapability ( + IN UINTN Bar, + OUT SD_HC_SLOT_CAP *Capability + ) +{ + EFI_STATUS Status; + UINT64 Cap; + + Status = SdPeimHcRwMmio (Bar + SD_HC_CAP, TRUE, sizeof (Cap), &Cap); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem (Capability, &Cap, sizeof (Cap)); + + return EFI_SUCCESS; +} + +/** + Detect whether there is a SD card attached at the specified SD host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS There is a SD card attached. + @retval EFI_NO_MEDIA There is not a SD card attached. + @retval Others The detection fails. + +**/ +EFI_STATUS +SdPeimHcCardDetect ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT16 Data; + UINT32 PresentState; + + // + // Check Normal Interrupt Status Register + // + Status = SdPeimHcRwMmio (Bar + SD_HC_NOR_INT_STS, TRUE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Data & (BIT6 | BIT7)) != 0) { + // + // Clear BIT6 and BIT7 by writing 1 to these two bits if set. + // + Data &= BIT6 | BIT7; + Status = SdPeimHcRwMmio (Bar + SD_HC_NOR_INT_STS, FALSE, sizeof (Data), &Data); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Check Present State Register to see if there is a card presented. + // + Status = SdPeimHcRwMmio (Bar + SD_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PresentState & BIT16) != 0) { + return EFI_SUCCESS; + } else { + return EFI_NO_MEDIA; + } +} + +/** + Stop SD card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS Succeed to stop SD clock. + @retval Others Fail to stop SD clock. + +**/ +EFI_STATUS +SdPeimHcStopClock ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT32 PresentState; + UINT16 ClockCtrl; + + // + // Ensure no SD transactions are occurring on the SD Bus by + // waiting for Command Inhibit (DAT) and Command Inhibit (CMD) + // in the Present State register to be 0. + // + Status = SdPeimHcWaitMmioSet ( + Bar + SD_HC_PRESENT_STATE, + sizeof (PresentState), + BIT0 | BIT1, + 0, + SD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 0 + // + ClockCtrl = (UINT16)~BIT2; + Status = SdPeimHcAndMmio (Bar + SD_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + SD card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdPeimHcClockSupply ( + IN UINTN Bar, + IN UINT64 ClockFreq + ) +{ + EFI_STATUS Status; + SD_HC_SLOT_CAP Capability; + UINT32 BaseClkFreq; + UINT32 SettingFreq; + UINT32 Divisor; + UINT32 Remainder; + UINT16 ControllerVer; + UINT16 ClockCtrl; + + // + // Calculate a divisor for SD clock frequency + // + Status = SdPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Capability.BaseClkFreq != 0); + + BaseClkFreq = Capability.BaseClkFreq; + if ((ClockFreq > (BaseClkFreq * 1000)) || (ClockFreq == 0)) { + return EFI_INVALID_PARAMETER; + } + // + // Calculate the divisor of base frequency. + // + Divisor = 0; + SettingFreq = BaseClkFreq * 1000; + while (ClockFreq < SettingFreq) { + Divisor++; + + SettingFreq = (BaseClkFreq * 1000) / (2 * Divisor); + Remainder = (BaseClkFreq * 1000) % (2 * Divisor); + if ((ClockFreq == SettingFreq) && (Remainder == 0)) { + break; + } + if ((ClockFreq == SettingFreq) && (Remainder != 0)) { + SettingFreq ++; + } + } + + DEBUG ((EFI_D_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); + + Status = SdPeimHcRwMmio (Bar + SD_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set SDCLK Frequency Select and Internal Clock Enable fields in Clock Control register. + // + if ((ControllerVer & 0xFF) == 2) { + ASSERT (Divisor <= 0x3FF); + ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2); + } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + // + // Only the most significant bit can be used as divisor. + // + if (((Divisor - 1) & Divisor) != 0) { + Divisor = 1 << (HighBitSet32 (Divisor) + 1); + } + ASSERT (Divisor <= 0x80); + ClockCtrl = (Divisor & 0xFF) << 8; + } else { + DEBUG ((EFI_D_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); + return EFI_UNSUPPORTED; + } + + // + // Stop bus clock at first + // + Status = SdPeimHcStopClock (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Supply clock frequency with specified divisor + // + ClockCtrl |= BIT0; + Status = SdPeimHcRwMmio (Bar + SD_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); + return Status; + } + + // + // Wait Internal Clock Stable in the Clock Control register to be 1 + // + Status = SdPeimHcWaitMmioSet ( + Bar + SD_HC_CLOCK_CTRL, + sizeof (ClockCtrl), + BIT1, + BIT1, + SD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + Status = SdPeimHcOrMmio (Bar + SD_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + + return Status; +} + +/** + SD bus power control. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] PowerCtrl The value setting to the power control register. + + @retval TRUE There is a SD card attached. + @retval FALSE There is no a SD card attached. + +**/ +EFI_STATUS +SdPeimHcPowerControl ( + IN UINTN Bar, + IN UINT8 PowerCtrl + ) +{ + EFI_STATUS Status; + + // + // Clr SD Bus Power + // + PowerCtrl &= (UINT8)~BIT0; + Status = SdPeimHcRwMmio (Bar + SD_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + PowerCtrl |= BIT0; + Status = SdPeimHcRwMmio (Bar + SD_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl); + + return Status; +} + +/** + Set the SD bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] BusWidth The bus width used by the SD device, it must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +SdPeimHcSetBusWidth ( + IN UINTN Bar, + IN UINT16 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (BusWidth == 1) { + HostCtrl1 = (UINT8)~(BIT5 | BIT1); + Status = SdPeimHcAndMmio (Bar + SD_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 4) { + Status = SdPeimHcRwMmio (Bar + SD_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 |= BIT1; + HostCtrl1 &= (UINT8)~BIT5; + Status = SdPeimHcRwMmio (Bar + SD_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else if (BusWidth == 8) { + Status = SdPeimHcRwMmio (Bar + SD_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl1 &= (UINT8)~BIT1; + HostCtrl1 |= BIT5; + Status = SdPeimHcRwMmio (Bar + SD_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1); + } else { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + return Status; +} + +/** + Supply SD card with lowest clock frequency at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +SdPeimHcInitClockFreq ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + SD_HC_SLOT_CAP Capability; + UINT32 InitFreq; + + // + // Calculate a divisor for SD clock frequency + // + Status = SdPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Capability.BaseClkFreq == 0) { + // + // Don't support get Base Clock Frequency information via another method + // + return EFI_UNSUPPORTED; + } + // + // Supply 400KHz clock frequency at initialization phase. + // + InitFreq = 400; + Status = SdPeimHcClockSupply (Bar, InitFreq); + return Status; +} + +/** + Supply SD card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +SdPeimHcInitPowerVoltage ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + SD_HC_SLOT_CAP Capability; + UINT8 MaxVoltage; + UINT8 HostCtrl2; + + // + // Get the support voltage of the Host Controller + // + Status = SdPeimHcGetCapability (Bar, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Calculate supported maximum voltage according to SD Bus Voltage Select + // + if (Capability.Voltage33 != 0) { + // + // Support 3.3V + // + MaxVoltage = 0x0E; + } else if (Capability.Voltage30 != 0) { + // + // Support 3.0V + // + MaxVoltage = 0x0C; + } else if (Capability.Voltage18 != 0) { + // + // Support 1.8V + // + MaxVoltage = 0x0A; + HostCtrl2 = BIT3; + Status = SdPeimHcOrMmio (Bar + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + MicroSecondDelay (5000); + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + // + // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register + // + Status = SdPeimHcPowerControl (Bar, MaxVoltage); + + return Status; +} + +/** + Initialize the Timeout Control register with most conservative value at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +SdPeimHcInitTimeoutCtrl ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + UINT8 Timeout; + + Timeout = 0x0E; + Status = SdPeimHcRwMmio (Bar + SD_HC_TIMEOUT_CTRL, FALSE, sizeof (Timeout), &Timeout); + + return Status; +} + +/** + Initial SD host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +SdPeimHcInitHost ( + IN UINTN Bar + ) +{ + EFI_STATUS Status; + + Status = SdPeimHcInitClockFreq (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimHcInitPowerVoltage (Bar); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimHcInitTimeoutCtrl (Bar); + return Status; +} + +/** + Turn on/off LED. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] On The boolean to turn on/off LED. + + @retval EFI_SUCCESS The LED is turned on/off successfully. + @retval Others The LED isn't turned on/off successfully. + +**/ +EFI_STATUS +SdPeimHcLedOnOff ( + IN UINTN Bar, + IN BOOLEAN On + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl1; + + if (On) { + HostCtrl1 = BIT0; + Status = SdPeimHcOrMmio (Bar + SD_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } else { + HostCtrl1 = (UINT8)~BIT0; + Status = SdPeimHcAndMmio (Bar + SD_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + } + + return Status; +} + +/** + Build ADMA descriptor table for transfer. + + Refer to SD Host Controller Simplified spec 3.0 Section 1.13 for details. + + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The ADMA descriptor table is created successfully. + @retval Others The ADMA descriptor table isn't created successfully. + +**/ +EFI_STATUS +BuildAdmaDescTable ( + IN SD_TRB *Trb + ) +{ + EFI_PHYSICAL_ADDRESS Data; + UINT64 DataLen; + UINT64 Entries; + UINT32 Index; + UINT64 Remaining; + UINT32 Address; + + Data = (EFI_PHYSICAL_ADDRESS)(UINTN)Trb->Data; + DataLen = Trb->DataLen; + // + // Only support 32bit ADMA Descriptor Table + // + if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + return EFI_INVALID_PARAMETER; + } + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) + // for 32-bit address descriptor table. + // + if ((Data & (BIT0 | BIT1)) != 0) { + DEBUG ((EFI_D_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data)); + } + + Entries = DivU64x32 ((DataLen + ADMA_MAX_DATA_PER_LINE - 1), ADMA_MAX_DATA_PER_LINE); + + Trb->AdmaDescSize = (UINTN)MultU64x32 (Entries, sizeof (SD_HC_ADMA_DESC_LINE)); + Trb->AdmaDesc = SdPeimAllocateMem (Trb->Slot->Private->Pool, Trb->AdmaDescSize); + if (Trb->AdmaDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Remaining = DataLen; + Address = (UINT32)Data; + for (Index = 0; Index < Entries; Index++) { + if (Remaining <= ADMA_MAX_DATA_PER_LINE) { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = (UINT16)Remaining; + Trb->AdmaDesc[Index].Address = Address; + break; + } else { + Trb->AdmaDesc[Index].Valid = 1; + Trb->AdmaDesc[Index].Act = 2; + Trb->AdmaDesc[Index].Length = 0; + Trb->AdmaDesc[Index].Address = Address; + } + + Remaining -= ADMA_MAX_DATA_PER_LINE; + Address += ADMA_MAX_DATA_PER_LINE; + } + + // + // Set the last descriptor line as end of descriptor table + // + Trb->AdmaDesc[Index].End = 1; + return EFI_SUCCESS; +} + +/** + Create a new TRB for the SD cmd request. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Packet A pointer to the SD command data structure. + + @return Created Trb or NULL. + +**/ +SD_TRB * +SdPeimCreateTrb ( + IN SD_PEIM_HC_SLOT *Slot, + IN SD_COMMAND_PACKET *Packet + ) +{ + SD_TRB *Trb; + EFI_STATUS Status; + SD_HC_SLOT_CAP Capability; + + // + // Calculate a divisor for SD clock frequency + // + Status = SdPeimHcGetCapability (Slot->SdHcBase, &Capability); + if (EFI_ERROR (Status)) { + return NULL; + } + + Trb = SdPeimAllocateMem (Slot->Private->Pool, sizeof (SD_TRB)); + if (Trb == NULL) { + return NULL; + } + + Trb->Slot = Slot; + Trb->BlockSize = 0x200; + Trb->Packet = Packet; + Trb->Timeout = Packet->Timeout; + + if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { + Trb->Data = Packet->InDataBuffer; + Trb->DataLen = Packet->InTransferLength; + Trb->Read = TRUE; + } else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) { + Trb->Data = Packet->OutDataBuffer; + Trb->DataLen = Packet->OutTransferLength; + Trb->Read = FALSE; + } else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) { + Trb->Data = NULL; + Trb->DataLen = 0; + } else { + goto Error; + } + + if ((Trb->DataLen % Trb->BlockSize) != 0) { + if (Trb->DataLen < Trb->BlockSize) { + Trb->BlockSize = (UINT16)Trb->DataLen; + } + } + + if (Trb->DataLen == 0) { + Trb->Mode = SdNoData; + } else if (Capability.Adma2 != 0) { + Trb->Mode = SdAdmaMode; + Status = BuildAdmaDescTable (Trb); + if (EFI_ERROR (Status)) { + goto Error; + } + } else if (Capability.Sdma != 0) { + Trb->Mode = SdSdmaMode; + } else { + Trb->Mode = SdPioMode; + } + + return Trb; + +Error: + SdPeimFreeTrb (Trb); + return NULL; +} + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the SD_TRB instance. + +**/ +VOID +SdPeimFreeTrb ( + IN SD_TRB *Trb + ) +{ + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + SdPeimFreeMem (Trb->Slot->Private->Pool, Trb->AdmaDesc, Trb->AdmaDescSize); + } + + if (Trb != NULL) { + SdPeimFreeMem (Trb->Slot->Private->Pool, Trb, sizeof (SD_TRB)); + } + return; +} + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdPeimCheckTrbEnv ( + IN UINTN Bar, + IN SD_TRB *Trb + ) +{ + EFI_STATUS Status; + SD_COMMAND_PACKET *Packet; + UINT32 PresentState; + + Packet = Trb->Packet; + + if ((Packet->SdCmdBlk->CommandType == SdCommandTypeAdtc) || + (Packet->SdCmdBlk->ResponseType == SdResponseTypeR1b) || + (Packet->SdCmdBlk->ResponseType == SdResponseTypeR5b)) { + // + // Wait Command Inhibit (CMD) and Command Inhibit (DAT) in + // the Present State register to be 0 + // + PresentState = BIT0 | BIT1; + if (Packet->SdCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK) { + PresentState = BIT0; + } + } else { + // + // Wait Command Inhibit (CMD) in the Present State register + // to be 0 + // + PresentState = BIT0; + } + + Status = SdPeimHcCheckMmioSet ( + Bar + SD_HC_PRESENT_STATE, + sizeof (PresentState), + PresentState, + 0 + ); + + return Status; +} + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +SdPeimWaitTrbEnv ( + IN UINTN Bar, + IN SD_TRB *Trb + ) +{ + EFI_STATUS Status; + SD_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Packet = Trb->Packet; + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdPeimCheckTrbEnv (Bar, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Execute the specified TRB. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the host controller. + +**/ +EFI_STATUS +SdPeimExecTrb ( + IN UINTN Bar, + IN SD_TRB *Trb + ) +{ + EFI_STATUS Status; + SD_COMMAND_PACKET *Packet; + UINT16 Cmd; + UINT16 IntStatus; + UINT32 Argument; + UINT16 BlkCount; + UINT16 BlkSize; + UINT16 TransMode; + UINT8 HostCtrl1; + UINT32 SdmaAddr; + UINT64 AdmaAddr; + + Packet = Trb->Packet; + // + // Clear all bits in Error Interrupt Status Register + // + IntStatus = 0xFFFF; + Status = SdPeimHcRwMmio (Bar + SD_HC_ERR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Clear all bits in Normal Interrupt Status Register + // + IntStatus = 0xFFFF; + Status = SdPeimHcRwMmio (Bar + SD_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Set Host Control 1 register DMA Select field + // + if (Trb->Mode == SdAdmaMode) { + HostCtrl1 = BIT4; + Status = SdPeimHcOrMmio (Bar + SD_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + SdPeimHcLedOnOff (Bar, TRUE); + + if (Trb->Mode == SdSdmaMode) { + if ((UINT64)(UINTN)Trb->Data >= 0x100000000ul) { + return EFI_INVALID_PARAMETER; + } + + SdmaAddr = (UINT32)(UINTN)Trb->Data; + Status = SdPeimHcRwMmio (Bar + SD_HC_SDMA_ADDR, FALSE, sizeof (SdmaAddr), &SdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } else if (Trb->Mode == SdAdmaMode) { + AdmaAddr = (UINT64)(UINTN)Trb->AdmaDesc; + Status = SdPeimHcRwMmio (Bar + SD_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr); + if (EFI_ERROR (Status)) { + return Status; + } + } + + BlkSize = Trb->BlockSize; + if (Trb->Mode == SdSdmaMode) { + // + // Set SDMA boundary to be 512K bytes. + // + BlkSize |= 0x7000; + } + + Status = SdPeimHcRwMmio (Bar + SD_HC_BLK_SIZE, FALSE, sizeof (BlkSize), &BlkSize); + if (EFI_ERROR (Status)) { + return Status; + } + + BlkCount = (UINT16)(Trb->DataLen / Trb->BlockSize); + Status = SdPeimHcRwMmio (Bar + SD_HC_BLK_COUNT, FALSE, sizeof (BlkCount), &BlkCount); + if (EFI_ERROR (Status)) { + return Status; + } + + Argument = Packet->SdCmdBlk->CommandArgument; + Status = SdPeimHcRwMmio (Bar + SD_HC_ARG1, FALSE, sizeof (Argument), &Argument); + if (EFI_ERROR (Status)) { + return Status; + } + + TransMode = 0; + if (Trb->Mode != SdNoData) { + if (Trb->Mode != SdPioMode) { + TransMode |= BIT0; + } + if (Trb->Read) { + TransMode |= BIT4; + } + if (BlkCount != 0) { + TransMode |= BIT5 | BIT1; + } + if (BlkCount > 1) { + TransMode |= BIT2; + } + } + + Status = SdPeimHcRwMmio (Bar + SD_HC_TRANS_MOD, FALSE, sizeof (TransMode), &TransMode); + if (EFI_ERROR (Status)) { + return Status; + } + + Cmd = (UINT16)LShiftU64(Packet->SdCmdBlk->CommandIndex, 8); + if (Packet->SdCmdBlk->CommandType == SdCommandTypeAdtc) { + Cmd |= BIT5; + } + // + // Convert ResponseType to value + // + if (Packet->SdCmdBlk->CommandType != SdCommandTypeBc) { + switch (Packet->SdCmdBlk->ResponseType) { + case SdResponseTypeR1: + case SdResponseTypeR5: + case SdResponseTypeR6: + case SdResponseTypeR7: + Cmd |= (BIT1 | BIT3 | BIT4); + break; + case SdResponseTypeR2: + Cmd |= (BIT0 | BIT3); + break; + case SdResponseTypeR3: + case SdResponseTypeR4: + Cmd |= BIT1; + break; + case SdResponseTypeR1b: + case SdResponseTypeR5b: + Cmd |= (BIT0 | BIT1 | BIT3 | BIT4); + break; + default: + ASSERT (FALSE); + break; + } + } + // + // Execute cmd + // + Status = SdPeimHcRwMmio (Bar + SD_HC_COMMAND, FALSE, sizeof (Cmd), &Cmd); + return Status; +} + +/** + Check the TRB execution result. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdPeimCheckTrbResult ( + IN UINTN Bar, + IN SD_TRB *Trb + ) +{ + EFI_STATUS Status; + SD_COMMAND_PACKET *Packet; + UINT16 IntStatus; + UINT32 Response[4]; + UINT32 SdmaAddr; + UINT8 Index; + UINT8 SwReset; + + SwReset = 0; + Packet = Trb->Packet; + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdPeimHcRwMmio ( + Bar + SD_HC_NOR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Check Transfer Complete bit is set or not. + // + if ((IntStatus & BIT1) == BIT1) { + if ((IntStatus & BIT15) == BIT15) { + // + // Read Error Interrupt Status register to check if the error is + // Data Timeout Error. + // If yes, treat it as success as Transfer Complete has higher + // priority than Data Timeout Error. + // + Status = SdPeimHcRwMmio ( + Bar + SD_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (!EFI_ERROR (Status)) { + if ((IntStatus & BIT4) == BIT4) { + Status = EFI_SUCCESS; + } else { + Status = EFI_DEVICE_ERROR; + } + } + } + + goto Done; + } + // + // Check if there is a error happened during cmd execution. + // If yes, then do error recovery procedure to follow SD Host Controller + // Simplified Spec 3.0 section 3.10.1. + // + if ((IntStatus & BIT15) == BIT15) { + Status = SdPeimHcRwMmio ( + Bar + SD_HC_ERR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + if ((IntStatus & 0x0F) != 0) { + SwReset |= BIT1; + } + if ((IntStatus & 0xF0) != 0) { + SwReset |= BIT2; + } + + Status = SdPeimHcRwMmio ( + Bar + SD_HC_SW_RST, + FALSE, + sizeof (SwReset), + &SwReset + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Status = SdPeimHcWaitMmioSet ( + Bar + SD_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0, + SD_TIMEOUT + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = EFI_DEVICE_ERROR; + goto Done; + } + // + // Check if DMA interrupt is signalled for the SDMA transfer. + // + if ((Trb->Mode == SdSdmaMode) && ((IntStatus & BIT3) == BIT3)) { + // + // Clear DMA interrupt bit. + // + IntStatus = BIT3; + Status = SdPeimHcRwMmio ( + Bar + SD_HC_NOR_INT_STS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Update SDMA Address register. + // + SdmaAddr = SD_SDMA_ROUND_UP ((UINT32)(UINTN)Trb->Data, SD_SDMA_BOUNDARY); + Status = SdPeimHcRwMmio ( + Bar + SD_HC_SDMA_ADDR, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Trb->Data = (VOID*)(UINTN)SdmaAddr; + } + + if ((Packet->SdCmdBlk->CommandType != SdCommandTypeAdtc) && + (Packet->SdCmdBlk->ResponseType != SdResponseTypeR1b) && + (Packet->SdCmdBlk->ResponseType != SdResponseTypeR5b)) { + if ((IntStatus & BIT0) == BIT0) { + Status = EFI_SUCCESS; + goto Done; + } + } + + if (Packet->SdCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK) { + Status = EFI_SUCCESS; + goto Done; + } + + Status = EFI_NOT_READY; +Done: + // + // Get response data when the cmd is executed successfully. + // + if (!EFI_ERROR (Status)) { + if (Packet->SdCmdBlk->CommandType != SdCommandTypeBc) { + for (Index = 0; Index < 4; Index++) { + Status = SdPeimHcRwMmio ( + Bar + SD_HC_RESPONSE + Index * 4, + TRUE, + sizeof (UINT32), + &Response[Index] + ); + if (EFI_ERROR (Status)) { + SdPeimHcLedOnOff (Bar, FALSE); + return Status; + } + } + CopyMem (Packet->SdStatusBlk, Response, sizeof (Response)); + } + } + + if (Status != EFI_NOT_READY) { + SdPeimHcLedOnOff (Bar, FALSE); + } + + return Status; +} + +/** + Wait for the TRB execution result. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[in] Trb The pointer to the SD_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +SdPeimWaitTrbResult ( + IN UINTN Bar, + IN SD_TRB *Trb + ) +{ + EFI_STATUS Status; + SD_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + Packet = Trb->Packet; + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register + // + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdPeimCheckTrbResult (Bar, Trb); + if (Status != EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + MicroSecondDelay (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Sends SD command to an SD card that is attached to the SD controller. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned. + + If Slot is not in a valid range for the SD controller, then EFI_INVALID_PARAMETER + is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL, + EFI_INVALID_PARAMETER is returned. + + @param[in] Slot The slot number of the Sd card to send the command to. + @param[in,out] Packet A pointer to the SD command data structure. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SD + command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and + OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet is not + supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength exceeds the + limit supported by SD card ( i.e. if the number of bytes + exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +SdPeimExecCmd ( + IN SD_PEIM_HC_SLOT *Slot, + IN OUT SD_COMMAND_PACKET *Packet + ) +{ + EFI_STATUS Status; + SD_TRB *Trb; + + if (Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SdCmdBlk == NULL) || (Packet->SdStatusBlk == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + Trb = SdPeimCreateTrb (Slot, Packet); + if (Trb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = SdPeimWaitTrbEnv (Slot->SdHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdPeimExecTrb (Slot->SdHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = SdPeimWaitTrbResult (Slot->SdHcBase, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + SdPeimFreeTrb (Trb); + + return Status; +} + +/** + Send command GO_IDLE_STATE to the device to make it go to Idle State. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The SD device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +SdPeimReset ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_GO_IDLE_STATE; + SdCmdBlk.CommandType = SdCommandTypeBc; + SdCmdBlk.ResponseType = 0; + SdCmdBlk.CommandArgument = 0; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_IF_COND to the device to inquiry the SD Memory Card interface + condition. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] SupplyVoltage The supplied voltage by the host. + @param[in] CheckPattern The check pattern to be sent to the device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimVoltageCheck ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT8 SupplyVoltage, + IN UINT8 CheckPattern + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SEND_IF_COND; + SdCmdBlk.CommandType = SdCommandTypeBcr; + SdCmdBlk.ResponseType = SdResponseTypeR7; + SdCmdBlk.CommandArgument = (SupplyVoltage << 8) | CheckPattern; + + Status = SdPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + if (SdStatusBlk.Resp0 != SdCmdBlk.CommandArgument) { + return EFI_DEVICE_ERROR; + } + } + + return Status; +} + +/** + Send command SDIO_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SDIO Simplified Spec 3 Section 3.2 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18r The boolean to show if it should switch to 1.8v. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdioSendOpCond ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT32 VoltageWindow, + IN BOOLEAN S18r + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SDIO_SEND_OP_COND; + SdCmdBlk.CommandType = SdCommandTypeBcr; + SdCmdBlk.ResponseType = SdResponseTypeR4; + + Switch = S18r ? BIT24 : 0; + + SdCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SD_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18r The boolean to show if it should switch to 1.8v. + @param[in] Xpc The boolean to show if it should provide 0.36w power control. + @param[in] Hcs The boolean to show if it support host capacity info. + @param[out] Ocr The buffer to store returned OCR register value. + + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSendOpCond ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + IN UINT32 VoltageWindow, + IN BOOLEAN S18r, + IN BOOLEAN Xpc, + IN BOOLEAN Hcs, + OUT UINT32 *Ocr + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + UINT32 MaxPower; + UINT32 HostCapacity; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_APP_CMD; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + SdCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdPeimExecCmd (Slot, &Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + SdCmdBlk.CommandIndex = SD_SEND_OP_COND; + SdCmdBlk.CommandType = SdCommandTypeBcr; + SdCmdBlk.ResponseType = SdResponseTypeR3; + + Switch = S18r ? BIT24 : 0; + MaxPower = Xpc ? BIT28 : 0; + HostCapacity = Hcs ? BIT30 : 0; + SdCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch | MaxPower | HostCapacity; + + Status = SdPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + *Ocr = SdStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the SD devices to send the + data of their CID registers. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimAllSendCid ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_ALL_SEND_CID; + SdCmdBlk.CommandType = SdCommandTypeBcr; + SdCmdBlk.ResponseType = SdResponseTypeR2; + SdCmdBlk.CommandArgument = 0; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the SD device to assign a Relative device + Address (RCA). + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSetRca ( + IN SD_PEIM_HC_SLOT *Slot, + OUT UINT16 *Rca + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR; + SdCmdBlk.CommandType = SdCommandTypeBcr; + SdCmdBlk.ResponseType = SdResponseTypeR6; + + Status = SdPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + *Rca = (UINT16)(SdStatusBlk.Resp0 >> 16); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of this + buffer as the content of this byte is meaningless even + if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimGetCsd ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + OUT SD_CSD *Csd + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SEND_CSD; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR2; + SdCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &SdStatusBlk.Resp0, sizeof (SD_CSD) - 1); + } + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the SD device to select/deselect it. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSelect ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1b; + SdCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command VOLTAGE_SWITCH to the SD device to switch the voltage of the device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimVoltageSwitch ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_VOLTAGE_SWITCH; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + SdCmdBlk.CommandArgument = 0; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SET_BUS_WIDTH to the SD device to set the bus width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[in] BusWidth The bus width to be set, it could be 1 or 4. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSetBusWidth ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 Value; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_APP_CMD; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + SdCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdPeimExecCmd (Slot, &Packet); + if (EFI_ERROR (Status)) { + return Status; + } + + SdCmdBlk.CommandIndex = SD_SET_BUS_WIDTH; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + + if (BusWidth == 1) { + Value = 0; + } else if (BusWidth == 4) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + SdCmdBlk.CommandArgument = Value & 0x3; + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SWITCH_FUNC to the SD device to check switchable function or switch card function. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] AccessMode The value for access mode group. + @param[in] CommandSystem The value for command set group. + @param[in] DriveStrength The value for drive length group. + @param[in] PowerLimit The value for power limit group. + @param[in] Mode Switch or check function. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSwitch ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT8 AccessMode, + IN UINT8 CommandSystem, + IN UINT8 DriveStrength, + IN UINT8 PowerLimit, + IN BOOLEAN Mode + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 ModeValue; + UINT8 Data[64]; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SWITCH_FUNC; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + + ModeValue = Mode ? BIT31 : 0; + SdCmdBlk.CommandArgument = (AccessMode & 0xF) | ((PowerLimit & 0xF) << 4) | \ + ((DriveStrength & 0xF) << 8) | ((DriveStrength & 0xF) << 12) | \ + ModeValue; + Packet.InDataBuffer = Data; + Packet.InTransferLength = sizeof (Data); + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed SD device to get its status register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address of addressed device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSendStatus ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SEND_STATUS; + SdCmdBlk.CommandType = SdCommandTypeAc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + SdCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = SdPeimExecCmd (Slot, &Packet); + if (!EFI_ERROR (Status)) { + *DevStatus = SdStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command READ_SINGLE_BLOCK/WRITE_SINGLE_BLOCK to the addressed SD device + to read/write the specified number of blocks. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified SD device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimRwSingleBlock ( + IN SD_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's the lowest + // transfer speed of class 2. + // + Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;; + + if (IsRead) { + Packet.InDataBuffer = Buffer; + Packet.InTransferLength = (UINT32)BufferSize; + + SdCmdBlk.CommandIndex = SD_READ_SINGLE_BLOCK; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + } else { + Packet.OutDataBuffer = Buffer; + Packet.OutTransferLength = (UINT32)BufferSize; + + SdCmdBlk.CommandIndex = SD_WRITE_SINGLE_BLOCK; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + } + + if (Slot->SectorAddressing) { + SdCmdBlk.CommandArgument = (UINT32)Lba; + } else { + SdCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, BlockSize); + } + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command READ_MULTIPLE_BLOCK/WRITE_MULTIPLE_BLOCK to the addressed SD device + to read/write the specified number of blocks. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified SD device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimRwMultiBlocks ( + IN SD_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor is because it's the lowest + // transfer speed of class 2. + // + Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;; + + if (IsRead) { + Packet.InDataBuffer = Buffer; + Packet.InTransferLength = (UINT32)BufferSize; + + SdCmdBlk.CommandIndex = SD_READ_MULTIPLE_BLOCK; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + } else { + Packet.OutDataBuffer = Buffer; + Packet.OutTransferLength = (UINT32)BufferSize; + + SdCmdBlk.CommandIndex = SD_WRITE_MULTIPLE_BLOCK; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + } + + if (Slot->SectorAddressing) { + SdCmdBlk.CommandArgument = (UINT32)Lba; + } else { + SdCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, BlockSize); + } + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the SD device for SDR104/SDR50 optimal sampling point + detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSendTuningBlk ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[64]; + + ZeroMem (&SdCmdBlk, sizeof (SdCmdBlk)); + ZeroMem (&SdStatusBlk, sizeof (SdStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdCmdBlk = &SdCmdBlk; + Packet.SdStatusBlk = &SdStatusBlk; + Packet.Timeout = SD_TIMEOUT; + + SdCmdBlk.CommandIndex = SD_SEND_TUNING_BLOCK; + SdCmdBlk.CommandType = SdCommandTypeAdtc; + SdCmdBlk.ResponseType = SdResponseTypeR1; + SdCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + Packet.InTransferLength = sizeof (TuningBlock); + + Status = SdPeimExecCmd (Slot, &Packet); + + return Status; +} + +/** + Tunning the sampling point of SDR104 or SDR50 bus speed mode. + + Command SD_SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes the + tuning procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and SD Host Controller + Simplified Spec 3.0 Figure 2-29 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimTuningClock ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl2; + UINT8 Retry; + + // + // Notify the host that the sampling clock tuning procedure starts. + // + HostCtrl2 = BIT6; + Status = SdPeimHcOrMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Ask the device to send a sequence of tuning blocks till the tuning procedure is done. + // + Retry = 0; + do { + Status = SdPeimSendTuningBlk (Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + break; + } + } while (++Retry < 40); + + if (Retry == 40) { + Status = EFI_TIMEOUT; + } + return Status; +} + +/** + Switch the bus width to specified width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 3-7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSwitchBusWidth ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT32 DevStatus; + + Status = SdPeimSetBusWidth (Slot, Rca, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimSendStatus (Slot, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus >> 16) != 0) { + return EFI_DEVICE_ERROR; + } + + Status = SdPeimHcSetBusWidth (Slot->SdHcBase, BusWidth); + + return Status; +} + +/** + Switch the high speed timing according to request. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 2-29 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Rca The relative device address to be assigned. + @param[in] S18a The boolean to show if it's a UHS-I SD card. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSetBusMode ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT16 Rca, + IN BOOLEAN S18a + ) +{ + EFI_STATUS Status; + SD_HC_SLOT_CAP Capability; + UINT32 ClockFreq; + UINT8 BusWidth; + UINT8 AccessMode; + UINT8 HostCtrl1; + UINT8 HostCtrl2; + + Status = SdPeimGetCsd (Slot, Rca, &Slot->Csd); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimGetCsd fails with %r\n", Status)); + return Status; + } + + Status = SdPeimHcGetCapability (Slot->SdHcBase, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimSelect (Slot, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimSelect fails with %r\n", Status)); + return Status; + } + + BusWidth = 4; + Status = SdPeimSwitchBusWidth (Slot, Rca, BusWidth); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimSwitchBusWidth fails with %r\n", Status)); + return Status; + } + + // + // Calculate supported bus speed/bus width/clock frequency. + // + ClockFreq = 0; + if (S18a && (Capability.Sdr104 != 0)) { + ClockFreq = 208; + AccessMode = 3; + } else if (S18a && (Capability.Sdr50 != 0)) { + ClockFreq = 100; + AccessMode = 2; + } else if (S18a && (Capability.Ddr50 != 0)) { + ClockFreq = 50; + AccessMode = 4; + } else { + ClockFreq = 50; + AccessMode = 1; + } + + DEBUG ((EFI_D_INFO, "AccessMode %d ClockFreq %d BusWidth %d\n", AccessMode, ClockFreq, BusWidth)); + + Status = SdPeimSwitch (Slot, AccessMode, 0, 0, 0, TRUE); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimSwitch fails with %r\n", Status)); + return Status; + } + + // + // Set to Hight Speed timing + // + if (AccessMode == 1) { + HostCtrl1 = BIT2; + Status = SdPeimHcOrMmio (Slot->SdHcBase + SD_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } + } + + HostCtrl2 = (UINT8)~0x7; + Status = SdPeimHcAndMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + HostCtrl2 = AccessMode; + Status = SdPeimHcOrMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimHcClockSupply (Slot->SdHcBase, ClockFreq * 1000); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimHcClockSupply %r\n", Status)); + return Status; + } + + if ((AccessMode == 3) || ((AccessMode == 2) && (Capability.TuningSDR50 != 0))) { + Status = SdPeimTuningClock (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimTuningClock fails with %r\n", Status)); + return Status; + } + } + + DEBUG ((EFI_D_INFO, "SdPeimSetBusMode: SdPeimSetBusMode %r\n", Status)); + + return Status; +} + +/** + Execute SD device identification procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 3.6 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS There is a SD card. + @retval Others There is not a SD card. + +**/ +EFI_STATUS +SdPeimIdentification ( + IN SD_PEIM_HC_SLOT *Slot + ) +{ + EFI_STATUS Status; + UINT32 Ocr; + UINT16 Rca; + BOOLEAN Xpc; + BOOLEAN S18r; + UINT64 MaxCurrent; + UINT64 Current; + UINT16 ControllerVer; + UINT8 PowerCtrl; + UINT32 PresentState; + UINT8 HostCtrl2; + SD_HC_SLOT_CAP Capability; + + // + // 1. Send Cmd0 to the device + // + Status = SdPeimReset (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing Cmd0 fails with %r\n", Status)); + return Status; + } + // + // 2. Send Cmd8 to the device + // + Status = SdPeimVoltageCheck (Slot, 0x1, 0xFF); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing Cmd8 fails with %r\n", Status)); + return Status; + } + // + // 3. Send SDIO Cmd5 to the device to the SDIO device OCR register. + // + Status = SdioSendOpCond (Slot, 0, FALSE); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Found SDIO device, ignore it as we don't support\n")); + return EFI_DEVICE_ERROR; + } + // + // 4. Send Acmd41 with voltage window 0 to the device + // + Status = SdPeimSendOpCond (Slot, 0, 0, FALSE, FALSE, FALSE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing SdPeimSendOpCond fails with %r\n", Status)); + return EFI_DEVICE_ERROR; + } + + Status = SdPeimHcGetCapability (Slot->SdHcBase, &Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_MAX_CURRENT_CAP, TRUE, sizeof (Current), &Current); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Capability.Voltage33 != 0) { + // + // Support 3.3V + // + MaxCurrent = ((UINT32)Current & 0xFF) * 4; + } else if (Capability.Voltage30 != 0) { + // + // Support 3.0V + // + MaxCurrent = (((UINT32)Current >> 8) & 0xFF) * 4; + } else if (Capability.Voltage18 != 0) { + // + // Support 1.8V + // + MaxCurrent = (((UINT32)Current >> 16) & 0xFF) * 4; + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + if (MaxCurrent >= 150) { + Xpc = TRUE; + } else { + Xpc = FALSE; + } + + Status = SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((ControllerVer & 0xFF) == 2) { + S18r = TRUE; + } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + S18r = FALSE; + } else { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + // + // 5. Repeatly send Acmd41 with supply voltage window to the device. + // Note here we only support the cards complied with SD physical + // layer simplified spec version 2.0 and version 3.0 and above. + // + do { + Status = SdPeimSendOpCond (Slot, 0, Ocr, S18r, Xpc, TRUE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: SdPeimSendOpCond fails with %r Ocr %x, S18r %x, Xpc %x\n", Status, Ocr, S18r, Xpc)); + return EFI_DEVICE_ERROR; + } + } while ((Ocr & BIT31) == 0); + + // + // 6. If the S18a bit is set and the Host Controller supports 1.8V signaling + // (One of support bits is set to 1: SDR50, SDR104 or DDR50 in the + // Capabilities register), switch its voltage to 1.8V. + // + if ((Capability.Sdr50 != 0 || + Capability.Sdr104 != 0 || + Capability.Ddr50 != 0) && + ((Ocr & BIT24) != 0)) { + Status = SdPeimVoltageSwitch (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing SdPeimVoltageSwitch fails with %r\n", Status)); + Status = EFI_DEVICE_ERROR; + goto Error; + } else { + Status = SdPeimHcStopClock (Slot->SdHcBase); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + goto Error; + } + + SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (((PresentState >> 20) & 0xF) != 0) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: SwitchVoltage fails with PresentState = 0x%x\n", PresentState)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + HostCtrl2 = BIT3; + SdPeimHcOrMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + + MicroSecondDelay (5000); + + SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2); + if ((HostCtrl2 & BIT3) == 0) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: SwitchVoltage fails with HostCtrl2 = 0x%x\n", HostCtrl2)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + + SdPeimHcInitClockFreq (Slot->SdHcBase); + + MicroSecondDelay (1); + + SdPeimHcRwMmio (Slot->SdHcBase + SD_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (((PresentState >> 20) & 0xF) != 0xF) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: SwitchVoltage fails with PresentState = 0x%x, It should be 0xF\n", PresentState)); + Status = EFI_DEVICE_ERROR; + goto Error; + } + } + DEBUG ((EFI_D_INFO, "SdPeimIdentification: Switch to 1.8v signal voltage success\n")); + } + + Status = SdPeimAllSendCid (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing SdPeimAllSendCid fails with %r\n", Status)); + return Status; + } + + Status = SdPeimSetRca (Slot, &Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: Executing SdPeimSetRca fails with %r\n", Status)); + return Status; + } + // + // Enter Data Tranfer Mode. + // + DEBUG ((EFI_D_INFO, "Found a SD device at slot [%d]\n", Slot)); + + Status = SdPeimSetBusMode (Slot, Rca, ((Ocr & BIT24) != 0)); + + return Status; + +Error: + // + // Set SD Bus Power = 0 + // + PowerCtrl = (UINT8)~BIT0; + Status = SdPeimHcAndMmio (Slot->SdHcBase + SD_HC_POWER_CTRL, sizeof (PowerCtrl), &PowerCtrl); + return EFI_DEVICE_ERROR; +} diff --git a/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h new file mode 100644 index 0000000000..2f443045b7 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h @@ -0,0 +1,354 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_HCI_H_ +#define _SD_HCI_H_ + +// +// SD Host Controller MMIO Register Offset +// +#define SD_HC_SDMA_ADDR 0x00 +#define SD_HC_ARG2 0x00 +#define SD_HC_BLK_SIZE 0x04 +#define SD_HC_BLK_COUNT 0x06 +#define SD_HC_ARG1 0x08 +#define SD_HC_TRANS_MOD 0x0C +#define SD_HC_COMMAND 0x0E +#define SD_HC_RESPONSE 0x10 +#define SD_HC_BUF_DAT_PORT 0x20 +#define SD_HC_PRESENT_STATE 0x24 +#define SD_HC_HOST_CTRL1 0x28 +#define SD_HC_POWER_CTRL 0x29 +#define SD_HC_BLK_GAP_CTRL 0x2A +#define SD_HC_WAKEUP_CTRL 0x2B +#define SD_HC_CLOCK_CTRL 0x2C +#define SD_HC_TIMEOUT_CTRL 0x2E +#define SD_HC_SW_RST 0x2F +#define SD_HC_NOR_INT_STS 0x30 +#define SD_HC_ERR_INT_STS 0x32 +#define SD_HC_NOR_INT_STS_EN 0x34 +#define SD_HC_ERR_INT_STS_EN 0x36 +#define SD_HC_NOR_INT_SIG_EN 0x38 +#define SD_HC_ERR_INT_SIG_EN 0x3A +#define SD_HC_AUTO_CMD_ERR_STS 0x3C +#define SD_HC_HOST_CTRL2 0x3E +#define SD_HC_CAP 0x40 +#define SD_HC_MAX_CURRENT_CAP 0x48 +#define SD_HC_FORCE_EVT_AUTO_CMD 0x50 +#define SD_HC_FORCE_EVT_ERR_INT 0x52 +#define SD_HC_ADMA_ERR_STS 0x54 +#define SD_HC_ADMA_SYS_ADDR 0x58 +#define SD_HC_PRESET_VAL 0x60 +#define SD_HC_SHARED_BUS_CTRL 0xE0 +#define SD_HC_SLOT_INT_STS 0xFC +#define SD_HC_CTRL_VER 0xFE + +// +// The transfer modes supported by SD Host Controller +// Simplified Spec 3.0 Table 1-2 +// +typedef enum { + SdNoData, + SdPioMode, + SdSdmaMode, + SdAdmaMode +} SD_HC_TRANSFER_MODE; + +// +// The maximum data length of each descriptor line +// +#define ADMA_MAX_DATA_PER_LINE 0x10000 +#define SD_SDMA_BOUNDARY 512 * 1024 +#define SD_SDMA_ROUND_UP(x, n) (((x) + n) & ~(n - 1)) + +typedef enum { + SdCommandTypeBc, // Broadcast commands, no response + SdCommandTypeBcr, // Broadcast commands with response + SdCommandTypeAc, // Addressed(point-to-point) commands + SdCommandTypeAdtc // Addressed(point-to-point) data transfer commands +} SD_COMMAND_TYPE; + +typedef enum { + SdResponseTypeR1, + SdResponseTypeR1b, + SdResponseTypeR2, + SdResponseTypeR3, + SdResponseTypeR4, + SdResponseTypeR5, + SdResponseTypeR5b, + SdResponseTypeR6, + SdResponseTypeR7 +} SD_RESPONSE_TYPE; + +typedef struct _SD_COMMAND_BLOCK { + UINT16 CommandIndex; + UINT32 CommandArgument; + UINT32 CommandType; // One of the SD_COMMAND_TYPE values + UINT32 ResponseType; // One of the SD_RESPONSE_TYPE values +} SD_COMMAND_BLOCK; + +typedef struct _SD_STATUS_BLOCK { + UINT32 Resp0; + UINT32 Resp1; + UINT32 Resp2; + UINT32 Resp3; +} SD_STATUS_BLOCK; + +typedef struct _SD_COMMAND_PACKET { + UINT64 Timeout; + SD_COMMAND_BLOCK *SdCmdBlk; + SD_STATUS_BLOCK *SdStatusBlk; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT32 InTransferLength; + UINT32 OutTransferLength; +} SD_COMMAND_PACKET; + +#pragma pack(1) + +typedef struct { + UINT32 Valid:1; + UINT32 End:1; + UINT32 Int:1; + UINT32 Reserved:1; + UINT32 Act:2; + UINT32 Reserved1:10; + UINT32 Length:16; + UINT32 Address; +} SD_HC_ADMA_DESC_LINE; + +typedef struct { + UINT32 TimeoutFreq:6; // bit 0:5 + UINT32 Reserved:1; // bit 6 + UINT32 TimeoutUnit:1; // bit 7 + UINT32 BaseClkFreq:8; // bit 8:15 + UINT32 MaxBlkLen:2; // bit 16:17 + UINT32 BusWidth8:1; // bit 18 + UINT32 Adma2:1; // bit 19 + UINT32 Reserved2:1; // bit 20 + UINT32 HighSpeed:1; // bit 21 + UINT32 Sdma:1; // bit 22 + UINT32 SuspRes:1; // bit 23 + UINT32 Voltage33:1; // bit 24 + UINT32 Voltage30:1; // bit 25 + UINT32 Voltage18:1; // bit 26 + UINT32 Reserved3:1; // bit 27 + UINT32 SysBus64:1; // bit 28 + UINT32 AsyncInt:1; // bit 29 + UINT32 SlotType:2; // bit 30:31 + UINT32 Sdr50:1; // bit 32 + UINT32 Sdr104:1; // bit 33 + UINT32 Ddr50:1; // bit 34 + UINT32 Reserved4:1; // bit 35 + UINT32 DriverTypeA:1; // bit 36 + UINT32 DriverTypeC:1; // bit 37 + UINT32 DriverTypeD:1; // bit 38 + UINT32 DriverType4:1; // bit 39 + UINT32 TimerCount:4; // bit 40:43 + UINT32 Reserved5:1; // bit 44 + UINT32 TuningSDR50:1; // bit 45 + UINT32 RetuningMod:2; // bit 46:47 + UINT32 ClkMultiplier:8; // bit 48:55 + UINT32 Reserved6:7; // bit 56:62 + UINT32 Hs400:1; // bit 63 +} SD_HC_SLOT_CAP; + +#pragma pack() + +/** + Software reset the specified SD host controller and enable all interrupts. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +SdPeimHcReset ( + IN UINTN Bar + ); + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimHcEnableInterrupt ( + IN UINTN Bar + ); + +/** + Get the capability data from the specified slot. + + @param[in] Bar The mmio base address of the slot to be accessed. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimHcGetCapability ( + IN UINTN Bar, + OUT SD_HC_SLOT_CAP *Capability + ); + +/** + Detect whether there is a SD card attached at the specified SD host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS There is a SD card attached. + @retval EFI_NO_MEDIA There is not a SD card attached. + @retval Others The detection fails. + +**/ +EFI_STATUS +SdPeimHcCardDetect ( + IN UINTN Bar + ); + +/** + Initial SD host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] Bar The mmio base address of the slot to be accessed. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +SdPeimHcInitHost ( + IN UINTN Bar + ); + +/** + Send command SWITCH_FUNC to the SD device to check switchable function or switch card function. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] AccessMode The value for access mode group. + @param[in] CommandSystem The value for command set group. + @param[in] DriveStrength The value for drive length group. + @param[in] PowerLimit The value for power limit group. + @param[in] Mode Switch or check function. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimSwitch ( + IN SD_PEIM_HC_SLOT *Slot, + IN UINT8 AccessMode, + IN UINT8 CommandSystem, + IN UINT8 DriveStrength, + IN UINT8 PowerLimit, + IN BOOLEAN Mode + ); + +/** + Send command READ_SINGLE_BLOCK/WRITE_SINGLE_BLOCK to the addressed SD device + to read/write the specified number of blocks. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified SD device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimRwSingleBlock ( + IN SD_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ); + +/** + Send command READ_MULTIPLE_BLOCK/WRITE_MULTIPLE_BLOCK to the addressed SD device + to read/write the specified number of blocks. + + Refer to SD Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] Slot The slot number of the Sd card to send the command to. + @param[in] Lba The logical block address of starting access. + @param[in] BlockSize The block size of specified SD device partition. + @param[in] Buffer The pointer to the transfer buffer. + @param[in] BufferSize The size of transfer buffer. + @param[in] IsRead Boolean to show the operation direction. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdPeimRwMultiBlocks ( + IN SD_PEIM_HC_SLOT *Slot, + IN EFI_LBA Lba, + IN UINT32 BlockSize, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead + ); + +/** + Execute SD device identification procedure. + + Refer to SD Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Slot The slot number of the Sd card to send the command to. + + @retval EFI_SUCCESS There is a SD card. + @retval Others There is not a SD card. + +**/ +EFI_STATUS +SdPeimIdentification ( + IN SD_PEIM_HC_SLOT *Slot + ); + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the SD_TRB instance. + +**/ +VOID +SdPeimFreeTrb ( + IN SD_TRB *Trb + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c b/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c new file mode 100644 index 0000000000..246378381f --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c @@ -0,0 +1,240 @@ +/** @file + UEFI Component Name(2) protocol implementation for SdDxe driver. + + Copyright (c) 2015, Intel Corporation. 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 "SdDxe.h" + +// +// Driver name table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSdDxeDriverNameTable[] = { + { "eng;en", L"Edkii Sd Memory Card Device Driver" }, + { NULL , NULL } +}; + +// +// Controller name table +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mSdDxeControllerNameTable[] = { + { "eng;en", L"Edkii Sd Host Controller" }, + { NULL , NULL } +}; + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gSdDxeComponentName = { + SdDxeComponentNameGetDriverName, + SdDxeComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gSdDxeComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) SdDxeComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) SdDxeComponentNameGetControllerName, + "en" +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mSdDxeDriverNameTable, + DriverName, + (BOOLEAN)(This == &gSdDxeComponentName) + ); + +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + SD_DEVICE *Device; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gSdDxeDriverBinding.DriverBindingHandle, + &gEfiSdMmcPassThruProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ControllerNameTable = mSdDxeControllerNameTable; + if (ChildHandle != NULL) { + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiSdMmcPassThruProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Get the child context + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + gSdDxeDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Device = SD_DEVICE_DATA_FROM_BLKIO (BlockIo); + ControllerNameTable = Device->ControllerNameTable; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gSdDxeComponentName) + ); +} + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c b/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c new file mode 100644 index 0000000000..341d7885c2 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c @@ -0,0 +1,971 @@ +/** @file + The helper functions for BlockIo and BlockIo2 protocol. + + Copyright (c) 2015, Intel Corporation. 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 "SdDxe.h" + +/** + Nonblocking I/O callback funtion when the event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +AsyncIoCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + SD_REQUEST *Request; + + gBS->CloseEvent (Event); + + Request = (SD_REQUEST *) Context; + + DEBUG_CODE_BEGIN (); + DEBUG ((EFI_D_INFO, "Sd Async Request: CmdIndex[%d] Arg[%08x] %r\n", + Request->SdMmcCmdBlk.CommandIndex, Request->SdMmcCmdBlk.CommandArgument, + Request->Packet.TransactionStatus)); + DEBUG_CODE_END (); + + if (EFI_ERROR (Request->Packet.TransactionStatus)) { + Request->Token->TransactionStatus = Request->Packet.TransactionStatus; + } + + RemoveEntryList (&Request->Link); + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); +} + +/** + Send command SET_RELATIVE_ADDRESS to the device to set the device address. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[out] Rca The relative device address to assign. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSetRca ( + IN SD_DEVICE *Device, + OUT UINT16 *Rca + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Set RCA succeeds with Resp0 = 0x%x\n", SdMmcStatusBlk.Resp0)); + *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16); + } + + return Status; +} + +/** + Send command SELECT to the device to select/deselect the device. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSelect ( + IN SD_DEVICE *Device, + IN UINT16 Rca + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the device to get device status. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] DevStatus The buffer to store the device status. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSendStatus ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + if (!EFI_ERROR (Status)) { + CopyMem (DevStatus, &SdMmcStatusBlk.Resp0, sizeof (UINT32)); + } + return Status; +} + +/** + Send command SEND_CSD to the device to get the CSD register data. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Csd The buffer to store the SD_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdGetCsd ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT SD_CSD *Csd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + ZeroMem (Csd, sizeof (SD_CSD)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1); + } + + return Status; +} + +/** + Send command SEND_CID to the device to get the CID register data. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Cid The buffer to store the SD_CID register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdGetCid ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT SD_CID *Cid + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + PassThru = Device->Private->PassThru; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + ZeroMem (Cid, sizeof (SD_CID)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = SD_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = PassThru->PassThru (PassThru, Device->Slot, &Packet, NULL); + + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + CopyMem (((UINT8*)Cid) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CID) - 1); + } + + return Status; +} + +/** + Read/write single block through sync or async I/O request. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Lba The starting logical block address to be read/written. + The caller is responsible for reading/writing to only + legitimate locations. + @param[in] Buffer A pointer to the destination/source buffer for the data. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] IsRead Indicates it is a read or write operation. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdRwSingleBlock ( + IN SD_DEVICE *Device, + IN EFI_LBA Lba, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_REQUEST *RwSingleBlkReq; + EFI_TPL OldTpl; + + RwSingleBlkReq = NULL; + PassThru = Device->Private->PassThru; + + RwSingleBlkReq = AllocateZeroPool (sizeof (SD_REQUEST)); + if (RwSingleBlkReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + RwSingleBlkReq->Signature = SD_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Device->Queue, &RwSingleBlkReq->Link); + gBS->RestoreTPL (OldTpl); + RwSingleBlkReq->Packet.SdMmcCmdBlk = &RwSingleBlkReq->SdMmcCmdBlk; + RwSingleBlkReq->Packet.SdMmcStatusBlk = &RwSingleBlkReq->SdMmcStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor as it's the lowest transfer speed + // above class 2. + // Refer to SD Physical Layer Simplified spec section 3.4 for details. + // + RwSingleBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000; + + if (IsRead) { + RwSingleBlkReq->Packet.InDataBuffer = Buffer; + RwSingleBlkReq->Packet.InTransferLength = (UINT32)BufferSize; + + RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_SINGLE_BLOCK; + RwSingleBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } else { + RwSingleBlkReq->Packet.OutDataBuffer = Buffer; + RwSingleBlkReq->Packet.OutTransferLength = (UINT32)BufferSize; + + RwSingleBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_SINGLE_BLOCK; + RwSingleBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwSingleBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } + + if (Device->SectorAddressing) { + RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba; + } else { + RwSingleBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize); + } + + RwSingleBlkReq->IsEnd = IsEnd; + RwSingleBlkReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + RwSingleBlkReq, + &RwSingleBlkReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + RwSingleBlkReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &RwSingleBlkReq->Packet, RwSingleBlkReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (RwSingleBlkReq != NULL)) { + RemoveEntryList (&RwSingleBlkReq->Link); + if (RwSingleBlkReq->Event != NULL) { + gBS->CloseEvent (RwSingleBlkReq->Event); + } + FreePool (RwSingleBlkReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (RwSingleBlkReq != NULL) { + RemoveEntryList (&RwSingleBlkReq->Link); + FreePool (RwSingleBlkReq); + } + } + + return Status; +} + +/** + Read/write multiple blocks through sync or async I/O request. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Lba The starting logical block address to be read/written. + The caller is responsible for reading/writing to only + legitimate locations. + @param[in] Buffer A pointer to the destination/source buffer for the data. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] IsRead Indicates it is a read or write operation. + @param[in] Token A pointer to the token associated with the transaction. + @param[in] IsEnd A boolean to show whether it's the last cmd in a series of cmds. + This parameter is only meaningful in async I/O request. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdRwMultiBlocks ( + IN SD_DEVICE *Device, + IN EFI_LBA Lba, + IN VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + SD_REQUEST *RwMultiBlkReq; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_TPL OldTpl; + + RwMultiBlkReq = NULL; + + PassThru = Device->Private->PassThru; + + RwMultiBlkReq = AllocateZeroPool (sizeof (SD_REQUEST)); + if (RwMultiBlkReq == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + RwMultiBlkReq->Signature = SD_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + InsertTailList (&Device->Queue, &RwMultiBlkReq->Link); + gBS->RestoreTPL (OldTpl); + RwMultiBlkReq->Packet.SdMmcCmdBlk = &RwMultiBlkReq->SdMmcCmdBlk; + RwMultiBlkReq->Packet.SdMmcStatusBlk = &RwMultiBlkReq->SdMmcStatusBlk; + // + // Calculate timeout value through the below formula. + // Timeout = (transfer size) / (2MB/s). + // Taking 2MB/s as divisor as it's the lowest transfer speed + // above class 2. + // Refer to SD Physical Layer Simplified spec section 3.4 for details. + // + RwMultiBlkReq->Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000; + + if (IsRead) { + RwMultiBlkReq->Packet.InDataBuffer = Buffer; + RwMultiBlkReq->Packet.InTransferLength = (UINT32)BufferSize; + + RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_READ_MULTIPLE_BLOCK; + RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } else { + RwMultiBlkReq->Packet.OutDataBuffer = Buffer; + RwMultiBlkReq->Packet.OutTransferLength = (UINT32)BufferSize; + + RwMultiBlkReq->SdMmcCmdBlk.CommandIndex = SD_WRITE_MULTIPLE_BLOCK; + RwMultiBlkReq->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + RwMultiBlkReq->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + } + + if (Device->SectorAddressing) { + RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)Lba; + } else { + RwMultiBlkReq->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, Device->BlockMedia.BlockSize); + } + + RwMultiBlkReq->IsEnd = IsEnd; + RwMultiBlkReq->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + AsyncIoCallback, + RwMultiBlkReq, + &RwMultiBlkReq->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + RwMultiBlkReq->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &RwMultiBlkReq->Packet, RwMultiBlkReq->Event); + +Error: + if ((Token != NULL) && (Token->Event != NULL)) { + // + // For asynchronous operation, only free request and event in error case. + // The request and event will be freed in asynchronous callback for success case. + // + if (EFI_ERROR (Status) && (RwMultiBlkReq != NULL)) { + RemoveEntryList (&RwMultiBlkReq->Link); + if (RwMultiBlkReq->Event != NULL) { + gBS->CloseEvent (RwMultiBlkReq->Event); + } + FreePool (RwMultiBlkReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (RwMultiBlkReq != NULL) { + RemoveEntryList (&RwMultiBlkReq->Link); + FreePool (RwMultiBlkReq); + } + } + + return Status; +} + +/** + This function transfers data from/to the sd memory card device. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] MediaId The media ID that the read/write request is for. + @param[in] Lba The starting logical block address to be read/written. + The caller is responsible for reading/writing to only + legitimate locations. + @param[in, out] Buffer A pointer to the destination/source buffer for the data. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] IsRead Indicates it is a read or write operation. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS The data was read/written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be read/written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read/write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read/write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +SdReadWrite ( + IN SD_DEVICE *Device, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT VOID *Buffer, + IN UINTN BufferSize, + IN BOOLEAN IsRead, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN BlockNum; + UINTN IoAlign; + UINTN Remaining; + UINT32 MaxBlock; + BOOLEAN LastRw; + + Status = EFI_SUCCESS; + Media = &Device->BlockMedia; + LastRw = FALSE; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (!IsRead && Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + // + // Check parameters. + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + return EFI_SUCCESS; + } + + BlockSize = Media->BlockSize; + if ((BufferSize % BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + BlockNum = BufferSize / BlockSize; + if ((Lba + BlockNum - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + IoAlign = Media->IoAlign; + if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + } + + // + // Start to execute data transfer. The max block number in single cmd is 65535 blocks. + // + Remaining = BlockNum; + MaxBlock = 0xFFFF; + + while (Remaining > 0) { + if (Remaining <= MaxBlock) { + BlockNum = Remaining; + LastRw = TRUE; + } else { + BlockNum = MaxBlock; + } + + BufferSize = BlockNum * BlockSize; + if (BlockNum == 1) { + Status = SdRwSingleBlock (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw); + } else { + Status = SdRwMultiBlocks (Device, Lba, Buffer, BufferSize, IsRead, Token, LastRw); + } + if (EFI_ERROR (Status)) { + return Status; + } + DEBUG ((EFI_D_INFO, "Sd%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", IsRead ? "Read" : "Write", Lba, BlockNum, Token->Event, Status)); + + Lba += BlockNum; + Buffer = (UINT8*)Buffer + BufferSize; + Remaining -= BlockNum; + } + + return Status; +} + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +SdReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + + Device = SD_DEVICE_DATA_FROM_BLKIO (This); + + PassThru = Device->Private->PassThru; + Status = PassThru->ResetDevice (PassThru, Device->Slot); + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + + Device = SD_DEVICE_DATA_FROM_BLKIO (This); + + Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, NULL); + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + + Device = SD_DEVICE_DATA_FROM_BLKIO (This); + + Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, NULL); + return Status; +} + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +SdFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + // + // return directly + // + return EFI_SUCCESS; +} + +/** + Reset the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +SdResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + SD_DEVICE *Device; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_REQUEST *Request; + EFI_TPL OldTpl; + + Device = SD_DEVICE_DATA_FROM_BLKIO2 (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + for (Link = GetFirstNode (&Device->Queue); + !IsNull (&Device->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Device->Queue, Link); + RemoveEntryList (Link); + + Request = SD_REQUEST_FROM_LINK (Link); + + gBS->CloseEvent (Request->Event); + Request->Token->TransactionStatus = EFI_ABORTED; + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); + } + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Event is not NULL. + The data was read correctly from the device if + the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +SdReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + + Device = SD_DEVICE_DATA_FROM_BLKIO2 (This); + + Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, TRUE, Token); + return Status; +} + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + + Device = SD_DEVICE_DATA_FROM_BLKIO2 (This); + + Status = SdReadWrite (Device, MediaId, Lba, Buffer, BufferSize, FALSE, Token); + return Status; +} + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +SdFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ) +{ + // + // Signal event and return directly. + // + if (Token != NULL && Token->Event != NULL) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h b/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h new file mode 100644 index 0000000000..36e20dee72 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h @@ -0,0 +1,221 @@ +/** @file + Header file for SdDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_BLOCK_IO_H_ +#define _SD_BLOCK_IO_H_ + +/** + Reset the Block Device. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +SdReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId Id of the media, changes every time the media is replaced. + @param Lba The starting Logical Block Address to read from + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while performing the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. The caller is + responsible for writing to only legitimate locations. + @param BufferSize Size of Buffer, must be a multiple of device block size. + @param Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +SdFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +/** + Reset the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in] ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could + not be reset. + +**/ +EFI_STATUS +EFIAPI +SdResetEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Read BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId Id of the media, changes every time the media is replaced. + @param[in] Lba The starting Logical Block Address to read from. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[out] Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The read request was queued if Event is not NULL. + The data was read correctly from the device if + the Event is NULL. + @retval EFI_DEVICE_ERROR The device reported an error while performing + the read. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the + intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + +**/ +EFI_STATUS +EFIAPI +SdReadBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Write BufferSize bytes from Lba into Buffer. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the write request is for. + @param[in] Lba The starting logical block address to be written. The + caller is responsible for writing to only legitimate + locations. + @param[in, out] Token A pointer to the token associated with the transaction. + @param[in] BufferSize Size of Buffer, must be a multiple of device block size. + @param[in] Buffer A pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data was written correctly to the device. + @retval EFI_WRITE_PROTECTED The device can not be written to. + @retval EFI_DEVICE_ERROR The device reported an error while performing the write. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device. + @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +SdWriteBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_BLOCK_IO2_TOKEN *Token, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flush the Block Device. + + @param[in] This Indicates a pointer to the calling context. + @param[in, out] Token A pointer to the token associated with the transaction. + + @retval EFI_SUCCESS All outstanding data was written to the device + @retval EFI_DEVICE_ERROR The device reported an error while writing back the data + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +SdFlushBlocksEx ( + IN EFI_BLOCK_IO2_PROTOCOL *This, + IN OUT EFI_BLOCK_IO2_TOKEN *Token + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c new file mode 100644 index 0000000000..7ed80b6bd0 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c @@ -0,0 +1,888 @@ +/** @file + The SdDxe driver is used to manage the SD memory card device. + + It produces BlockIo and BlockIo2 protocols to allow upper layer + access the SD memory card device. + + Copyright (c) 2015 - 2016, Intel Corporation. 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 "SdDxe.h" + +// +// SdDxe Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gSdDxeDriverBinding = { + SdDxeDriverBindingSupported, + SdDxeDriverBindingStart, + SdDxeDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for SD_DEVICE data structure. +// +SD_DEVICE mSdDeviceTemplate = { + SD_DEVICE_SIGNATURE, // Signature + NULL, // Handle + NULL, // DevicePath + 0xFF, // Slot + FALSE, // SectorAddressing + { // BlockIo + EFI_BLOCK_IO_PROTOCOL_REVISION, + NULL, + SdReset, + SdReadBlocks, + SdWriteBlocks, + SdFlushBlocks + }, + { // BlockIo2 + NULL, + SdResetEx, + SdReadBlocksEx, + SdWriteBlocksEx, + SdFlushBlocksEx + }, + { // BlockMedia + 0, // MediaId + FALSE, // RemovableMedia + TRUE, // MediaPresent + FALSE, // LogicPartition + FALSE, // ReadOnly + FALSE, // WritingCache + 0x200, // BlockSize + 0, // IoAlign + 0 // LastBlock + }, + { // Queue + NULL, + NULL + }, + { // Csd + 0, + }, + { // Cid + 0, + }, + NULL, // ControllerNameTable + { // ModelName + 0, + }, + NULL // Private +}; + +/** + Decode and print SD CSD Register content. + + @param[in] Csd Pointer to SD_CSD data structure. + + @retval EFI_SUCCESS The function completed successfully +**/ +EFI_STATUS +DumpCsd ( + IN SD_CSD *Csd + ) +{ + SD_CSD2 *Csd2; + + DEBUG((DEBUG_INFO, "== Dump Sd Csd Register==\n")); + DEBUG((DEBUG_INFO, " CSD structure 0x%x\n", Csd->CsdStructure)); + DEBUG((DEBUG_INFO, " Data read access-time 1 0x%x\n", Csd->Taac)); + DEBUG((DEBUG_INFO, " Data read access-time 2 0x%x\n", Csd->Nsac)); + DEBUG((DEBUG_INFO, " Max. bus clock frequency 0x%x\n", Csd->TranSpeed)); + DEBUG((DEBUG_INFO, " Device command classes 0x%x\n", Csd->Ccc)); + DEBUG((DEBUG_INFO, " Max. read data block length 0x%x\n", Csd->ReadBlLen)); + DEBUG((DEBUG_INFO, " Partial blocks for read allowed 0x%x\n", Csd->ReadBlPartial)); + DEBUG((DEBUG_INFO, " Write block misalignment 0x%x\n", Csd->WriteBlkMisalign)); + DEBUG((DEBUG_INFO, " Read block misalignment 0x%x\n", Csd->ReadBlkMisalign)); + DEBUG((DEBUG_INFO, " DSR implemented 0x%x\n", Csd->DsrImp)); + if (Csd->CsdStructure == 0) { + DEBUG((DEBUG_INFO, " Device size 0x%x\n", Csd->CSizeLow | (Csd->CSizeHigh << 2))); + DEBUG((DEBUG_INFO, " Max. read current @ VDD min 0x%x\n", Csd->VddRCurrMin)); + DEBUG((DEBUG_INFO, " Max. read current @ VDD max 0x%x\n", Csd->VddRCurrMax)); + DEBUG((DEBUG_INFO, " Max. write current @ VDD min 0x%x\n", Csd->VddWCurrMin)); + DEBUG((DEBUG_INFO, " Max. write current @ VDD max 0x%x\n", Csd->VddWCurrMax)); + } else { + Csd2 = (SD_CSD2*)(VOID*)Csd; + DEBUG((DEBUG_INFO, " Device size 0x%x\n", Csd2->CSizeLow | (Csd->CSizeHigh << 16))); + } + DEBUG((DEBUG_INFO, " Erase sector size 0x%x\n", Csd->SectorSize)); + DEBUG((DEBUG_INFO, " Erase single block enable 0x%x\n", Csd->EraseBlkEn)); + DEBUG((DEBUG_INFO, " Write protect group size 0x%x\n", Csd->WpGrpSize)); + DEBUG((DEBUG_INFO, " Write protect group enable 0x%x\n", Csd->WpGrpEnable)); + DEBUG((DEBUG_INFO, " Write speed factor 0x%x\n", Csd->R2WFactor)); + DEBUG((DEBUG_INFO, " Max. write data block length 0x%x\n", Csd->WriteBlLen)); + DEBUG((DEBUG_INFO, " Partial blocks for write allowed 0x%x\n", Csd->WriteBlPartial)); + DEBUG((DEBUG_INFO, " File format group 0x%x\n", Csd->FileFormatGrp)); + DEBUG((DEBUG_INFO, " Copy flag (OTP) 0x%x\n", Csd->Copy)); + DEBUG((DEBUG_INFO, " Permanent write protection 0x%x\n", Csd->PermWriteProtect)); + DEBUG((DEBUG_INFO, " Temporary write protection 0x%x\n", Csd->TmpWriteProtect)); + DEBUG((DEBUG_INFO, " File format 0x%x\n", Csd->FileFormat)); + + return EFI_SUCCESS; +} + +/** + Get SD device model name. + + @param[in, out] Device The pointer to the SD_DEVICE data structure. + @param[in] Cid Pointer to SD_CID data structure. + + @retval EFI_SUCCESS The function completed successfully + +**/ +EFI_STATUS +GetSdModelName ( + IN OUT SD_DEVICE *Device, + IN SD_CID *Cid + ) +{ + CHAR8 String[SD_MODEL_NAME_MAX_LEN]; + + ZeroMem (String, sizeof (String)); + CopyMem (String, Cid->OemId, sizeof (Cid->OemId)); + String[sizeof (Cid->OemId)] = ' '; + CopyMem (String + sizeof (Cid->OemId) + 1, Cid->ProductName, sizeof (Cid->ProductName)); + String[sizeof (Cid->OemId) + sizeof (Cid->ProductName)] = ' '; + CopyMem (String + sizeof (Cid->OemId) + sizeof (Cid->ProductName) + 1, Cid->ProductSerialNumber, sizeof (Cid->ProductSerialNumber)); + + AsciiStrToUnicodeStr (String, Device->ModelName); + + return EFI_SUCCESS; +} + +/** + Discover user area partition in the SD device. + + @param[in] Device The pointer to the SD_DEVICE data structure. + + @retval EFI_SUCCESS The user area partition in the SD device is successfully identified. + @return Others Some error occurs when identifying the user area. + +**/ +EFI_STATUS +DiscoverUserArea ( + IN SD_DEVICE *Device + ) +{ + EFI_STATUS Status; + SD_CSD *Csd; + SD_CSD2 *Csd2; + SD_CID *Cid; + UINT64 Capacity; + UINT32 DevStatus; + UINT16 Rca; + UINT32 CSize; + UINT32 CSizeMul; + UINT32 ReadBlLen; + + // + // Deselect the device to force it enter stby mode. + // Note here we don't judge return status as some SD devices return + // error but the state has been stby. + // + SdSelect (Device, 0); + + Status = SdSetRca (Device, &Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DiscoverUserArea(): Assign new Rca = 0x%x fails with %r\n", Rca, Status)); + return Status; + } + + Csd = &Device->Csd; + Status = SdGetCsd (Device, Rca, Csd); + if (EFI_ERROR (Status)) { + return Status; + } + DumpCsd (Csd); + + Cid = &Device->Cid; + Status = SdGetCid (Device, Rca, Cid); + if (EFI_ERROR (Status)) { + return Status; + } + GetSdModelName (Device, Cid); + + Status = SdSelect (Device, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "DiscoverUserArea(): Reselect the device 0x%x fails with %r\n", Rca, Status)); + return Status; + } + + Status = SdSendStatus (Device, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Csd->CsdStructure == 0) { + Device->SectorAddressing = FALSE; + CSize = (Csd->CSizeHigh << 2 | Csd->CSizeLow) + 1; + CSizeMul = (1 << (Csd->CSizeMul + 2)); + ReadBlLen = (1 << (Csd->ReadBlLen)); + Capacity = MultU64x32 (MultU64x32 ((UINT64)CSize, CSizeMul), ReadBlLen); + } else { + Device->SectorAddressing = TRUE; + Csd2 = (SD_CSD2*)(VOID*)Csd; + CSize = (Csd2->CSizeHigh << 16 | Csd2->CSizeLow) + 1; + Capacity = MultU64x32 ((UINT64)CSize, SIZE_512KB); + } + + Device->BlockIo.Media = &Device->BlockMedia; + Device->BlockIo2.Media = &Device->BlockMedia; + Device->BlockMedia.IoAlign = Device->Private->PassThru->IoAlign; + Device->BlockMedia.BlockSize = 0x200; + Device->BlockMedia.LastBlock = 0x00; + Device->BlockMedia.RemovableMedia = TRUE; + Device->BlockMedia.MediaPresent = TRUE; + Device->BlockMedia.LogicalPartition = FALSE; + Device->BlockMedia.LastBlock = DivU64x32 (Capacity, Device->BlockMedia.BlockSize) - 1; + + return Status; +} + +/** + Scan SD Bus to discover the device. + + @param[in] Private The SD driver private data structure. + @param[in] Slot The slot number to check device present. + + @retval EFI_SUCCESS Successfully to discover the device and attach + SdMmcIoProtocol to it. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack + of resources. + @retval EFI_ALREADY_STARTED The device was discovered before. + @retval Others Fail to discover the device. + +**/ +EFI_STATUS +EFIAPI +DiscoverSdDevice ( + IN SD_DRIVER_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; + EFI_HANDLE DeviceHandle; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + + Device = NULL; + DevicePath = NULL; + NewDevicePath = NULL; + RemainingDevicePath = NULL; + PassThru = Private->PassThru; + + // + // Build Device Path + // + Status = PassThru->BuildDevicePath ( + PassThru, + Slot, + &DevicePath + ); + if (EFI_ERROR(Status)) { + return Status; + } + + if (DevicePath->SubType != MSG_SD_DP) { + Status = EFI_UNSUPPORTED; + goto Error; + } + + NewDevicePath = AppendDevicePathNode ( + Private->ParentDevicePath, + DevicePath + ); + + if (NewDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + DeviceHandle = NULL; + RemainingDevicePath = NewDevicePath; + Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle); + if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd(RemainingDevicePath)) { + // + // The device has been started, directly return to fast boot. + // + Status = EFI_ALREADY_STARTED; + goto Error; + } + + // + // Allocate buffer to store SD_DEVICE private data. + // + Device = AllocateCopyPool (sizeof (SD_DEVICE), &mSdDeviceTemplate); + if (Device == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Device->DevicePath = NewDevicePath; + Device->Slot = Slot; + Device->Private = Private; + InitializeListHead (&Device->Queue); + + // + // Expose user area in the Sd memory card to upper layer. + // + Status = DiscoverUserArea (Device); + if (EFI_ERROR(Status)) { + goto Error; + } + + Device->ControllerNameTable = NULL; + AddUnicodeString2 ( + "eng", + gSdDxeComponentName.SupportedLanguages, + &Device->ControllerNameTable, + Device->ModelName, + TRUE + ); + AddUnicodeString2 ( + "en", + gSdDxeComponentName.SupportedLanguages, + &Device->ControllerNameTable, + Device->ModelName, + FALSE + ); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Device->Handle, + &gEfiDevicePathProtocolGuid, + Device->DevicePath, + &gEfiBlockIoProtocolGuid, + &Device->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Device->BlockIo2, + NULL + ); + + if (!EFI_ERROR (Status)) { + gBS->OpenProtocol ( + Private->Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **) &(Private->PassThru), + Private->DriverBindingHandle, + Device->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + +Error: + FreePool (DevicePath); + + if (EFI_ERROR (Status) && (NewDevicePath != NULL)) { + FreePool (NewDevicePath); + } + + if (EFI_ERROR (Status) && (Device != NULL)) { + FreePool (Device); + } + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT8 Slot; + + // + // Test EFI_SD_MMC_PASS_THRU_PROTOCOL on the controller handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Test RemainingDevicePath is valid or not. + // + if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { + Status = PassThru->GetSlotNumber (PassThru, RemainingDevicePath, &Slot); + if (EFI_ERROR (Status)) { + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return Status; + } + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + return Status; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + SD_DRIVER_PRIVATE_DATA *Private; + UINT8 Slot; + + Private = NULL; + PassThru = NULL; + Status = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if ((EFI_ERROR (Status)) && (Status != EFI_ALREADY_STARTED)) { + return Status; + } + + // + // Check EFI_ALREADY_STARTED to reuse the original SD_DRIVER_PRIVATE_DATA. + // + if (Status != EFI_ALREADY_STARTED) { + Private = AllocateZeroPool (sizeof (SD_DRIVER_PRIVATE_DATA)); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + Private->PassThru = PassThru; + Private->Controller = Controller; + Private->ParentDevicePath = ParentDevicePath; + Private->DriverBindingHandle = This->DriverBindingHandle; + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiCallerIdGuid, + EFI_NATIVE_INTERFACE, + Private + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &Private, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + if (RemainingDevicePath == NULL) { + Slot = 0xFF; + while (TRUE) { + Status = PassThru->GetNextSlot (PassThru, &Slot); + if (EFI_ERROR (Status)) { + // + // Cannot find more legal slots. + // + Status = EFI_SUCCESS; + break; + } + + Status = DiscoverSdDevice (Private, Slot); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + break; + } + } + } else if (!IsDevicePathEnd (RemainingDevicePath)) { + Status = PassThru->GetSlotNumber (PassThru, RemainingDevicePath, &Slot); + if (!EFI_ERROR (Status)) { + Status = DiscoverSdDevice (Private, Slot); + } + } + +Error: + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + if (Private != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Controller, + &gEfiCallerIdGuid, + Private, + NULL + ); + FreePool (Private); + } + } + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + BOOLEAN AllChildrenStopped; + UINTN Index; + SD_DRIVER_PRIVATE_DATA *Private; + SD_DEVICE *Device; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_BLOCK_IO2_PROTOCOL *BlockIo2; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + SD_REQUEST *Request; + EFI_TPL OldTpl; + + if (NumberOfChildren == 0) { + Status = gBS->OpenProtocol ( + Controller, + &gEfiCallerIdGuid, + (VOID **) &Private, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + gBS->UninstallProtocolInterface ( + Controller, + &gEfiCallerIdGuid, + Private + ); + gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool (Private); + + return EFI_SUCCESS; + } + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + BlockIo = NULL; + BlockIo2 = NULL; + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + ChildHandleBuffer[Index], + &gEfiBlockIo2ProtocolGuid, + (VOID **) &BlockIo2, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + continue; + } + } + + if (BlockIo != NULL) { + Device = SD_DEVICE_DATA_FROM_BLKIO (BlockIo); + } else { + ASSERT (BlockIo2 != NULL); + Device = SD_DEVICE_DATA_FROM_BLKIO2 (BlockIo2); + } + + // + // Free all on-going async tasks. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + for (Link = GetFirstNode (&Device->Queue); + !IsNull (&Device->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Device->Queue, Link); + RemoveEntryList (Link); + + Request = SD_REQUEST_FROM_LINK (Link); + + gBS->CloseEvent (Request->Event); + Request->Token->TransactionStatus = EFI_ABORTED; + + if (Request->IsEnd) { + gBS->SignalEvent (Request->Token->Event); + } + + FreePool (Request); + } + gBS->RestoreTPL (OldTpl); + + // + // Close the child handle + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + This->DriverBindingHandle, + ChildHandleBuffer[Index] + ); + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ChildHandleBuffer[Index], + &gEfiDevicePathProtocolGuid, + Device->DevicePath, + &gEfiBlockIoProtocolGuid, + &Device->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Device->BlockIo2, + NULL + ); + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID **)&PassThru, + This->DriverBindingHandle, + ChildHandleBuffer[Index], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } else { + FreePool (Device->DevicePath); + FreeUnicodeStringTable (Device->ControllerNameTable); + FreePool (Device); + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + The user Entry Point for module SdDxe. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some errors occur when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeSdDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gSdDxeDriverBinding, + ImageHandle, + &gSdDxeComponentName, + &gSdDxeComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h new file mode 100644 index 0000000000..ca1609e900 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h @@ -0,0 +1,469 @@ +/** @file + Header file for SdDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SD_DXE_H_ +#define _SD_DXE_H_ + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SdBlockIo.h" +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gSdDxeDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gSdDxeComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gSdDxeComponentName2; + +#define SD_DEVICE_SIGNATURE SIGNATURE_32 ('S', 'D', 't', 'f') + +#define SD_DEVICE_DATA_FROM_BLKIO(a) \ + CR(a, SD_DEVICE, BlockIo, SD_DEVICE_SIGNATURE) + +#define SD_DEVICE_DATA_FROM_BLKIO2(a) \ + CR(a, SD_DEVICE, BlockIo2, SD_DEVICE_SIGNATURE) + +// +// Take 2.5 seconds as generic time out value, 1 microsecond as unit. +// +#define SD_GENERIC_TIMEOUT 2500 * 1000 + +#define SD_REQUEST_SIGNATURE SIGNATURE_32 ('S', 'D', 'R', 'E') + +#define SD_MODEL_NAME_MAX_LEN 32 + +typedef struct _SD_DEVICE SD_DEVICE; +typedef struct _SD_DRIVER_PRIVATE_DATA SD_DRIVER_PRIVATE_DATA; + +// +// Asynchronous I/O request. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + + BOOLEAN IsEnd; + + EFI_BLOCK_IO2_TOKEN *Token; + + EFI_EVENT Event; +} SD_REQUEST; + +#define SD_REQUEST_FROM_LINK(a) \ + CR(a, SD_REQUEST, Link, SD_REQUEST_SIGNATURE) + +struct _SD_DEVICE { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINT8 Slot; + BOOLEAN SectorAddressing; + EFI_BLOCK_IO_PROTOCOL BlockIo; + EFI_BLOCK_IO2_PROTOCOL BlockIo2; + EFI_BLOCK_IO_MEDIA BlockMedia; + + LIST_ENTRY Queue; + + SD_CSD Csd; + SD_CID Cid; + EFI_UNICODE_STRING_TABLE *ControllerNameTable; + // + // The model name consists of three fields in CID register + // 1) OEM/Application ID (2 bytes) + // 2) Product Name (5 bytes) + // 3) Product Serial Number (4 bytes) + // The delimiters of these fields are whitespace. + // + CHAR16 ModelName[SD_MODEL_NAME_MAX_LEN]; + SD_DRIVER_PRIVATE_DATA *Private; +} ; + +// +// SD DXE driver private data structure +// +struct _SD_DRIVER_PRIVATE_DATA { + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EFI_HANDLE Controller; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_HANDLE DriverBindingHandle; +} ; + +/** + Tests to see if this driver supports a given controller. If a child device is provided, + it further tests to see if this driver supports creating a handle for the specified child device. + + This function checks to see if the driver specified by This supports the device specified by + ControllerHandle. Drivers will typically use the device path attached to + ControllerHandle and/or the services from the bus I/O abstraction attached to + ControllerHandle to determine if the driver supports ControllerHandle. This function + may be called many times during platform initialization. In order to reduce boot times, the tests + performed by this function must be very small, and take as little time as possible to execute. This + function must not change the state of any hardware devices, and this function must be aware that the + device specified by ControllerHandle may already be managed by the same driver or a + different driver. This function must match its calls to AllocatePages() with FreePages(), + AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if a protocol is + already in the opened state, then it must not be closed with CloseProtocol(). This is required + to guarantee the state of ControllerHandle is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to test. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For bus drivers, if this parameter is not NULL, then + the bus driver must determine if the bus controller specified + by ControllerHandle and the child controller specified + by RemainingDevicePath are both supported by this + bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by the driver + specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed by a different + driver or an application that requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the driver specified by This. +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +SdDxeDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdDxeComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is NULL. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +SdDxeComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Send command SET_RELATIVE_ADDRESS to the device to set the device address. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[out] Rca The relative device address to assign. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSetRca ( + IN SD_DEVICE *Device, + OUT UINT16 *Rca + ); + +/** + Send command SELECT to the device to select/deselect the device. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSelect ( + IN SD_DEVICE *Device, + IN UINT16 Rca + ); + +/** + Send command SEND_STATUS to the device to get device status. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] DevStatus The buffer to store the device status. + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdSendStatus ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ); + +/** + Send command SEND_CSD to the device to get the CSD register data. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Csd The buffer to store the SD_CSD register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdGetCsd ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT SD_CSD *Csd + ); + +/** + Send command SEND_CID to the device to get the CID register data. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] Rca The relative device address to use. + @param[out] Cid The buffer to store the SD_CID register data. + + @retval EFI_SUCCESS The request is executed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be executed due to a lack of resources. + @retval Others The request could not be executed successfully. + +**/ +EFI_STATUS +SdGetCid ( + IN SD_DEVICE *Device, + IN UINT16 Rca, + OUT SD_CID *Cid + ); + +#endif + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf new file mode 100644 index 0000000000..b24b721236 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf @@ -0,0 +1,65 @@ +## @file +# SdDxe driver is used to manage the SD memory card device. +# +# It produces BlockIo and BlockIo2 protocols to allow upper layer +# access the SD memory card device. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SdDxe + MODULE_UNI_FILE = SdDxe.uni + FILE_GUID = 430AC2F7-EEC6-4093-94F7-9F825A7C1C40 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeSdDxe + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gSdDxeDriverBinding +# COMPONENT_NAME = gSdDxeComponentName +# COMPONENT_NAME2 = gSdDxeComponentName2 +# + +[Sources.common] + ComponentName.c + SdDxe.c + SdDxe.h + SdBlockIo.c + SdBlockIo.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiSdMmcPassThruProtocolGuid ## TO_START + gEfiBlockIoProtocolGuid ## BY_START + gEfiBlockIo2ProtocolGuid ## BY_START + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni new file mode 100644 index 0000000000..dc77ab86f0 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni @@ -0,0 +1,20 @@ +// /** @file +// SD memory card device driver to manage the SD memory card device and provide interface for upper layer +// access. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "SD device driver to manage the SD memory card device and provide interface for upper layer access" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver follows the UEFI driver model and layers on the SdMmcPassThru protocol. It installs BlockIo and BlockIo2 protocols on the SD device." + diff --git a/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni b/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni new file mode 100644 index 0000000000..dc77ab86f0 --- /dev/null +++ b/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// SD memory card device driver to manage the SD memory card device and provide interface for upper layer +// access. +// +// Copyright (c) 2015, Intel Corporation. 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. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "SD device driver to manage the SD memory card device and provide interface for upper layer access" + +#string STR_MODULE_DESCRIPTION #language en-US "This driver follows the UEFI driver model and layers on the SdMmcPassThru protocol. It installs BlockIo and BlockIo2 protocols on the SD device." + diff --git a/MdeModulePkg/Include/Ppi/SdMmcHostController.h b/MdeModulePkg/Include/Ppi/SdMmcHostController.h new file mode 100644 index 0000000000..85dee24526 --- /dev/null +++ b/MdeModulePkg/Include/Ppi/SdMmcHostController.h @@ -0,0 +1,64 @@ +/** @file + +Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _EDKII_PEI_SD_MMC_HOST_CONTROLLER_PPI_H_ +#define _EDKII_PEI_SD_MMC_HOST_CONTROLLER_PPI_H_ + +/// +/// Global ID for the EDKII_SD_MMC_HOST_CONTROLLER_PPI. +/// +#define EDKII_SD_MMC_HOST_CONTROLLER_PPI_GUID \ + { \ + 0xb30dfeed, 0x947f, 0x4396, { 0xb1, 0x5a, 0xdf, 0xbd, 0xb9, 0x16, 0xdc, 0x24 } \ + } + +/// +/// Forward declaration for the SD_MMC_HOST_CONTROLLER_PPI. +/// +typedef struct _EDKII_SD_MMC_HOST_CONTROLLER_PPI EDKII_SD_MMC_HOST_CONTROLLER_PPI; + +/** + Get the MMIO base address of SD/MMC host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the SD/MMC host controller. + @param[in,out] MmioBar The pointer to store the array of available + SD/MMC host controller slot MMIO base addresses. + The entry number of the array is specified by BarNum. + @param[out] BarNum The pointer to store the supported bar number. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_SD_MMC_HC_GET_MMIO_BAR)( + IN EDKII_SD_MMC_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + IN OUT UINTN **MmioBar, + OUT UINT8 *BarNum + ); + +/// +/// This PPI contains a set of services to interact with the SD_MMC host controller. +/// +struct _EDKII_SD_MMC_HOST_CONTROLLER_PPI { + EDKII_SD_MMC_HC_GET_MMIO_BAR GetSdMmcHcMmioBar; +}; + +extern EFI_GUID gEdkiiPeiSdMmcHostControllerPpiGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 1a205617a5..aa9e8066d8 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -388,6 +388,9 @@ ## Include/Ppi/IpmiPpi.h gPeiIpmiPpiGuid = { 0xa9731431, 0xd968, 0x4277, { 0xb7, 0x52, 0xa3, 0xa9, 0xa6, 0xae, 0x18, 0x98 }} + ## Include/Ppi/SdMmcHostController.h + gEdkiiPeiSdMmcHostControllerPpiGuid = { 0xb30dfeed, 0x947f, 0x4396, { 0xb1, 0x5a, 0xdf, 0xbd, 0xb9, 0x16, 0xdc, 0x24 }} + [Protocols] ## Load File protocol provides capability to load and unload EFI image into memory and execute it. # Include/Protocol/LoadPe32Image.h @@ -1495,6 +1498,12 @@ # @Prompt Set NX for stack. gEfiMdeModulePkgTokenSpaceGuid.PcdSetNxForStack|FALSE|BOOLEAN|0x0001006f + ## This PCD specifies the PCI-based SD/MMC host controller mmio base address. + # Define the mmio base address of the pci-based SD/MMC host controller. If there are multiple SD/MMC + # host controllers, their mmio base addresses are calculated one by one from this base address. + # @Prompt Mmio base address of pci-based SD/MMC host controller. + gEfiMdeModulePkgTokenSpaceGuid.PcdSdMmcPciHostControllerMmioBase|0xd0000000|UINT32|0x30001043 + [PcdsPatchableInModule] ## Specify memory size with page number for PEI code when # Loading Module at Fixed Address feature is enabled. diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 0b54030f5b..b1ece7b2cf 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -215,6 +215,12 @@ MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf + MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf + MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf + MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf + MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf + MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf + MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf -- 2.39.2