From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../MdeModulePkg/Bus/Sd/EmmcBlockIoPei/DmaMem.c | 242 ++ .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c | 843 ++++++ .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h | 514 ++++ .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf | 60 + .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni | 14 + .../Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni | 14 + .../MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c | 429 +++ .../MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h | 56 + .../MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c | 2894 +++++++++++++++++++ .../MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h | 339 +++ .../MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c | 236 ++ .../edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c | 2161 ++++++++++++++ .../edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h | 497 ++++ .../MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.c | 134 + .../MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.h | 109 + roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c | 1205 ++++++++ roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h | 501 ++++ roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf | 65 + roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni | 15 + .../MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni | 14 + .../edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/DmaMem.c | 242 ++ .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.c | 653 +++++ .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.h | 510 ++++ .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf | 60 + .../Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni | 14 + .../Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni | 14 + .../MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c | 429 +++ .../MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h | 56 + roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c | 2957 ++++++++++++++++++++ roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h | 350 +++ .../edk2/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c | 234 ++ roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c | 1381 +++++++++ roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h | 252 ++ roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.c | 132 + roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.h | 109 + roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c | 908 ++++++ roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h | 475 ++++ roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf | 64 + roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni | 15 + roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni | 15 + 40 files changed, 19212 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/DmaMem.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/DmaMem.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni create mode 100644 roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni (limited to 'roms/edk2/MdeModulePkg/Bus/Sd') diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/DmaMem.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/DmaMem.c new file mode 100644 index 000000000..2d8bfa1b0 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/DmaMem.c @@ -0,0 +1,242 @@ +/** @file + The DMA memory help function. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EmmcBlockIoPei.h" + +EDKII_IOMMU_PPI *mIoMmu; + +/** + Provides the controller-specific addresses required to access system memory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +IoMmuMap ( + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINT64 Attribute; + + if (mIoMmu != NULL) { + Status = mIoMmu->Map ( + mIoMmu, + Operation, + HostAddress, + NumberOfBytes, + DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + switch (Operation) { + case EdkiiIoMmuOperationBusMasterRead: + case EdkiiIoMmuOperationBusMasterRead64: + Attribute = EDKII_IOMMU_ACCESS_READ; + break; + case EdkiiIoMmuOperationBusMasterWrite: + case EdkiiIoMmuOperationBusMasterWrite64: + Attribute = EDKII_IOMMU_ACCESS_WRITE; + break; + case EdkiiIoMmuOperationBusMasterCommonBuffer: + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE; + break; + default: + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + Status = mIoMmu->SetAttribute ( + mIoMmu, + *Mapping, + Attribute + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; + *Mapping = NULL; + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. +**/ +EFI_STATUS +IoMmuUnmap ( + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + + if (mIoMmu != NULL) { + Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0); + Status = mIoMmu->Unmap (mIoMmu, Mapping); + } else { + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + EFI_PHYSICAL_ADDRESS HostPhyAddress; + + *HostAddress = NULL; + *DeviceAddress = 0; + + if (mIoMmu != NULL) { + Status = mIoMmu->AllocateBuffer ( + mIoMmu, + EfiBootServicesData, + Pages, + HostAddress, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + NumberOfBytes = EFI_PAGES_TO_SIZE(Pages); + Status = mIoMmu->Map ( + mIoMmu, + EdkiiIoMmuOperationBusMasterCommonBuffer, + *HostAddress, + &NumberOfBytes, + DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + Status = mIoMmu->SetAttribute ( + mIoMmu, + *Mapping, + EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &HostPhyAddress + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + *HostAddress = (VOID *)(UINTN)HostPhyAddress; + *DeviceAddress = HostPhyAddress; + *Mapping = NULL; + } + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + + if (mIoMmu != NULL) { + Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0); + Status = mIoMmu->Unmap (mIoMmu, Mapping); + Status = mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress); + } else { + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Initialize IOMMU. +**/ +VOID +IoMmuInit ( + VOID + ) +{ + PeiServicesLocatePpi ( + &gEdkiiIoMmuPpiGuid, + 0, + NULL, + (VOID **)&mIoMmu + ); +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c new file mode 100644 index 000000000..267db4a89 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.c @@ -0,0 +1,843 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 + }, + { // EndOfPeiNotifyList + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + EmmcBlockIoPeimEndOfPei + }, + { // 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; +} + +/** + One notified function to cleanup the allocated DMA buffers at the end of PEI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notification + event that caused this function to execute. + @param[in] Ppi Pointer to the PPI data associated with this function. + + @retval EFI_SUCCESS The function completes successfully + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimEndOfPei ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EMMC_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor); + + if ((Private->Pool != NULL) && (Private->Pool->Head != NULL)) { + EmmcPeimFreeMemPool (Private->Pool); + } + + 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 +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; + } + + IoMmuInit (); + + 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); + PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList); + } else { + if (Private->Pool->Head != NULL) { + EmmcPeimFreeMemPool (Private->Pool); + } + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h new file mode 100644 index 000000000..774274e48 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.h @@ -0,0 +1,514 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EMMC_BLOCK_IO_PEI_H_ +#define _EMMC_BLOCK_IO_PEI_H_ + +#include + +#include +#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; + + // + // EndOfPei callback is used to do the cleanups before exit of PEI phase. + // + EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList; + + 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) +#define GET_EMMC_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) CR (a, EMMC_PEIM_HC_PRIVATE_DATA, EndOfPeiNotifyList, EMMC_PEIM_SIG) + +struct _EMMC_TRB { + EMMC_PEIM_HC_SLOT *Slot; + UINT16 BlockSize; + + EMMC_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + EFI_PHYSICAL_ADDRESS DataPhy; + VOID *DataMap; + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Initialize IOMMU. +**/ +VOID +IoMmuInit ( + VOID + ); + +/** + Provides the controller-specific addresses required to access system memory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +IoMmuMap ( + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. +**/ +EFI_STATUS +IoMmuUnmap ( + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ); + +/** + One notified function to cleanup the allocated DMA buffers at the end of PEI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notification + event that caused this function to execute. + @param[in] Ppi Pointer to the PPI data associated with this function. + + @retval EFI_SUCCESS The function completes successfully + +**/ +EFI_STATUS +EFIAPI +EmmcBlockIoPeimEndOfPei ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf new file mode 100644 index 000000000..8ab632eb3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf @@ -0,0 +1,60 @@ +## @file +# Description file for the Embedded MMC (eMMC) Peim driver. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[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 EBC +# + +[Sources] + EmmcBlockIoPei.c + EmmcBlockIoPei.h + EmmcHci.c + EmmcHci.h + EmmcHcMem.c + EmmcHcMem.h + DmaMem.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES + gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES + gEdkiiPeiSdMmcHostControllerPpiGuid ## CONSUMES + gEdkiiIoMmuPpiGuid ## CONSUMES + gEfiEndOfPeiSignalPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiSdMmcHostControllerPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + EmmcBlockIoPeiExtra.uni + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni new file mode 100644 index 000000000..8c4c79c1a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.uni @@ -0,0 +1,14 @@ +// /** @file +// The EmmcBlockIoPei driver is used to support recovery from EMMC device. +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni new file mode 100644 index 000000000..feeb1b34f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// EmmcBlockIoPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EMMC BlockIo Peim for Recovery" + + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c new file mode 100644 index 000000000..19a0afcb6 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.c @@ -0,0 +1,429 @@ +/** @file + +Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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; + VOID *BufHost; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS MappedAddr; + EFI_STATUS Status; + VOID *TempPtr; + + 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 = IoMmuAllocateBuffer ( + Pages, + &BufHost, + &MappedAddr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)BufHost, EFI_PAGES_TO_SIZE (Pages)); + + Block->BufHost = (UINT8 *) (UINTN) BufHost; + Block->Buf = (UINT8 *) (UINTN) MappedAddr; + Block->Mapping = Mapping; + 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)); + + IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping); +} + +/** + 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 consecutive 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; +} + + + +/** + 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. + // + 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 array + // + 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h new file mode 100644 index 000000000..255ad345d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHcMem.h @@ -0,0 +1,56 @@ +/** @file + +Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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; + UINT8 *BufHost; + UINTN BufLen; // Memory size in bytes + VOID *Mapping; + 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c new file mode 100644 index 000000000..287a10266 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.c @@ -0,0 +1,2894 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 == 0) { + return EFI_INVALID_PARAMETER; + } + + if (ClockFreq > (BaseClkFreq * 1000)) { + ClockFreq = BaseClkFreq * 1000; + } + + // + // 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 = Trb->DataPhy; + 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; + EDKII_IOMMU_OPERATION MapOp; + UINTN MapLength; + + // + // Calculate a divisor for SD clock frequency + // + Status = EmmcPeimHcGetCapability (Slot->EmmcHcBase, &Capability); + if (EFI_ERROR (Status)) { + return NULL; + } + + Trb = AllocateZeroPool (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 != 0) && (Trb->DataLen < Trb->BlockSize)) { + Trb->BlockSize = (UINT16)Trb->DataLen; + } + + if (Packet->EmmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK) { + Trb->Mode = EmmcPioMode; + } else { + if (Trb->Read) { + MapOp = EdkiiIoMmuOperationBusMasterWrite; + } else { + MapOp = EdkiiIoMmuOperationBusMasterRead; + } + + if (Trb->DataLen != 0) { + MapLength = Trb->DataLen; + Status = IoMmuMap (MapOp, Trb->Data, &MapLength, &Trb->DataPhy, &Trb->DataMap); + + if (EFI_ERROR (Status) || (MapLength != Trb->DataLen)) { + DEBUG ((DEBUG_ERROR, "EmmcPeimCreateTrb: Fail to map data buffer.\n")); + goto Error; + } + } + + 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->DataMap != NULL)) { + IoMmuUnmap (Trb->DataMap); + } + + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + EmmcPeimFreeMem (Trb->Slot->Private->Pool, Trb->AdmaDesc, Trb->AdmaDescSize); + } + + if (Trb != NULL) { + FreePool (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; + } 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->DataPhy >= 0x100000000ul) { + return EFI_INVALID_PARAMETER; + } + + SdmaAddr = (UINT32)(UINTN)Trb->DataPhy; + 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 = 0; + if (Trb->Mode != EmmcNoData) { + // + // Calculate Block Count. + // + 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 > 1) { + 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; + UINT32 PioLength; + + 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->DataPhy, EMMC_SDMA_BOUNDARY); + Status = EmmcPeimHcRwMmio ( + Bar + EMMC_HC_SDMA_ADDR, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Trb->DataPhy = (UINT32)(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) { + // + // When performing tuning procedure (Execute Tuning is set to 1) through PIO mode, + // wait Buffer Read Ready bit of Normal Interrupt Status Register to be 1. + // Refer to SD Host Controller Simplified Specification 3.0 figure 2-29 for details. + // + if ((IntStatus & BIT5) == BIT5) { + // + // Clear Buffer Read Ready interrupt at first. + // + IntStatus = BIT5; + EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + // + // Read data out from Buffer Port register + // + for (PioLength = 0; PioLength < Trb->DataLen; PioLength += 4) { + EmmcPeimHcRwMmio (Bar + EMMC_HC_BUF_DAT_PORT, TRUE, 4, (UINT8*)Trb->Data + PioLength); + } + 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 SWITCH 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; +} + +/** + Tuning 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)) == 0) { + break; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + return EFI_SUCCESS; + } + } while (++Retry < 40); + + DEBUG ((EFI_D_ERROR, "EmmcPeimTuningClkForHs200: Send tuning block fails at %d times with HostCtrl2 %02x\n", Retry, HostCtrl2)); + // + // Abort the tuning procedure and reset the tuning circuit. + // + HostCtrl2 = (UINT8)~(BIT6 | BIT7); + Status = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + return EFI_DEVICE_ERROR; +} + +/** + 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 High 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); + + 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 High 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 Device_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, IsDdr, BusWidth); + } + + 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; + UINTN Retry; + + Status = EmmcPeimReset (Slot); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimReset fails with %r\n", Status)); + return Status; + } + + Ocr = 0; + Retry = 0; + do { + Status = EmmcPeimGetOcr (Slot, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimGetOcr fails with %r\n", Status)); + return Status; + } + + if (Retry++ == 100) { + DEBUG ((EFI_D_ERROR, "EmmcPeimIdentification: EmmcPeimGetOcr fails too many times\n")); + return EFI_DEVICE_ERROR; + } + MicroSecondDelay (10 * 1000); + } 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h new file mode 100644 index 000000000..4e3e51d14 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcHci.h @@ -0,0 +1,339 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 SWITCH 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c new file mode 100644 index 000000000..6a4d18d4d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/ComponentName.c @@ -0,0 +1,236 @@ +/** @file + UEFI Component Name(2) protocol implementation for EmmcDxe driver. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c new file mode 100644 index 000000000..afdc0a57e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c @@ -0,0 +1,2161 @@ +/** @file + The helper functions for BlockIo and BlockIo2 protocol. + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EmmcDxe.h" + +/** + Nonblocking I/O callback function 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_NOTIFY); + 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_NOTIFY, + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY, + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY, + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY); + 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_NOTIFY, + 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_NOTIFY); + 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_NOTIFY); + 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_CHANGED The MediaId does not match 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 ((DEBUG_BLKIO, + "Emmc%a(): Part %d Lba 0x%x BlkNo 0x%x Event %p with %r\n", + IsRead ? "Read " : "Write", Partition->PartitionType, Lba, BlockNum, + (Token != NULL) ? Token->Event : NULL, 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 match 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_CHANGED The MediaId does not match 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_NOTIFY); + 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_CHANGED The MediaId does not match 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; +} + +/** + Set the erase start address through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] StartLba The starting logical block address to be erased. + @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 +EmmcEraseBlockStart ( + IN EMMC_PARTITION *Partition, + IN EFI_LBA StartLba, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EMMC_DEVICE *Device; + EMMC_REQUEST *EraseBlockStart; + EFI_TPL OldTpl; + + EraseBlockStart = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + EraseBlockStart = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (EraseBlockStart == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlockStart->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Partition->Queue, &EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + EraseBlockStart->Packet.SdMmcCmdBlk = &EraseBlockStart->SdMmcCmdBlk; + EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk; + EraseBlockStart->Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + EraseBlockStart->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_START; + EraseBlockStart->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (Device->SectorAddressing) { + EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba; + } else { + EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Partition->BlockMedia.BlockSize); + } + + EraseBlockStart->IsEnd = IsEnd; + EraseBlockStart->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlockStart, + &EraseBlockStart->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlockStart->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->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) && (EraseBlockStart != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlockStart->Event != NULL) { + gBS->CloseEvent (EraseBlockStart->Event); + } + FreePool (EraseBlockStart); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlockStart != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlockStart); + } + } + + return Status; +} + +/** + Set the erase end address through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] EndLba The ending logical block address to be erased. + @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 +EmmcEraseBlockEnd ( + IN EMMC_PARTITION *Partition, + IN EFI_LBA EndLba, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EMMC_DEVICE *Device; + EMMC_REQUEST *EraseBlockEnd; + EFI_TPL OldTpl; + + EraseBlockEnd = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + EraseBlockEnd = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (EraseBlockEnd == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlockEnd->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Partition->Queue, &EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + EraseBlockEnd->Packet.SdMmcCmdBlk = &EraseBlockEnd->SdMmcCmdBlk; + EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk; + EraseBlockEnd->Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + EraseBlockEnd->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_END; + EraseBlockEnd->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (Device->SectorAddressing) { + EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba; + } else { + EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Partition->BlockMedia.BlockSize); + } + + EraseBlockEnd->IsEnd = IsEnd; + EraseBlockEnd->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlockEnd, + &EraseBlockEnd->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlockEnd->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->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) && (EraseBlockEnd != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlockEnd->Event != NULL) { + gBS->CloseEvent (EraseBlockEnd->Event); + } + FreePool (EraseBlockEnd); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlockEnd != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlockEnd); + } + } + + return Status; +} + +/** + Erase specified blocks through sync or async I/O request. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @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 +EmmcEraseBlock ( + IN EMMC_PARTITION *Partition, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + EMMC_DEVICE *Device; + EMMC_REQUEST *EraseBlock; + EFI_TPL OldTpl; + + EraseBlock = NULL; + + Device = Partition->Device; + PassThru = Device->Private->PassThru; + + EraseBlock = AllocateZeroPool (sizeof (EMMC_REQUEST)); + if (EraseBlock == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlock->Signature = EMMC_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Partition->Queue, &EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + EraseBlock->Packet.SdMmcCmdBlk = &EraseBlock->SdMmcCmdBlk; + EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk; + EraseBlock->Packet.Timeout = EMMC_GENERIC_TIMEOUT; + + EraseBlock->SdMmcCmdBlk.CommandIndex = EMMC_ERASE; + EraseBlock->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + if ((Device->ExtCsd.SecFeatureSupport & BIT4) != 0) { + // + // Perform a Trim operation which applies the erase operation to write blocks + // instead of erase groups. (Spec JESD84-B51, eMMC Electrical Standard 5.1, + // Section 6.6.10 and 6.10.4) + // + EraseBlock->SdMmcCmdBlk.CommandArgument = 1; + } + + EraseBlock->IsEnd = IsEnd; + EraseBlock->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlock, + &EraseBlock->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlock->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->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) && (EraseBlock != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlock->Event != NULL) { + gBS->CloseEvent (EraseBlock->Event); + } + FreePool (EraseBlock); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlock != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlock); + } + } + + return Status; +} + +/** + Write zeros to specified blocks. + + @param[in] Partition A pointer to the EMMC_PARTITION instance. + @param[in] StartLba The starting logical block address to write zeros. + @param[in] Size The size in bytes to fill with zeros. This must be a multiple of + the physical block size of the device. + + @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 +EmmcWriteZeros ( + IN EMMC_PARTITION *Partition, + IN EFI_LBA StartLba, + IN UINTN Size + ) +{ + EFI_STATUS Status; + UINT8 *Buffer; + UINT32 MediaId; + + Buffer = AllocateZeroPool (Size); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MediaId = Partition->BlockMedia.MediaId; + + Status = EmmcReadWrite (Partition, MediaId, StartLba, Buffer, Size, FALSE, NULL); + FreePool (Buffer); + + return Status; +} + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +EmmcEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN BlockNum; + EFI_LBA FirstLba; + EFI_LBA LastLba; + EFI_LBA StartGroupLba; + EFI_LBA EndGroupLba; + UINT32 EraseGroupSize; + UINT32 Remainder; + UINTN WriteZeroSize; + UINT8 PartitionConfig; + EMMC_PARTITION *Partition; + EMMC_DEVICE *Device; + + Status = EFI_SUCCESS; + Partition = EMMC_PARTITION_DATA_FROM_ERASEBLK (This); + Device = Partition->Device; + Media = &Partition->BlockMedia; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + // + // Check parameters. + // + BlockSize = Media->BlockSize; + if ((Size % BlockSize) != 0) { + return EFI_INVALID_PARAMETER; + } + + BlockNum = Size / BlockSize; + if ((Lba + BlockNum - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + } + + FirstLba = Lba; + LastLba = Lba + BlockNum - 1; + + // + // 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, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + Device->ExtCsd.PartitionConfig = PartitionConfig; + } + + if ((Device->ExtCsd.SecFeatureSupport & BIT4) == 0) { + // + // If the Trim operation is not supported by the device, handle the erase + // of blocks that do not form a complete erase group separately. + // + EraseGroupSize = This->EraseLengthGranularity; + + DivU64x32Remainder (FirstLba, EraseGroupSize, &Remainder); + StartGroupLba = (Remainder == 0) ? FirstLba : (FirstLba + EraseGroupSize - Remainder); + + DivU64x32Remainder (LastLba + 1, EraseGroupSize, &Remainder); + EndGroupLba = LastLba + 1 - Remainder; + + // + // If the size to erase is smaller than the erase group size, the whole + // erase operation is performed by writing zeros. + // + if (BlockNum < EraseGroupSize) { + Status = EmmcWriteZeros (Partition, FirstLba, Size); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG (( + DEBUG_INFO, + "EmmcEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", + Lba, + BlockNum, + (Token != NULL) ? Token->Event : NULL, + Status + )); + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + return EFI_SUCCESS; + } + + // + // If the starting LBA to erase is not aligned with the start of an erase + // group, write zeros to erase the data from starting LBA to the end of the + // current erase group. + // + if (StartGroupLba > FirstLba) { + WriteZeroSize = (UINTN)(StartGroupLba - FirstLba) * BlockSize; + Status = EmmcWriteZeros (Partition, FirstLba, WriteZeroSize); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // If the ending LBA to erase is not aligned with the end of an erase + // group, write zeros to erase the data from the start of the erase group + // to the ending LBA. + // + if (EndGroupLba <= LastLba) { + WriteZeroSize = (UINTN)(LastLba + 1 - EndGroupLba) * BlockSize; + Status = EmmcWriteZeros (Partition, EndGroupLba, WriteZeroSize); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Check whether there is erase group to erase. + // + if (EndGroupLba <= StartGroupLba) { + DEBUG (( + DEBUG_INFO, + "EmmcEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", + Lba, + BlockNum, + (Token != NULL) ? Token->Event : NULL, + Status + )); + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + gBS->SignalEvent (Token->Event); + } + return EFI_SUCCESS; + } + + FirstLba = StartGroupLba; + LastLba = EndGroupLba - 1; + } + + Status = EmmcEraseBlockStart (Partition, FirstLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcEraseBlockEnd (Partition, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EmmcEraseBlock (Partition, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG (( + DEBUG_INFO, + "EmmcEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", + Lba, + BlockNum, + (Token != NULL) ? Token->Event : NULL, + Status + )); + + return Status; +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h new file mode 100644 index 000000000..62e70ae91 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.h @@ -0,0 +1,497 @@ +/** @file + Header file for EmmcDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 match 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_CHANGED The MediaId does not match 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_CHANGED The MediaId does not match 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 + ); + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +EmmcEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + +#endif + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.c new file mode 100644 index 000000000..e9e31aa2c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.c @@ -0,0 +1,134 @@ +/** @file + Implement the EFI_DISK_INFO_PROTOCOL interface on EMMC devices. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "EmmcDxe.h" + +/** + Provides inquiry information for the controller type. + + This function is used by the driver entity to get inquiry data. Data format of + Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] InquiryData Pointer to a buffer for the inquiry data. + @param[in,out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading InquiryData from device. + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ) +{ + EFI_STATUS Status; + EMMC_PARTITION *Partition; + EMMC_DEVICE *Device; + + Partition = EMMC_PARTITION_DATA_FROM_DISKINFO (This); + Device = Partition->Device; + + if (*InquiryDataSize >= sizeof (Device->Cid)) { + Status = EFI_SUCCESS; + CopyMem (InquiryData, &Device->Cid, sizeof (Device->Cid)); + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *InquiryDataSize = sizeof (Device->Cid); + + return Status; +} + +/** + Provides identify information for the controller type. + + This function is used by the driver entity to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in,out] IdentifyData Pointer to a buffer for the identify data. + @param[in,out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device. + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ) +{ + return EFI_NOT_FOUND; +} + +/** + Provides sense data information for the controller type. + + This function is used by the driver entity to get sense data. Data format of + Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] SenseData Pointer to the SenseData. + @param[in,out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ) +{ + return EFI_NOT_FOUND; +} + +/** + Provides IDE channel and device information for the interface. + + This function is used by the driver entity to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.h new file mode 100644 index 000000000..44e691ac3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDiskInfo.h @@ -0,0 +1,109 @@ +/** @file + Header file for EFI_DISK_INFO_PROTOCOL interface on EMMC devices. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _EMMC_DISKINFO_H_ +#define _EMMC_DISKINFO_H_ + +/** + Provides inquiry information for the controller type. + + This function is used by the driver entity to get inquiry data. Data format of + Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] InquiryData Pointer to a buffer for the inquiry data. + @param[in,out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading InquiryData from device. + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ); + +/** + Provides identify information for the controller type. + + This function is used by the driver entity to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in,out] IdentifyData Pointer to a buffer for the identify data. + @param[in,out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device. + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ); + +/** + Provides sense data information for the controller type. + + This function is used by the driver entity to get sense data. Data format of + Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] SenseData Pointer to the SenseData. + @param[in,out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ); + +/** + Provides IDE channel and device information for the interface. + + This function is used by the driver entity to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +EmmcDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c new file mode 100644 index 000000000..e9095b904 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.c @@ -0,0 +1,1205 @@ +/** @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 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 + }, + { // EraseBlock + EFI_ERASE_BLOCK_PROTOCOL_REVISION, + 1, + EmmcEraseBlocks + }, + { // DiskInfo + EFI_DISK_INFO_SD_MMC_INTERFACE_GUID, + EmmcDiskInfoInquiry, + EmmcDiskInfoIdentify, + EmmcDiskInfoSenseData, + EmmcDiskInfoWhichIde + }, + { + 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)); + + AsciiStrToUnicodeStrS (String, Device->ModelName, sizeof (Device->ModelName) / sizeof (Device->ModelName[0])); + + 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; + 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; + + 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; + } + + if ((ExtCsd->EraseGroupDef & BIT0) == 0) { + if (Csd->WriteBlLen < 9) { + Partition->EraseBlock.EraseLengthGranularity = 1; + } else { + Partition->EraseBlock.EraseLengthGranularity = (Csd->EraseGrpMult + 1) * (Csd->EraseGrpSize + 1) * (1 << (Csd->WriteBlLen - 9)); + } + } else { + Partition->EraseBlock.EraseLengthGranularity = 1024 * ExtCsd->HcEraseGrpSize; + } + } + + 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 + // + if (Partition->PartitionType != EmmcPartitionRPMB) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &Partition->Handle, + &gEfiDevicePathProtocolGuid, + Partition->DevicePath, + &gEfiBlockIoProtocolGuid, + &Partition->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Partition->BlockIo2, + &gEfiEraseBlockProtocolGuid, + &Partition->EraseBlock, + &gEfiDiskInfoProtocolGuid, + &Partition->DiskInfo, + 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, + &gEfiEraseBlockProtocolGuid, + &Partition->EraseBlock, + &gEfiDiskInfoProtocolGuid, + &Partition->DiskInfo, + 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. + @param[in] RemainingDevicePath The pointer to the remaining device path. + + @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", + gEmmcDxeComponentName2.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 failed 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, + &gEfiEraseBlockProtocolGuid, + &Partition->EraseBlock, + &gEfiDiskInfoProtocolGuid, + &Partition->DiskInfo, + 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h new file mode 100644 index 000000000..5ecce41de --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.h @@ -0,0 +1,501 @@ +/** @file + Header file for EmmcDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 +#include + +#include "EmmcBlockIo.h" +#include "EmmcDiskInfo.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) + +#define EMMC_PARTITION_DATA_FROM_ERASEBLK(a) \ + CR(a, EMMC_PARTITION, EraseBlock, EMMC_PARTITION_SIGNATURE) + +#define EMMC_PARTITION_DATA_FROM_DISKINFO(a) \ + CR(a, EMMC_PARTITION, DiskInfo, 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; + EFI_ERASE_BLOCK_PROTOCOL EraseBlock; + EFI_DISK_INFO_PROTOCOL DiskInfo; + + 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 failed 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] 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 + ); + +/** + 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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf new file mode 100644 index 000000000..2afe4f42a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf @@ -0,0 +1,65 @@ +## @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 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[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 EBC +# +# DRIVER_BINDING = gEmmcDxeDriverBinding +# COMPONENT_NAME = gEmmcDxeComponentName +# COMPONENT_NAME2 = gEmmcDxeComponentName2 +# + +[Sources.common] + ComponentName.c + EmmcDxe.c + EmmcDxe.h + EmmcBlockIo.c + EmmcBlockIo.h + EmmcDiskInfo.c + EmmcDiskInfo.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 + gEfiEraseBlockProtocolGuid ## BY_START + gEfiDiskInfoProtocolGuid ## BY_START + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni new file mode 100644 index 000000000..b47ce27bd --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.uni @@ -0,0 +1,15 @@ +// /** @file +// EMMC device driver to manage the EMMC device and provide interface for upper layer +// access. +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#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/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni new file mode 100644 index 000000000..0f75ea64c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// EmmcDxe Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"EMMC Device Driver" + + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/DmaMem.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/DmaMem.c new file mode 100644 index 000000000..63ad6ce46 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/DmaMem.c @@ -0,0 +1,242 @@ +/** @file + The DMA memory help function. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SdBlockIoPei.h" + +EDKII_IOMMU_PPI *mIoMmu; + +/** + Provides the controller-specific addresses required to access system memory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +IoMmuMap ( + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINT64 Attribute; + + if (mIoMmu != NULL) { + Status = mIoMmu->Map ( + mIoMmu, + Operation, + HostAddress, + NumberOfBytes, + DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + switch (Operation) { + case EdkiiIoMmuOperationBusMasterRead: + case EdkiiIoMmuOperationBusMasterRead64: + Attribute = EDKII_IOMMU_ACCESS_READ; + break; + case EdkiiIoMmuOperationBusMasterWrite: + case EdkiiIoMmuOperationBusMasterWrite64: + Attribute = EDKII_IOMMU_ACCESS_WRITE; + break; + case EdkiiIoMmuOperationBusMasterCommonBuffer: + case EdkiiIoMmuOperationBusMasterCommonBuffer64: + Attribute = EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE; + break; + default: + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + Status = mIoMmu->SetAttribute ( + mIoMmu, + *Mapping, + Attribute + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; + *Mapping = NULL; + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. +**/ +EFI_STATUS +IoMmuUnmap ( + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + + if (mIoMmu != NULL) { + Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0); + Status = mIoMmu->Unmap (mIoMmu, Mapping); + } else { + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + EFI_PHYSICAL_ADDRESS HostPhyAddress; + + *HostAddress = NULL; + *DeviceAddress = 0; + + if (mIoMmu != NULL) { + Status = mIoMmu->AllocateBuffer ( + mIoMmu, + EfiBootServicesData, + Pages, + HostAddress, + 0 + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + NumberOfBytes = EFI_PAGES_TO_SIZE(Pages); + Status = mIoMmu->Map ( + mIoMmu, + EdkiiIoMmuOperationBusMasterCommonBuffer, + *HostAddress, + &NumberOfBytes, + DeviceAddress, + Mapping + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + Status = mIoMmu->SetAttribute ( + mIoMmu, + *Mapping, + EDKII_IOMMU_ACCESS_READ | EDKII_IOMMU_ACCESS_WRITE + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + Pages, + &HostPhyAddress + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + *HostAddress = (VOID *)(UINTN)HostPhyAddress; + *DeviceAddress = HostPhyAddress; + *Mapping = NULL; + } + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + + if (mIoMmu != NULL) { + Status = mIoMmu->SetAttribute (mIoMmu, Mapping, 0); + Status = mIoMmu->Unmap (mIoMmu, Mapping); + Status = mIoMmu->FreeBuffer (mIoMmu, Pages, HostAddress); + } else { + Status = EFI_SUCCESS; + } + return Status; +} + +/** + Initialize IOMMU. +**/ +VOID +IoMmuInit ( + VOID + ) +{ + PeiServicesLocatePpi ( + &gEdkiiIoMmuPpiGuid, + 0, + NULL, + (VOID **)&mIoMmu + ); +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c new file mode 100644 index 000000000..1d53fcd23 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.c @@ -0,0 +1,653 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 + }, + { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + SdBlockIoPeimEndOfPei + }, + { // 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) || (DeviceIndex > SD_PEIM_MAX_SLOTS)) { + 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) || (DeviceIndex > SD_PEIM_MAX_SLOTS)) { + 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; +} + +/** + One notified function to cleanup the allocated DMA buffers at the end of PEI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notification + event that caused this function to execute. + @param[in] Ppi Pointer to the PPI data associated with this function. + + @retval EFI_SUCCESS The function completes successfully + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimEndOfPei ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + SD_PEIM_HC_PRIVATE_DATA *Private; + + Private = GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor); + + if ((Private->Pool != NULL) && (Private->Pool->Head != NULL)) { + SdPeimFreeMemPool (Private->Pool); + } + + 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 +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; + } + + IoMmuInit (); + + 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); + PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList); + } else { + if (Private->Pool->Head != NULL) { + SdPeimFreeMemPool (Private->Pool); + } + } + } + + return EFI_SUCCESS; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h new file mode 100644 index 000000000..7fd6dba92 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.h @@ -0,0 +1,510 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SD_BLOCK_IO_PEI_H_ +#define _SD_BLOCK_IO_PEI_H_ + +#include + +#include +#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; + + // + // EndOfPei callback is used to do the cleanups before exit of PEI phase. + // + EFI_PEI_NOTIFY_DESCRIPTOR EndOfPeiNotifyList; + + 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) +#define GET_SD_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY(a) CR (a, SD_PEIM_HC_PRIVATE_DATA, EndOfPeiNotifyList, SD_PEIM_SIG) + +struct _SD_TRB { + SD_PEIM_HC_SLOT *Slot; + UINT16 BlockSize; + + SD_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + EFI_PHYSICAL_ADDRESS DataPhy; + VOID *DataMap; + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Initialize IOMMU. +**/ +VOID +IoMmuInit ( + VOID + ); + +/** + Provides the controller-specific addresses required to access system memory from a + DMA bus master. + + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +IoMmuMap ( + IN EDKII_IOMMU_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. +**/ +EFI_STATUS +IoMmuUnmap ( + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an OperationBusMasterCommonBuffer or + OperationBusMasterCommonBuffer64 mapping. + + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +IoMmuAllocateBuffer ( + IN UINTN Pages, + OUT VOID **HostAddress, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +IoMmuFreeBuffer ( + IN UINTN Pages, + IN VOID *HostAddress, + IN VOID *Mapping + ); + +/** + One notified function to cleanup the allocated DMA buffers at the end of PEI. + + @param[in] PeiServices Pointer to PEI Services Table. + @param[in] NotifyDescriptor Pointer to the descriptor for the Notification + event that caused this function to execute. + @param[in] Ppi Pointer to the PPI data associated with this function. + + @retval EFI_SUCCESS The function completes successfully + +**/ +EFI_STATUS +EFIAPI +SdBlockIoPeimEndOfPei ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf new file mode 100644 index 000000000..2647192e3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf @@ -0,0 +1,60 @@ +## @file +# Description file for the SD memory card Peim driver. +# +# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[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 EBC +# + +[Sources] + SdBlockIoPei.c + SdBlockIoPei.h + SdHci.c + SdHci.h + SdHcMem.c + SdHcMem.h + DmaMem.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES + gEfiPeiVirtualBlockIo2PpiGuid ## PRODUCES + gEdkiiPeiSdMmcHostControllerPpiGuid ## CONSUMES + gEdkiiIoMmuPpiGuid ## CONSUMES + gEfiEndOfPeiSignalPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiSdMmcHostControllerPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SdBlockIoPeiExtra.uni + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni new file mode 100644 index 000000000..d2de43e5e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.uni @@ -0,0 +1,14 @@ +// /** @file +// The SdBlockIoPei driver is used to support recovery from SD memory card device. +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#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/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni new file mode 100644 index 000000000..1bbdb8c71 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPeiExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// SdBlockIoPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"SD BlockIo Peim for Recovery" + + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c new file mode 100644 index 000000000..fb043c19f --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.c @@ -0,0 +1,429 @@ +/** @file + +Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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; + VOID *BufHost; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS MappedAddr; + EFI_STATUS Status; + VOID *TempPtr; + + 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 = IoMmuAllocateBuffer ( + Pages, + &BufHost, + &MappedAddr, + &Mapping + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)BufHost, EFI_PAGES_TO_SIZE (Pages)); + + Block->BufHost = (UINT8 *) (UINTN) BufHost; + Block->Buf = (UINT8 *) (UINTN) MappedAddr; + Block->Mapping = Mapping; + 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)); + + IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping); +} + +/** + 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 consecutive 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; +} + + + +/** + 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. + // + 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 array + // + 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/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h new file mode 100644 index 000000000..c6bda2d1e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHcMem.h @@ -0,0 +1,56 @@ +/** @file + +Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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; + UINT8 *BufHost; + UINTN BufLen; // Memory size in bytes + VOID *Mapping; + 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/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c new file mode 100644 index 000000000..756c3063b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.c @@ -0,0 +1,2957 @@ +/** @file + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 == 0) { + return EFI_INVALID_PARAMETER; + } + + if (ClockFreq > (BaseClkFreq * 1000)) { + ClockFreq = BaseClkFreq * 1000; + } + + // + // 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 = Trb->DataPhy; + 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; + EDKII_IOMMU_OPERATION MapOp; + UINTN MapLength; + + // + // Calculate a divisor for SD clock frequency + // + Status = SdPeimHcGetCapability (Slot->SdHcBase, &Capability); + if (EFI_ERROR (Status)) { + return NULL; + } + + Trb = AllocateZeroPool (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 != 0) && (Trb->DataLen < Trb->BlockSize)) { + Trb->BlockSize = (UINT16)Trb->DataLen; + } + + if (Packet->SdCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK) { + Trb->Mode = SdPioMode; + } else { + if (Trb->Read) { + MapOp = EdkiiIoMmuOperationBusMasterWrite; + } else { + MapOp = EdkiiIoMmuOperationBusMasterRead; + } + + if (Trb->DataLen != 0) { + MapLength = Trb->DataLen; + Status = IoMmuMap (MapOp, Trb->Data, &MapLength, &Trb->DataPhy, &Trb->DataMap); + + if (EFI_ERROR (Status) || (MapLength != Trb->DataLen)) { + DEBUG ((DEBUG_ERROR, "SdPeimCreateTrb: Fail to map data buffer.\n")); + goto Error; + } + } + + 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->DataMap != NULL)) { + IoMmuUnmap (Trb->DataMap); + } + + if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) { + SdPeimFreeMem (Trb->Slot->Private->Pool, Trb->AdmaDesc, Trb->AdmaDescSize); + } + + if (Trb != NULL) { + FreePool (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; + } 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->DataPhy >= 0x100000000ul) { + return EFI_INVALID_PARAMETER; + } + + SdmaAddr = (UINT32)(UINTN)Trb->DataPhy; + 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 = 0; + if (Trb->Mode != SdNoData) { + // + // Calculate Block Count. + // + 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 > 1) { + TransMode |= BIT5 | BIT1; + } + // + // SD memory card needs to use AUTO CMD12 feature. + // + 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; + UINT32 PioLength; + + 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->DataPhy, SD_SDMA_BOUNDARY); + Status = SdPeimHcRwMmio ( + Bar + SD_HC_SDMA_ADDR, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + if (EFI_ERROR (Status)) { + goto Done; + } + Trb->DataPhy = (UINT32)(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) { + // + // When performing tuning procedure (Execute Tuning is set to 1) through PIO mode, + // wait Buffer Read Ready bit of Normal Interrupt Status Register to be 1. + // Refer to SD Host Controller Simplified Specification 3.0 figure 2-29 for details. + // + if ((IntStatus & BIT5) == BIT5) { + // + // Clear Buffer Read Ready interrupt at first. + // + IntStatus = BIT5; + SdPeimHcRwMmio (Bar + SD_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + // + // Read data out from Buffer Port register + // + for (PioLength = 0; PioLength < Trb->DataLen; PioLength += 4) { + SdPeimHcRwMmio (Bar + SD_HC_BUF_DAT_PORT, TRUE, 4, (UINT8*)Trb->Data + PioLength); + } + 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. + @param[out] SwitchResp The return switch function status. + + @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, + OUT UINT8 *SwitchResp + ) +{ + SD_COMMAND_BLOCK SdCmdBlk; + SD_STATUS_BLOCK SdStatusBlk; + SD_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 ModeValue; + + 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 = SwitchResp; + Packet.InTransferLength = 64; + + 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; +} + +/** + Tuning 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)) == 0) { + break; + } + + if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) { + return EFI_SUCCESS; + } + } while (++Retry < 40); + + DEBUG ((EFI_D_ERROR, "SdPeimTuningClock: Send tuning block fails at %d times with HostCtrl2 %02x\n", Retry, HostCtrl2)); + // + // Abort the tuning procedure and reset the tuning circuit. + // + HostCtrl2 = (UINT8)~(BIT6 | BIT7); + Status = SdPeimHcAndMmio (Slot->SdHcBase + SD_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + return EFI_DEVICE_ERROR; +} + +/** + 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; + UINT8 SwitchResp[64]; + + 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; + } + + // + // Get the supported bus speed from SWITCH cmd return data group #1. + // + ZeroMem (SwitchResp, sizeof (SwitchResp)); + Status = SdPeimSwitch (Slot, 0xF, 0xF, 0xF, 0xF, FALSE, SwitchResp); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Calculate supported bus speed/bus width/clock frequency by host and device capability. + // + ClockFreq = 0; + if (S18a && (Capability.Sdr104 != 0) && ((SwitchResp[13] & BIT3) != 0)) { + ClockFreq = 208; + AccessMode = 3; + } else if (S18a && (Capability.Sdr50 != 0) && ((SwitchResp[13] & BIT2) != 0)) { + ClockFreq = 100; + AccessMode = 2; + } else if (S18a && (Capability.Ddr50 != 0) && ((SwitchResp[13] & BIT4) != 0)) { + ClockFreq = 50; + AccessMode = 4; + } else if ((SwitchResp[13] & BIT1) != 0) { + ClockFreq = 50; + AccessMode = 1; + } else { + ClockFreq = 25; + AccessMode = 0; + } + + DEBUG ((EFI_D_INFO, "SdPeimSetBusMode: AccessMode %d ClockFreq %d BusWidth %d\n", AccessMode, ClockFreq, BusWidth)); + + Status = SdPeimSwitch (Slot, AccessMode, 0xF, 0xF, 0xF, TRUE, SwitchResp); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimSwitch fails with %r\n", Status)); + return Status; + } + + if ((SwitchResp[16] & 0xF) != AccessMode) { + DEBUG ((EFI_D_ERROR, "SdPeimSetBusMode: SdPeimSwitch to AccessMode %d ClockFreq %d BusWidth %d fails! The Switch response is 0x%1x\n", AccessMode, ClockFreq, BusWidth, SwitchResp[16] & 0xF)); + return EFI_DEVICE_ERROR; + } + // + // Set to High 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; + UINTN Retry; + // + // 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. + // + Ocr = 0; + Retry = 0; + 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; + } + + if (Retry++ == 100) { + DEBUG ((EFI_D_ERROR, "SdPeimIdentification: SdPeimSendOpCond fails too many times\n")); + return EFI_DEVICE_ERROR; + } + MicroSecondDelay (10 * 1000); + } 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 (1000); + + 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/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h new file mode 100644 index 000000000..c31297521 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdBlockIoPei/SdHci.h @@ -0,0 +1,350 @@ +/** @file + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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. + @param[out] SwitchResp The return switch function status. + + @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, + OUT UINT8 *SwitchResp + ); + +/** + 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/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c new file mode 100644 index 000000000..acd29d72c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/ComponentName.c @@ -0,0 +1,234 @@ +/** @file + UEFI Component Name(2) protocol implementation for SdDxe driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c new file mode 100644 index 000000000..721f26437 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.c @@ -0,0 +1,1381 @@ +/** @file + The helper functions for BlockIo and BlockIo2 protocol. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SdDxe.h" + +/** + Nonblocking I/O callback function 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; + if (Rca != 0) { + 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_NOTIFY); + 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_NOTIFY, + 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)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&RwSingleBlkReq->Link); + gBS->RestoreTPL (OldTpl); + if (RwSingleBlkReq->Event != NULL) { + gBS->CloseEvent (RwSingleBlkReq->Event); + } + FreePool (RwSingleBlkReq); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (RwSingleBlkReq != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&RwSingleBlkReq->Link); + gBS->RestoreTPL (OldTpl); + 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_NOTIFY); + 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_NOTIFY, + 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_NOTIFY); + 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_NOTIFY); + RemoveEntryList (&RwMultiBlkReq->Link); + gBS->RestoreTPL (OldTpl); + 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_CHANGED The MediaId does not match 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 ((DEBUG_BLKIO, "Sd%a(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", + IsRead ? "Read" : "Write", Lba, BlockNum, + (Token != NULL) ? Token->Event : NULL, 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 match 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_CHANGED The MediaId does not match 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_NOTIFY); + 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_CHANGED The MediaId does not match 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; +} + +/** + Set the erase start address through sync or async I/O request. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] StartLba The starting logical block address to be erased. + @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 +SdEraseBlockStart ( + IN SD_DEVICE *Device, + IN EFI_LBA StartLba, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_REQUEST *EraseBlockStart; + EFI_TPL OldTpl; + + EraseBlockStart = NULL; + PassThru = Device->Private->PassThru; + + EraseBlockStart = AllocateZeroPool (sizeof (SD_REQUEST)); + if (EraseBlockStart == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlockStart->Signature = SD_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Device->Queue, &EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + EraseBlockStart->Packet.SdMmcCmdBlk = &EraseBlockStart->SdMmcCmdBlk; + EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk; + EraseBlockStart->Packet.Timeout = SD_GENERIC_TIMEOUT; + + EraseBlockStart->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_START; + EraseBlockStart->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (Device->SectorAddressing) { + EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba; + } else { + EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Device->BlockMedia.BlockSize); + } + + EraseBlockStart->IsEnd = IsEnd; + EraseBlockStart->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlockStart, + &EraseBlockStart->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlockStart->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->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) && (EraseBlockStart != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlockStart->Event != NULL) { + gBS->CloseEvent (EraseBlockStart->Event); + } + FreePool (EraseBlockStart); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlockStart != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockStart->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlockStart); + } + } + + return Status; +} + +/** + Set the erase end address through sync or async I/O request. + + @param[in] Device A pointer to the SD_DEVICE instance. + @param[in] EndLba The ending logical block address to be erased. + @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 +SdEraseBlockEnd ( + IN SD_DEVICE *Device, + IN EFI_LBA EndLba, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_REQUEST *EraseBlockEnd; + EFI_TPL OldTpl; + + EraseBlockEnd = NULL; + PassThru = Device->Private->PassThru; + + EraseBlockEnd = AllocateZeroPool (sizeof (SD_REQUEST)); + if (EraseBlockEnd == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlockEnd->Signature = SD_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Device->Queue, &EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + EraseBlockEnd->Packet.SdMmcCmdBlk = &EraseBlockEnd->SdMmcCmdBlk; + EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk; + EraseBlockEnd->Packet.Timeout = SD_GENERIC_TIMEOUT; + + EraseBlockEnd->SdMmcCmdBlk.CommandIndex = SD_ERASE_WR_BLK_END; + EraseBlockEnd->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (Device->SectorAddressing) { + EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba; + } else { + EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Device->BlockMedia.BlockSize); + } + + EraseBlockEnd->IsEnd = IsEnd; + EraseBlockEnd->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlockEnd, + &EraseBlockEnd->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlockEnd->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->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) && (EraseBlockEnd != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlockEnd->Event != NULL) { + gBS->CloseEvent (EraseBlockEnd->Event); + } + FreePool (EraseBlockEnd); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlockEnd != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlockEnd->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlockEnd); + } + } + + return Status; +} + +/** + Erase specified blocks through sync or async I/O request. + + @param[in] Device A pointer to the SD_DEVICE instance. + @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 +SdEraseBlock ( + IN SD_DEVICE *Device, + IN EFI_BLOCK_IO2_TOKEN *Token, + IN BOOLEAN IsEnd + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + SD_REQUEST *EraseBlock; + EFI_TPL OldTpl; + + EraseBlock = NULL; + PassThru = Device->Private->PassThru; + + EraseBlock = AllocateZeroPool (sizeof (SD_REQUEST)); + if (EraseBlock == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + EraseBlock->Signature = SD_REQUEST_SIGNATURE; + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Device->Queue, &EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + EraseBlock->Packet.SdMmcCmdBlk = &EraseBlock->SdMmcCmdBlk; + EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk; + EraseBlock->Packet.Timeout = SD_GENERIC_TIMEOUT; + + EraseBlock->SdMmcCmdBlk.CommandIndex = SD_ERASE; + EraseBlock->SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + + EraseBlock->IsEnd = IsEnd; + EraseBlock->Token = Token; + + if ((Token != NULL) && (Token->Event != NULL)) { + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + AsyncIoCallback, + EraseBlock, + &EraseBlock->Event + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } else { + EraseBlock->Event = NULL; + } + + Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->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) && (EraseBlock != NULL)) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + if (EraseBlock->Event != NULL) { + gBS->CloseEvent (EraseBlock->Event); + } + FreePool (EraseBlock); + } + } else { + // + // For synchronous operation, free request whatever the execution result is. + // + if (EraseBlock != NULL) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + RemoveEntryList (&EraseBlock->Link); + gBS->RestoreTPL (OldTpl); + FreePool (EraseBlock); + } + } + + return Status; +} + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +SdEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_MEDIA *Media; + UINTN BlockSize; + UINTN BlockNum; + EFI_LBA LastLba; + SD_DEVICE *Device; + + Status = EFI_SUCCESS; + Device = SD_DEVICE_DATA_FROM_ERASEBLK (This); + Media = &Device->BlockMedia; + + if (MediaId != Media->MediaId) { + return EFI_MEDIA_CHANGED; + } + + if (Media->ReadOnly) { + return EFI_WRITE_PROTECTED; + } + + // + // Check parameters. + // + BlockSize = Media->BlockSize; + if ((Size % BlockSize) != 0) { + return EFI_INVALID_PARAMETER; + } + + BlockNum = Size / BlockSize; + if ((Lba + BlockNum - 1) > Media->LastBlock) { + return EFI_INVALID_PARAMETER; + } + + if ((Token != NULL) && (Token->Event != NULL)) { + Token->TransactionStatus = EFI_SUCCESS; + } + + LastLba = Lba + BlockNum - 1; + + Status = SdEraseBlockStart (Device, Lba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdEraseBlockEnd (Device, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdEraseBlock (Device, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG (( + DEBUG_INFO, + "SdEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", + Lba, + BlockNum, + (Token != NULL) ? Token->Event : NULL, + Status + )); + + return Status; +} + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h new file mode 100644 index 000000000..b6b4c45f1 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdBlockIo.h @@ -0,0 +1,252 @@ +/** @file + Header file for SdDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 match 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_CHANGED The MediaId does not match 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_CHANGED The MediaId does not match 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 + ); + +/** + Erase a specified number of device blocks. + + @param[in] This Indicates a pointer to the calling context. + @param[in] MediaId The media ID that the erase request is for. + @param[in] Lba The starting logical block address to be + erased. The caller is responsible for erasing + only legitimate locations. + @param[in, out] Token A pointer to the token associated with the + transaction. + @param[in] Size The size in bytes to be erased. This must be + a multiple of the physical block size of the + device. + + @retval EFI_SUCCESS The erase request was queued if Event is not + NULL. The data was erased correctly to the + device if the Event is NULL.to the device. + @retval EFI_WRITE_PROTECTED The device cannot be erased due to write + protection. + @retval EFI_DEVICE_ERROR The device reported an error while attempting + to perform the erase operation. + @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not + valid. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + +**/ +EFI_STATUS +EFIAPI +SdEraseBlocks ( + IN EFI_ERASE_BLOCK_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN OUT EFI_ERASE_BLOCK_TOKEN *Token, + IN UINTN Size + ); + +#endif + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.c new file mode 100644 index 000000000..6563f7232 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.c @@ -0,0 +1,132 @@ +/** @file + Implement the EFI_DISK_INFO_PROTOCOL interface on SD memory card devices. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SdDxe.h" + +/** + Provides inquiry information for the controller type. + + This function is used by the driver entity to get inquiry data. Data format of + Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] InquiryData Pointer to a buffer for the inquiry data. + @param[in,out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading InquiryData from device. + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ) +{ + EFI_STATUS Status; + SD_DEVICE *Device; + + Device = SD_DEVICE_DATA_FROM_DISKINFO (This); + + if (*InquiryDataSize >= sizeof (Device->Cid)) { + Status = EFI_SUCCESS; + CopyMem (InquiryData, &Device->Cid, sizeof (Device->Cid)); + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *InquiryDataSize = sizeof (Device->Cid); + + return Status; +} + +/** + Provides identify information for the controller type. + + This function is used by the driver entity to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in,out] IdentifyData Pointer to a buffer for the identify data. + @param[in,out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device. + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ) +{ + return EFI_NOT_FOUND; +} + +/** + Provides sense data information for the controller type. + + This function is used by the driver entity to get sense data. Data format of + Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] SenseData Pointer to the SenseData. + @param[in,out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ) +{ + return EFI_NOT_FOUND; +} + +/** + Provides IDE channel and device information for the interface. + + This function is used by the driver entity to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.h new file mode 100644 index 000000000..64e2f77c9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDiskInfo.h @@ -0,0 +1,109 @@ +/** @file + Header file for EFI_DISK_INFO_PROTOCOL interface on SD memory card devices. + + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SD_DISKINFO_H_ +#define _SD_DISKINFO_H_ + +/** + Provides inquiry information for the controller type. + + This function is used by the driver entity to get inquiry data. Data format of + Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] InquiryData Pointer to a buffer for the inquiry data. + @param[in,out] InquiryDataSize Pointer to the value for the inquiry data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading InquiryData from device. + @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoInquiry ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *InquiryData, + IN OUT UINT32 *InquiryDataSize + ); + +/** + Provides identify information for the controller type. + + This function is used by the driver entity to get identify data. Data format + of Identify data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL + instance. + @param[in,out] IdentifyData Pointer to a buffer for the identify data. + @param[in,out] IdentifyDataSize Pointer to the value for the identify data + size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading IdentifyData from device. + @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoIdentify ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *IdentifyData, + IN OUT UINT32 *IdentifyDataSize + ); + +/** + Provides sense data information for the controller type. + + This function is used by the driver entity to get sense data. Data format of + Sense data is defined by the Interface GUID. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[in,out] SenseData Pointer to the SenseData. + @param[in,out] SenseDataSize Size of SenseData in bytes. + @param[out] SenseDataNumber Pointer to the value for the sense data size. + + @retval EFI_SUCCESS The command was accepted without any errors. + @retval EFI_NOT_FOUND Device does not support this data class. + @retval EFI_DEVICE_ERROR Error reading SenseData from device. + @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoSenseData ( + IN EFI_DISK_INFO_PROTOCOL *This, + IN OUT VOID *SenseData, + IN OUT UINT32 *SenseDataSize, + OUT UINT8 *SenseDataNumber + ); + +/** + Provides IDE channel and device information for the interface. + + This function is used by the driver entity to get controller information. + + @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. + @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary. + @param[out] IdeDevice Pointer to the Ide Device number. Master or slave. + + @retval EFI_SUCCESS IdeChannel and IdeDevice are valid. + @retval EFI_UNSUPPORTED This is not an IDE device. + +**/ +EFI_STATUS +EFIAPI +SdDiskInfoWhichIde ( + IN EFI_DISK_INFO_PROTOCOL *This, + OUT UINT32 *IdeChannel, + OUT UINT32 *IdeDevice + ); + +#endif diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c new file mode 100644 index 000000000..73bcc181c --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.c @@ -0,0 +1,908 @@ +/** @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 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#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 + }, + { // EraseBlock + EFI_ERASE_BLOCK_PROTOCOL_REVISION, + 1, + SdEraseBlocks + }, + { // DiskInfo + EFI_DISK_INFO_SD_MMC_INTERFACE_GUID, + SdDiskInfoInquiry, + SdDiskInfoIdentify, + SdDiskInfoSenseData, + SdDiskInfoWhichIde + }, + { // 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)); + + AsciiStrToUnicodeStrS (String, Device->ModelName, sizeof (Device->ModelName) / sizeof (Device->ModelName[0])); + + 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; + + if (Csd->EraseBlkEn) { + Device->EraseBlock.EraseLengthGranularity = 1; + } else { + Device->EraseBlock.EraseLengthGranularity = (Csd->SectorSize + 1) * (1 << (Csd->WriteBlLen - 9)); + } + + 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", + gSdDxeComponentName2.SupportedLanguages, + &Device->ControllerNameTable, + Device->ModelName, + FALSE + ); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Device->Handle, + &gEfiDevicePathProtocolGuid, + Device->DevicePath, + &gEfiBlockIoProtocolGuid, + &Device->BlockIo, + &gEfiBlockIo2ProtocolGuid, + &Device->BlockIo2, + &gEfiEraseBlockProtocolGuid, + &Device->EraseBlock, + &gEfiDiskInfoProtocolGuid, + &Device->DiskInfo, + 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 failed 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_NOTIFY); + 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, + &gEfiEraseBlockProtocolGuid, + &Device->EraseBlock, + &gEfiDiskInfoProtocolGuid, + &Device->DiskInfo, + 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/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h new file mode 100644 index 000000000..ff740a521 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.h @@ -0,0 +1,475 @@ +/** @file + Header file for SdDxe Driver. + + This file defines common data structures, macro definitions and some module + internal function header files. + + Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _SD_DXE_H_ +#define _SD_DXE_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SdBlockIo.h" +#include "SdDiskInfo.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) + +#define SD_DEVICE_DATA_FROM_ERASEBLK(a) \ + CR(a, SD_DEVICE, EraseBlock, SD_DEVICE_SIGNATURE) + +#define SD_DEVICE_DATA_FROM_DISKINFO(a) \ + CR(a, SD_DEVICE, DiskInfo, 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; + EFI_ERASE_BLOCK_PROTOCOL EraseBlock; + EFI_DISK_INFO_PROTOCOL DiskInfo; + + 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 failed 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/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf new file mode 100644 index 000000000..69b856b66 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf @@ -0,0 +1,64 @@ +## @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 - 2018, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[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 EBC +# +# DRIVER_BINDING = gSdDxeDriverBinding +# COMPONENT_NAME = gSdDxeComponentName +# COMPONENT_NAME2 = gSdDxeComponentName2 +# + +[Sources.common] + ComponentName.c + SdDxe.c + SdDxe.h + SdBlockIo.c + SdBlockIo.h + SdDiskInfo.c + SdDiskInfo.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + +[Protocols] + gEfiSdMmcPassThruProtocolGuid ## TO_START + gEfiBlockIoProtocolGuid ## BY_START + gEfiBlockIo2ProtocolGuid ## BY_START + gEfiEraseBlockProtocolGuid ## BY_START + gEfiDiskInfoProtocolGuid ## BY_START + ## TO_START + ## BY_START + gEfiDevicePathProtocolGuid + diff --git a/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni new file mode 100644 index 000000000..eb8ebd5a3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxe.uni @@ -0,0 +1,15 @@ +// /** @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.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#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/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni new file mode 100644 index 000000000..eb8ebd5a3 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Bus/Sd/SdDxe/SdDxeExtra.uni @@ -0,0 +1,15 @@ +// /** @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.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#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." + -- cgit 1.2.3-korg