From: Feng Tian Date: Wed, 29 Apr 2015 02:42:58 +0000 (+0000) Subject: MdeModulePkg: Add UFS (Universal Flash Storage) Stack X-Git-Tag: edk2-stable201903~9981 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=0591696eff9962b52b3b0137e865198096353573;ds=sidebyside MdeModulePkg: Add UFS (Universal Flash Storage) Stack It includes 4 drivers: 1. UfsPassThruDxe, which is a UEFI driver and consumes EFI_UFS_HOST_CONTROLLER_PROTOCOL and produces EFI_EXT_SCSI_PASS_THRU_PROTOCOL 2. UfsPciHcDxe, which is specific for pci-based UFS HC implementation and is a UEFI driver to produce EFI_UFS_HOST_CONTROLLER_PROTOCOL. 3. UfsBlockIoPei, which is a PEI driver and consumes EFI_UFS_HOST_CONTROLLER_PPI and produces EFI_PEI_VIRTUAL_BLOCK_IO_PPI. 4. UfsPciHcPei, which is specific for pci-based UFS HC implementation and is a PEI driver to produce EFI_UFS_HOST_CONTROLLER_PPI. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Feng Tian Reviewed-by: Star Zeng git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17246 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c new file mode 100644 index 0000000000..1e2db1a876 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/ComponentName.c @@ -0,0 +1,225 @@ +/** @file + UfsHcDxe driver produces EFI_UFS_HOST_CONTROLLER_PROTOCOL. The upper layer module + uses it to query the MMIO base address of the UFS host controller. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsPciHcDxe.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsHcComponentName = { + UfsHcComponentNameGetDriverName, + UfsHcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsHcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UfsHcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UfsHcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsHcDriverNameTable[] = { + { + "eng;en", + L"Universal Flash Storage (UFS) Pci Host Controller Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsHcControllerNameTable[] = { + { + "eng;en", + L"Universal Flash Storage (UFS) Pci Host Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UfsHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUfsHcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUfsHcComponentName) + ); +} + +/** + 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 +UfsHcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + if (Language == NULL || ControllerName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing Controller Handle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gUfsHcDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUfsHcControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUfsHcComponentName) + ); + +} diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c new file mode 100644 index 0000000000..586a52b380 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.c @@ -0,0 +1,699 @@ +/** @file + UfsHcDxe driver is used to provide platform-dependent info, mainly UFS host controller + MMIO base, to upper layer UFS drivers. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsPciHcDxe.h" + +// +// NVM Express Driver Binding Protocol Instance +// +EFI_DRIVER_BINDING_PROTOCOL gUfsHcDriverBinding = { + UfsHcDriverBindingSupported, + UfsHcDriverBindingStart, + UfsHcDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for Ufs host controller private data. +// +UFS_HOST_CONTROLLER_PRIVATE_DATA gUfsHcTemplate = { + UFS_HC_PRIVATE_DATA_SIGNATURE, // Signature + NULL, // Handle + { // UfsHcProtocol + UfsHcGetMmioBar, + UfsHcAllocateBuffer, + UfsHcFreeBuffer, + UfsHcMap, + UfsHcUnmap, + UfsHcFlush + }, + NULL, // PciIo + 0 // PciAttributes +}; + +/** + Get the MMIO base of the UFS host controller. + + @param[in] This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param[out] MmioBar The MMIO base address of UFS host controller. + + @retval EFI_SUCCESS The operation succeeds. + @retval others The operation fails. +**/ +EFI_STATUS +EFIAPI +UfsHcGetMmioBar ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + OUT UINTN *MmioBar + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + if ((This == NULL) || (MmioBar == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + PCI_BASE_ADDRESSREG_OFFSET, + sizeof (UINT32), + MmioBar + ); + + if (!EFI_ERROR (Status)) { + *MmioBar &= (UINTN)~0xF; + } + return Status; +} + +/** + Provides the UFS controller-specific addresses needed to access system memory. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @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 UFS 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 UFS 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 +EFIAPI +UfsHcMap ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EDKII_UFS_HOST_CONTROLLER_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + if ((This == NULL) || (HostAddress == NULL) || (NumberOfBytes == NULL) || (DeviceAddress == NULL) || (Mapping == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->Map (PciIo, Operation, HostAddress, NumberOfBytes, DeviceAddress, Mapping); + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +EFI_STATUS +EFIAPI +UfsHcUnmap ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN VOID *Mapping + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + if ((This == NULL) || (Mapping == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->Unmap (PciIo, Mapping); + return Status; +} + +/** + Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @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 +EFIAPI +UfsHcAllocateBuffer ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + if ((This == NULL) || (HostAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->AllocateBuffer (PciIo, Type, MemoryType, Pages, HostAddress, Attributes); + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @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 +EFIAPI +UfsHcFreeBuffer ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + if ((This == NULL) || (HostAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->FreeBuffer (PciIo, Pages, HostAddress); + return Status; +} + +/** + Flushes all posted write transactions from the UFS bus to attached UFS device. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + + @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus + to attached UFS device. + @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS + bus to attached UFS device due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +UfsHcFlush ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This + ) +{ + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (This); + PciIo = Private->PciIo; + + Status = PciIo->Flush (PciIo); + 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 +UfsHcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + BOOLEAN UfsHcFound; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 PciData; + + PciIo = NULL; + ParentDevicePath = NULL; + UfsHcFound = FALSE; + + // + // UfsHcDxe is a device driver, and should ingore the + // "RemainingDevicePath" according to EFI spec + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID *) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error + // + return Status; + } + // + // Close the protocol because we don't use it here + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Now test the EfiPciIoProtocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Now further check the PCI header: Base class (offset 0x0B) and + // Sub Class (offset 0x0A). This controller should be an UFS controller + // + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + return EFI_UNSUPPORTED; + } + // + // Since we already got the PciData, we can close protocol to avoid to carry it on for multiple exit points. + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Examine UFS Host Controller PCI Configuration table fields + // + if (PciData.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE) { + if (PciData.Hdr.ClassCode[1] == 0x09 ) { //UFS Controller Subclass + UfsHcFound = TRUE; + } + } + + if (!UfsHcFound) { + return EFI_UNSUPPORTED; + } + + return Status; +} + + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +UfsHcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + UINT64 Supports; + + PciIo = NULL; + Private = NULL; + Supports = 0; + + // + // Now test and open the EfiPciIoProtocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + // + // Status == 0 - A normal execution flow, SUCCESS and the program proceeds. + // Status == ALREADY_STARTED - A non-zero Status code returned. It indicates + // that the protocol has been opened and should be treated as a + // normal condition and the program proceeds. The Protocol will not + // opened 'again' by this call. + // Status != ALREADY_STARTED - Error status, terminate program execution + // + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error + // + return Status; + } + + Private = AllocateCopyPool (sizeof (UFS_HOST_CONTROLLER_PRIVATE_DATA), &gUfsHcTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Private->PciIo = PciIo; + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationGet, + 0, + &Private->PciAttributes + ); + + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + + if (!EFI_ERROR (Status)) { + Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE; + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationEnable, + Supports, + NULL + ); + } else { + goto Done; + } + + /// + /// Install UFS_HOST_CONTROLLER protocol + /// + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEdkiiUfsHostControllerProtocolGuid, + EFI_NATIVE_INTERFACE, + (VOID*)&(Private->UfsHc) + ); + +Done: + if (EFI_ERROR (Status)) { + if ((Private != NULL) && (Private->PciAttributes != 0)) { + // + // Restore original PCI attributes + // + Status = PciIo->Attributes ( + PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + } + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + if (Private != NULL) { + FreePool (Private); + } + } + + return Status; +} + + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been moved + into this common boot service. It is legal to call Stop() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this + same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in this driver's + Start() function, and the Start() function must have called OpenProtocol() on + ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +UfsHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UFS_HOST_CONTROLLER_PRIVATE_DATA *Private; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + /// + /// Get private data + /// + Status = gBS->OpenProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + (VOID **) &UfsHc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Private = UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC (UfsHc); + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + &(Private->UfsHc) + ); + if (!EFI_ERROR (Status)) { + // + // Restore original PCI attributes + // + Status = Private->PciIo->Attributes ( + Private->PciIo, + EfiPciIoAttributeOperationSet, + Private->PciAttributes, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // + // Close protocols opened by UFS host controller driver + // + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool (Private); + } + + return Status; +} + +/** + The entry point for UFS host controller driver, used to install this driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +UfsHcDriverEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUfsHcDriverBinding, + ImageHandle, + &gUfsHcComponentName, + &gUfsHcComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h new file mode 100644 index 0000000000..f3b7eab24a --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.h @@ -0,0 +1,453 @@ +/** @file + UfsHcDxe driver is used to provide platform-dependent info, mainly UFS host controller + MMIO base, to upper layer UFS drivers. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EFI_UFS_HOST_CONTROLLER_H_ +#define _EFI_UFS_HOST_CONTROLLER_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern EFI_DRIVER_BINDING_PROTOCOL gUfsHcDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gUfsHcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gUfsHcComponentName2; + +// +// Unique signature for private data structure. +// +#define UFS_HC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('U','F','S','H') + +typedef struct _UFS_HOST_CONTROLLER_PRIVATE_DATA UFS_HOST_CONTROLLER_PRIVATE_DATA; + +// +// Nvme private data structure. +// +struct _UFS_HOST_CONTROLLER_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Handle; + + EDKII_UFS_HOST_CONTROLLER_PROTOCOL UfsHc; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 PciAttributes; +}; + +#define UFS_HOST_CONTROLLER_PRIVATE_DATA_FROM_UFSHC(a) \ + CR (a, \ + UFS_HOST_CONTROLLER_PRIVATE_DATA, \ + UfsHc, \ + UFS_HC_PRIVATE_DATA_SIGNATURE \ + ) + +/** + 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 +UfsHcComponentNameGetDriverName ( + 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 +UfsHcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + 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 +UfsHcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +UfsHcDriverBindingStart ( + 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 +UfsHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Get the MMIO base of the UFS host controller. + + @param[in] This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param[out] MmioBar The MMIO base address of UFS host controller. + + @retval EFI_SUCCESS The operation succeeds. + @retval others The operation fails. +**/ +EFI_STATUS +EFIAPI +UfsHcGetMmioBar ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + OUT UINTN *MmioBar + ); + +/** + Provides the UFS controller-specific addresses needed to access system memory. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @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 UFS 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 UFS 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 +EFIAPI +UfsHcMap ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EDKII_UFS_HOST_CONTROLLER_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 This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +EFI_STATUS +EFIAPI +UfsHcUnmap ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @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 +EFIAPI +UfsHcAllocateBuffer ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @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 +EFIAPI +UfsHcFreeBuffer ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Flushes all posted write transactions from the UFS bus to attached UFS device. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + + @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus + to attached UFS device. + @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS + bus to attached UFS device due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +UfsHcFlush ( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf new file mode 100644 index 0000000000..c1ce9ea851 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf @@ -0,0 +1,56 @@ +## @file +# Component Description File For Universal Flash Storage Pci Host Controller Module. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UfsPciHcDxe + MODULE_UNI_FILE = UfsPciHcDxe.uni + FILE_GUID = AF43E178-C2E9-4712-A7CD-08BFDAC7482C + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 0.9 + ENTRY_POINT = UfsHcDriverEntry + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gUfsHcDriverBinding +# COMPONENT_NAME = gUfsHcComponentName +# COMPONENT_NAME2 = gUfsHcComponentName2 + +[Sources] + ComponentName.c + UfsPciHcDxe.c + UfsPciHcDxe.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + DevicePathLib + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiLib + +[Protocols] + gEfiPciIoProtocolGuid ## TO_START + gEfiDevicePathProtocolGuid ## TO_START + gEdkiiUfsHostControllerProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + UfsPciHcDxeExtra.uni \ No newline at end of file diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni new file mode 100644 index 0000000000..5b3a137d80 Binary files /dev/null and b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.uni differ diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni new file mode 100644 index 0000000000..fb359fd615 Binary files /dev/null and b/MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxeExtra.uni differ diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c new file mode 100644 index 0000000000..36325641e7 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.c @@ -0,0 +1,152 @@ +/** @file + UfsPciHcPei driver is used to provide platform-dependent info, mainly UFS host controller + MMIO base, to upper layer UFS drivers. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsPciHcPei.h" + +EDKII_UFS_HOST_CONTROLLER_PPI mUfsHostControllerPpi = { GetUfsHcMmioBar }; + +EFI_PEI_PPI_DESCRIPTOR mPpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEdkiiPeiUfsHostControllerPpiGuid, + &mUfsHostControllerPpi +}; + +/** + Get the MMIO base address of UFS host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the UFS host controller. + @param[out] MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetUfsHcMmioBar ( + IN EDKII_UFS_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ) +{ + UFS_HC_PEI_PRIVATE_DATA *Private; + + if ((This == NULL) || (MmioBar == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = UFS_HC_PEI_PRIVATE_DATA_FROM_THIS (This); + + if (ControllerId >= Private->TotalUfsHcs) { + return EFI_INVALID_PARAMETER; + } + + *MmioBar = (UINTN)Private->UfsHcPciAddr[ControllerId]; + + 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 +InitializeUfsHcPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_BOOT_MODE BootMode; + EFI_STATUS Status; + UINT16 Bus; + UINT16 Device; + UINT16 Function; + UINT32 Size; + UINT8 SubClass; + UINT8 BaseClass; + UFS_HC_PEI_PRIVATE_DATA *Private; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + Status = PeiServicesGetBootMode (&BootMode); + /// + /// We do not export this in S3 boot path, because it is only for recovery. + /// + if (BootMode == BOOT_ON_S3_RESUME) { + return EFI_SUCCESS; + } + + Private = (UFS_HC_PEI_PRIVATE_DATA *) AllocateZeroPool (sizeof (UFS_HC_PEI_PRIVATE_DATA)); + if (Private == NULL) { + DEBUG ((EFI_D_ERROR, "Failed to allocate memory for UFS_HC_PEI_PRIVATE_DATA! \n")); + return EFI_OUT_OF_RESOURCES; + } + + Private->Signature = UFS_HC_PEI_SIGNATURE; + Private->UfsHostControllerPpi = mUfsHostControllerPpi; + Private->PpiList = mPpiList; + Private->PpiList.Ppi = &Private->UfsHostControllerPpi; + + for (Bus = 0; Bus < 256; Bus++) { + for (Device = 0; Device < 32; Device++) { + for (Function = 0; Function < 8; Function++) { + SubClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0A)); + BaseClass = PciRead8 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x0B)); + + if ((SubClass == 0x09) && (BaseClass == PCI_CLASS_MASS_STORAGE)) { + // + // Get the Ufs Pci host controller's MMIO region size. + // + PciAnd16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (UINT16)~(EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE)); + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), 0xFFFFFFFF); + Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET)); + // + // Assign resource to the Ufs Pci host controller's MMIO BAR. + // Enable the Ufs Pci host controller by setting BME and MSE bits of PCI_CMD register. + // + PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_BASE_ADDRESSREG_OFFSET), (UINT32)(PcdGet32 (PcdUfsPciHostControllerMmioBase) + Size * Private->TotalUfsHcs)); + PciOr16 (PCI_LIB_ADDRESS (Bus, Device, Function, PCI_COMMAND_OFFSET), (EFI_PCI_COMMAND_BUS_MASTER | EFI_PCI_COMMAND_MEMORY_SPACE)); + // + // Record the allocated Mmio base address. + // + Private->UfsHcPciAddr[Private->TotalUfsHcs] = PcdGet32 (PcdUfsPciHostControllerMmioBase) + Size * Private->TotalUfsHcs; + Private->TotalUfsHcs++; + ASSERT (Private->TotalUfsHcs < MAX_UFS_HCS); + } + } + } + } + + /// + /// Install Ufs Host Controller PPI + /// + Status = PeiServicesInstallPpi (&Private->PpiList); + + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h new file mode 100644 index 0000000000..2405dd76d8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.h @@ -0,0 +1,62 @@ +/** @file + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_PCI_HOST_CONTROLLER_PEI_H_ +#define _UFS_PCI_HOST_CONTROLLER_PEI_H_ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define UFS_HC_PEI_SIGNATURE SIGNATURE_32 ('U', 'F', 'S', 'P') +#define MAX_UFS_HCS 8 + +typedef struct { + UINTN Signature; + EDKII_UFS_HOST_CONTROLLER_PPI UfsHostControllerPpi; + EFI_PEI_PPI_DESCRIPTOR PpiList; + UINTN TotalUfsHcs; + UINTN UfsHcPciAddr[MAX_UFS_HCS]; +} UFS_HC_PEI_PRIVATE_DATA; + +#define UFS_HC_PEI_PRIVATE_DATA_FROM_THIS(a) CR (a, UFS_HC_PEI_PRIVATE_DATA, UfsHostControllerPpi, UFS_HC_PEI_SIGNATURE) + +/** + Get the MMIO base address of UFS host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the UFS host controller. + @param[out] MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +GetUfsHcMmioBar ( + IN EDKII_UFS_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf new file mode 100644 index 0000000000..b16935628c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf @@ -0,0 +1,56 @@ +## @file +# Component Description File For Universal Flash Storage Pci Host Controller Pei Module. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UfsPciHcPei + MODULE_UNI_FILE = UfsPciHcPei.uni + FILE_GUID = 905DC1AD-C44D-4965-98AC-B6B4444BFD65 + MODULE_TYPE = PEIM + VERSION_STRING = 0.9 + + ENTRY_POINT = InitializeUfsHcPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + UfsPciHcPei.c + UfsPciHcPei.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PciLib + DebugLib + PeiServicesLib + MemoryAllocationLib + PeimEntryPoint + +[Pcd] + gEfiMdeModulePkgTokenSpaceGuid.PcdUfsPciHostControllerMmioBase ## CONSUMES + +[Ppis] + gEdkiiPeiUfsHostControllerPpiGuid ## PRODUCES + +[Depex] + gEfiPeiMasterBootModePpiGuid AND gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + UfsPciHcPeiExtra.uni \ No newline at end of file diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni new file mode 100644 index 0000000000..7b7ea3af7b Binary files /dev/null and b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.uni differ diff --git a/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni new file mode 100644 index 0000000000..4487eb968f Binary files /dev/null and b/MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPeiExtra.uni differ diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c new file mode 100644 index 0000000000..05730ff589 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.c @@ -0,0 +1,995 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsBlockIoPei.h" + +// +// Template for UFS HC Peim Private Data. +// +UFS_PEIM_HC_PRIVATE_DATA gUfsHcPeimTemplate = { + UFS_PEIM_HC_SIG, // Signature + NULL, // Controller + NULL, // Pool + { // BlkIoPpi + UfsBlockIoPeimGetDeviceNo, + UfsBlockIoPeimGetMediaInfo, + UfsBlockIoPeimReadBlocks + }, + { // BlkIoPpiList + EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, + &gEfiPeiVirtualBlockIoPpiGuid, + NULL + }, + { // Media + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + }, + { + UfsDevice, + TRUE, + 0, + 0x1000 + } + }, + 0, // UfsHcBase + 0, // Capabilities + 0, // TaskTag + 0, // UtpTrlBase + 0, // Nutrs + 0, // UtpTmrlBase + 0, // Nutmrs + { // Luns + { + UFS_LUN_0, // Ufs Common Lun 0 + UFS_LUN_1, // Ufs Common Lun 1 + UFS_LUN_2, // Ufs Common Lun 2 + UFS_LUN_3, // Ufs Common Lun 3 + UFS_LUN_4, // Ufs Common Lun 4 + UFS_LUN_5, // Ufs Common Lun 5 + UFS_LUN_6, // Ufs Common Lun 6 + UFS_LUN_7, // Ufs Common Lun 7 + }, + 0x0000, // By default exposing all Luns. + 0x0 + } +}; + +/** + Execute Request Sense SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to output sense data. + @param[out] DataBufferLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimRequestSense ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataBufferLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_REQUEST_SENSE; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.DataDirection = UfsDataIn; + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataBufferLength; + Packet.SenseData = NULL; + Packet.SenseDataLength = 0; + + Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet); + + if (!EFI_ERROR (Status)) { + *DataBufferLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute TEST UNITY READY SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimTestUnitReady ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_TEST_UNIT_READY; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.DataDirection = UfsNoData; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private,(UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + return Status; +} + +/** + Execute INQUIRY SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] Inquiry A pointer to Inquiry data buffer. + @param[out] InquiryLengths The length of output Inquiry data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimInquiry ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *Inquiry, + OUT UINT32 *InquiryLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIX]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_INQUIRY; + Cdb[4] = sizeof (EFI_SCSI_INQUIRY_DATA); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.InDataBuffer = Inquiry; + Packet.InTransferLength = *InquiryLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR (Status)) { + *InquiryLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ CAPACITY(10) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to READ_CAPACITY data buffer. + @param[out] DataLength The length of output READ_CAPACITY data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimReadCapacity ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ_CAPACITY; + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR (Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ CAPACITY(16) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[out] DataBuffer A pointer to READ_CAPACITY data buffer. + @param[out] DataLength The length of output READ_CAPACITY data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimReadCapacity16 ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ_CAPACITY16; + Cdb[1] = 0x10; // Service Action should be 0x10 for UFS device. + Cdb[13] = 0x20; // The maximum number of bytes for returned data. + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR (Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ (10) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be read. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimRead10 ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_TEN]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ10; + WriteUnaligned32 ((UINT32 *)&Cdb[2], SwapBytes32 ((UINT32) StartLba)); + WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 ((UINT16) SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR (Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Execute READ (16) SCSI command on a specific UFS device. + + @param[in] Private A pointer to UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The lun on which the SCSI cmd executed. + @param[in] StartLba The start LBA. + @param[in] SectorNum The sector number to be read. + @param[out] DataBuffer A pointer to data buffer. + @param[out] DataLength The length of output data. + @param[out] SenseData A pointer to output sense data. + @param[out] SenseDataLength The length of output sense data. + + @retval EFI_SUCCESS The command executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send SCSI Request Packet. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsPeimRead16 ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINTN Lun, + IN UINTN StartLba, + IN UINT32 SectorNum, + OUT VOID *DataBuffer, + OUT UINT32 *DataLength, + OUT VOID *SenseData, OPTIONAL + OUT UINT8 *SenseDataLength + ) +{ + UFS_SCSI_REQUEST_PACKET Packet; + UINT8 Cdb[UFS_SCSI_OP_LENGTH_SIXTEEN]; + EFI_STATUS Status; + + ZeroMem (&Packet, sizeof (UFS_SCSI_REQUEST_PACKET)); + ZeroMem (Cdb, sizeof (Cdb)); + + Cdb[0] = EFI_SCSI_OP_READ16; + WriteUnaligned64 ((UINT64 *)&Cdb[2], SwapBytes64 (StartLba)); + WriteUnaligned32 ((UINT32 *)&Cdb[10], SwapBytes32 (SectorNum)); + + Packet.Timeout = UFS_TIMEOUT; + Packet.Cdb = Cdb; + Packet.CdbLength = sizeof (Cdb); + Packet.InDataBuffer = DataBuffer; + Packet.InTransferLength = *DataLength; + Packet.DataDirection = UfsDataIn; + Packet.SenseData = SenseData; + Packet.SenseDataLength = *SenseDataLength; + + Status = UfsExecScsiCmds (Private, (UINT8)Lun, &Packet); + + if (*SenseDataLength != 0) { + *SenseDataLength = Packet.SenseDataLength; + } + + if (!EFI_ERROR (Status)) { + *DataLength = Packet.InTransferLength; + } + + return Status; +} + +/** + Parsing Sense Keys from sense data. + + @param Media The pointer of EFI_PEI_BLOCK_IO_MEDIA + @param SenseData The pointer of EFI_SCSI_SENSE_DATA + @param NeedRetry The pointer of action which indicates what is need to retry + + @retval EFI_DEVICE_ERROR Indicates that error occurs + @retval EFI_SUCCESS Successfully to complete the parsing + +**/ +EFI_STATUS +UfsPeimParsingSenseKeys ( + IN EFI_PEI_BLOCK_IO_MEDIA *Media, + IN EFI_SCSI_SENSE_DATA *SenseData, + OUT BOOLEAN *NeedRetry + ) +{ + if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) { + Media->MediaPresent = FALSE; + *NeedRetry = FALSE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is No Media\n")); + return EFI_DEVICE_ERROR; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) { + *NeedRetry = TRUE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Is Media Change\n")); + return EFI_SUCCESS; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) { + *NeedRetry = TRUE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n")); + return EFI_SUCCESS; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_MEDIUM_ERROR) || + ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN))) { + *NeedRetry = FALSE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Media Error\n")); + return EFI_DEVICE_ERROR; + } + + if (SenseData->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) { + *NeedRetry = FALSE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Hardware Error\n")); + return EFI_DEVICE_ERROR; + } + + if ((SenseData->Sense_Key == EFI_SCSI_SK_NOT_READY) && + (SenseData->Addnl_Sense_Code == EFI_SCSI_ASC_NOT_READY) && + (SenseData->Addnl_Sense_Code_Qualifier == EFI_SCSI_ASCQ_IN_PROGRESS)) { + *NeedRetry = TRUE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Was Reset Before\n")); + return EFI_SUCCESS; + } + + *NeedRetry = FALSE; + DEBUG ((EFI_D_VERBOSE, "UfsBlockIoPei: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code)); + return EFI_DEVICE_ERROR; +} + + +/** + 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 +UfsBlockIoPeimGetDeviceNo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + OUT UINTN *NumberBlockDevices + ) +{ + // + // For Ufs device, it has up to 8 normal Luns plus some well-known Luns. + // At PEI phase, we will only expose normal Luns to user. + // For those disabled Lun, when user try to access it, the operation would fail. + // + *NumberBlockDevices = UFS_PEIM_MAX_LUNS; + 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 +UfsBlockIoPeimGetMediaInfo ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, + IN UINTN DeviceIndex, + OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo + ) +{ + EFI_STATUS Status; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EFI_SCSI_SENSE_DATA SenseData; + UINT8 SenseDataLength; + EFI_SCSI_DISK_CAPACITY_DATA Capacity; + EFI_SCSI_DISK_CAPACITY_DATA16 Capacity16; + UINTN DataLength; + BOOLEAN NeedRetry; + + Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + NeedRetry = TRUE; + + if (DeviceIndex >= UFS_PEIM_MAX_LUNS) { + return EFI_INVALID_PARAMETER; + } + + if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) { + return EFI_ACCESS_DENIED; + } + + ZeroMem (&SenseData, sizeof (SenseData)); + ZeroMem (&Capacity, sizeof (Capacity)); + ZeroMem (&Capacity16, sizeof (Capacity16)); + SenseDataLength = sizeof (SenseData); + // + // First test unit ready + // + do { + Status = UfsPeimTestUnitReady ( + Private, + DeviceIndex, + &SenseData, + &SenseDataLength + ); + if (!EFI_ERROR (Status)) { + break; + } + + if (SenseDataLength == 0) { + continue; + } + + Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + } while (NeedRetry); + + DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA); + SenseDataLength = 0; + Status = UfsPeimReadCapacity (Private, DeviceIndex, &Capacity, (UINT32 *)&DataLength, NULL, &SenseDataLength); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if ((Capacity.LastLba3 == 0xff) && (Capacity.LastLba2 == 0xff) && + (Capacity.LastLba1 == 0xff) && (Capacity.LastLba0 == 0xff)) { + DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16); + SenseDataLength = 0; + Status = UfsPeimReadCapacity16 (Private, DeviceIndex, &Capacity16, (UINT32 *)&DataLength, NULL, &SenseDataLength); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + MediaInfo->LastBlock = (Capacity16.LastLba3 << 24) | (Capacity16.LastLba2 << 16) | (Capacity16.LastLba1 << 8) | Capacity16.LastLba0; + MediaInfo->LastBlock |= ((UINT64)Capacity16.LastLba7 << 56) | ((UINT64)Capacity16.LastLba6 << 48) | ((UINT64)Capacity16.LastLba5 << 40) | ((UINT64)Capacity16.LastLba4 << 32); + MediaInfo->BlockSize = (Capacity16.BlockSize3 << 24) | (Capacity16.BlockSize2 << 16) | (Capacity16.BlockSize1 << 8) | Capacity16.BlockSize0; + } else { + MediaInfo->LastBlock = (Capacity.LastLba3 << 24) | (Capacity.LastLba2 << 16) | (Capacity.LastLba1 << 8) | Capacity.LastLba0; + MediaInfo->BlockSize = (Capacity.BlockSize3 << 24) | (Capacity.BlockSize2 << 16) | (Capacity.BlockSize1 << 8) | Capacity.BlockSize0; + } + + MediaInfo->DeviceType = UfsDevice; + MediaInfo->MediaPresent = TRUE; + + 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 +UfsBlockIoPeimReadBlocks ( + 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; + UINTN BlockSize; + UINTN NumberOfBlocks; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EFI_SCSI_SENSE_DATA SenseData; + UINT8 SenseDataLength; + BOOLEAN NeedRetry; + + Status = EFI_SUCCESS; + NeedRetry = TRUE; + Private = GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS (This); + + ZeroMem (&SenseData, sizeof (SenseData)); + SenseDataLength = sizeof (SenseData); + + // + // Check parameters + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + if (DeviceIndex >= UFS_PEIM_MAX_LUNS) { + return EFI_INVALID_PARAMETER; + } + + if ((Private->Luns.BitMask & (BIT0 << DeviceIndex)) == 0) { + return EFI_ACCESS_DENIED; + } + + BlockSize = Private->Media[DeviceIndex].BlockSize; + + if (BufferSize % BlockSize != 0) { + Status = EFI_BAD_BUFFER_SIZE; + } + + if (StartLBA > Private->Media[DeviceIndex].LastBlock) { + Status = EFI_INVALID_PARAMETER; + } + + NumberOfBlocks = BufferSize / BlockSize; + + do { + Status = UfsPeimTestUnitReady ( + Private, + DeviceIndex, + &SenseData, + &SenseDataLength + ); + if (!EFI_ERROR (Status)) { + break; + } + + if (SenseDataLength == 0) { + continue; + } + + Status = UfsPeimParsingSenseKeys (&(Private->Media[DeviceIndex]), &SenseData, &NeedRetry); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + } while (NeedRetry); + + SenseDataLength = 0; + if (Private->Media[DeviceIndex].LastBlock != ~((UINTN)0)) { + Status = UfsPeimRead10 ( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + } else { + Status = UfsPeimRead16 ( + Private, + DeviceIndex, + (UINT32)StartLBA, + (UINT32)NumberOfBlocks, + Buffer, + (UINT32 *)&BufferSize, + NULL, + &SenseDataLength + ); + } + return Status; +} + +/** + The user code starts with this function. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS The driver is successfully initialized. + @retval Others Can't initialize the driver. + +**/ +EFI_STATUS +EFIAPI +InitializeUfsBlockIoPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + UFS_PEIM_HC_PRIVATE_DATA *Private; + EDKII_UFS_HOST_CONTROLLER_PPI *UfsHcPpi; + UINT32 Index; + UFS_CONFIG_DESC Config; + UINTN MmioBase; + UINT8 Controller; + + // + // Shadow this PEIM to run from memory + // + if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) { + return EFI_SUCCESS; + } + + // + // locate ufs host controller PPI + // + Status = PeiServicesLocatePpi ( + &gEdkiiPeiUfsHostControllerPpiGuid, + 0, + NULL, + (VOID **) &UfsHcPpi + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Controller = 0; + MmioBase = 0; + while (TRUE) { + Status = UfsHcPpi->GetUfsHcMmioBar (UfsHcPpi, Controller, &MmioBase); + // + // When status is error, meant no controller is found + // + if (EFI_ERROR (Status)) { + break; + } + + Private = AllocateCopyPool (sizeof (UFS_PEIM_HC_PRIVATE_DATA), &gUfsHcPeimTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi; + Private->UfsHcBase = MmioBase; + + // + // Initialize the memory pool which will be used in all transactions. + // + Status = UfsPeimInitMemPool (Private); + if (EFI_ERROR (Status)) { + Status = EFI_OUT_OF_RESOURCES; + break; + } + + // + // Initialize UFS Host Controller H/W. + // + Status = UfsControllerInit (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsDevicePei: Host Controller Initialization Error, Status = %r\n", Status)); + Controller++; + continue; + } + + // + // UFS 2.0 spec Section 13.1.3.3: + // At the end of the UFS Interconnect Layer initialization on both host and device side, + // the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready. + // + Status = UfsExecNopCmds (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status)); + Controller++; + continue; + } + + // + // The host enables the device initialization completion by setting fDeviceInit flag. + // + Status = UfsSetFlag (Private, UfsFlagDevInit); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status)); + Controller++; + continue; + } + + // + // Get Ufs Device's Lun Info by reading Configuration Descriptor. + // + Status = UfsRwDeviceDesc (Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC)); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Get Configuration Descriptor Error, Status = %r\n", Status)); + Controller++; + continue; + } + + for (Index = 0; Index < UFS_PEIM_MAX_LUNS; Index++) { + if (Config.UnitDescConfParams[Index].LunEn != 0) { + Private->Luns.BitMask |= (BIT0 << Index); + DEBUG ((EFI_D_INFO, "Ufs %d Lun %d is enabled\n", Controller, Index)); + } + } + + Status = PeiServicesInstallPpi (&Private->BlkIoPpiList); + Controller++; + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h new file mode 100644 index 0000000000..835b9c61cf --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.h @@ -0,0 +1,434 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_BLOCK_IO_PEI_H_ +#define _UFS_BLOCK_IO_PEI_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "UfsHci.h" +#include "UfsHcMem.h" + +#define UFS_PEIM_HC_SIG SIGNATURE_32 ('U', 'F', 'S', 'H') + +#define UFS_PEIM_MAX_LUNS 8 + +typedef struct { + UINT8 Lun[UFS_PEIM_MAX_LUNS]; + UINT16 BitMask:12; // Bit 0~7 is for common luns. Bit 8~11 is reserved for those well known luns + UINT16 Rsvd:4; +} UFS_PEIM_EXPOSED_LUNS; + +typedef struct { + /// + /// The timeout, in 100 ns units, to use for the execution of this SCSI + /// Request Packet. A Timeout value of 0 means that this function + /// will wait indefinitely for the SCSI Request Packet to execute. If + /// Timeout is greater than zero, then this function will return + /// EFI_TIMEOUT if the time required to execute the SCSI + /// Request Packet is greater than Timeout. + /// + UINT64 Timeout; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for read and bidirectional commands. + /// + VOID *InDataBuffer; + /// + /// A pointer to the data buffer to transfer between the SCSI + /// controller and the SCSI device for write or bidirectional commands. + /// + VOID *OutDataBuffer; + /// + /// A pointer to the sense data that was generated by the execution of + /// the SCSI Request Packet. + /// + VOID *SenseData; + /// + /// A pointer to buffer that contains the Command Data Block to + /// send to the SCSI device specified by Target and Lun. + /// + VOID *Cdb; + /// + /// On Input, the size, in bytes, of InDataBuffer. On output, the + /// number of bytes transferred between the SCSI controller and the SCSI device. + /// + UINT32 InTransferLength; + /// + /// On Input, the size, in bytes of OutDataBuffer. On Output, the + /// Number of bytes transferred between SCSI Controller and the SCSI device. + /// + UINT32 OutTransferLength; + /// + /// The length, in bytes, of the buffer Cdb. The standard values are 6, + /// 10, 12, and 16, but other values are possible if a variable length CDB is used. + /// + UINT8 CdbLength; + /// + /// The direction of the data transfer. 0 for reads, 1 for writes. A + /// value of 2 is Reserved for Bi-Directional SCSI commands. + /// + UINT8 DataDirection; + /// + /// On input, the length in bytes of the SenseData buffer. On + /// output, the number of bytes written to the SenseData buffer. + /// + UINT8 SenseDataLength; +} UFS_SCSI_REQUEST_PACKET; + +typedef struct _UFS_PEIM_HC_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Controller; + + UFS_PEIM_MEM_POOL *Pool; + + EFI_PEI_RECOVERY_BLOCK_IO_PPI BlkIoPpi; + EFI_PEI_PPI_DESCRIPTOR BlkIoPpiList; + EFI_PEI_BLOCK_IO_MEDIA Media[UFS_PEIM_MAX_LUNS]; + + UINTN UfsHcBase; + UINT32 Capabilities; + + UINT8 TaskTag; + + VOID *UtpTrlBase; + UINT8 Nutrs; + VOID *UtpTmrlBase; + UINT8 Nutmrs; + + UFS_PEIM_EXPOSED_LUNS Luns; +} UFS_PEIM_HC_PRIVATE_DATA; + +#define UFS_TIMEOUT MultU64x32((UINT64)(3), 10000000) + +#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8) + +#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0) + +#define GET_UFS_PEIM_HC_PRIVATE_DATA_FROM_THIS(a) CR (a, UFS_PEIM_HC_PRIVATE_DATA, BlkIoPpi, UFS_PEIM_HC_SIG) + +#define UFS_SCSI_OP_LENGTH_SIX 0x6 +#define UFS_SCSI_OP_LENGTH_TEN 0xa +#define UFS_SCSI_OP_LENGTH_SIXTEEN 0x10 + +typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET { + UINT64 Timeout; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT32 InTransferLength; + UINT32 OutTransferLength; + UINT8 DataDirection; + UINT8 Ocs; +} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET; + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmds ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT UFS_SCSI_REQUEST_PACKET *Packet + ); + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ); + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ); + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + 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 +UfsBlockIoPeimGetDeviceNo ( + 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 +UfsBlockIoPeimGetMediaInfo ( + 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 +UfsBlockIoPeimReadBlocks ( + 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 + ); + +/** + Initialize the memory management pool for the host controller. + + @param Private The Ufs Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +UfsPeimInitMemPool ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ); + +/** + Allocate some memory from the host controller's memory pool + which can be used to communicate with host controller. + + @param Pool The host controller's memory pool. + @param Size Size of the memory to allocate. + + @return The allocated memory or NULL. + +**/ +VOID * +UfsPeimAllocateMem ( + IN UFS_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 +UfsPeimFreeMem ( + IN UFS_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ); + +#endif diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf new file mode 100644 index 0000000000..574e244d3a --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf @@ -0,0 +1,61 @@ +## @file +# Description file for the Universal Flash Storage (UFS) Peim driver. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UfsBlockIoPei + MODULE_UNI_FILE = UfsBlockIoPei.uni + FILE_GUID = BE189D38-C963-41CF-B695-D90E9E545A13 + MODULE_TYPE = PEIM + VERSION_STRING = 0.9 + + ENTRY_POINT = InitializeUfsBlockIoPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + UfsBlockIoPei.c + UfsBlockIoPei.h + UfsHci.c + UfsHci.h + UfsHcMem.c + UfsHcMem.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + IoLib + TimerLib + BaseMemoryLib + PeimEntryPoint + PeiServicesLib + DebugLib + +[Ppis] + gEfiPeiVirtualBlockIoPpiGuid ## PRODUCES + gEdkiiPeiUfsHostControllerPpiGuid ## CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid AND gEdkiiPeiUfsHostControllerPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + UfsBlockIoPeiExtra.uni + diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni new file mode 100644 index 0000000000..23b6e633b9 Binary files /dev/null and b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.uni differ diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni new file mode 100644 index 0000000000..706fcdc8f4 Binary files /dev/null and b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPeiExtra.uni differ diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c new file mode 100644 index 0000000000..cc6c3d4e3e --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.c @@ -0,0 +1,455 @@ +/** @file + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsBlockIoPei.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. + +**/ +UFS_PEIM_MEM_BLOCK * +UfsPeimAllocMemBlock ( + IN UINTN Pages + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + EFI_STATUS Status; + VOID *TempPtr; + EFI_PHYSICAL_ADDRESS Address; + + TempPtr = NULL; + Block = NULL; + + Status = PeiServicesAllocatePool (sizeof(UFS_PEIM_MEM_BLOCK), &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof(UFS_PEIM_MEM_BLOCK)); + + // + // each bit in the bit array represents UFS_PEIM_MEM_UNIT + // bytes of memory in the memory block. + // + ASSERT (UFS_PEIM_MEM_UNIT * 8 <= EFI_PAGE_SIZE); + + Block = (UFS_PEIM_MEM_BLOCK*)(UINTN)TempPtr; + Block->BufLen = EFI_PAGES_TO_SIZE (Pages); + Block->BitsLen = Block->BufLen / (UFS_PEIM_MEM_UNIT * 8); + + Status = PeiServicesAllocatePool (Block->BitsLen, &TempPtr); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, Block->BitsLen); + + Block->Bits = (UINT8*)(UINTN)TempPtr; + + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + Pages, + &Address + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem ((VOID*)(UINTN)Address, EFI_PAGES_TO_SIZE (Pages)); + + Block->Buf = (UINT8*)((UINTN)Address); + Block->Next = NULL; + + return Block; +} + +/** + Free the memory block from the memory pool. + + @param Pool The memory pool to free the block from. + @param Block The memory block to free. + +**/ +VOID +UfsPeimFreeMemBlock ( + IN UFS_PEIM_MEM_POOL *Pool, + IN UFS_PEIM_MEM_BLOCK *Block + ) +{ + ASSERT ((Pool != NULL) && (Block != NULL)); +} + +/** + Alloc some memory from the block. + + @param Block The memory block to allocate memory from. + @param Units Number of memory units to allocate. + + @return The pointer to the allocated memory. If couldn't allocate the needed memory, + the return value is NULL. + +**/ +VOID * +UfsPeimAllocMemFromBlock ( + IN UFS_PEIM_MEM_BLOCK *Block, + IN UINTN Units + ) +{ + UINTN Byte; + UINT8 Bit; + UINTN StartByte; + UINT8 StartBit; + UINTN Available; + UINTN Count; + + ASSERT ((Block != 0) && (Units != 0)); + + StartByte = 0; + StartBit = 0; + Available = 0; + + for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) { + // + // If current bit is zero, the corresponding memory unit is + // available, otherwise we need to restart our searching. + // Available counts the consective number of zero bit. + // + if (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)) { + Available++; + + if (Available >= Units) { + break; + } + + UFS_PEIM_NEXT_BIT (Byte, Bit); + + } else { + UFS_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 (!UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) UFS_PEIM_MEM_BIT (Bit)); + UFS_PEIM_NEXT_BIT (Byte, Bit); + } + + return Block->Buf + (StartByte * 8 + StartBit) * UFS_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 +UfsPeimInsertMemBlockToPool ( + IN UFS_PEIM_MEM_BLOCK *Head, + IN UFS_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 +UfsPeimIsMemBlockEmpty ( + IN UFS_PEIM_MEM_BLOCK *Block + ) +{ + UINTN Index; + + + for (Index = 0; Index < Block->BitsLen; Index++) { + if (Block->Bits[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Unlink the memory block from the pool's list. + + @param Head The block list head of the memory's pool. + @param BlockToUnlink The memory block to unlink. + +**/ +VOID +UfsPeimUnlinkMemBlock ( + IN UFS_PEIM_MEM_BLOCK *Head, + IN UFS_PEIM_MEM_BLOCK *BlockToUnlink + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + + ASSERT ((Head != NULL) && (BlockToUnlink != NULL)); + + for (Block = Head; Block != NULL; Block = Block->Next) { + if (Block->Next == BlockToUnlink) { + Block->Next = BlockToUnlink->Next; + BlockToUnlink->Next = NULL; + break; + } + } +} + +/** + Initialize the memory management pool for the host controller. + + @param Private The Ufs Peim driver private data. + + @retval EFI_SUCCESS The memory pool is initialized. + @retval Others Fail to init the memory pool. + +**/ +EFI_STATUS +UfsPeimInitMemPool ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UFS_PEIM_MEM_POOL *Pool; + EFI_STATUS Status; + VOID *TempPtr; + + TempPtr = NULL; + Pool = NULL; + + Status = PeiServicesAllocatePool (sizeof (UFS_PEIM_MEM_POOL), &TempPtr); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem ((VOID*)(UINTN)TempPtr, sizeof (UFS_PEIM_MEM_POOL)); + + Pool = (UFS_PEIM_MEM_POOL *)((UINTN)TempPtr); + + Pool->Head = UfsPeimAllocMemBlock (UFS_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 +UfsPeimFreeMemPool ( + IN UFS_PEIM_MEM_POOL *Pool + ) +{ + UFS_PEIM_MEM_BLOCK *Block; + + ASSERT (Pool->Head != NULL); + + // + // Unlink all the memory blocks from the pool, then free them. + // UfsPeimUnlinkMemBlock can't be used to unlink and free the + // first block. + // + for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) { + UfsPeimFreeMemBlock (Pool, Block); + } + + UfsPeimFreeMemBlock (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 * +UfsPeimAllocateMem ( + IN UFS_PEIM_MEM_POOL *Pool, + IN UINTN Size + ) +{ + UFS_PEIM_MEM_BLOCK *Head; + UFS_PEIM_MEM_BLOCK *Block; + UFS_PEIM_MEM_BLOCK *NewBlock; + VOID *Mem; + UINTN AllocSize; + UINTN Pages; + + Mem = NULL; + AllocSize = UFS_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 = UfsPeimAllocMemFromBlock (Block, AllocSize / UFS_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 (UFS_PEIM_MEM_DEFAULT_PAGES)) { + Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1; + } else { + Pages = UFS_PEIM_MEM_DEFAULT_PAGES; + } + + NewBlock = UfsPeimAllocMemBlock (Pages); + if (NewBlock == NULL) { + return NULL; + } + + // + // Add the new memory block to the pool, then allocate memory from it + // + UfsPeimInsertMemBlockToPool (Head, NewBlock); + Mem = UfsPeimAllocMemFromBlock (NewBlock, AllocSize / UFS_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 +UfsPeimFreeMem ( + IN UFS_PEIM_MEM_POOL *Pool, + IN VOID *Mem, + IN UINTN Size + ) +{ + UFS_PEIM_MEM_BLOCK *Head; + UFS_PEIM_MEM_BLOCK *Block; + UINT8 *ToFree; + UINTN AllocSize; + UINTN Byte; + UINTN Bit; + UINTN Count; + + Head = Pool->Head; + AllocSize = UFS_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) / UFS_PEIM_MEM_UNIT) / 8; + Bit = ((ToFree - Block->Buf) / UFS_PEIM_MEM_UNIT) % 8; + + // + // reset associated bits in bit arry + // + for (Count = 0; Count < (AllocSize / UFS_PEIM_MEM_UNIT); Count++) { + ASSERT (UFS_PEIM_MEM_BIT_IS_SET (Block->Bits[Byte], Bit)); + + Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ UFS_PEIM_MEM_BIT (Bit)); + UFS_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) && UfsPeimIsMemBlockEmpty (Block)) { + UfsPeimFreeMemBlock (Pool, Block); + } + + return ; +} diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h new file mode 100644 index 0000000000..3c4b2407c8 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHcMem.h @@ -0,0 +1,61 @@ +/** @file + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_PEIM_MEM_H_ +#define _UFS_PEIM_MEM_H_ + +#define UFS_PEIM_MEM_BIT(a) ((UINTN)(1 << (a))) + +#define UFS_PEIM_MEM_BIT_IS_SET(Data, Bit) \ + ((BOOLEAN)(((Data) & UFS_PEIM_MEM_BIT(Bit)) == UFS_PEIM_MEM_BIT(Bit))) + +typedef struct _UFS_PEIM_MEM_BLOCK UFS_PEIM_MEM_BLOCK; + +struct _UFS_PEIM_MEM_BLOCK { + UINT8 *Bits; // Bit array to record which unit is allocated + UINTN BitsLen; + UINT8 *Buf; + UINTN BufLen; // Memory size in bytes + UFS_PEIM_MEM_BLOCK *Next; +}; + +typedef struct _UFS_PEIM_MEM_POOL { + UFS_PEIM_MEM_BLOCK *Head; +} UFS_PEIM_MEM_POOL; + +// +// Memory allocation unit, note that the value must meet UFS spec alignment requirement. +// +#define UFS_PEIM_MEM_UNIT 128 + +#define UFS_PEIM_MEM_UNIT_MASK (UFS_PEIM_MEM_UNIT - 1) +#define UFS_PEIM_MEM_DEFAULT_PAGES 16 + +#define UFS_PEIM_MEM_ROUND(Len) (((Len) + UFS_PEIM_MEM_UNIT_MASK) & (~UFS_PEIM_MEM_UNIT_MASK)) + +// +// Advance the byte and bit to the next bit, adjust byte accordingly. +// +#define UFS_PEIM_NEXT_BIT(Byte, Bit) \ + do { \ + (Bit)++; \ + if ((Bit) > 7) { \ + (Byte)++; \ + (Bit) = 0; \ + } \ + } while (0) + +#endif + diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c new file mode 100644 index 0000000000..68800570cb --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.c @@ -0,0 +1,1787 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsBlockIoPei.h" + +/** + Wait for the value of the specified system memory set to the test value. + + @param Address The system memory address to test. + @param MaskValue The mask value of memory. + @param TestValue The test value of memory. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +EFIAPI +UfsWaitMemSet ( + IN UINTN Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT64 Delay; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32 (Timeout, 10) + 1; + + do { + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = MmioRead32 (Address) & MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 1 microseconds. + // + MicroSecondDelay (1); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + +/** + Dump UIC command execution result for debugging. + + @param[in] UicOpcode The executed UIC opcode. + @param[in] Result The result to be parsed. + +**/ +VOID +DumpUicCmdExecResult ( + IN UINT8 UicOpcode, + IN UINT8 Result + ) +{ + if (UicOpcode <= UfsUicDmePeerSet) { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n")); + break; + case 0x02: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n")); + break; + case 0x03: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x04: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x05: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n")); + break; + case 0x06: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n")); + break; + case 0x07: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n")); + break; + case 0x08: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n")); + break; + case 0x09: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n")); + break; + case 0x0A: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n")); + break; + default : + ASSERT (FALSE); + break; + } + } else { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG ((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n")); + break; + default : + ASSERT (FALSE); + break; + } + } +} + +/** + Dump QUERY RESPONSE UPIU result for debugging. + + @param[in] Result The result to be parsed. + +**/ +VOID +DumpQueryResponseResult ( + IN UINT8 Result + ) +{ + switch (Result) { + case 0xF6: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n")); + break; + case 0xF7: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n")); + break; + case 0xF8: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n")); + break; + case 0xF9: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Length\n")); + break; + case 0xFA: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Value\n")); + break; + case 0xFB: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Selector\n")); + break; + case 0xFC: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Index\n")); + break; + case 0xFD: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Idn\n")); + break; + case 0xFE: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n")); + break; + case 0xFF: + DEBUG ((EFI_D_VERBOSE, "Query Response with General Failure\n")); + break; + default : + ASSERT (FALSE); + break; + } +} + +/** + Swap little endian to big endian. + + @param[in, out] Buffer The data buffer. In input, it contains little endian data. + In output, it will become big endian. + @param[in] BufferSize The length of converted data. + +**/ +VOID +SwapLittleEndianToBigEndian ( + IN OUT UINT8 *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 Index; + UINT8 Temp; + UINT32 SwapCount; + + SwapCount = BufferSize / 2; + for (Index = 0; Index < SwapCount; Index++) { + Temp = Buffer[Index]; + Buffer[Index] = Buffer[BufferSize - 1 - Index]; + Buffer[BufferSize - 1 - Index] = Temp; + } +} + +/** + Fill TSF field of QUERY REQUEST UPIU. + + @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] Length The length of transferred data. The maximum is 4. + @param[in] Value The value of transferred data. + +**/ +VOID +UfsFillTsfOfQueryReqUpiu ( + IN OUT UTP_UPIU_TSF *TsfBase, + IN UINT8 Opcode, + IN UINT8 DescId OPTIONAL, + IN UINT8 Index OPTIONAL, + IN UINT8 Selector OPTIONAL, + IN UINT16 Length OPTIONAL, + IN UINT32 Value OPTIONAL + ) +{ + ASSERT (TsfBase != NULL); + ASSERT (Opcode <= UtpQueryFuncOpcodeTogFlag); + + TsfBase->Opcode = Opcode; + if (Opcode != UtpQueryFuncOpcodeNop) { + TsfBase->DescId = DescId; + TsfBase->Index = Index; + TsfBase->Selector = Selector; + + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + SwapLittleEndianToBigEndian ((UINT8*)&Length, sizeof (Length)); + TsfBase->Length = Length; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + SwapLittleEndianToBigEndian ((UINT8*)&Value, sizeof (Value)); + TsfBase->Value = Value; + } + } +} + +/** + Initialize COMMAND UPIU. + + @param[in, out] Command The base address of COMMAND UPIU. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] TaskTag The task tag of request. + @param[in] Cdb The cdb buffer containing SCSI command. + @param[in] CdbLength The cdb length. + @param[in] DataDirection The direction of data transfer. + @param[in] ExpDataTranLen The expected transfer data length. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitCommandUpiu ( + IN OUT UTP_COMMAND_UPIU *Command, + IN UINT8 Lun, + IN UINT8 TaskTag, + IN UINT8 *Cdb, + IN UINT8 CdbLength, + IN UFS_DATA_DIRECTION DataDirection, + IN UINT32 ExpDataTranLen + ) +{ + UINT8 Flags; + + ASSERT ((Command != NULL) && (Cdb != NULL)); + + // + // Task attribute is hard-coded to Ordered. + // + if (DataDirection == UfsDataIn) { + Flags = BIT0 | BIT6; + } else if (DataDirection == UfsDataOut) { + Flags = BIT0 | BIT5; + } else { + Flags = BIT0; + } + + // + // Fill UTP COMMAND UPIU associated fields. + // + Command->TransCode = 0x01; + Command->Flags = Flags; + Command->Lun = Lun; + Command->TaskTag = TaskTag; + Command->CmdSet = 0x00; + SwapLittleEndianToBigEndian ((UINT8*)&ExpDataTranLen, sizeof (ExpDataTranLen)); + Command->ExpDataTranLen = ExpDataTranLen; + + CopyMem (Command->Cdb, Cdb, CdbLength); + + return EFI_SUCCESS; +} + +/** + Initialize UTP PRDT for data transfer. + + @param[in] Prdt The base address of PRDT. + @param[in] Buffer The buffer to be read or written. + @param[in] BufferSize The data size to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitUtpPrdt ( + IN UTP_TR_PRD *Prdt, + IN VOID *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 PrdtIndex; + UINT32 RemainingLen; + UINT8 *Remaining; + UINTN PrdtNumber; + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0); + + RemainingLen = BufferSize; + Remaining = Buffer; + PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD); + + for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) { + if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) { + Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1; + } else { + Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1; + } + + Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 2); + Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 32); + RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD; + Remaining += UFS_MAX_DATA_LEN_PER_PRD; + } + + return EFI_SUCCESS; +} + +/** + Initialize QUERY REQUEST UPIU. + + @param[in, out] QueryReq The base address of QUERY REQUEST UPIU. + @param[in] TaskTag The task tag of request. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] DataSize The data size to be read or written. + @param[in] Data The buffer to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitQueryRequestUpiu ( + IN OUT UTP_QUERY_REQ_UPIU *QueryReq, + IN UINT8 TaskTag, + IN UINT8 Opcode, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN UINTN DataSize OPTIONAL, + IN UINT8 *Data OPTIONAL + ) +{ + ASSERT (QueryReq != NULL); + + QueryReq->TransCode = 0x16; + QueryReq->TaskTag = TaskTag; + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) { + QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ; + } else { + QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32*)Data); + } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0); + } else { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0); + } + + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + CopyMem (QueryReq + 1, Data, DataSize); + } + + return EFI_SUCCESS; +} + +/** + Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] Packet The pointer to the UFS_SCSI_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateScsiCommandDesc ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN UFS_SCSI_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UINTN PrdtNumber; + VOID *Buffer; + UINT32 Length; + UTP_COMMAND_UPIU *CommandUpiu; + UTP_TR_PRD *PrdtBase; + UFS_DATA_DIRECTION DataDirection; + + ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + if (Packet->DataDirection == UfsDataIn) { + Buffer = Packet->InDataBuffer; + Length = Packet->InTransferLength; + DataDirection = UfsDataIn; + } else { + Buffer = Packet->OutDataBuffer; + Length = Packet->OutTransferLength; + DataDirection = UfsDataOut; + } + + if (Length == 0) { + DataDirection = UfsNoData; + } + + PrdtNumber = (UINTN)DivU64x32 ((UINT64)Length + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD); + + TotalLen = ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof (UTP_TR_PRD); + CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CommandUpiu = (UTP_COMMAND_UPIU*)CommandDesc; + PrdtBase = (UTP_TR_PRD*)(CommandDesc + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))); + + UfsInitCommandUpiu (CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, Length); + UfsInitUtpPrdt (PrdtBase, Buffer, Length); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table + // *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)CommandUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)CommandUpiu, 32); + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)), sizeof (UINT32)); + Trd->PrdtL = (UINT16)PrdtNumber; + Trd->PrdtO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))), sizeof (UINT32)); + return EFI_SUCCESS; +} + +/** + Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + @retval EFI_INVALID_PARAMETER The parameter passed in is invalid. + +**/ +EFI_STATUS +UfsCreateDMCommandDesc ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UTP_QUERY_REQ_UPIU *QueryReqUpiu; + UINT8 Opcode; + UINT32 DataSize; + UINT8 *Data; + UINT8 DataDirection; + + ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + Opcode = Packet->Opcode; + if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) { + return EFI_INVALID_PARAMETER; + } + + DataDirection = Packet->DataDirection; + if (DataDirection == UfsDataIn) { + DataSize = Packet->InTransferLength; + Data = Packet->InDataBuffer; + } else if (DataDirection == UfsDataOut) { + DataSize = Packet->OutTransferLength; + Data = Packet->OutDataBuffer; + } else { + DataSize = 0; + Data = NULL; + } + + if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag)) + && ((DataSize == 0) || (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag)) + && ((DataSize != 0) || (Data != NULL))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) { + TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize); + } else { + TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)); + } + + CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize UTP QUERY REQUEST UPIU + // + QueryReqUpiu = (UTP_QUERY_REQ_UPIU*)CommandDesc; + UfsInitQueryRequestUpiu ( + QueryReqUpiu, + Private->TaskTag++, + Opcode, + Packet->DescId, + Packet->Index, + Packet->Selector, + DataSize, + Data + ); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->Ocs = 0x0F; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)QueryReqUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)QueryReqUpiu, 32); + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32)); + } else { + Trd->RuL = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)), sizeof (UINT32)); + } + + return EFI_SUCCESS; +} + +/** + Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateNopCommandDesc ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UTP_TRD *Trd + ) +{ + UINT8 *CommandDesc; + UINTN TotalLen; + UTP_NOP_OUT_UPIU *NopOutUpiu; + + ASSERT ((Private != NULL) && (Trd != NULL)); + + TotalLen = ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)) + ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)); + CommandDesc = UfsPeimAllocateMem (Private->Pool, TotalLen); + if (CommandDesc == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NopOutUpiu = (UTP_NOP_OUT_UPIU*)CommandDesc; + + NopOutUpiu->TaskTag = Private->TaskTag++; + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = 0x00; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)(UINTN)NopOutUpiu, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)(UINTN)NopOutUpiu, 32); + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)), sizeof (UINT32)); + + return EFI_SUCCESS; +} + +/** + Find out available slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTrl ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT ((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Find out available slot in task management transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTmrl ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT ((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Start specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Slot The slot to be started. + +**/ +VOID +UfsStartExecCmd ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) { + MmioWrite32 (Address, UFS_HC_UTRLRSR); + } + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + MmioWrite32 (Address, BIT0 << Slot); +} + +/** + Stop specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Slot The slot to be stop. + +**/ +VOID +UfsStopExecCmd ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Data = MmioRead32 (Address); + if ((Data & (BIT0 << Slot)) != 0) { + Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET; + Data = MmioRead32 (Address); + MmioWrite32 (Address, (Data & ~(BIT0 << Slot))); + } +} + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINT16 ReturnDataSize; + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.InDataBuffer = Descriptor; + Packet.InTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeRdDesc; + } else { + Packet.DataDirection = UfsDataOut; + Packet.OutDataBuffer = Descriptor; + Packet.OutTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeWrDesc; + } + Packet.DescId = DescId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnDataSize = QueryResp->Tsf.Length; + SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16)); + + if (Read) { + CopyMem (Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize); + Packet.InTransferLength = ReturnDataSize; + } else { + Packet.OutTransferLength = ReturnDataSize; + } + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd (Private, Slot); + UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Read or write specified attribute of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] AttrId The ID of Attribute. + @param[in] Index The Index of Attribute. + @param[in] Selector The Selector of Attribute. + @param[in, out] Attributes The value of Attribute to be read or written. + + @retval EFI_SUCCESS The Attribute was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute. + +**/ +EFI_STATUS +UfsRwAttributes ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 AttrId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINT32 ReturnData; + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdAttr; + } else { + Packet.DataDirection = UfsDataOut; + Packet.Opcode = UtpQueryFuncOpcodeWrAttr; + } + Packet.DescId = AttrId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnData = QueryResp->Tsf.Value; + SwapLittleEndianToBigEndian ((UINT8*)&ReturnData, sizeof (UINT32)); + *Attributes = ReturnData; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd (Private, Slot); + UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Read or write specified flag of a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] FlagId The ID of flag to be read or written. + @param[in, out] Value The value to set or clear flag. + + @retval EFI_SUCCESS The flag was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag. + +**/ +EFI_STATUS +UfsRwFlags ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 FlagId, + IN OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + + if (Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + ASSERT (Value != NULL); + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdFlag; + } else { + Packet.DataDirection = UfsDataOut; + if (*Value == 1) { + Packet.Opcode = UtpQueryFuncOpcodeSetFlag; + } else if (*Value == 0) { + Packet.Opcode = UtpQueryFuncOpcodeClrFlag; + } else { + return EFI_INVALID_PARAMETER; + } + } + Packet.DescId = FlagId; + Packet.Index = 0; + Packet.Selector = 0; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill transfer request descriptor to this slot. + // + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7)); + QueryResp = (UTP_QUERY_RESP_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + *Value = (UINT8)QueryResp->Tsf.Value; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd (Private, Slot); + UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 1; + Status = UfsRwFlags (Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Clear specified flag to 0 on a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be cleared. + + @retval EFI_SUCCESS The flag was cleared successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag. + +**/ +EFI_STATUS +UfsClearFlag ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 0; + Status = UfsRwFlags (Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Read specified flag from a UFS device. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be read. + @param[out] Value The flag's value. + + @retval EFI_SUCCESS The flag was read successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag. + +**/ +EFI_STATUS +UfsReadFlag ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 FlagId, + OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + + Status = UfsRwFlags (Private, TRUE, FlagId, Value); + + return Status; +} + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UTP_NOP_IN_UPIU *NopInUpiu; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UINTN Address; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + Status = UfsCreateNopCommandDesc (Private, Trd); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + CmdDescBase = (UINT8 *)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7)); + NopInUpiu = (UTP_NOP_IN_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (NopInUpiu->Resp != 0) { + Status = EFI_DEVICE_ERROR; + } else { + Status = EFI_SUCCESS; + } + +Exit: + UfsStopExecCmd (Private, Slot); + UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmds ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT UFS_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UINT8 *CmdDescBase; + UINT32 CmdDescSize; + UTP_RESPONSE_UPIU *Response; + UINT16 SenseDataLen; + UINT32 ResTranCount; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd); + if (EFI_ERROR (Status)) { + return Status; + } + + CmdDescBase = (UINT8*)(UINTN)(LShiftU64 ((UINT64)Trd->UcdBaU, 32) | LShiftU64 ((UINT64)Trd->UcdBa, 7)); + CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet->Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get sense data if exists + // + Response = (UTP_RESPONSE_UPIU*)(CmdDescBase + Trd->RuO * sizeof (UINT32)); + SenseDataLen = Response->SenseDataLen; + SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16)); + + if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) { + CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen); + Packet->SenseDataLength = (UINT8)SenseDataLen; + } + + // + // Check the transfer request result. + // + if (Response->Response != 0) { + DEBUG ((EFI_D_ERROR, "UfsExecScsiCmds() fails with Target Failure\n")); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + if (Packet->DataDirection == UfsDataIn) { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32)); + Packet->InTransferLength -= ResTranCount; + } + } else if (Packet->DataDirection == UfsDataOut) { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32)); + Packet->OutTransferLength -= ResTranCount; + } + } + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsStopExecCmd (Private, Slot); + UfsPeimFreeMem (Private->Pool, CmdDescBase, CmdDescSize); + + return Status; +} + + +/** + Sent UIC DME_LINKSTARTUP command to start the link startup procedure. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + @param[in] UicOpcode The opcode of the UIC command. + @param[in] Arg1 The value for 1st argument of the UIC command. + @param[in] Arg2 The value for 2nd argument of the UIC command. + @param[in] Arg3 The value for 3rd argument of the UIC command. + + @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device. + @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device. + @return EFI_NOT_FOUND The presence of the UFS device isn't detected. + +**/ +EFI_STATUS +UfsExecUicCommands ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private, + IN UINT8 UicOpcode, + IN UINT32 Arg1, + IN UINT32 Arg2, + IN UINT32 Arg3 + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + UINTN UfsHcBase; + + UfsHcBase = Private->UfsHcBase; + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) { + // + // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first. + // + MmioWrite32 (Address, Data); + } + + // + // When programming UIC command registers, host software shall set the register UICCMD + // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3) + // are set. + // + Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET; + MmioWrite32 (Address, Arg1); + + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + MmioWrite32 (Address, Arg2); + + Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET; + MmioWrite32 (Address, Arg3); + + // + // Host software shall only set the UICCMD if HCS.UCRDY is set to 1. + // + Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET; + Status = UfsWaitMemSet (Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET; + MmioWrite32 (Address, (UINT32)UicOpcode); + + // + // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS) + // This bit is set to '1' by the host controller upon completion of a UIC command. + // + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = MmioRead32 (Address); + Status = UfsWaitMemSet (Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + if (UicOpcode != UfsUicDmeReset) { + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + Data = MmioRead32 (Address); + if ((Data & 0xFF) != 0) { + DEBUG_CODE_BEGIN(); + DumpUicCmdExecResult (UicOpcode, (UINT8)(Data & 0xFF)); + DEBUG_CODE_END(); + return EFI_DEVICE_ERROR; + } + } + + // + // Check value of HCS.DP and make sure that there is a device attached to the Link. + // + Address = UfsHcBase + UFS_HC_STATUS_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_HCS_DP) == 0) { + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Status = UfsWaitMemSet (Address, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return EFI_NOT_FOUND; + } + + DEBUG ((EFI_D_INFO, "UfsblockioPei: found a attached UFS device\n")); + + return EFI_SUCCESS; +} + +/** + Enable the UFS host controller for accessing. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS host controller enabling was executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller. + +**/ +EFI_STATUS +UfsEnableHostController ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // UFS 2.0 spec section 7.1.1 - Host Controller Initialization + // + // Reinitialize the UFS host controller if HCE bit of HC register is set. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) { + // + // Write a 0 to the HCE register at first to disable the host controller. + // + MmioWrite32 (Address, 0); + // + // Wait until HCE is read as '0' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + // + // Write a 1 to the HCE register to enable the UFS host controller. + // + MmioWrite32 (Address, UFS_HC_HCE_EN); + // + // Wait until HCE is read as '1' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Detect if a UFS device attached. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS device detection was executed successfully. + @retval EFI_NOT_FOUND Not found a UFS device attached. + @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device. + +**/ +EFI_STATUS +UfsDeviceDetection ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Retry; + EFI_STATUS Status; + + // + // Start UFS device detection. + // Try up to 3 times for establishing data link with device. + // + for (Retry = 0; Retry < 3; Retry++) { + Status = UfsExecUicCommands (Private, UfsUicDmeLinkStartup, 0, 0, 0); + if (!EFI_ERROR (Status)) { + break; + } + + if (Status == EFI_NOT_FOUND) { + continue; + } + + return EFI_DEVICE_ERROR; + } + + if (Retry == 3) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Initialize UFS task management request list related h/w context. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS task management list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTaskManagementRequestList ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutmrs; + EFI_PHYSICAL_ADDRESS Buffer; + EFI_STATUS Status; + + // + // Initial h/w and s/w context for future operations. + // + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = MmioRead32 (Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Task Management Request List. + // + Nutmrs = (UINT8) (RShiftU64 ((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1); + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (Nutmrs * sizeof (UTP_TMRD)), + &Buffer + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + ZeroMem ((VOID*)(UINTN)Buffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Nutmrs * sizeof (UTP_TMRD)))); + + // + // Program the UTP Task Management Request List Base Address and UTP Task Management + // Request List Base Address with a 64-bit address allocated at step 6. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET; + MmioWrite32 (Address, (UINT32)(UINTN)Buffer); + Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET; + MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)Buffer, 32)); + Private->UtpTmrlBase = (VOID*)(UINTN)Buffer; + Private->Nutmrs = Nutmrs; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + MmioWrite32 (Address, UFS_HC_UTMRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize UFS transfer request list related h/w context. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS transfer list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTransferRequestList ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutrs; + EFI_PHYSICAL_ADDRESS Buffer; + EFI_STATUS Status; + + // + // Initial h/w and s/w context for future operations. + // + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = MmioRead32 (Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Transfer Request List. + // + Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1); + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (Nutrs * sizeof (UTP_TRD)), + &Buffer + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + ZeroMem ((VOID*)(UINTN)Buffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Nutrs * sizeof (UTP_TRD)))); + + // + // Program the UTP Transfer Request List Base Address and UTP Transfer Request List + // Base Address with a 64-bit address allocated at step 8. + // + Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET; + MmioWrite32 (Address, (UINT32)(UINTN)Buffer); + Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET; + MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)Buffer, 32)); + Private->UtpTrlBase = (VOID*)(UINTN)Buffer; + Private->Nutrs = Nutrs; + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + MmioWrite32 (Address, UFS_HC_UTRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + Status = UfsEnableHostController (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsDevicePei: Enable Host Controller Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsDeviceDetection (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsDevicePei: Device Detection Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsInitTaskManagementRequestList (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsDevicePei: Task management list initialization Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsInitTransferRequestList (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsDevicePei: Transfer list initialization Fails, Status = %r\n", Status)); + return Status; + } + + DEBUG ((EFI_D_INFO, "UfsDevicePei Finished\n")); + return EFI_SUCCESS; +} + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PEIM_HC_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop ( + IN UFS_PEIM_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + MmioWrite32 (Address, 0); + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + MmioWrite32 (Address, 0); + + // + // Write a 0 to the HCE register in order to disable the host controller. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = MmioRead32 (Address); + ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN); + MmioWrite32 (Address, 0); + + // + // Wait until HCE is read as '0' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DEBUG ((EFI_D_INFO, "UfsDevicePei: Stop the UFS Host Controller\n")); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h new file mode 100644 index 0000000000..a423a921de --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsHci.h @@ -0,0 +1,1339 @@ +/** @file + UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface + for upper layer application to execute UFS-supported SCSI cmds. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_PASS_THRU_HCI_H_ +#define _UFS_PASS_THRU_HCI_H_ + +// +// Host Capabilities Register Offsets +// +#define UFS_HC_CAP_OFFSET 0x0000 // Controller Capabilities +#define UFS_HC_VER_OFFSET 0x0008 // Version +#define UFS_HC_DDID_OFFSET 0x0010 // Device ID and Device Class +#define UFS_HC_PMID_OFFSET 0x0014 // Product ID and Manufacturer ID +#define UFS_HC_AHIT_OFFSET 0x0018 // Auto-Hibernate Idle Timer +// +// Operation and Runtime Register Offsets +// +#define UFS_HC_IS_OFFSET 0x0020 // Interrupt Status +#define UFS_HC_IE_OFFSET 0x0024 // Interrupt Enable +#define UFS_HC_STATUS_OFFSET 0x0030 // Host Controller Status +#define UFS_HC_ENABLE_OFFSET 0x0034 // Host Controller Enable +#define UFS_HC_UECPA_OFFSET 0x0038 // Host UIC Error Code PHY Adapter Layer +#define UFS_HC_UECDL_OFFSET 0x003c // Host UIC Error Code Data Link Layer +#define UFS_HC_UECN_OFFSET 0x0040 // Host UIC Error Code Network Layer +#define UFS_HC_UECT_OFFSET 0x0044 // Host UIC Error Code Transport Layer +#define UFS_HC_UECDME_OFFSET 0x0048 // Host UIC Error Code DME +#define UFS_HC_UTRIACR_OFFSET 0x004c // UTP Transfer Request Interrupt Aggregation Control Register +// +// UTP Transfer Register Offsets +// +#define UFS_HC_UTRLBA_OFFSET 0x0050 // UTP Transfer Request List Base Address +#define UFS_HC_UTRLBAU_OFFSET 0x0054 // UTP Transfer Request List Base Address Upper 32-Bits +#define UFS_HC_UTRLDBR_OFFSET 0x0058 // UTP Transfer Request List Door Bell Register +#define UFS_HC_UTRLCLR_OFFSET 0x005c // UTP Transfer Request List CLear Register +#define UFS_HC_UTRLRSR_OFFSET 0x0060 // UTP Transfer Request Run-Stop Register +// +// UTP Task Management Register Offsets +// +#define UFS_HC_UTMRLBA_OFFSET 0x0070 // UTP Task Management Request List Base Address +#define UFS_HC_UTMRLBAU_OFFSET 0x0074 // UTP Task Management Request List Base Address Upper 32-Bits +#define UFS_HC_UTMRLDBR_OFFSET 0x0078 // UTP Task Management Request List Door Bell Register +#define UFS_HC_UTMRLCLR_OFFSET 0x007c // UTP Task Management Request List CLear Register +#define UFS_HC_UTMRLRSR_OFFSET 0x0080 // UTP Task Management Run-Stop Register +// +// UIC Command Register Offsets +// +#define UFS_HC_UIC_CMD_OFFSET 0x0090 // UIC Command Register +#define UFS_HC_UCMD_ARG1_OFFSET 0x0094 // UIC Command Argument 1 +#define UFS_HC_UCMD_ARG2_OFFSET 0x0098 // UIC Command Argument 2 +#define UFS_HC_UCMD_ARG3_OFFSET 0x009c // UIC Command Argument 3 +// +// UMA Register Offsets +// +#define UFS_HC_UMA_OFFSET 0x00b0 // Reserved for Unified Memory Extension + +#define UFS_HC_HCE_EN BIT0 +#define UFS_HC_HCS_DP BIT0 +#define UFS_HC_HCS_UCRDY BIT3 +#define UFS_HC_IS_ULSS BIT8 +#define UFS_HC_IS_UCCS BIT10 +#define UFS_HC_CAP_64ADDR BIT24 +#define UFS_HC_CAP_NUTMRS (BIT16 | BIT17 | BIT18) +#define UFS_HC_CAP_NUTRS (BIT0 | BIT1 | BIT2 | BIT3 | BIT4) +#define UFS_HC_UTMRLRSR BIT0 +#define UFS_HC_UTRLRSR BIT0 + +// +// A maximum of length of 256KB is supported by PRDT entry +// +#define UFS_MAX_DATA_LEN_PER_PRD 0x40000 + +#define UFS_STORAGE_COMMAND_TYPE 0x01 + +#define UFS_REGULAR_COMMAND 0x00 +#define UFS_INTERRUPT_COMMAND 0x01 + +#define UFS_LUN_0 0x00 +#define UFS_LUN_1 0x01 +#define UFS_LUN_2 0x02 +#define UFS_LUN_3 0x03 +#define UFS_LUN_4 0x04 +#define UFS_LUN_5 0x05 +#define UFS_LUN_6 0x06 +#define UFS_LUN_7 0x07 +#define UFS_WLUN_REPORT_LUNS 0x81 +#define UFS_WLUN_UFS_DEV 0xD0 +#define UFS_WLUN_BOOT 0xB0 +#define UFS_WLUN_RPMB 0xC4 + +#pragma pack(1) + +// +// UFSHCI 2.0 Spec Section 5.2.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT8 Nutrs:4; // Number of UTP Transfer Request Slots + UINT8 Rsvd1:4; + + UINT8 NoRtt; // Number of outstanding READY TO TRANSFER (RTT) requests supported + + UINT8 Nutmrs:3; // Number of UTP Task Management Request Slots + UINT8 Rsvd2:4; + UINT8 AutoHs:1; // Auto-Hibernation Support + + UINT8 As64:1; // 64-bit addressing supported + UINT8 Oodds:1; // Out of order data delivery supported + UINT8 UicDmetms:1; // UIC DME_TEST_MODE command supported + UINT8 Ume:1; // Reserved for Unified Memory Extension + UINT8 Rsvd4:4; +} UFS_HC_CAP; + +// +// UFSHCI 2.0 Spec Section 5.2.2 Offset 08h: VER - UFS Version +// +typedef struct { + UINT8 Vs:4; // Version Suffix + UINT8 Mnr:4; // Minor version number + + UINT8 Mjr; // Major version number + + UINT16 Rsvd1; +} UFS_HC_VER; + +// +// UFSHCI 2.0 Spec Section 5.2.3 Offset 10h: HCPID - Host Controller Product ID +// +#define UFS_HC_PID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.4 Offset 14h: HCMID - Host Controller Manufacturer ID +// +#define UFS_HC_MID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.5 Offset 18h: AHIT - Auto-Hibernate Idle Timer +// +typedef struct { + UINT32 Ahitv:10; // Auto-Hibernate Idle Timer Value + UINT32 Ts:3; // Timer scale + UINT32 Rsvd1:19; +} UFS_HC_AHIT; + +// +// UFSHCI 2.0 Spec Section 5.3.1 Offset 20h: IS - Interrupt Status +// +typedef struct { + UINT16 Utrcs:1; // UTP Transfer Request Completion Status + UINT16 Udepri:1; // UIC DME_ENDPOINT_RESET Indication + UINT16 Ue:1; // UIC Error + UINT16 Utms:1; // UIC Test Mode Status + + UINT16 Upms:1; // UIC Power Mode Status + UINT16 Uhxs:1; // UIC Hibernate Exit Status + UINT16 Uhes:1; // UIC Hibernate Enter Status + UINT16 Ulls:1; // UIC Link Lost Status + + UINT16 Ulss:1; // UIC Link Startup Status + UINT16 Utmrcs:1; // UTP Task Management Request Completion Status + UINT16 Uccs:1; // UIC Command Completion Status + UINT16 Dfes:1; // Device Fatal Error Status + + UINT16 Utpes:1; // UTP Error Status + UINT16 Rsvd1:3; + + UINT16 Hcfes:1; // Host Controller Fatal Error Status + UINT16 Sbfes:1; // System Bus Fatal Error Status + UINT16 Rsvd2:14; +} UFS_HC_IS; + +// +// UFSHCI 2.0 Spec Section 5.3.2 Offset 24h: IE - Interrupt Enable +// +typedef struct { + UINT16 Utrce:1; // UTP Transfer Request Completion Enable + UINT16 Udeprie:1; // UIC DME_ENDPOINT_RESET Enable + UINT16 Uee:1; // UIC Error Enable + UINT16 Utmse:1; // UIC Test Mode Status Enable + + UINT16 Upmse:1; // UIC Power Mode Status Enable + UINT16 Uhxse:1; // UIC Hibernate Exit Status Enable + UINT16 Uhese:1; // UIC Hibernate Enter Status Enable + UINT16 Ullse:1; // UIC Link Lost Status Enable + + UINT16 Ulsse:1; // UIC Link Startup Status Enable + UINT16 Utmrce:1; // UTP Task Management Request Completion Enable + UINT16 Ucce:1; // UIC Command Completion Enable + UINT16 Dfee:1; // Device Fatal Error Enable + + UINT16 Utpee:1; // UTP Error Enable + UINT16 Rsvd1:3; + + UINT16 Hcfee:1; // Host Controller Fatal Error Enable + UINT16 Sbfee:1; // System Bus Fatal Error Enable + UINT16 Rsvd2:14; +} UFS_HC_IE; + +// +// UFSHCI 2.0 Spec Section 5.3.3 Offset 30h: HCS - Host Controller Status +// +typedef struct { + UINT8 Dp:1; // Device Present + UINT8 UtrlRdy:1; // UTP Transfer Request List Ready + UINT8 UtmrlRdy:1; // UTP Task Management Request List Ready + UINT8 UcRdy:1; // UIC COMMAND Ready + UINT8 Rsvd1:4; + + UINT8 Upmcrs:3; // UIC Power Mode Change Request Status + UINT8 Rsvd2:1; // UIC Hibernate Exit Status Enable + UINT8 Utpec:4; // UTP Error Code + + UINT8 TtagUtpE; // Task Tag of UTP error + UINT8 TlunUtpE; // Target LUN of UTP error +} UFS_HC_STATUS; + +// +// UFSHCI 2.0 Spec Section 5.3.4 Offset 34h: HCE - Host Controller Enable +// +typedef struct { + UINT32 Hce:1; // Host Controller Enable + UINT32 Rsvd1:31; +} UFS_HC_ENABLE; + +// +// UFSHCI 2.0 Spec Section 5.3.5 Offset 38h: UECPA - Host UIC Error Code PHY Adapter Layer +// +typedef struct { + UINT32 Ec:5; // UIC PHY Adapter Layer Error Code + UINT32 Rsvd1:26; + UINT32 Err:1; // UIC PHY Adapter Layer Error +} UFS_HC_UECPA; + +// +// UFSHCI 2.0 Spec Section 5.3.6 Offset 3ch: UECDL - Host UIC Error Code Data Link Layer +// +typedef struct { + UINT32 Ec:15; // UIC Data Link Layer Error Code + UINT32 Rsvd1:16; + UINT32 Err:1; // UIC Data Link Layer Error +} UFS_HC_UECDL; + +// +// UFSHCI 2.0 Spec Section 5.3.7 Offset 40h: UECN - Host UIC Error Code Network Layer +// +typedef struct { + UINT32 Ec:3; // UIC Network Layer Error Code + UINT32 Rsvd1:28; + UINT32 Err:1; // UIC Network Layer Error +} UFS_HC_UECN; + +// +// UFSHCI 2.0 Spec Section 5.3.8 Offset 44h: UECT - Host UIC Error Code Transport Layer +// +typedef struct { + UINT32 Ec:7; // UIC Transport Layer Error Code + UINT32 Rsvd1:24; + UINT32 Err:1; // UIC Transport Layer Error +} UFS_HC_UECT; + +// +// UFSHCI 2.0 Spec Section 5.3.9 Offset 48h: UECDME - Host UIC Error Code +// +typedef struct { + UINT32 Ec:1; // UIC DME Error Code + UINT32 Rsvd1:30; + UINT32 Err:1; // UIC DME Error +} UFS_HC_UECDME; + +// +// UFSHCI 2.0 Spec Section 5.3.10 Offset 4Ch: UTRIACR - UTP Transfer Request Interrupt Aggregation Control Register +// +typedef struct { + UINT8 IaToVal; // Interrupt aggregation timeout value + + UINT8 IacTh:5; // Interrupt aggregation counter threshold + UINT8 Rsvd1:3; + + UINT8 Ctr:1; // Counter and Timer Reset + UINT8 Rsvd2:3; + UINT8 Iasb:1; // Interrupt aggregation status bit + UINT8 Rsvd3:3; + + UINT8 IapwEn:1; // Interrupt aggregation parameter write enable + UINT8 Rsvd4:6; + UINT8 IaEn:1; // Interrupt Aggregation Enable/Disable +} UFS_HC_UTRIACR; + +// +// UFSHCI 2.0 Spec Section 5.4.1 Offset 50h: UTRLBA - UTP Transfer Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtrlBa:22; // UTP Transfer Request List Base Address +} UFS_HC_UTRLBA; + +// +// UFSHCI 2.0 Spec Section 5.4.2 Offset 54h: UTRLBAU - UTP Transfer Request List Base Address Upper 32-bits +// +#define UFS_HC_UTRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.3 Offset 58h: UTRLDBR - UTP Transfer Request List Door Bell Register +// +#define UFS_HC_UTRLDBR UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.4 Offset 5Ch: UTRLCLR - UTP Transfer Request List CLear Register +// +#define UFS_HC_UTRLCLR UINT32 + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.4.5 Offset 60h: UTRLRSR - UTP Transfer Request List Run Stop Register +// +typedef struct { + UINT32 UtrlRsr:1; // UTP Transfer Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.5.1 Offset 70h: UTMRLBA - UTP Task Management Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtmrlBa:22; // UTP Task Management Request List Base Address +} UFS_HC_UTMRLBA; + +// +// UFSHCI 2.0 Spec Section 5.5.2 Offset 74h: UTMRLBAU - UTP Task Management Request List Base Address Upper 32-bits +// +#define UFS_HC_UTMRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.5.3 Offset 78h: UTMRLDBR - UTP Task Management Request List Door Bell Register +// +typedef struct { + UINT32 UtmrlDbr:8; // UTP Task Management Request List Door bell Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLDBR; + +// +// UFSHCI 2.0 Spec Section 5.5.4 Offset 7Ch: UTMRLCLR - UTP Task Management Request List CLear Register +// +typedef struct { + UINT32 UtmrlClr:8; // UTP Task Management List Clear Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLCLR; + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.5.5 Offset 80h: UTMRLRSR - UTP Task Management Request List Run Stop Register +// +typedef struct { + UINT32 UtmrlRsr:1; // UTP Task Management Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTMRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.6.1 Offset 90h: UICCMD - UIC Command +// +typedef struct { + UINT32 CmdOp:8; // Command Opcode + UINT32 Rsvd1:24; +} UFS_HC_UICCMD; + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 94h: UICCMDARG1 - UIC Command Argument 1 +// +#define UFS_HC_UICCMD_ARG1 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 98h: UICCMDARG2 - UIC Command Argument 2 +// +#define UFS_HC_UICCMD_ARG2 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 9ch: UICCMDARG3 - UIC Command Argument 3 +// +#define UFS_HC_UICCMD_ARG3 UINT32 + +// +// UIC command opcodes +// +typedef enum { + UfsUicDmeGet = 0x01, + UfsUicDmeSet = 0x02, + UfsUicDmePeerGet = 0x03, + UfsUicDmePeerSet = 0x04, + UfsUicDmePwrOn = 0x10, + UfsUicDmePwrOff = 0x11, + UfsUicDmeEnable = 0x12, + UfsUicDmeReset = 0x14, + UfsUicDmeEndpointReset = 0x15, + UfsUicDmeLinkStartup = 0x16, + UfsUicDmeHibernateEnter = 0x17, + UfsUicDmeHibernateExit = 0x18, + UfsUicDmeTestMode = 0x1A +} UFS_UIC_OPCODE; + +// +// UTP Transfer Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Dd:2; /* Data Direction */ + UINT32 Rsvd2:1; + UINT32 Ct:4; /* Command Type */ + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 + // + UINT32 Rsvd6:7; + UINT32 UcdBa:25; /* UTP Command Descriptor Base Address */ + + // + // DW5 + // + UINT32 UcdBaU; /* UTP Command Descriptor Base Address Upper 32-bits */ + + // + // DW6 + // + UINT16 RuL; /* Response UPIU Length */ + UINT16 RuO; /* Response UPIU Offset */ + + // + // DW7 + // + UINT16 PrdtL; /* PRDT Length */ + UINT16 PrdtO; /* PRDT Offset */ +} UTP_TRD; + +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:2; + UINT32 DbAddr:30; /* Data Base Address */ + + // + // DW1 + // + UINT32 DbAddrU; /* Data Base Address Upper 32-bits */ + + // + // DW2 + // + UINT32 Rsvd2; + + // + // DW3 + // + UINT32 DbCount:18; /* Data Byte Count */ + UINT32 Rsvd3:14; +} UTP_TR_PRD; + +// +// UFS 2.0 Spec Section 10.5.3 - UTP Command UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x01*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Rsvd3; + UINT8 Rsvd4; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd5; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 ExpDataTranLen; /* Expected Data Transfer Length - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Cdb[16]; +} UTP_COMMAND_UPIU; + +// +// UFS 2.0 Spec Section 10.5.4 - UTP Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x21*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Response; /* Response */ + UINT8 Status; /* Status */ + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 ResTranCount; /* Residual Transfer Count - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Rsvd3[16]; + + // + // Data Segment - Sense Data + // + UINT16 SenseDataLen; /* Sense Data Length - Big Endian */ + UINT8 SenseData[18]; /* Sense Data */ +} UTP_RESPONSE_UPIU; + +// +// UFS 2.0 Spec Section 10.5.5 - UTP Data-Out UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x02*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be sent out + // + //UINT8 Data[]; /* Data to be sent out, maximum is 65535 bytes */ +} UTP_DATA_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.6 - UTP Data-In UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x22*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_DATA_IN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.7 - UTP Ready-To-Transfer UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x31*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_RDY_TO_TRAN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.8 - UTP Task Management Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x04*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1; + UINT8 TskManFunc; /* Task Management Function */ + UINT8 Rsvd2[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 InputParam1; /* Input Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 InputParam2; /* Input Parameter 2 - Big Endian */ + + // + // DW5 + // + UINT32 InputParam3; /* Input Parameter 3 - Big Endian */ + + // + // DW6 - DW7 + // + UINT8 Rsvd4[8]; +} UTP_TM_REQ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.9 - UTP Task Management Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x24*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Resp; /* Response */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 OutputParam1; /* Output Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 OutputParam2; /* Output Parameter 2 - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd4[12]; +} UTP_TM_RESP_UPIU; + +// +// UTP Task Management Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Rsvd2:7; + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 - DW11 + // + UTP_TM_REQ_UPIU TmReq; /* Task Management Request UPIU */ + + // + // DW12 - DW19 + // + UTP_TM_RESP_UPIU TmResp; /* Task Management Response UPIU */ +} UTP_TMRD; + + +typedef struct { + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT16 Rsvd1; + UINT16 Length; + UINT32 Value; + UINT32 Rsvd2; +} UTP_UPIU_TSF; + +// +// UFS 2.0 Spec Section 10.5.10 - UTP Query Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x16*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 Rsvd3[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd4; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd5[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_REQ_UPIU; + +#define QUERY_FUNC_STD_READ_REQ 0x01 +#define QUERY_FUNC_STD_WRITE_REQ 0x81 + +typedef enum { + UtpQueryFuncOpcodeNop = 0x00, + UtpQueryFuncOpcodeRdDesc = 0x01, + UtpQueryFuncOpcodeWrDesc = 0x02, + UtpQueryFuncOpcodeRdAttr = 0x03, + UtpQueryFuncOpcodeWrAttr = 0x04, + UtpQueryFuncOpcodeRdFlag = 0x05, + UtpQueryFuncOpcodeSetFlag = 0x06, + UtpQueryFuncOpcodeClrFlag = 0x07, + UtpQueryFuncOpcodeTogFlag = 0x08 +} UTP_QUERY_FUNC_OPCODE; + +// +// UFS 2.0 Spec Section 10.5.11 - UTP Query Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x36*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 QueryResp; /* Query Response */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd4[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_RESP_UPIU; + +typedef enum { + UfsUtpQueryResponseSuccess = 0x00, + UfsUtpQueryResponseParamNotReadable = 0xF6, + UfsUtpQueryResponseParamNotWriteable = 0xF7, + UfsUtpQueryResponseParamAlreadyWritten = 0xF8, + UfsUtpQueryResponseInvalidLen = 0xF9, + UfsUtpQueryResponseInvalidVal = 0xFA, + UfsUtpQueryResponseInvalidSelector = 0xFB, + UfsUtpQueryResponseInvalidIndex = 0xFC, + UfsUtpQueryResponseInvalidIdn = 0xFD, + UfsUtpQueryResponseInvalidOpc = 0xFE, + UfsUtpQueryResponseGeneralFailure = 0xFF +} UTP_QUERY_RESP_CODE; + +// +// UFS 2.0 Spec Section 10.5.12 - UTP Reject UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x3F*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Response; /* Response - 0x01 */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT8 HdrSts; /* Basic Header Status */ + UINT8 Rsvd3; + UINT8 E2ESts; /* End-to-End Status */ + UINT8 Rsvd4; + + // + // DW4 - DW7 + // + UINT8 Rsvd5[16]; +} UTP_REJ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.13 - UTP NOP OUT UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x00*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.14 - UTP NOP IN UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x20*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[2]; + UINT8 Resp; /* Response - 0x00 */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_IN_UPIU; + +// +// UFS Descriptors +// +typedef enum { + UfsDeviceDesc = 0x00, + UfsConfigDesc = 0x01, + UfsUnitDesc = 0x02, + UfsInterConnDesc = 0x04, + UfsStringDesc = 0x05, + UfsGeometryDesc = 0x07, + UfsPowerDesc = 0x08 +} UFS_DESC_IDN; + +// +// UFS 2.0 Spec Section 14.1.6.2 - Device Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Device; + UINT8 DevClass; + UINT8 DevSubClass; + UINT8 Protocol; + UINT8 NumLun; + UINT8 NumWLun; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 SecurityLun; + UINT8 BgOpsTermLat; + UINT8 InitActiveIccLevel; + UINT16 SpecVersion; + UINT16 ManufactureDate; + UINT8 ManufacturerName; + UINT8 ProductName; + UINT8 SerialName; + UINT8 OemId; + UINT16 ManufacturerId; + UINT8 Ud0BaseOffset; + UINT8 Ud0ConfParamLen; + UINT8 DevRttCap; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd1[17]; + UINT8 Rsvd2[16]; +} UFS_DEV_DESC; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Rsvd1; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 InitActiveIccLevel; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd2[5]; +} UFS_CONFIG_DESC_GEN_HEADER; + +typedef struct { + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 MemType; + UINT32 NumAllocUnits; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT8 ProvisionType; + UINT16 CtxCap; + UINT8 Rsvd1[3]; +} UFS_UNIT_DESC_CONFIG_PARAMS; + +// +// UFS 2.0 Spec Section 14.1.6.3 - Configuration Descriptor +// +typedef struct { + UFS_CONFIG_DESC_GEN_HEADER Header; + UFS_UNIT_DESC_CONFIG_PARAMS UnitDescConfParams[8]; +} UFS_CONFIG_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.4 - Geometry Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 MediaTech; + UINT8 Rsvd1; + UINT64 TotalRawDevCapacity; + UINT8 Rsvd2; + UINT32 SegSize; + UINT8 AllocUnitSize; + UINT8 MinAddrBlkSize; + UINT8 OptReadBlkSize; + UINT8 OptWriteBlkSize; + UINT8 MaxInBufSize; + UINT8 MaxOutBufSize; + UINT8 RpmbRwSize; + UINT8 Rsvd3; + UINT8 DataOrder; + UINT8 MaxCtxIdNum; + UINT8 SysDataTagUnitSize; + UINT8 SysDataResUnitSize; + UINT8 SupSecRemovalTypes; + UINT16 SupMemTypes; + UINT32 SysCodeMaxNumAllocUnits; + UINT16 SupCodeCapAdjFac; + UINT32 NonPersMaxNumAllocUnits; + UINT16 NonPersCapAdjFac; + UINT32 Enhance1MaxNumAllocUnits; + UINT16 Enhance1CapAdjFac; + UINT32 Enhance2MaxNumAllocUnits; + UINT16 Enhance2CapAdjFac; + UINT32 Enhance3MaxNumAllocUnits; + UINT16 Enhance3CapAdjFac; + UINT32 Enhance4MaxNumAllocUnits; + UINT16 Enhance4CapAdjFac; +} UFS_GEOMETRY_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.5 - Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT16 CtxCap; + UINT8 LargeUnitGranularity; +} UFS_UNIT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.6 - RPMB Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 Rsvd2; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT8 Rsvd3[3]; +} UFS_RPMB_UNIT_DESC; + +typedef struct { + UINT16 Value:10; + UINT16 Rsvd1:4; + UINT16 Unit:2; +} UFS_POWER_PARAM_ELEMENT; + +// +// UFS 2.0 Spec Section 14.1.6.7 - Power Parameter Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVcc[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ2[16]; +} UFS_POWER_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.8 - InterConnect Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT16 UniProVer; + UINT16 MphyVer; +} UFS_INTER_CONNECT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.9 - 14.1.6.12 - String Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + CHAR16 Unicode[126]; +} UFS_STRING_DESC; + +// +// UFS 2.0 Spec Section 14.2 - Flags +// +typedef enum { + UfsFlagDevInit = 0x01, + UfsFlagPermWpEn = 0x02, + UfsFlagPowerOnWpEn = 0x03, + UfsFlagBgOpsEn = 0x04, + UfsFlagPurgeEn = 0x06, + UfsFlagPhyResRemoval = 0x08, + UfsFlagBusyRtc = 0x09, + UfsFlagPermDisFwUpdate = 0x0B +} UFS_FLAGS_IDN; + +// +// UFS 2.0 Spec Section 14.2 - Attributes +// +typedef enum { + UfsAttrBootLunEn = 0x00, + UfsAttrCurPowerMode = 0x02, + UfsAttrActiveIccLevel = 0x03, + UfsAttrOutOfOrderDataEn = 0x04, + UfsAttrBgOpStatus = 0x05, + UfsAttrPurgeStatus = 0x06, + UfsAttrMaxDataInSize = 0x07, + UfsAttrMaxDataOutSize = 0x08, + UfsAttrDynCapNeeded = 0x09, + UfsAttrRefClkFreq = 0x0a, + UfsAttrConfigDescLock = 0x0b, + UfsAttrMaxNumOfRtt = 0x0c, + UfsAttrExceptionEvtCtrl = 0x0d, + UfsAttrExceptionEvtSts = 0x0e, + UfsAttrSecondsPassed = 0x0f, + UfsAttrContextConf = 0x10, + UfsAttrCorrPrgBlkNum = 0x11 +} UFS_ATTR_IDN; + +typedef enum { + UfsNoData = 0, + UfsDataOut = 1, + UfsDataIn = 2, + UfsDdReserved +} UFS_DATA_DIRECTION; + + +#pragma pack() + +#endif + diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c new file mode 100644 index 0000000000..7da21110e3 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/ComponentName.c @@ -0,0 +1,222 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include "UfsPassThru.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsPassThruComponentName = { + UfsPassThruComponentNameGetDriverName, + UfsPassThruComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) UfsPassThruComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) UfsPassThruComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsPassThruDriverNameTable[] = { + { + "eng;en", + L"Universal Flash Storage (UFS) Pass Thru Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mUfsPassThruControllerNameTable[] = { + { + "eng;en", + L"Universal Flash Storage (UFS) Host Controller" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUfsPassThruDriverNameTable, + DriverName, + (BOOLEAN)(This == &gUfsPassThruComponentName) + ); +} + +/** + 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 +UfsPassThruComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; + + if (Language == NULL || ControllerName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing Controller Handle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gUfsPassThruDriverBinding.DriverBindingHandle, + &gEdkiiUfsHostControllerProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mUfsPassThruControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gUfsPassThruComponentName) + ); +} diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c new file mode 100644 index 0000000000..306fd37a29 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.c @@ -0,0 +1,1054 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsPassThru.h" + +// +// Template for Ufs Pass Thru private data. +// +UFS_PASS_THRU_PRIVATE_DATA gUfsPassThruTemplate = { + UFS_PASS_THRU_SIG, // Signature + NULL, // Handle + { // ExtScsiPassThruMode + 0xFFFFFFFF, + // + // Note that the driver doesn't support ExtScsiPassThru non blocking I/O. + // + EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, + sizeof (UINTN) + }, + { // ExtScsiPassThru + NULL, + UfsPassThruPassThru, + UfsPassThruGetNextTargetLun, + UfsPassThruBuildDevicePath, + UfsPassThruGetTargetLun, + UfsPassThruResetChannel, + UfsPassThruResetTargetLun, + UfsPassThruGetNextTarget + }, + 0, // UfsHostController + 0, // UfsHcBase + 0, // Capabilities + 0, // TaskTag + 0, // UtpTrlBase + 0, // Nutrs + 0, // TrlMapping + 0, // UtpTmrlBase + 0, // Nutmrs + 0, // TmrlMapping + { // Luns + { + UFS_LUN_0, // Ufs Common Lun 0 + UFS_LUN_1, // Ufs Common Lun 1 + UFS_LUN_2, // Ufs Common Lun 2 + UFS_LUN_3, // Ufs Common Lun 3 + UFS_LUN_4, // Ufs Common Lun 4 + UFS_LUN_5, // Ufs Common Lun 5 + UFS_LUN_6, // Ufs Common Lun 6 + UFS_LUN_7, // Ufs Common Lun 7 + UFS_WLUN_REPORT_LUNS, // Ufs Reports Luns Well Known Lun + UFS_WLUN_UFS_DEV, // Ufs Device Well Known Lun + UFS_WLUN_BOOT, // Ufs Boot Well Known Lun + UFS_WLUN_RPMB // RPMB Well Known Lun + }, + 0x0000, // By default don't expose any Luns. + 0x0 + } +}; + +EFI_DRIVER_BINDING_PROTOCOL gUfsPassThruDriverBinding = { + UfsPassThruDriverBindingSupported, + UfsPassThruDriverBindingStart, + UfsPassThruDriverBindingStop, + 0x10, + NULL, + NULL +}; + +UFS_DEVICE_PATH mUfsDevicePathTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_UFS_DP, + { + (UINT8) (sizeof (UFS_DEVICE_PATH)), + (UINT8) ((sizeof (UFS_DEVICE_PATH)) >> 8) + } + }, + 0, + 0 +}; + +UINT8 mUfsTargetId[TARGET_MAX_BYTES]; + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function + supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the + nonblocking I/O functionality is optional. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it represents + the id of the SCSI device to send the SCSI Request Packet. Each + transport driver may choose to utilize a subset of this size to suit the needs + of transport target representation. For example, a Fibre Channel driver + may use only 8 bytes (WWN) to represent an FC target. + @param Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param Packet A pointer to the SCSI Request Packet to send to the SCSI device + specified by Target and Lun. + @param Event If nonblocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then + nonblocking I/O is performed, and Event will be signaled when the + SCSI Request Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that + could be transferred is returned in InTransferLength. For write + and bi-directional commands, OutTransferLength bytes were + transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many + SCSI Request Packets already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported + by the host adapter. This includes the case of Bi-directional SCSI + commands not supported by the implementation. The SCSI Request + Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + UFS_PASS_THRU_PRIVATE_DATA *Private; + UINT8 UfsLun; + UINT16 Index; + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); + + if ((Packet == NULL) || (Packet->Cdb == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Don't support variable length CDB + // + if ((Packet->CdbLength != 6) && (Packet->CdbLength != 10) && + (Packet->CdbLength != 12) && (Packet->CdbLength != 16)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SenseDataLength != 0) && (Packet->SenseData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->InDataBuffer, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->OutDataBuffer, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + if ((This->Mode->IoAlign > 1) && !IS_ALIGNED(Packet->SenseData, This->Mode->IoAlign)) { + return EFI_INVALID_PARAMETER; + } + + // + // For UFS 2.0 compatible device, 0 is always used to represent the location of the UFS device. + // + SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00); + if ((Target == NULL) || (CompareMem(Target, mUfsTargetId, TARGET_MAX_BYTES) != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // UFS 2.0 spec Section 10.6.7 - Translation of 8-bit UFS LUN to 64-bit SCSI LUN Address + // 0xC1 in the first 8 bits of the 64-bit address indicates a well known LUN address in the SAM SCSI format. + // The second 8 bits of the 64-bit address saves the corresponding 8-bit UFS LUN. + // + if ((UINT8)Lun == UFS_WLUN_PREFIX) { + UfsLun = BIT7 | (((UINT8*)&Lun)[1] & 0xFF); + } else if ((UINT8)Lun == 0) { + UfsLun = ((UINT8*)&Lun)[1] & 0xFF; + } else { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < UFS_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] == UfsLun) { + break; + } + } + + if (Index == UFS_MAX_LUNS) { + return EFI_INVALID_PARAMETER; + } + + Status = UfsExecScsiCmds (Private, UfsLun, Packet); + + return Status; +} + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These + can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal + Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the + Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI + channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target On input, a pointer to the Target ID (an array of size + TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI + channel. On output, a pointer to the LUN of the next SCSI device present + on a SCSI channel. + + @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI + channel was returned in Target and Lun. + @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were + not returned on a previous call to GetNextTargetLun(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ) +{ + UFS_PASS_THRU_PRIVATE_DATA *Private; + UINT8 UfsLun; + UINT16 Index; + UINT16 Next; + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); + + if (Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + UfsLun = 0; + SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0xFF); + if (CompareMem (*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) { + // + // If the array is all 0xFF's, return the first exposed Lun to caller. + // + SetMem (*Target, TARGET_MAX_BYTES, 0x00); + for (Index = 0; Index < UFS_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) != 0) { + UfsLun = Private->Luns.Lun[Index]; + break; + } + } + if (Index != UFS_MAX_LUNS) { + *Lun = 0; + if ((UfsLun & BIT7) == BIT7) { + ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX; + ((UINT8*)Lun)[1] = UfsLun & ~BIT7; + } else { + ((UINT8*)Lun)[1] = UfsLun; + } + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } + + SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00); + if (CompareMem (*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) { + if (((UINT8*)Lun)[0] == UFS_WLUN_PREFIX) { + UfsLun = BIT7 | (((UINT8*)Lun)[1] & 0xFF); + } else if (((UINT8*)Lun)[0] == 0) { + UfsLun = ((UINT8*)Lun)[1] & 0xFF; + } else { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < UFS_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] != UfsLun) { + continue; + } + + for (Next = Index + 1; Next < UFS_MAX_LUNS; Next++) { + if ((Private->Luns.BitMask & (BIT0 << Next)) != 0) { + UfsLun = Private->Luns.Lun[Next]; + break; + } + } + + if (Next == UFS_MAX_LUNS) { + return EFI_NOT_FOUND; + } else { + break; + } + } + + if (Index != UFS_MAX_LUNS) { + *Lun = 0; + if ((UfsLun & BIT7) == BIT7) { + ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX; + ((UINT8*)Lun)[1] = UfsLun & ~BIT7; + } else { + ((UINT8*)Lun)[1] = UfsLun; + } + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } + } + + return EFI_NOT_FOUND; +} + +/** + Used to allocate and build a device path node for a SCSI device on a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the + Target ID of the SCSI device for which a device path node is to be + allocated and built. Transport drivers may chose to utilize a subset of + this size to suit the representation of targets. For example, a Fibre + Channel driver may use only 8 bytes (WWN) in the array to represent a + FC target. + @param Lun The LUN of the SCSI device for which a device path node is to be + allocated and built. + @param DevicePath A pointer to a single device path node that describes the SCSI device + specified by Target and Lun. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI device specified by + Target and Lun was allocated and returned in + DevicePath. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist + on the SCSI channel. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + UFS_PASS_THRU_PRIVATE_DATA *Private; + EFI_DEV_PATH *DevicePathNode; + UINT8 UfsLun; + UINT16 Index; + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); + + // + // Validate parameters passed in. + // + SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0x00); + if (CompareMem (Target, mUfsTargetId, TARGET_MAX_BYTES) != 0) { + return EFI_INVALID_PARAMETER; + } + + if ((UINT8)Lun == UFS_WLUN_PREFIX) { + UfsLun = BIT7 | (((UINT8*)&Lun)[1] & 0xFF); + } else if ((UINT8)Lun == 0) { + UfsLun = ((UINT8*)&Lun)[1] & 0xFF; + } else { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < UFS_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] == UfsLun) { + break; + } + } + + if (Index == UFS_MAX_LUNS) { + return EFI_NOT_FOUND; + } + + DevicePathNode = AllocateCopyPool (sizeof (UFS_DEVICE_PATH), &mUfsDevicePathTemplate); + if (DevicePathNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + DevicePathNode->Ufs.Pun = 0; + DevicePathNode->Ufs.Lun = UfsLun; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) DevicePathNode; + + return EFI_SUCCESS; +} + +/** + Used to translate a device path node to a Target ID and LUN. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param DevicePath A pointer to a single device path node that describes the SCSI device + on the SCSI channel. + @param Target A pointer to the Target Array which represents the ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of a SCSI device on the SCSI channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and + LUN, and they were returned in Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN + does not exist. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in + DevicePath. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ) +{ + UFS_PASS_THRU_PRIVATE_DATA *Private; + EFI_DEV_PATH *DevicePathNode; + UINT8 Pun; + UINT8 UfsLun; + UINT16 Index; + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); + + // + // Validate parameters passed in. + // + if (DevicePath == NULL || Target == NULL || Lun == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (*Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the DevicePath belongs to SCSI_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || (DevicePath->SubType != MSG_UFS_DP) || + (DevicePathNodeLength(DevicePath) != sizeof(SCSI_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + DevicePathNode = (EFI_DEV_PATH *) DevicePath; + + Pun = (UINT8) DevicePathNode->Ufs.Pun; + UfsLun = (UINT8) DevicePathNode->Ufs.Lun; + + if (Pun != 0) { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < UFS_MAX_LUNS; Index++) { + if ((Private->Luns.BitMask & (BIT0 << Index)) == 0) { + continue; + } + + if (Private->Luns.Lun[Index] == UfsLun) { + break; + } + } + + if (Index == UFS_MAX_LUNS) { + return EFI_NOT_FOUND; + } + + SetMem (*Target, TARGET_MAX_BYTES, 0x00); + *Lun = 0; + if ((UfsLun & BIT7) == BIT7) { + ((UINT8*)Lun)[0] = UFS_WLUN_PREFIX; + ((UINT8*)Lun)[1] = UfsLun & ~BIT7; + } else { + ((UINT8*)Lun)[1] = UfsLun; + } + return EFI_SUCCESS; +} + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + + @retval EFI_SUCCESS The SCSI channel was reset. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. + @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ) +{ + // + // Return success directly then upper layer driver could think reset channel operation is done. + // + return EFI_SUCCESS; +} + +/** + Resets a SCSI logical unit that is connected to a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the + target port ID of the SCSI device containing the SCSI logical unit to + reset. Transport drivers may chose to utilize a subset of this array to suit + the representation of their targets. + @param Lun The LUN of the SCSI device to reset. + + @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device + specified by Target and Lun. + @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device + specified by Target and Lun. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ) +{ + // + // Return success directly then upper layer driver could think reset target LUN operation is done. + // + return EFI_SUCCESS; +} + +/** + Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either + be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs + for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to + see if a SCSI device is actually present at that location on the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ) +{ + UFS_PASS_THRU_PRIVATE_DATA *Private; + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); + + if (Target == NULL || *Target == NULL) { + return EFI_INVALID_PARAMETER; + } + + SetMem (mUfsTargetId, TARGET_MAX_BYTES, 0xFF); + if (CompareMem(*Target, mUfsTargetId, TARGET_MAX_BYTES) == 0) { + SetMem (*Target, TARGET_MAX_BYTES, 0x00); + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + 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 +UfsPassThruDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHostController; + + // + // Ufs Pass Thru driver is a device driver, and should ingore the + // "RemainingDevicePath" according to UEFI spec + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID *) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error + // + return Status; + } + // + // Close the protocol because we don't use it here + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + Status = gBS->OpenProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + (VOID **) &UfsHostController, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error + // + return Status; + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + UFS_PASS_THRU_PRIVATE_DATA *Private; + UINTN UfsHcBase; + UINT32 Index; + UFS_CONFIG_DESC Config; + + Status = EFI_SUCCESS; + UfsHc = NULL; + Private = NULL; + UfsHcBase = 0; + + DEBUG ((EFI_D_INFO, "==UfsPassThru Start== Controller = %x\n", Controller)); + + Status = gBS->OpenProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + (VOID **) &UfsHc, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Open Ufs Host Controller Protocol Error, Status = %r\n", Status)); + goto Error; + } + + // + // Get the UFS Host Controller MMIO Bar Base Address. + // + Status = UfsHc->GetUfsHcMmioBar (UfsHc, &UfsHcBase); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Get Ufs Host Controller Mmio Bar Error, Status = %r\n", Status)); + goto Error; + } + + // + // Initialize Ufs Pass Thru private data for managed UFS Host Controller. + // + Private = AllocateCopyPool (sizeof (UFS_PASS_THRU_PRIVATE_DATA), &gUfsPassThruTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Private->ExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode; + Private->UfsHostController = UfsHc; + Private->UfsHcBase = UfsHcBase; + + // + // Initialize UFS Host Controller H/W. + // + Status = UfsControllerInit (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Host Controller Initialization Error, Status = %r\n", Status)); + goto Error; + } + + // + // UFS 2.0 spec Section 13.1.3.3: + // At the end of the UFS Interconnect Layer initialization on both host and device side, + // the host shall send a NOP OUT UPIU to verify that the device UTP Layer is ready. + // + Status = UfsExecNopCmds (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Sending NOP IN command Error, Status = %r\n", Status)); + goto Error; + } + + // + // The host enables the device initialization completion by setting fDeviceInit flag. + // + Status = UfsSetFlag (Private, UfsFlagDevInit); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Set fDeviceInit Flag Error, Status = %r\n", Status)); + goto Error; + } + + // + // Get Ufs Device's Lun Info by reading Configuration Descriptor. + // + Status = UfsRwDeviceDesc (Private, TRUE, UfsConfigDesc, 0, 0, &Config, sizeof (UFS_CONFIG_DESC)); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Ufs Get Configuration Descriptor Error, Status = %r\n", Status)); + goto Error; + } + + // + // Check if 8 common luns are active and set corresponding bit mask. + // TODO: Parse device descriptor to decide if exposing RPMB LUN to upper layer for authentication access. + // + for (Index = 0; Index < 8; Index++) { + if (Config.UnitDescConfParams[Index].LunEn != 0) { + Private->Luns.BitMask |= (BIT0 << Index); + DEBUG ((EFI_D_INFO, "Ufs Lun %d is enabled\n", Index)); + } + } + + Status = gBS->InstallProtocolInterface ( + &Controller, + &gEfiExtScsiPassThruProtocolGuid, + EFI_NATIVE_INTERFACE, + &(Private->ExtScsiPassThru) + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; + +Error: + if (Private != NULL) { + if (Private->TmrlMapping != NULL) { + UfsHc->Unmap (UfsHc, Private->TmrlMapping); + } + if (Private->UtpTmrlBase != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutmrs * sizeof (UTP_TMRD)), Private->UtpTmrlBase); + } + + if (Private->TrlMapping != NULL) { + UfsHc->Unmap (UfsHc, Private->TrlMapping); + } + if (Private->UtpTrlBase != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase); + } + + FreePool (Private); + } + + if (UfsHc != NULL) { + gBS->CloseProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + This->DriverBindingHandle, + Controller + ); + } + + 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 +UfsPassThruDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UFS_PASS_THRU_PRIVATE_DATA *Private; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + DEBUG ((EFI_D_INFO, "==UfsPassThru Stop== Controller Controller = %x\n", Controller)); + + Status = gBS->OpenProtocol ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + (VOID **) &ExtScsiPassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Private = UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS (ExtScsiPassThru); + UfsHc = Private->UfsHostController; + + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiExtScsiPassThruProtocolGuid, + &(Private->ExtScsiPassThru) + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + // + // Stop Ufs Host Controller + // + Status = UfsControllerStop (Private); + ASSERT_EFI_ERROR (Status); + + if (Private->TmrlMapping != NULL) { + UfsHc->Unmap (UfsHc, Private->TmrlMapping); + } + if (Private->UtpTmrlBase != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutmrs * sizeof (UTP_TMRD)), Private->UtpTmrlBase); + } + + if (Private->TrlMapping != NULL) { + UfsHc->Unmap (UfsHc, Private->TrlMapping); + } + if (Private->UtpTrlBase != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (Private->Nutrs * sizeof (UTP_TMRD)), Private->UtpTrlBase); + } + + FreePool (Private); + + // + // Close protocols opened by UfsPassThru controller driver + // + gBS->CloseProtocol ( + Controller, + &gEdkiiUfsHostControllerProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return Status; +} + + +/** + The user Entry Point for module UfsPassThru. 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 error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +InitializeUfsPassThru ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gUfsPassThruDriverBinding, + ImageHandle, + &gUfsPassThruComponentName, + &gUfsPassThruComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h new file mode 100644 index 0000000000..c435fe19aa --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.h @@ -0,0 +1,727 @@ +/** @file + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_PASS_THRU_H_ +#define _UFS_PASS_THRU_H_ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UfsPassThruHci.h" + +#define UFS_PASS_THRU_SIG SIGNATURE_32 ('U', 'F', 'S', 'P') + +// +// Lun 0~7 is for 8 common luns. +// Lun 8~11 is for those 4 well known luns (Refer to UFS 2.0 spec Table 10.58 for details): +// Lun 8: REPORT LUNS +// Lun 9: UFS DEVICE +// Lun 10: BOOT +// Lun 11: RPMB +// +#define UFS_MAX_LUNS 12 +#define UFS_WLUN_PREFIX 0xC1 + +typedef struct { + UINT8 Lun[UFS_MAX_LUNS]; + UINT16 BitMask:12; // Bit 0~7 is 1/1 mapping to common luns. Bit 8~11 is 1/1 mapping to well-known luns. + UINT16 Rsvd:4; +} UFS_EXPOSED_LUNS; + +typedef struct _UFS_PASS_THRU_PRIVATE_DATA { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode; + EFI_EXT_SCSI_PASS_THRU_PROTOCOL ExtScsiPassThru; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHostController; + UINTN UfsHcBase; + UINT32 Capabilities; + + UINT8 TaskTag; + + VOID *UtpTrlBase; + UINT8 Nutrs; + VOID *TrlMapping; + VOID *UtpTmrlBase; + UINT8 Nutmrs; + VOID *TmrlMapping; + + UFS_EXPOSED_LUNS Luns; +} UFS_PASS_THRU_PRIVATE_DATA; + +#define UFS_TIMEOUT EFI_TIMER_PERIOD_SECONDS(3) + +#define ROUNDUP8(x) (((x) % 8 == 0) ? (x) : ((x) / 8 + 1) * 8) + +#define IS_ALIGNED(addr, size) (((UINTN) (addr) & (size - 1)) == 0) + +#define UFS_PASS_THRU_PRIVATE_DATA_FROM_THIS(a) \ + CR (a, \ + UFS_PASS_THRU_PRIVATE_DATA, \ + ExtScsiPassThru, \ + UFS_PASS_THRU_SIG \ + ) + +typedef struct _UFS_DEVICE_MANAGEMENT_REQUEST_PACKET { + UINT64 Timeout; + VOID *InDataBuffer; + VOID *OutDataBuffer; + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT32 InTransferLength; + UINT32 OutTransferLength; + UINT8 DataDirection; + UINT8 Ocs; +} UFS_DEVICE_MANAGEMENT_REQUEST_PACKET; + +// +// function prototype +// +/** + 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 +UfsPassThruDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Starts a device controller or a bus controller. + + The Start() function is designed to be invoked from the EFI boot service ConnectController(). + As a result, much of the error checking on the parameters to Start() has been moved into this + common boot service. It is legal to call Start() from other locations, + but the following calling restrictions must be followed or the system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE. + 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned + EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified by This must + have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle The handle of the controller to start. This handle + must support a protocol interface that supplies + an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This + parameter is ignored by device drivers, and is optional for bus + drivers. For a bus driver, if this parameter is NULL, then handles + for all the children of Controller are created by this driver. + If this parameter is not NULL and the first Device Path Node is + not the End of Device Path Node, then only the handle for the + child device specified by the first Device Path Node of + RemainingDevicePath is created by this driver. + If the first Device Path Node of RemainingDevicePath is + the End of Device Path Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruDriverBindingStart ( + 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 +UfsPassThruDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruComponentNameGetDriverName ( + 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 +UfsPassThruComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function + supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the + nonblocking I/O functionality is optional. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it represents + the id of the SCSI device to send the SCSI Request Packet. Each + transport driver may choose to utilize a subset of this size to suit the needs + of transport target representation. For example, a Fibre Channel driver + may use only 8 bytes (WWN) to represent an FC target. + @param Lun The LUN of the SCSI device to send the SCSI Request Packet. + @param Packet A pointer to the SCSI Request Packet to send to the SCSI device + specified by Target and Lun. + @param Event If nonblocking I/O is not supported then Event is ignored, and blocking + I/O is performed. If Event is NULL, then blocking I/O is performed. If + Event is not NULL and non blocking I/O is supported, then + nonblocking I/O is performed, and Event will be signaled when the + SCSI Request Packet completes. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that + could be transferred is returned in InTransferLength. For write + and bi-directional commands, OutTransferLength bytes were + transferred by OutDataBuffer. + @retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many + SCSI Request Packets already queued. The caller may retry again later. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. + @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported + by the host adapter. This includes the case of Bi-directional SCSI + commands not supported by the implementation. The SCSI Request + Packet was not sent, so no additional status information is available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruPassThru ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These + can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal + Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the + Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI + channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target On input, a pointer to the Target ID (an array of size + TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + @param Lun On input, a pointer to the LUN of a SCSI device present on the SCSI + channel. On output, a pointer to the LUN of the next SCSI device present + on a SCSI channel. + + @retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI + channel was returned in Target and Lun. + @retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were + not returned on a previous call to GetNextTargetLun(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetNextTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target, + IN OUT UINT64 *Lun + ); + +/** + Used to allocate and build a device path node for a SCSI device on a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the + Target ID of the SCSI device for which a device path node is to be + allocated and built. Transport drivers may chose to utilize a subset of + this size to suit the representation of targets. For example, a Fibre + Channel driver may use only 8 bytes (WWN) in the array to represent a + FC target. + @param Lun The LUN of the SCSI device for which a device path node is to be + allocated and built. + @param DevicePath A pointer to a single device path node that describes the SCSI device + specified by Target and Lun. This function is responsible for + allocating the buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibility to free + DevicePath when the caller is finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SCSI device specified by + Target and Lun was allocated and returned in + DevicePath. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist + on the SCSI channel. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruBuildDevicePath ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + Used to translate a device path node to a Target ID and LUN. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param DevicePath A pointer to a single device path node that describes the SCSI device + on the SCSI channel. + @param Target A pointer to the Target Array which represents the ID of a SCSI device + on the SCSI channel. + @param Lun A pointer to the LUN of a SCSI device on the SCSI channel. + + @retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and + LUN, and they were returned in Target and Lun. + @retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. + @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN + does not exist. + @retval EFI_UNSUPPORTED This driver does not support the device path node type in + DevicePath. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 **Target, + OUT UINT64 *Lun + ); + +/** + Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + + @retval EFI_SUCCESS The SCSI channel was reset. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. + @retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruResetChannel ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This + ); + +/** + Resets a SCSI logical unit that is connected to a SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target The Target is an array of size TARGET_MAX_BYTE and it represents the + target port ID of the SCSI device containing the SCSI logical unit to + reset. Transport drivers may chose to utilize a subset of this array to suit + the representation of their targets. + @param Lun The LUN of the SCSI device to reset. + + @retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device + specified by Target and Lun. + @retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device + specified by Target and Lun. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruResetTargetLun ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN UINT8 *Target, + IN UINT64 Lun + ); + +/** + Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either + be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs + for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to + see if a SCSI device is actually present at that location on the SCSI channel. + + @param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. + @param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. + On output, a pointer to the Target ID (an array of + TARGET_MAX_BYTES) of the next SCSI device present on a SCSI + channel. An input value of 0xF(all bytes in the array are 0xF) in the + Target array retrieves the Target ID of the first SCSI device present on a + SCSI channel. + + @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI + channel was returned in Target. + @retval EFI_INVALID_PARAMETER Target or Lun is NULL. + @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not + returned on a previous call to GetNextTarget(). + @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. + +**/ +EFI_STATUS +EFIAPI +UfsPassThruGetNextTarget ( + IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 **Target + ); + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmds ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ); + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NVM Express Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ); + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ); + +/** + Allocate common buffer for host and UFS bus master access simultaneously. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Size The length of buffer to be allocated. + @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range. + @param[out] CmdDescPhyAddr The resulting map address for the UFS bus master to use to access the hosts CmdDescHost. + @param[out] CmdDescMapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The common buffer was allocated successfully. + @retval EFI_DEVICE_ERROR The allocation fails. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsAllocateAlignCommonBuffer ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINTN Size, + OUT VOID **CmdDescHost, + OUT EFI_PHYSICAL_ADDRESS *CmdDescPhyAddr, + OUT VOID **CmdDescMapping + ); + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 FlagId + ); + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ); + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ); + +extern GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gUfsPassThruComponentName; +extern GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gUfsPassThruComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gUfsPassThruDriverBinding; + +#endif diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni new file mode 100644 index 0000000000..1e627bd9cd Binary files /dev/null and b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThru.uni differ diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf new file mode 100644 index 0000000000..21b8e6c0c2 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf @@ -0,0 +1,63 @@ +## @file +# Description file for the Universal Flash Storage (UFS) Pass Thru driver. +# +# Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = UfsPassThruDxe + MODULE_UNI_FILE = UfsPassThru.uni + FILE_GUID = E7F1DFF9-DAB6-498A-9ADF-57F344EDDF57 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeUfsPassThru + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gUfsPassThruDriverBinding +# COMPONENT_NAME = gUfsPassThruComponentName +# + +[Sources] + ComponentName.c + UfsPassThru.c + UfsPassThru.h + UfsPassThruHci.c + UfsPassThruHci.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + DevicePathLib + IoLib + TimerLib + +[Protocols] + gEfiExtScsiPassThruProtocolGuid ## BY_START + gEdkiiUfsHostControllerProtocolGuid ## TO_START + +[UserExtensions.TianoCore."ExtraFiles"] + UfsPassThruExtra.uni diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni new file mode 100644 index 0000000000..771b7ba3b5 Binary files /dev/null and b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruExtra.uni differ diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c new file mode 100644 index 0000000000..4cc32f0727 --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.c @@ -0,0 +1,2000 @@ +/** @file + UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface + for upper layer application to execute UFS-supported SCSI cmds. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "UfsPassThru.h" + +/** + Wait for the value of the specified system memory set to the test value. + + @param Address The system memory address to test. + @param MaskValue The mask value of memory. + @param TestValue The test value of memory. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +EFIAPI +UfsWaitMemSet ( + IN UINTN Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT64 Delay; + BOOLEAN InfiniteWait; + + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + Delay = DivU64x32 (Timeout, 10) + 1; + + do { + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = MmioRead32 (Address) & MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 1 microseconds. + // + MicroSecondDelay (1); + + Delay--; + + } while (InfiniteWait || (Delay > 0)); + + return EFI_TIMEOUT; +} + +/** + Dump UIC command execution result for debugging. + + @param[in] UicOpcode The executed UIC opcode. + @param[in] Result The result to be parsed. + +**/ +VOID +DumpUicCmdExecResult ( + IN UINT8 UicOpcode, + IN UINT8 Result + ) +{ + if (UicOpcode <= UfsUicDmePeerSet) { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE\n")); + break; + case 0x02: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - INVALID_MIB_ATTRIBUTE_VALUE\n")); + break; + case 0x03: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - READ_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x04: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - WRITE_ONLY_MIB_ATTRIBUTE\n")); + break; + case 0x05: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_INDEX\n")); + break; + case 0x06: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - LOCKED_MIB_ATTRIBUTE\n")); + break; + case 0x07: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BAD_TEST_FEATURE_INDEX\n")); + break; + case 0x08: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - PEER_COMMUNICATION_FAILURE\n")); + break; + case 0x09: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - BUSY\n")); + break; + case 0x0A: + DEBUG ((EFI_D_VERBOSE, "UIC configuration command fails - DME_FAILURE\n")); + break; + default : + ASSERT (FALSE); + break; + } + } else { + switch (Result) { + case 0x00: + break; + case 0x01: + DEBUG ((EFI_D_VERBOSE, "UIC control command fails - FAILURE\n")); + break; + default : + ASSERT (FALSE); + break; + } + } +} + +/** + Dump QUERY RESPONSE UPIU result for debugging. + + @param[in] Result The result to be parsed. + +**/ +VOID +DumpQueryResponseResult ( + IN UINT8 Result + ) +{ + switch (Result) { + case 0xF6: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Readable\n")); + break; + case 0xF7: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Not Writeable\n")); + break; + case 0xF8: + DEBUG ((EFI_D_VERBOSE, "Query Response with Parameter Already Written\n")); + break; + case 0xF9: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Length\n")); + break; + case 0xFA: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Value\n")); + break; + case 0xFB: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Selector\n")); + break; + case 0xFC: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Index\n")); + break; + case 0xFD: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Idn\n")); + break; + case 0xFE: + DEBUG ((EFI_D_VERBOSE, "Query Response with Invalid Opcode\n")); + break; + case 0xFF: + DEBUG ((EFI_D_VERBOSE, "Query Response with General Failure\n")); + break; + default : + ASSERT (FALSE); + break; + } +} + +/** + Swap little endian to big endian. + + @param[in, out] Buffer The data buffer. In input, it contains little endian data. + In output, it will become big endian. + @param[in] BufferSize The length of converted data. + +**/ +VOID +SwapLittleEndianToBigEndian ( + IN OUT UINT8 *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 Index; + UINT8 Temp; + UINT32 SwapCount; + + SwapCount = BufferSize / 2; + for (Index = 0; Index < SwapCount; Index++) { + Temp = Buffer[Index]; + Buffer[Index] = Buffer[BufferSize - 1 - Index]; + Buffer[BufferSize - 1 - Index] = Temp; + } +} + +/** + Fill TSF field of QUERY REQUEST UPIU. + + @param[in, out] TsfBase The base address of TSF field of QUERY REQUEST UPIU. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] Length The length of transferred data. The maximum is 4. + @param[in] Value The value of transferred data. + +**/ +VOID +UfsFillTsfOfQueryReqUpiu ( + IN OUT UTP_UPIU_TSF *TsfBase, + IN UINT8 Opcode, + IN UINT8 DescId OPTIONAL, + IN UINT8 Index OPTIONAL, + IN UINT8 Selector OPTIONAL, + IN UINT16 Length OPTIONAL, + IN UINT32 Value OPTIONAL + ) +{ + ASSERT (TsfBase != NULL); + ASSERT (Opcode <= UtpQueryFuncOpcodeTogFlag); + + TsfBase->Opcode = Opcode; + if (Opcode != UtpQueryFuncOpcodeNop) { + TsfBase->DescId = DescId; + TsfBase->Index = Index; + TsfBase->Selector = Selector; + + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + SwapLittleEndianToBigEndian ((UINT8*)&Length, sizeof (Length)); + TsfBase->Length = Length; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + SwapLittleEndianToBigEndian ((UINT8*)&Value, sizeof (Value)); + TsfBase->Value = Value; + } + } +} + +/** + Initialize COMMAND UPIU. + + @param[in, out] Command The base address of COMMAND UPIU. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] TaskTag The task tag of request. + @param[in] Cdb The cdb buffer containing SCSI command. + @param[in] CdbLength The cdb length. + @param[in] DataDirection The direction of data transfer. + @param[in] ExpDataTranLen The expected transfer data length. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitCommandUpiu ( + IN OUT UTP_COMMAND_UPIU *Command, + IN UINT8 Lun, + IN UINT8 TaskTag, + IN UINT8 *Cdb, + IN UINT8 CdbLength, + IN UFS_DATA_DIRECTION DataDirection, + IN UINT32 ExpDataTranLen + ) +{ + UINT8 Flags; + + ASSERT ((Command != NULL) && (Cdb != NULL)); + + // + // Task attribute is hard-coded to Ordered. + // + if (DataDirection == UfsDataIn) { + Flags = BIT0 | BIT6; + } else if (DataDirection == UfsDataOut) { + Flags = BIT0 | BIT5; + } else { + Flags = BIT0; + } + + // + // Fill UTP COMMAND UPIU associated fields. + // + Command->TransCode = 0x01; + Command->Flags = Flags; + Command->Lun = Lun; + Command->TaskTag = TaskTag; + Command->CmdSet = 0x00; + SwapLittleEndianToBigEndian ((UINT8*)&ExpDataTranLen, sizeof (ExpDataTranLen)); + Command->ExpDataTranLen = ExpDataTranLen; + + CopyMem (Command->Cdb, Cdb, CdbLength); + + return EFI_SUCCESS; +} + +/** + Initialize UTP PRDT for data transfer. + + @param[in] Prdt The base address of PRDT. + @param[in] Buffer The buffer to be read or written. + @param[in] BufferSize The data size to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitUtpPrdt ( + IN UTP_TR_PRD *Prdt, + IN VOID *Buffer, + IN UINT32 BufferSize + ) +{ + UINT32 PrdtIndex; + UINT32 RemainingLen; + UINT8 *Remaining; + UINTN PrdtNumber; + + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + ASSERT (((UINTN)Buffer & (BIT0 | BIT1)) == 0); + + RemainingLen = BufferSize; + Remaining = Buffer; + PrdtNumber = (UINTN)DivU64x32 ((UINT64)BufferSize + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD); + + for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) { + if (RemainingLen < UFS_MAX_DATA_LEN_PER_PRD) { + Prdt[PrdtIndex].DbCount = (UINT32)RemainingLen - 1; + } else { + Prdt[PrdtIndex].DbCount = UFS_MAX_DATA_LEN_PER_PRD - 1; + } + + Prdt[PrdtIndex].DbAddr = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 2); + Prdt[PrdtIndex].DbAddrU = (UINT32)RShiftU64 ((UINT64)(UINTN)Remaining, 32); + RemainingLen -= UFS_MAX_DATA_LEN_PER_PRD; + Remaining += UFS_MAX_DATA_LEN_PER_PRD; + } + + return EFI_SUCCESS; +} + +/** + Initialize QUERY REQUEST UPIU. + + @param[in, out] QueryReq The base address of QUERY REQUEST UPIU. + @param[in] TaskTag The task tag of request. + @param[in] Opcode The opcode of request. + @param[in] DescId The descriptor ID of request. + @param[in] Index The index of request. + @param[in] Selector The selector of request. + @param[in] DataSize The data size to be read or written. + @param[in] Data The buffer to be read or written. + + @retval EFI_SUCCESS The initialization succeed. + +**/ +EFI_STATUS +UfsInitQueryRequestUpiu ( + IN OUT UTP_QUERY_REQ_UPIU *QueryReq, + IN UINT8 TaskTag, + IN UINT8 Opcode, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN UINTN DataSize OPTIONAL, + IN UINT8 *Data OPTIONAL + ) +{ + ASSERT (QueryReq != NULL); + + QueryReq->TransCode = 0x16; + QueryReq->TaskTag = TaskTag; + if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeRdFlag) || (Opcode == UtpQueryFuncOpcodeRdAttr)) { + QueryReq->QueryFunc = QUERY_FUNC_STD_READ_REQ; + } else { + QueryReq->QueryFunc = QUERY_FUNC_STD_WRITE_REQ; + } + + if (Opcode == UtpQueryFuncOpcodeWrAttr) { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, *(UINT32*)Data); + } else if ((Opcode == UtpQueryFuncOpcodeRdDesc) || (Opcode == UtpQueryFuncOpcodeWrDesc)) { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, (UINT16)DataSize, 0); + } else { + UfsFillTsfOfQueryReqUpiu (&QueryReq->Tsf, Opcode, DescId, Index, Selector, 0, 0); + } + + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + CopyMem (QueryReq + 1, Data, DataSize); + } + + return EFI_SUCCESS; +} + +/** + Allocate COMMAND/RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Lun The Lun on which the SCSI command is executed. + @param[in] Packet The pointer to the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range. + @param[out] CmdDescMapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateScsiCommandDesc ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd, + OUT VOID **CmdDescHost, + OUT VOID **CmdDescMapping + ) +{ + UINTN TotalLen; + UINTN PrdtNumber; + UTP_COMMAND_UPIU *CommandUpiu; + EFI_PHYSICAL_ADDRESS CmdDescPhyAddr; + EFI_STATUS Status; + UINT32 DataLen; + UFS_DATA_DIRECTION DataDirection; + + ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + DataLen = Packet->InTransferLength; + DataDirection = UfsDataIn; + } else { + DataLen = Packet->OutTransferLength; + DataDirection = UfsDataOut; + } + + if (DataLen == 0) { + DataDirection = UfsNoData; + } + + PrdtNumber = (UINTN)DivU64x32 ((UINT64)DataLen + UFS_MAX_DATA_LEN_PER_PRD - 1, UFS_MAX_DATA_LEN_PER_PRD); + + TotalLen = ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)) + PrdtNumber * sizeof (UTP_TR_PRD); + + Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + CommandUpiu = (UTP_COMMAND_UPIU*)*CmdDescHost; + + UfsInitCommandUpiu (CommandUpiu, Lun, Private->TaskTag++, Packet->Cdb, Packet->CdbLength, DataDirection, DataLen); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Response UPIU and the Physical Region Description Table + // *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32); + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)), sizeof (UINT32)); + Trd->PrdtL = (UINT16)PrdtNumber; + Trd->PrdtO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))), sizeof (UINT32)); + return EFI_SUCCESS; +} + +/** + Allocate QUERY REQUEST/QUERY RESPONSE UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Packet The pointer to the UFS_DEVICE_MANAGEMENT_REQUEST_PACKET data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range. + @param[out] CmdDescMapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + @retval EFI_INVALID_PARAMETER The parameter passed in is invalid. + +**/ +EFI_STATUS +UfsCreateDMCommandDesc ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UFS_DEVICE_MANAGEMENT_REQUEST_PACKET *Packet, + IN UTP_TRD *Trd, + OUT VOID **CmdDescHost, + OUT VOID **CmdDescMapping + ) +{ + UINTN TotalLen; + UTP_QUERY_REQ_UPIU *QueryReqUpiu; + UINT8 Opcode; + UINT32 DataSize; + UINT8 *Data; + UINT8 DataDirection; + EFI_PHYSICAL_ADDRESS CmdDescPhyAddr; + EFI_STATUS Status; + + ASSERT ((Private != NULL) && (Packet != NULL) && (Trd != NULL)); + + Opcode = Packet->Opcode; + if ((Opcode > UtpQueryFuncOpcodeTogFlag) || (Opcode == UtpQueryFuncOpcodeNop)) { + return EFI_INVALID_PARAMETER; + } + + DataDirection = Packet->DataDirection; + if (DataDirection == UfsDataIn) { + DataSize = Packet->InTransferLength; + Data = Packet->InDataBuffer; + } else if (DataDirection == UfsDataOut) { + DataSize = Packet->OutTransferLength; + Data = Packet->OutDataBuffer; + } else { + DataSize = 0; + Data = NULL; + } + + if (((Opcode != UtpQueryFuncOpcodeSetFlag) && (Opcode != UtpQueryFuncOpcodeClrFlag) && (Opcode != UtpQueryFuncOpcodeTogFlag)) + && ((DataSize == 0) || (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (((Opcode == UtpQueryFuncOpcodeSetFlag) || (Opcode == UtpQueryFuncOpcodeClrFlag) || (Opcode == UtpQueryFuncOpcodeTogFlag)) + && ((DataSize != 0) || (Data != NULL))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrAttr) && (DataSize != sizeof (UINT32))) { + return EFI_INVALID_PARAMETER; + } + + if ((Opcode == UtpQueryFuncOpcodeWrDesc) || (Opcode == UtpQueryFuncOpcodeRdDesc)) { + TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize); + } else { + TotalLen = ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)); + } + + Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize UTP QUERY REQUEST UPIU + // + QueryReqUpiu = (UTP_QUERY_REQ_UPIU*)*CmdDescHost; + ASSERT (QueryReqUpiu != NULL); + UfsInitQueryRequestUpiu ( + QueryReqUpiu, + Private->TaskTag++, + Opcode, + Packet->DescId, + Packet->Index, + Packet->Selector, + DataSize, + Data + ); + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Query Response UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = DataDirection; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->Ocs = 0x0F; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32); + if (Opcode == UtpQueryFuncOpcodeWrDesc) { + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32)); + } else { + Trd->RuL = (UINT16)DivU64x32 ((UINT64)(ROUNDUP8 (sizeof (UTP_QUERY_RESP_UPIU)) + ROUNDUP8 (DataSize)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_QUERY_REQ_UPIU)), sizeof (UINT32)); + } + + return EFI_SUCCESS; +} + +/** + Allocate NOP IN and NOP OUT UPIU for filling UTP TRD's command descriptor field. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Trd The pointer to the UTP Transfer Request Descriptor. + @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range. + @param[out] CmdDescMapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The creation succeed. + @retval EFI_DEVICE_ERROR The creation failed. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsCreateNopCommandDesc ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UTP_TRD *Trd, + OUT VOID **CmdDescHost, + OUT VOID **CmdDescMapping + ) +{ + UINTN TotalLen; + UTP_NOP_OUT_UPIU *NopOutUpiu; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS CmdDescPhyAddr; + + ASSERT ((Private != NULL) && (Trd != NULL)); + + TotalLen = ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)) + ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)); + Status = UfsAllocateAlignCommonBuffer (Private, TotalLen, CmdDescHost, &CmdDescPhyAddr, CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + NopOutUpiu = (UTP_NOP_OUT_UPIU*)*CmdDescHost; + ASSERT (NopOutUpiu != NULL); + NopOutUpiu->TaskTag = Private->TaskTag++; + + // + // Fill UTP_TRD associated fields + // NOTE: Some UFS host controllers request the Nop Out UPIU *MUST* be located at a 64-bit aligned boundary. + // + Trd->Int = UFS_INTERRUPT_COMMAND; + Trd->Dd = 0x00; + Trd->Ct = UFS_STORAGE_COMMAND_TYPE; + Trd->UcdBa = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 7); + Trd->UcdBaU = (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32); + Trd->RuL = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_IN_UPIU)), sizeof (UINT32)); + Trd->RuO = (UINT16)DivU64x32 ((UINT64)ROUNDUP8 (sizeof (UTP_NOP_OUT_UPIU)), sizeof (UINT32)); + + return EFI_SUCCESS; +} + +/** + Find out available slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTrl ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT ((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Find out available slot in task management transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[out] Slot The available slot. + + @retval EFI_SUCCESS The available slot was found successfully. + +**/ +EFI_STATUS +UfsFindAvailableSlotInTmrl ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + OUT UINT8 *Slot + ) +{ + ASSERT ((Private != NULL) && (Slot != NULL)); + + // + // The simplest algo to always use slot 0. + // TODO: enhance it to support async transfer with multiple slot. + // + *Slot = 0; + + return EFI_SUCCESS; +} + +/** + Start specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Slot The slot to be started. + +**/ +VOID +UfsStartExecCmd ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_UTRLRSR) != UFS_HC_UTRLRSR) { + MmioWrite32 (Address, UFS_HC_UTRLRSR); + } + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + MmioWrite32 (Address, BIT0 << Slot); +} + +/** + Stop specified slot in transfer list of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Slot The slot to be stop. + +**/ +VOID +UfsStopExecCmd ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 Slot + ) +{ + UINTN UfsHcBase; + UINTN Address; + UINT32 Data; + + UfsHcBase = Private->UfsHcBase; + + Address = UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Data = MmioRead32 (Address); + if ((Data & (BIT0 << Slot)) != 0) { + Address = UfsHcBase + UFS_HC_UTRLCLR_OFFSET; + Data = MmioRead32 (Address); + MmioWrite32 (Address, (Data & ~(BIT0 << Slot))); + } +} + +/** + Read or write specified device descriptor of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] DescId The ID of device descriptor. + @param[in] Index The Index of device descriptor. + @param[in] Selector The Selector of device descriptor. + @param[in, out] Descriptor The buffer of device descriptor to be read or written. + @param[in] DescSize The size of device descriptor buffer. + + @retval EFI_SUCCESS The device descriptor was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the device descriptor. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the device descriptor. + +**/ +EFI_STATUS +UfsRwDeviceDesc ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 DescId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT VOID *Descriptor, + IN UINT32 DescSize + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT32 CmdDescSize; + UINT16 ReturnDataSize; + VOID *CmdDescHost; + VOID *CmdDescMapping; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.InDataBuffer = Descriptor; + Packet.InTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeRdDesc; + } else { + Packet.DataDirection = UfsDataOut; + Packet.OutDataBuffer = Descriptor; + Packet.OutTransferLength = DescSize; + Packet.Opcode = UtpQueryFuncOpcodeWrDesc; + } + Packet.DescId = DescId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + UfsHc = Private->UfsHostController; + QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32)); + ASSERT (QueryResp != NULL); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnDataSize = QueryResp->Tsf.Length; + SwapLittleEndianToBigEndian ((UINT8*)&ReturnDataSize, sizeof (UINT16)); + + if (Read) { + CopyMem (Packet.InDataBuffer, (QueryResp + 1), ReturnDataSize); + Packet.InTransferLength = ReturnDataSize; + } else { + Packet.OutTransferLength = ReturnDataSize; + } + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsHc->Flush (UfsHc); + + UfsStopExecCmd (Private, Slot); + + if (CmdDescMapping != NULL) { + UfsHc->Unmap (UfsHc, CmdDescMapping); + } + if (CmdDescHost != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost); + } + + return Status; +} + +/** + Read or write specified attribute of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] AttrId The ID of Attribute. + @param[in] Index The Index of Attribute. + @param[in] Selector The Selector of Attribute. + @param[in, out] Attributes The value of Attribute to be read or written. + + @retval EFI_SUCCESS The Attribute was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the Attribute. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the Attribute. + +**/ +EFI_STATUS +UfsRwAttributes ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 AttrId, + IN UINT8 Index, + IN UINT8 Selector, + IN OUT UINT32 *Attributes + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT32 CmdDescSize; + UINT32 ReturnData; + VOID *CmdDescHost; + VOID *CmdDescMapping; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdAttr; + } else { + Packet.DataDirection = UfsDataOut; + Packet.Opcode = UtpQueryFuncOpcodeWrAttr; + } + Packet.DescId = AttrId; + Packet.Index = Index; + Packet.Selector = Selector; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + UfsHc = Private->UfsHostController; + QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32)); + ASSERT (QueryResp != NULL); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + ReturnData = QueryResp->Tsf.Value; + SwapLittleEndianToBigEndian ((UINT8*)&ReturnData, sizeof (UINT32)); + *Attributes = ReturnData; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsHc->Flush (UfsHc); + + UfsStopExecCmd (Private, Slot); + + if (CmdDescMapping != NULL) { + UfsHc->Unmap (UfsHc, CmdDescMapping); + } + + if (CmdDescHost != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost); + } + + return Status; +} + +/** + Read or write specified flag of a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Read The boolean variable to show r/w direction. + @param[in] FlagId The ID of flag to be read or written. + @param[in, out] Value The value to set or clear flag. + + @retval EFI_SUCCESS The flag was read/written successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to r/w the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of r/w the flag. + +**/ +EFI_STATUS +UfsRwFlags ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN BOOLEAN Read, + IN UINT8 FlagId, + IN OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + UFS_DEVICE_MANAGEMENT_REQUEST_PACKET Packet; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UTP_QUERY_RESP_UPIU *QueryResp; + UINT32 CmdDescSize; + VOID *CmdDescHost; + VOID *CmdDescMapping; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + if (Value == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Packet, sizeof (UFS_DEVICE_MANAGEMENT_REQUEST_PACKET)); + + if (Read) { + ASSERT (Value != NULL); + Packet.DataDirection = UfsDataIn; + Packet.Opcode = UtpQueryFuncOpcodeRdFlag; + } else { + Packet.DataDirection = UfsDataOut; + if (*Value == 1) { + Packet.Opcode = UtpQueryFuncOpcodeSetFlag; + } else if (*Value == 0) { + Packet.Opcode = UtpQueryFuncOpcodeClrFlag; + } else { + return EFI_INVALID_PARAMETER; + } + } + Packet.DescId = FlagId; + Packet.Index = 0; + Packet.Selector = 0; + Packet.Timeout = UFS_TIMEOUT; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill transfer request descriptor to this slot. + // + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + Status = UfsCreateDMCommandDesc (Private, &Packet, Trd, &CmdDescHost, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + UfsHc = Private->UfsHostController; + QueryResp = (UTP_QUERY_RESP_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32)); + ASSERT (QueryResp != NULL); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet.Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (QueryResp->QueryResp != 0) { + DumpQueryResponseResult (QueryResp->QueryResp); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + *Value = (UINT8)QueryResp->Tsf.Value; + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsHc->Flush (UfsHc); + + UfsStopExecCmd (Private, Slot); + + if (CmdDescMapping != NULL) { + UfsHc->Unmap (UfsHc, CmdDescMapping); + } + if (CmdDescHost != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost); + } + + return Status; +} + +/** + Set specified flag to 1 on a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be set. + + @retval EFI_SUCCESS The flag was set successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to set the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of setting the flag. + +**/ +EFI_STATUS +UfsSetFlag ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 1; + Status = UfsRwFlags (Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Clear specified flag to 0 on a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be cleared. + + @retval EFI_SUCCESS The flag was cleared successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to clear the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of clearing the flag. + +**/ +EFI_STATUS +UfsClearFlag ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 FlagId + ) +{ + EFI_STATUS Status; + UINT8 Value; + + Value = 0; + Status = UfsRwFlags (Private, FALSE, FlagId, &Value); + + return Status; +} + +/** + Read specified flag from a UFS device. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] FlagId The ID of flag to be read. + @param[out] Value The flag's value. + + @retval EFI_SUCCESS The flag was read successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to read the flag. + @retval EFI_TIMEOUT A timeout occurred while waiting for the completion of reading the flag. + +**/ +EFI_STATUS +UfsReadFlag ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 FlagId, + OUT UINT8 *Value + ) +{ + EFI_STATUS Status; + + Status = UfsRwFlags (Private, TRUE, FlagId, Value); + + return Status; +} + +/** + Sends NOP IN cmd to a UFS device for initialization process request. + For more details, please refer to UFS 2.0 spec Figure 13.3. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The NOP IN command was sent by the host. The NOP OUT response was + received successfully. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to execute NOP IN command. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the NOP IN command to execute. + +**/ +EFI_STATUS +UfsExecNopCmds ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UTP_NOP_IN_UPIU *NopInUpiu; + UINT32 CmdDescSize; + UINTN Address; + VOID *CmdDescHost; + VOID *CmdDescMapping; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + Status = UfsCreateNopCommandDesc (Private, Trd, &CmdDescHost, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Check the transfer request result. + // + UfsHc = Private->UfsHostController; + NopInUpiu = (UTP_NOP_IN_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32)); + ASSERT (NopInUpiu != NULL); + CmdDescSize = Trd->RuO * sizeof (UINT32) + Trd->RuL * sizeof (UINT32); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (NopInUpiu->Resp != 0) { + Status = EFI_DEVICE_ERROR; + } else { + Status = EFI_SUCCESS; + } + +Exit: + UfsHc->Flush (UfsHc); + + UfsStopExecCmd (Private, Slot); + + if (CmdDescMapping != NULL) { + UfsHc->Unmap (UfsHc, CmdDescMapping); + } + if (CmdDescHost != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost); + } + + return Status; +} + +/** + Sends a UFS-supported SCSI Request Packet to a UFS device that is attached to the UFS host controller. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Lun The LUN of the UFS device to send the SCSI Request Packet. + @param[in, out] Packet A pointer to the SCSI Request Packet to send to a specified Lun of the + UFS device. + + @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional + commands, InTransferLength bytes were transferred from + InDataBuffer. For write and bi-directional commands, + OutTransferLength bytes were transferred by + OutDataBuffer. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request + Packet. + @retval EFI_OUT_OF_RESOURCES The resource for transfer is not available. + @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. + +**/ +EFI_STATUS +UfsExecScsiCmds ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 Lun, + IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 Slot; + UTP_TRD *Trd; + UINTN Address; + UINT32 CmdDescSize; + UTP_RESPONSE_UPIU *Response; + UINT16 SenseDataLen; + UINT32 ResTranCount; + VOID *CmdDescHost; + VOID *CmdDescMapping; + VOID *DataBufMapping; + VOID *DataBuf; + EFI_PHYSICAL_ADDRESS DataBufPhyAddr; + UINT32 DataLen; + UINTN MapLength; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + EDKII_UFS_HOST_CONTROLLER_OPERATION Flag; + UFS_DATA_DIRECTION DataDirection; + UTP_TR_PRD *PrdtBase; + + Trd = NULL; + CmdDescHost = NULL; + CmdDescMapping = NULL; + DataBufMapping = NULL; + DataBufPhyAddr = 0; + UfsHc = Private->UfsHostController; + // + // Find out which slot of transfer request list is available. + // + Status = UfsFindAvailableSlotInTrl (Private, &Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + Trd = ((UTP_TRD*)Private->UtpTrlBase) + Slot; + + // + // Fill transfer request descriptor to this slot. + // + Status = UfsCreateScsiCommandDesc (Private, Lun, Packet, Trd, &CmdDescHost, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + CmdDescSize = Trd->PrdtO * sizeof (UINT32) + Trd->PrdtL * sizeof (UTP_TR_PRD); + + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + DataBuf = Packet->InDataBuffer; + DataLen = Packet->InTransferLength; + DataDirection = UfsDataIn; + Flag = EdkiiUfsHcOperationBusMasterWrite; + } else { + DataBuf = Packet->OutDataBuffer; + DataLen = Packet->OutTransferLength; + DataDirection = UfsDataOut; + Flag = EdkiiUfsHcOperationBusMasterRead; + } + + if (DataLen == 0) { + DataDirection = UfsNoData; + } else { + MapLength = DataLen; + Status = UfsHc->Map ( + UfsHc, + Flag, + DataBuf, + &MapLength, + &DataBufPhyAddr, + &DataBufMapping + ); + + if (EFI_ERROR (Status) || (DataLen != MapLength)) { + goto Exit1; + } + } + // + // Fill PRDT table of Command UPIU for executed SCSI cmd. + // + PrdtBase = (UTP_TR_PRD*)((UINT8*)CmdDescHost + ROUNDUP8 (sizeof (UTP_COMMAND_UPIU)) + ROUNDUP8 (sizeof (UTP_RESPONSE_UPIU))); + ASSERT (PrdtBase != NULL); + UfsInitUtpPrdt (PrdtBase, (VOID*)(UINTN)DataBufPhyAddr, DataLen); + + // + // Start to execute the transfer request. + // + UfsStartExecCmd (Private, Slot); + + // + // Wait for the completion of the transfer request. + // + Address = Private->UfsHcBase + UFS_HC_UTRLDBR_OFFSET; + Status = UfsWaitMemSet (Address, BIT0, 0, Packet->Timeout); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get sense data if exists + // + Response = (UTP_RESPONSE_UPIU*)((UINT8*)CmdDescHost + Trd->RuO * sizeof (UINT32)); + ASSERT (Response != NULL); + SenseDataLen = Response->SenseDataLen; + SwapLittleEndianToBigEndian ((UINT8*)&SenseDataLen, sizeof (UINT16)); + + if ((Packet->SenseDataLength != 0) && (Packet->SenseData != NULL)) { + CopyMem (Packet->SenseData, Response->SenseData, SenseDataLen); + Packet->SenseDataLength = (UINT8)SenseDataLen; + } + + // + // Check the transfer request result. + // + Packet->TargetStatus = Response->Status; + if (Response->Response != 0) { + DEBUG ((EFI_D_ERROR, "UfsExecScsiCmds() fails with Target Failure\n")); + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + if (Trd->Ocs == 0) { + if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32)); + Packet->InTransferLength -= ResTranCount; + } + } else { + if ((Response->Flags & BIT5) == BIT5) { + ResTranCount = Response->ResTranCount; + SwapLittleEndianToBigEndian ((UINT8*)&ResTranCount, sizeof (UINT32)); + Packet->OutTransferLength -= ResTranCount; + } + } + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + UfsHc->Flush (UfsHc); + + UfsStopExecCmd (Private, Slot); + + if (DataBufMapping != NULL) { + UfsHc->Unmap (UfsHc, DataBufMapping); + } + +Exit1: + if (CmdDescMapping != NULL) { + UfsHc->Unmap (UfsHc, CmdDescMapping); + } + if (CmdDescHost != NULL) { + UfsHc->FreeBuffer (UfsHc, EFI_SIZE_TO_PAGES (CmdDescSize), CmdDescHost); + } + return Status; +} + + +/** + Sent UIC DME_LINKSTARTUP command to start the link startup procedure. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] UicOpcode The opcode of the UIC command. + @param[in] Arg1 The value for 1st argument of the UIC command. + @param[in] Arg2 The value for 2nd argument of the UIC command. + @param[in] Arg3 The value for 3rd argument of the UIC command. + + @return EFI_SUCCESS Successfully execute this UIC command and detect attached UFS device. + @return EFI_DEVICE_ERROR Fail to execute this UIC command and detect attached UFS device. + @return EFI_NOT_FOUND The presence of the UFS device isn't detected. + +**/ +EFI_STATUS +UfsExecUicCommands ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINT8 UicOpcode, + IN UINT32 Arg1, + IN UINT32 Arg2, + IN UINT32 Arg3 + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + UINTN UfsHcBase; + + UfsHcBase = Private->UfsHcBase; + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_IS_UCCS) == UFS_HC_IS_UCCS) { + // + // Clear IS.BIT10 UIC Command Completion Status (UCCS) at first. + // + MmioWrite32 (Address, Data); + } + + // + // When programming UIC command registers, host software shall set the register UICCMD + // only after all the UIC command argument registers (UICCMDARG1, UICCMDARG2 and UICCMDARG3) + // are set. + // + Address = UfsHcBase + UFS_HC_UCMD_ARG1_OFFSET; + MmioWrite32 (Address, Arg1); + + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + MmioWrite32 (Address, Arg2); + + Address = UfsHcBase + UFS_HC_UCMD_ARG3_OFFSET; + MmioWrite32 (Address, Arg3); + + // + // Host software shall only set the UICCMD if HCS.UCRDY is set to 1. + // + Address = Private->UfsHcBase + UFS_HC_STATUS_OFFSET; + Status = UfsWaitMemSet (Address, UFS_HC_HCS_UCRDY, UFS_HC_HCS_UCRDY, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + Address = UfsHcBase + UFS_HC_UIC_CMD_OFFSET; + MmioWrite32 (Address, (UINT32)UicOpcode); + + // + // UFS 2.0 spec section 5.3.1 Offset:0x20 IS.Bit10 UIC Command Completion Status (UCCS) + // This bit is set to '1' by the host controller upon completion of a UIC command. + // + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Data = MmioRead32 (Address); + Status = UfsWaitMemSet (Address, UFS_HC_IS_UCCS, UFS_HC_IS_UCCS, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + if (UicOpcode != UfsUicDmeReset) { + Address = UfsHcBase + UFS_HC_UCMD_ARG2_OFFSET; + Data = MmioRead32 (Address); + if ((Data & 0xFF) != 0) { + DEBUG_CODE_BEGIN(); + DumpUicCmdExecResult (UicOpcode, (UINT8)(Data & 0xFF)); + DEBUG_CODE_END(); + return EFI_DEVICE_ERROR; + } + } + + // + // Check value of HCS.DP and make sure that there is a device attached to the Link. + // + Address = UfsHcBase + UFS_HC_STATUS_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_HCS_DP) == 0) { + Address = UfsHcBase + UFS_HC_IS_OFFSET; + Status = UfsWaitMemSet (Address, UFS_HC_IS_ULSS, UFS_HC_IS_ULSS, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return EFI_NOT_FOUND; + } + + DEBUG ((EFI_D_INFO, "UfsPassThruDxe: found a attached UFS device\n")); + + return EFI_SUCCESS; +} + +/** + Allocate common buffer for host and UFS bus master access simultaneously. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + @param[in] Size The length of buffer to be allocated. + @param[out] CmdDescHost A pointer to store the base system memory address of the allocated range. + @param[out] CmdDescPhyAddr The resulting map address for the UFS bus master to use to access the hosts CmdDescHost. + @param[out] CmdDescMapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The common buffer was allocated successfully. + @retval EFI_DEVICE_ERROR The allocation fails. + @retval EFI_OUT_OF_RESOURCES The memory resource is insufficient. + +**/ +EFI_STATUS +UfsAllocateAlignCommonBuffer ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private, + IN UINTN Size, + OUT VOID **CmdDescHost, + OUT EFI_PHYSICAL_ADDRESS *CmdDescPhyAddr, + OUT VOID **CmdDescMapping + ) +{ + EFI_STATUS Status; + UINTN Bytes; + BOOLEAN Is32BitAddr; + EDKII_UFS_HOST_CONTROLLER_PROTOCOL *UfsHc; + + if ((Private->Capabilities & UFS_HC_CAP_64ADDR) == UFS_HC_CAP_64ADDR) { + Is32BitAddr = TRUE; + } else { + Is32BitAddr = FALSE; + } + + UfsHc = Private->UfsHostController; + Status = UfsHc->AllocateBuffer ( + UfsHc, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (Size), + CmdDescHost, + 0 + ); + if (EFI_ERROR (Status)) { + *CmdDescMapping = NULL; + *CmdDescHost = NULL; + *CmdDescPhyAddr = 0; + return EFI_OUT_OF_RESOURCES; + } + + Bytes = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)); + Status = UfsHc->Map ( + UfsHc, + EdkiiUfsHcOperationBusMasterCommonBuffer, + *CmdDescHost, + &Bytes, + CmdDescPhyAddr, + CmdDescMapping + ); + + if (EFI_ERROR (Status) || (Bytes != EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)))) { + UfsHc->FreeBuffer ( + UfsHc, + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)), + *CmdDescHost + ); + *CmdDescHost = NULL; + return EFI_OUT_OF_RESOURCES; + } + + if (Is32BitAddr && ((*CmdDescPhyAddr) > 0x100000000ULL)) { + // + // The UFS host controller doesn't support 64bit addressing, so should not get a >4G UFS bus master address. + // + UfsHc->Unmap ( + UfsHc, + *CmdDescMapping + ); + UfsHc->FreeBuffer ( + UfsHc, + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size)), + *CmdDescHost + ); + *CmdDescMapping = NULL; + *CmdDescHost = NULL; + return EFI_DEVICE_ERROR; + } + + ZeroMem (*CmdDescHost, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Size))); + return EFI_SUCCESS; +} + +/** + Enable the UFS host controller for accessing. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS host controller enabling was executed successfully. + @retval EFI_DEVICE_ERROR A device error occurred while enabling the UFS host controller. + +**/ +EFI_STATUS +UfsEnableHostController ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // UFS 2.0 spec section 7.1.1 - Host Controller Initialization + // + // Reinitialize the UFS host controller if HCE bit of HC register is set. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = MmioRead32 (Address); + if ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN) { + // + // Write a 0 to the HCE register at first to disable the host controller. + // + MmioWrite32 (Address, 0); + // + // Wait until HCE is read as '0' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + // + // Write a 1 to the HCE register to enable the UFS host controller. + // + MmioWrite32 (Address, UFS_HC_HCE_EN); + // + // Wait until HCE is read as '1' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, UFS_HC_HCE_EN, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + +/** + Detect if a UFS device attached. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS device detection was executed successfully. + @retval EFI_NOT_FOUND Not found a UFS device attached. + @retval EFI_DEVICE_ERROR A device error occurred while detecting the UFS device. + +**/ +EFI_STATUS +UfsDeviceDetection ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + UINTN Retry; + EFI_STATUS Status; + + // + // Start UFS device detection. + // Try up to 3 times for establishing data link with device. + // + for (Retry = 0; Retry < 3; Retry++) { + Status = UfsExecUicCommands (Private, UfsUicDmeLinkStartup, 0, 0, 0); + if (!EFI_ERROR (Status)) { + break; + } + + if (Status == EFI_NOT_FOUND) { + continue; + } + + return EFI_DEVICE_ERROR; + } + + if (Retry == 3) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Initialize UFS task management request list related h/w context. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS task management list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTaskManagementRequestList ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutmrs; + VOID *CmdDescHost; + EFI_PHYSICAL_ADDRESS CmdDescPhyAddr; + VOID *CmdDescMapping; + EFI_STATUS Status; + + // + // Initial h/w and s/w context for future operations. + // + CmdDescHost = NULL; + CmdDescMapping = NULL; + CmdDescPhyAddr = 0; + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = MmioRead32 (Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Task Management Request List. + // + Nutmrs = (UINT8) (RShiftU64 ((Private->Capabilities & UFS_HC_CAP_NUTMRS), 16) + 1); + Status = UfsAllocateAlignCommonBuffer (Private, Nutmrs * sizeof (UTP_TMRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Program the UTP Task Management Request List Base Address and UTP Task Management + // Request List Base Address with a 64-bit address allocated at step 6. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLBA_OFFSET; + MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr); + Address = Private->UfsHcBase + UFS_HC_UTMRLBAU_OFFSET; + MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32)); + Private->UtpTmrlBase = CmdDescHost; + Private->Nutmrs = Nutmrs; + Private->TmrlMapping = CmdDescMapping; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + MmioWrite32 (Address, UFS_HC_UTMRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize UFS transfer request list related h/w context. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The UFS transfer list was initialzed successfully. + @retval EFI_DEVICE_ERROR The initialization fails. + +**/ +EFI_STATUS +UfsInitTransferRequestList ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + UINTN Address; + UINT32 Data; + UINT8 Nutrs; + VOID *CmdDescHost; + EFI_PHYSICAL_ADDRESS CmdDescPhyAddr; + VOID *CmdDescMapping; + EFI_STATUS Status; + + // + // Initial h/w and s/w context for future operations. + // + CmdDescHost = NULL; + CmdDescMapping = NULL; + CmdDescPhyAddr = 0; + Address = Private->UfsHcBase + UFS_HC_CAP_OFFSET; + Data = MmioRead32 (Address); + Private->Capabilities = Data; + + // + // Allocate and initialize UTP Transfer Request List. + // + Nutrs = (UINT8)((Private->Capabilities & UFS_HC_CAP_NUTRS) + 1); + Status = UfsAllocateAlignCommonBuffer (Private, Nutrs * sizeof (UTP_TRD), &CmdDescHost, &CmdDescPhyAddr, &CmdDescMapping); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Program the UTP Transfer Request List Base Address and UTP Transfer Request List + // Base Address with a 64-bit address allocated at step 8. + // + Address = Private->UfsHcBase + UFS_HC_UTRLBA_OFFSET; + MmioWrite32 (Address, (UINT32)(UINTN)CmdDescPhyAddr); + Address = Private->UfsHcBase + UFS_HC_UTRLBAU_OFFSET; + MmioWrite32 (Address, (UINT32)RShiftU64 ((UINT64)CmdDescPhyAddr, 32)); + Private->UtpTrlBase = CmdDescHost; + Private->Nutrs = Nutrs; + Private->TrlMapping = CmdDescMapping; + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + MmioWrite32 (Address, UFS_HC_UTRLRSR); + + return EFI_SUCCESS; +} + +/** + Initialize the UFS host controller. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is initialized successfully. + @retval Others A device error occurred while initializing the controller. + +**/ +EFI_STATUS +UfsControllerInit ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + + Status = UfsEnableHostController (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsControllerInit: Enable Host Controller Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsDeviceDetection (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsControllerInit: Device Detection Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsInitTaskManagementRequestList (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsControllerInit: Task management list initialization Fails, Status = %r\n", Status)); + return Status; + } + + Status = UfsInitTransferRequestList (Private); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "UfsControllerInit: Transfer list initialization Fails, Status = %r\n", Status)); + return Status; + } + + DEBUG ((EFI_D_INFO, "UfsControllerInit Finished\n")); + return EFI_SUCCESS; +} + +/** + Stop the UFS host controller. + + @param[in] Private The pointer to the UFS_PASS_THRU_PRIVATE_DATA data structure. + + @retval EFI_SUCCESS The Ufs Host Controller is stopped successfully. + @retval Others A device error occurred while stopping the controller. + +**/ +EFI_STATUS +UfsControllerStop ( + IN UFS_PASS_THRU_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + UINTN Address; + UINT32 Data; + + // + // Enable the UTP Task Management Request List by setting the UTP Task Management + // Request List RunStop Register (UTMRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTMRLRSR_OFFSET; + MmioWrite32 (Address, 0); + + // + // Enable the UTP Transfer Request List by setting the UTP Transfer Request List + // RunStop Register (UTRLRSR) to '1'. + // + Address = Private->UfsHcBase + UFS_HC_UTRLRSR_OFFSET; + MmioWrite32 (Address, 0); + + // + // Write a 0 to the HCE register in order to disable the host controller. + // + Address = Private->UfsHcBase + UFS_HC_ENABLE_OFFSET; + Data = MmioRead32 (Address); + ASSERT ((Data & UFS_HC_HCE_EN) == UFS_HC_HCE_EN); + MmioWrite32 (Address, 0); + + // + // Wait until HCE is read as '0' before continuing. + // + Status = UfsWaitMemSet (Address, UFS_HC_HCE_EN, 0, UFS_TIMEOUT); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + DEBUG ((EFI_D_INFO, "UfsController is stopped\n")); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h new file mode 100644 index 0000000000..a423a921de --- /dev/null +++ b/MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruHci.h @@ -0,0 +1,1339 @@ +/** @file + UfsPassThruDxe driver is used to produce EFI_EXT_SCSI_PASS_THRU protocol interface + for upper layer application to execute UFS-supported SCSI cmds. + + Copyright (c) 2014, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _UFS_PASS_THRU_HCI_H_ +#define _UFS_PASS_THRU_HCI_H_ + +// +// Host Capabilities Register Offsets +// +#define UFS_HC_CAP_OFFSET 0x0000 // Controller Capabilities +#define UFS_HC_VER_OFFSET 0x0008 // Version +#define UFS_HC_DDID_OFFSET 0x0010 // Device ID and Device Class +#define UFS_HC_PMID_OFFSET 0x0014 // Product ID and Manufacturer ID +#define UFS_HC_AHIT_OFFSET 0x0018 // Auto-Hibernate Idle Timer +// +// Operation and Runtime Register Offsets +// +#define UFS_HC_IS_OFFSET 0x0020 // Interrupt Status +#define UFS_HC_IE_OFFSET 0x0024 // Interrupt Enable +#define UFS_HC_STATUS_OFFSET 0x0030 // Host Controller Status +#define UFS_HC_ENABLE_OFFSET 0x0034 // Host Controller Enable +#define UFS_HC_UECPA_OFFSET 0x0038 // Host UIC Error Code PHY Adapter Layer +#define UFS_HC_UECDL_OFFSET 0x003c // Host UIC Error Code Data Link Layer +#define UFS_HC_UECN_OFFSET 0x0040 // Host UIC Error Code Network Layer +#define UFS_HC_UECT_OFFSET 0x0044 // Host UIC Error Code Transport Layer +#define UFS_HC_UECDME_OFFSET 0x0048 // Host UIC Error Code DME +#define UFS_HC_UTRIACR_OFFSET 0x004c // UTP Transfer Request Interrupt Aggregation Control Register +// +// UTP Transfer Register Offsets +// +#define UFS_HC_UTRLBA_OFFSET 0x0050 // UTP Transfer Request List Base Address +#define UFS_HC_UTRLBAU_OFFSET 0x0054 // UTP Transfer Request List Base Address Upper 32-Bits +#define UFS_HC_UTRLDBR_OFFSET 0x0058 // UTP Transfer Request List Door Bell Register +#define UFS_HC_UTRLCLR_OFFSET 0x005c // UTP Transfer Request List CLear Register +#define UFS_HC_UTRLRSR_OFFSET 0x0060 // UTP Transfer Request Run-Stop Register +// +// UTP Task Management Register Offsets +// +#define UFS_HC_UTMRLBA_OFFSET 0x0070 // UTP Task Management Request List Base Address +#define UFS_HC_UTMRLBAU_OFFSET 0x0074 // UTP Task Management Request List Base Address Upper 32-Bits +#define UFS_HC_UTMRLDBR_OFFSET 0x0078 // UTP Task Management Request List Door Bell Register +#define UFS_HC_UTMRLCLR_OFFSET 0x007c // UTP Task Management Request List CLear Register +#define UFS_HC_UTMRLRSR_OFFSET 0x0080 // UTP Task Management Run-Stop Register +// +// UIC Command Register Offsets +// +#define UFS_HC_UIC_CMD_OFFSET 0x0090 // UIC Command Register +#define UFS_HC_UCMD_ARG1_OFFSET 0x0094 // UIC Command Argument 1 +#define UFS_HC_UCMD_ARG2_OFFSET 0x0098 // UIC Command Argument 2 +#define UFS_HC_UCMD_ARG3_OFFSET 0x009c // UIC Command Argument 3 +// +// UMA Register Offsets +// +#define UFS_HC_UMA_OFFSET 0x00b0 // Reserved for Unified Memory Extension + +#define UFS_HC_HCE_EN BIT0 +#define UFS_HC_HCS_DP BIT0 +#define UFS_HC_HCS_UCRDY BIT3 +#define UFS_HC_IS_ULSS BIT8 +#define UFS_HC_IS_UCCS BIT10 +#define UFS_HC_CAP_64ADDR BIT24 +#define UFS_HC_CAP_NUTMRS (BIT16 | BIT17 | BIT18) +#define UFS_HC_CAP_NUTRS (BIT0 | BIT1 | BIT2 | BIT3 | BIT4) +#define UFS_HC_UTMRLRSR BIT0 +#define UFS_HC_UTRLRSR BIT0 + +// +// A maximum of length of 256KB is supported by PRDT entry +// +#define UFS_MAX_DATA_LEN_PER_PRD 0x40000 + +#define UFS_STORAGE_COMMAND_TYPE 0x01 + +#define UFS_REGULAR_COMMAND 0x00 +#define UFS_INTERRUPT_COMMAND 0x01 + +#define UFS_LUN_0 0x00 +#define UFS_LUN_1 0x01 +#define UFS_LUN_2 0x02 +#define UFS_LUN_3 0x03 +#define UFS_LUN_4 0x04 +#define UFS_LUN_5 0x05 +#define UFS_LUN_6 0x06 +#define UFS_LUN_7 0x07 +#define UFS_WLUN_REPORT_LUNS 0x81 +#define UFS_WLUN_UFS_DEV 0xD0 +#define UFS_WLUN_BOOT 0xB0 +#define UFS_WLUN_RPMB 0xC4 + +#pragma pack(1) + +// +// UFSHCI 2.0 Spec Section 5.2.1 Offset 00h: CAP - Controller Capabilities +// +typedef struct { + UINT8 Nutrs:4; // Number of UTP Transfer Request Slots + UINT8 Rsvd1:4; + + UINT8 NoRtt; // Number of outstanding READY TO TRANSFER (RTT) requests supported + + UINT8 Nutmrs:3; // Number of UTP Task Management Request Slots + UINT8 Rsvd2:4; + UINT8 AutoHs:1; // Auto-Hibernation Support + + UINT8 As64:1; // 64-bit addressing supported + UINT8 Oodds:1; // Out of order data delivery supported + UINT8 UicDmetms:1; // UIC DME_TEST_MODE command supported + UINT8 Ume:1; // Reserved for Unified Memory Extension + UINT8 Rsvd4:4; +} UFS_HC_CAP; + +// +// UFSHCI 2.0 Spec Section 5.2.2 Offset 08h: VER - UFS Version +// +typedef struct { + UINT8 Vs:4; // Version Suffix + UINT8 Mnr:4; // Minor version number + + UINT8 Mjr; // Major version number + + UINT16 Rsvd1; +} UFS_HC_VER; + +// +// UFSHCI 2.0 Spec Section 5.2.3 Offset 10h: HCPID - Host Controller Product ID +// +#define UFS_HC_PID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.4 Offset 14h: HCMID - Host Controller Manufacturer ID +// +#define UFS_HC_MID UINT32 + +// +// UFSHCI 2.0 Spec Section 5.2.5 Offset 18h: AHIT - Auto-Hibernate Idle Timer +// +typedef struct { + UINT32 Ahitv:10; // Auto-Hibernate Idle Timer Value + UINT32 Ts:3; // Timer scale + UINT32 Rsvd1:19; +} UFS_HC_AHIT; + +// +// UFSHCI 2.0 Spec Section 5.3.1 Offset 20h: IS - Interrupt Status +// +typedef struct { + UINT16 Utrcs:1; // UTP Transfer Request Completion Status + UINT16 Udepri:1; // UIC DME_ENDPOINT_RESET Indication + UINT16 Ue:1; // UIC Error + UINT16 Utms:1; // UIC Test Mode Status + + UINT16 Upms:1; // UIC Power Mode Status + UINT16 Uhxs:1; // UIC Hibernate Exit Status + UINT16 Uhes:1; // UIC Hibernate Enter Status + UINT16 Ulls:1; // UIC Link Lost Status + + UINT16 Ulss:1; // UIC Link Startup Status + UINT16 Utmrcs:1; // UTP Task Management Request Completion Status + UINT16 Uccs:1; // UIC Command Completion Status + UINT16 Dfes:1; // Device Fatal Error Status + + UINT16 Utpes:1; // UTP Error Status + UINT16 Rsvd1:3; + + UINT16 Hcfes:1; // Host Controller Fatal Error Status + UINT16 Sbfes:1; // System Bus Fatal Error Status + UINT16 Rsvd2:14; +} UFS_HC_IS; + +// +// UFSHCI 2.0 Spec Section 5.3.2 Offset 24h: IE - Interrupt Enable +// +typedef struct { + UINT16 Utrce:1; // UTP Transfer Request Completion Enable + UINT16 Udeprie:1; // UIC DME_ENDPOINT_RESET Enable + UINT16 Uee:1; // UIC Error Enable + UINT16 Utmse:1; // UIC Test Mode Status Enable + + UINT16 Upmse:1; // UIC Power Mode Status Enable + UINT16 Uhxse:1; // UIC Hibernate Exit Status Enable + UINT16 Uhese:1; // UIC Hibernate Enter Status Enable + UINT16 Ullse:1; // UIC Link Lost Status Enable + + UINT16 Ulsse:1; // UIC Link Startup Status Enable + UINT16 Utmrce:1; // UTP Task Management Request Completion Enable + UINT16 Ucce:1; // UIC Command Completion Enable + UINT16 Dfee:1; // Device Fatal Error Enable + + UINT16 Utpee:1; // UTP Error Enable + UINT16 Rsvd1:3; + + UINT16 Hcfee:1; // Host Controller Fatal Error Enable + UINT16 Sbfee:1; // System Bus Fatal Error Enable + UINT16 Rsvd2:14; +} UFS_HC_IE; + +// +// UFSHCI 2.0 Spec Section 5.3.3 Offset 30h: HCS - Host Controller Status +// +typedef struct { + UINT8 Dp:1; // Device Present + UINT8 UtrlRdy:1; // UTP Transfer Request List Ready + UINT8 UtmrlRdy:1; // UTP Task Management Request List Ready + UINT8 UcRdy:1; // UIC COMMAND Ready + UINT8 Rsvd1:4; + + UINT8 Upmcrs:3; // UIC Power Mode Change Request Status + UINT8 Rsvd2:1; // UIC Hibernate Exit Status Enable + UINT8 Utpec:4; // UTP Error Code + + UINT8 TtagUtpE; // Task Tag of UTP error + UINT8 TlunUtpE; // Target LUN of UTP error +} UFS_HC_STATUS; + +// +// UFSHCI 2.0 Spec Section 5.3.4 Offset 34h: HCE - Host Controller Enable +// +typedef struct { + UINT32 Hce:1; // Host Controller Enable + UINT32 Rsvd1:31; +} UFS_HC_ENABLE; + +// +// UFSHCI 2.0 Spec Section 5.3.5 Offset 38h: UECPA - Host UIC Error Code PHY Adapter Layer +// +typedef struct { + UINT32 Ec:5; // UIC PHY Adapter Layer Error Code + UINT32 Rsvd1:26; + UINT32 Err:1; // UIC PHY Adapter Layer Error +} UFS_HC_UECPA; + +// +// UFSHCI 2.0 Spec Section 5.3.6 Offset 3ch: UECDL - Host UIC Error Code Data Link Layer +// +typedef struct { + UINT32 Ec:15; // UIC Data Link Layer Error Code + UINT32 Rsvd1:16; + UINT32 Err:1; // UIC Data Link Layer Error +} UFS_HC_UECDL; + +// +// UFSHCI 2.0 Spec Section 5.3.7 Offset 40h: UECN - Host UIC Error Code Network Layer +// +typedef struct { + UINT32 Ec:3; // UIC Network Layer Error Code + UINT32 Rsvd1:28; + UINT32 Err:1; // UIC Network Layer Error +} UFS_HC_UECN; + +// +// UFSHCI 2.0 Spec Section 5.3.8 Offset 44h: UECT - Host UIC Error Code Transport Layer +// +typedef struct { + UINT32 Ec:7; // UIC Transport Layer Error Code + UINT32 Rsvd1:24; + UINT32 Err:1; // UIC Transport Layer Error +} UFS_HC_UECT; + +// +// UFSHCI 2.0 Spec Section 5.3.9 Offset 48h: UECDME - Host UIC Error Code +// +typedef struct { + UINT32 Ec:1; // UIC DME Error Code + UINT32 Rsvd1:30; + UINT32 Err:1; // UIC DME Error +} UFS_HC_UECDME; + +// +// UFSHCI 2.0 Spec Section 5.3.10 Offset 4Ch: UTRIACR - UTP Transfer Request Interrupt Aggregation Control Register +// +typedef struct { + UINT8 IaToVal; // Interrupt aggregation timeout value + + UINT8 IacTh:5; // Interrupt aggregation counter threshold + UINT8 Rsvd1:3; + + UINT8 Ctr:1; // Counter and Timer Reset + UINT8 Rsvd2:3; + UINT8 Iasb:1; // Interrupt aggregation status bit + UINT8 Rsvd3:3; + + UINT8 IapwEn:1; // Interrupt aggregation parameter write enable + UINT8 Rsvd4:6; + UINT8 IaEn:1; // Interrupt Aggregation Enable/Disable +} UFS_HC_UTRIACR; + +// +// UFSHCI 2.0 Spec Section 5.4.1 Offset 50h: UTRLBA - UTP Transfer Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtrlBa:22; // UTP Transfer Request List Base Address +} UFS_HC_UTRLBA; + +// +// UFSHCI 2.0 Spec Section 5.4.2 Offset 54h: UTRLBAU - UTP Transfer Request List Base Address Upper 32-bits +// +#define UFS_HC_UTRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.3 Offset 58h: UTRLDBR - UTP Transfer Request List Door Bell Register +// +#define UFS_HC_UTRLDBR UINT32 + +// +// UFSHCI 2.0 Spec Section 5.4.4 Offset 5Ch: UTRLCLR - UTP Transfer Request List CLear Register +// +#define UFS_HC_UTRLCLR UINT32 + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.4.5 Offset 60h: UTRLRSR - UTP Transfer Request List Run Stop Register +// +typedef struct { + UINT32 UtrlRsr:1; // UTP Transfer Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.5.1 Offset 70h: UTMRLBA - UTP Task Management Request List Base Address +// +typedef struct { + UINT32 Rsvd1:10; + UINT32 UtmrlBa:22; // UTP Task Management Request List Base Address +} UFS_HC_UTMRLBA; + +// +// UFSHCI 2.0 Spec Section 5.5.2 Offset 74h: UTMRLBAU - UTP Task Management Request List Base Address Upper 32-bits +// +#define UFS_HC_UTMRLBAU UINT32 + +// +// UFSHCI 2.0 Spec Section 5.5.3 Offset 78h: UTMRLDBR - UTP Task Management Request List Door Bell Register +// +typedef struct { + UINT32 UtmrlDbr:8; // UTP Task Management Request List Door bell Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLDBR; + +// +// UFSHCI 2.0 Spec Section 5.5.4 Offset 7Ch: UTMRLCLR - UTP Task Management Request List CLear Register +// +typedef struct { + UINT32 UtmrlClr:8; // UTP Task Management List Clear Register + UINT32 Rsvd1:24; +} UFS_HC_UTMRLCLR; + +#if 0 +// +// UFSHCI 2.0 Spec Section 5.5.5 Offset 80h: UTMRLRSR - UTP Task Management Request List Run Stop Register +// +typedef struct { + UINT32 UtmrlRsr:1; // UTP Task Management Request List Run-Stop Register + UINT32 Rsvd1:31; +} UFS_HC_UTMRLRSR; +#endif + +// +// UFSHCI 2.0 Spec Section 5.6.1 Offset 90h: UICCMD - UIC Command +// +typedef struct { + UINT32 CmdOp:8; // Command Opcode + UINT32 Rsvd1:24; +} UFS_HC_UICCMD; + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 94h: UICCMDARG1 - UIC Command Argument 1 +// +#define UFS_HC_UICCMD_ARG1 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 98h: UICCMDARG2 - UIC Command Argument 2 +// +#define UFS_HC_UICCMD_ARG2 UINT32 + +// +// UFSHCI 2.0 Spec Section 5.6.2 Offset 9ch: UICCMDARG3 - UIC Command Argument 3 +// +#define UFS_HC_UICCMD_ARG3 UINT32 + +// +// UIC command opcodes +// +typedef enum { + UfsUicDmeGet = 0x01, + UfsUicDmeSet = 0x02, + UfsUicDmePeerGet = 0x03, + UfsUicDmePeerSet = 0x04, + UfsUicDmePwrOn = 0x10, + UfsUicDmePwrOff = 0x11, + UfsUicDmeEnable = 0x12, + UfsUicDmeReset = 0x14, + UfsUicDmeEndpointReset = 0x15, + UfsUicDmeLinkStartup = 0x16, + UfsUicDmeHibernateEnter = 0x17, + UfsUicDmeHibernateExit = 0x18, + UfsUicDmeTestMode = 0x1A +} UFS_UIC_OPCODE; + +// +// UTP Transfer Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Dd:2; /* Data Direction */ + UINT32 Rsvd2:1; + UINT32 Ct:4; /* Command Type */ + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 + // + UINT32 Rsvd6:7; + UINT32 UcdBa:25; /* UTP Command Descriptor Base Address */ + + // + // DW5 + // + UINT32 UcdBaU; /* UTP Command Descriptor Base Address Upper 32-bits */ + + // + // DW6 + // + UINT16 RuL; /* Response UPIU Length */ + UINT16 RuO; /* Response UPIU Offset */ + + // + // DW7 + // + UINT16 PrdtL; /* PRDT Length */ + UINT16 PrdtO; /* PRDT Offset */ +} UTP_TRD; + +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:2; + UINT32 DbAddr:30; /* Data Base Address */ + + // + // DW1 + // + UINT32 DbAddrU; /* Data Base Address Upper 32-bits */ + + // + // DW2 + // + UINT32 Rsvd2; + + // + // DW3 + // + UINT32 DbCount:18; /* Data Byte Count */ + UINT32 Rsvd3:14; +} UTP_TR_PRD; + +// +// UFS 2.0 Spec Section 10.5.3 - UTP Command UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x01*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Rsvd3; + UINT8 Rsvd4; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd5; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 ExpDataTranLen; /* Expected Data Transfer Length - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Cdb[16]; +} UTP_COMMAND_UPIU; + +// +// UFS 2.0 Spec Section 10.5.4 - UTP Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x21*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 CmdSet:4; /* Command Set Type */ + UINT8 Rsvd1:4; + UINT8 Rsvd2; + UINT8 Response; /* Response */ + UINT8 Status; /* Status */ + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 ResTranCount; /* Residual Transfer Count - Big Endian */ + + // + // DW4 - DW7 + // + UINT8 Rsvd3[16]; + + // + // Data Segment - Sense Data + // + UINT16 SenseDataLen; /* Sense Data Length - Big Endian */ + UINT8 SenseData[18]; /* Sense Data */ +} UTP_RESPONSE_UPIU; + +// +// UFS 2.0 Spec Section 10.5.5 - UTP Data-Out UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x02*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be sent out + // + //UINT8 Data[]; /* Data to be sent out, maximum is 65535 bytes */ +} UTP_DATA_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.6 - UTP Data-In UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x22*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_DATA_IN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.7 - UTP Ready-To-Transfer UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x31*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd2; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 DataBufOffset; /* Data Buffer Offset - Big Endian */ + + // + // DW4 + // + UINT32 DataTranCount; /* Data Transfer Count - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd3[12]; + + // + // Data Segment - Data to be read + // + //UINT8 Data[]; /* Data to be read, maximum is 65535 bytes */ +} UTP_RDY_TO_TRAN_UPIU; + +// +// UFS 2.0 Spec Section 10.5.8 - UTP Task Management Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x04*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1; + UINT8 TskManFunc; /* Task Management Function */ + UINT8 Rsvd2[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 InputParam1; /* Input Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 InputParam2; /* Input Parameter 2 - Big Endian */ + + // + // DW5 + // + UINT32 InputParam3; /* Input Parameter 3 - Big Endian */ + + // + // DW6 - DW7 + // + UINT8 Rsvd4[8]; +} UTP_TM_REQ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.9 - UTP Task Management Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x24*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Resp; /* Response */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT32 OutputParam1; /* Output Parameter 1 - Big Endian */ + + // + // DW4 + // + UINT32 OutputParam2; /* Output Parameter 2 - Big Endian */ + + // + // DW5 - DW7 + // + UINT8 Rsvd4[12]; +} UTP_TM_RESP_UPIU; + +// +// UTP Task Management Request Descriptor +// +typedef struct { + // + // DW0 + // + UINT32 Rsvd1:24; + UINT32 Int:1; /* Interrupt */ + UINT32 Rsvd2:7; + + // + // DW1 + // + UINT32 Rsvd3; + + // + // DW2 + // + UINT32 Ocs:8; /* Overall Command Status */ + UINT32 Rsvd4:24; + + // + // DW3 + // + UINT32 Rsvd5; + + // + // DW4 - DW11 + // + UTP_TM_REQ_UPIU TmReq; /* Task Management Request UPIU */ + + // + // DW12 - DW19 + // + UTP_TM_RESP_UPIU TmResp; /* Task Management Response UPIU */ +} UTP_TMRD; + + +typedef struct { + UINT8 Opcode; + UINT8 DescId; + UINT8 Index; + UINT8 Selector; + UINT16 Rsvd1; + UINT16 Length; + UINT32 Value; + UINT32 Rsvd2; +} UTP_UPIU_TSF; + +// +// UFS 2.0 Spec Section 10.5.10 - UTP Query Request UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x16*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 Rsvd3[2]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd4; + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd5[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_REQ_UPIU; + +#define QUERY_FUNC_STD_READ_REQ 0x01 +#define QUERY_FUNC_STD_WRITE_REQ 0x81 + +typedef enum { + UtpQueryFuncOpcodeNop = 0x00, + UtpQueryFuncOpcodeRdDesc = 0x01, + UtpQueryFuncOpcodeWrDesc = 0x02, + UtpQueryFuncOpcodeRdAttr = 0x03, + UtpQueryFuncOpcodeWrAttr = 0x04, + UtpQueryFuncOpcodeRdFlag = 0x05, + UtpQueryFuncOpcodeSetFlag = 0x06, + UtpQueryFuncOpcodeClrFlag = 0x07, + UtpQueryFuncOpcodeTogFlag = 0x08 +} UTP_QUERY_FUNC_OPCODE; + +// +// UFS 2.0 Spec Section 10.5.11 - UTP Query Response UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x36*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2; + UINT8 QueryFunc; /* Query Function */ + UINT8 QueryResp; /* Query Response */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian */ + + // + // DW3 - 6 + // + UTP_UPIU_TSF Tsf; /* Transaction Specific Fields */ + + // + // DW7 + // + UINT8 Rsvd4[4]; + + // + // Data Segment - Data to be transferred + // + //UINT8 Data[]; /* Data to be transferred, maximum is 65535 bytes */ +} UTP_QUERY_RESP_UPIU; + +typedef enum { + UfsUtpQueryResponseSuccess = 0x00, + UfsUtpQueryResponseParamNotReadable = 0xF6, + UfsUtpQueryResponseParamNotWriteable = 0xF7, + UfsUtpQueryResponseParamAlreadyWritten = 0xF8, + UfsUtpQueryResponseInvalidLen = 0xF9, + UfsUtpQueryResponseInvalidVal = 0xFA, + UfsUtpQueryResponseInvalidSelector = 0xFB, + UfsUtpQueryResponseInvalidIndex = 0xFC, + UfsUtpQueryResponseInvalidIdn = 0xFD, + UfsUtpQueryResponseInvalidOpc = 0xFE, + UfsUtpQueryResponseGeneralFailure = 0xFF +} UTP_QUERY_RESP_CODE; + +// +// UFS 2.0 Spec Section 10.5.12 - UTP Reject UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x3F*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Lun; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd1[2]; + UINT8 Response; /* Response - 0x01 */ + UINT8 Rsvd2; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 + // + UINT8 HdrSts; /* Basic Header Status */ + UINT8 Rsvd3; + UINT8 E2ESts; /* End-to-End Status */ + UINT8 Rsvd4; + + // + // DW4 - DW7 + // + UINT8 Rsvd5[16]; +} UTP_REJ_UPIU; + +// +// UFS 2.0 Spec Section 10.5.13 - UTP NOP OUT UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x00*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[4]; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 Rsvd3; + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_OUT_UPIU; + +// +// UFS 2.0 Spec Section 10.5.14 - UTP NOP IN UPIU +// +typedef struct { + // + // DW0 + // + UINT8 TransCode:6; /* Transaction Type - 0x20*/ + UINT8 Dd:1; + UINT8 Hd:1; + UINT8 Flags; + UINT8 Rsvd1; + UINT8 TaskTag; /* Task Tag */ + + // + // DW1 + // + UINT8 Rsvd2[2]; + UINT8 Resp; /* Response - 0x00 */ + UINT8 Rsvd3; + + // + // DW2 + // + UINT8 EhsLen; /* Total EHS Length - 0x00 */ + UINT8 DevInfo; /* Device Information - 0x00 */ + UINT16 DataSegLen; /* Data Segment Length - Big Endian - 0x0000 */ + + // + // DW3 - DW7 + // + UINT8 Rsvd4[20]; +} UTP_NOP_IN_UPIU; + +// +// UFS Descriptors +// +typedef enum { + UfsDeviceDesc = 0x00, + UfsConfigDesc = 0x01, + UfsUnitDesc = 0x02, + UfsInterConnDesc = 0x04, + UfsStringDesc = 0x05, + UfsGeometryDesc = 0x07, + UfsPowerDesc = 0x08 +} UFS_DESC_IDN; + +// +// UFS 2.0 Spec Section 14.1.6.2 - Device Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Device; + UINT8 DevClass; + UINT8 DevSubClass; + UINT8 Protocol; + UINT8 NumLun; + UINT8 NumWLun; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 SecurityLun; + UINT8 BgOpsTermLat; + UINT8 InitActiveIccLevel; + UINT16 SpecVersion; + UINT16 ManufactureDate; + UINT8 ManufacturerName; + UINT8 ProductName; + UINT8 SerialName; + UINT8 OemId; + UINT16 ManufacturerId; + UINT8 Ud0BaseOffset; + UINT8 Ud0ConfParamLen; + UINT8 DevRttCap; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd1[17]; + UINT8 Rsvd2[16]; +} UFS_DEV_DESC; + +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 Rsvd1; + UINT8 BootEn; + UINT8 DescAccessEn; + UINT8 InitPowerMode; + UINT8 HighPriorityLun; + UINT8 SecureRemovalType; + UINT8 InitActiveIccLevel; + UINT16 PeriodicRtcUpdate; + UINT8 Rsvd2[5]; +} UFS_CONFIG_DESC_GEN_HEADER; + +typedef struct { + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 MemType; + UINT32 NumAllocUnits; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT8 ProvisionType; + UINT16 CtxCap; + UINT8 Rsvd1[3]; +} UFS_UNIT_DESC_CONFIG_PARAMS; + +// +// UFS 2.0 Spec Section 14.1.6.3 - Configuration Descriptor +// +typedef struct { + UFS_CONFIG_DESC_GEN_HEADER Header; + UFS_UNIT_DESC_CONFIG_PARAMS UnitDescConfParams[8]; +} UFS_CONFIG_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.4 - Geometry Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 MediaTech; + UINT8 Rsvd1; + UINT64 TotalRawDevCapacity; + UINT8 Rsvd2; + UINT32 SegSize; + UINT8 AllocUnitSize; + UINT8 MinAddrBlkSize; + UINT8 OptReadBlkSize; + UINT8 OptWriteBlkSize; + UINT8 MaxInBufSize; + UINT8 MaxOutBufSize; + UINT8 RpmbRwSize; + UINT8 Rsvd3; + UINT8 DataOrder; + UINT8 MaxCtxIdNum; + UINT8 SysDataTagUnitSize; + UINT8 SysDataResUnitSize; + UINT8 SupSecRemovalTypes; + UINT16 SupMemTypes; + UINT32 SysCodeMaxNumAllocUnits; + UINT16 SupCodeCapAdjFac; + UINT32 NonPersMaxNumAllocUnits; + UINT16 NonPersCapAdjFac; + UINT32 Enhance1MaxNumAllocUnits; + UINT16 Enhance1CapAdjFac; + UINT32 Enhance2MaxNumAllocUnits; + UINT16 Enhance2CapAdjFac; + UINT32 Enhance3MaxNumAllocUnits; + UINT16 Enhance3CapAdjFac; + UINT32 Enhance4MaxNumAllocUnits; + UINT16 Enhance4CapAdjFac; +} UFS_GEOMETRY_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.5 - Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 DataReliability; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT16 CtxCap; + UINT8 LargeUnitGranularity; +} UFS_UNIT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.6 - RPMB Unit Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT8 UnitIdx; + UINT8 LunEn; + UINT8 BootLunId; + UINT8 LunWriteProt; + UINT8 LunQueueDep; + UINT8 Rsvd1; + UINT8 MemType; + UINT8 Rsvd2; + UINT8 LogicBlkSize; + UINT64 LogicBlkCount; + UINT32 EraseBlkSize; + UINT8 ProvisionType; + UINT64 PhyMemResCount; + UINT8 Rsvd3[3]; +} UFS_RPMB_UNIT_DESC; + +typedef struct { + UINT16 Value:10; + UINT16 Rsvd1:4; + UINT16 Unit:2; +} UFS_POWER_PARAM_ELEMENT; + +// +// UFS 2.0 Spec Section 14.1.6.7 - Power Parameter Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVcc[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ[16]; + UFS_POWER_PARAM_ELEMENT ActiveIccLevelVccQ2[16]; +} UFS_POWER_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.8 - InterConnect Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + UINT16 UniProVer; + UINT16 MphyVer; +} UFS_INTER_CONNECT_DESC; + +// +// UFS 2.0 Spec Section 14.1.6.9 - 14.1.6.12 - String Descriptor +// +typedef struct { + UINT8 Length; + UINT8 DescType; + CHAR16 Unicode[126]; +} UFS_STRING_DESC; + +// +// UFS 2.0 Spec Section 14.2 - Flags +// +typedef enum { + UfsFlagDevInit = 0x01, + UfsFlagPermWpEn = 0x02, + UfsFlagPowerOnWpEn = 0x03, + UfsFlagBgOpsEn = 0x04, + UfsFlagPurgeEn = 0x06, + UfsFlagPhyResRemoval = 0x08, + UfsFlagBusyRtc = 0x09, + UfsFlagPermDisFwUpdate = 0x0B +} UFS_FLAGS_IDN; + +// +// UFS 2.0 Spec Section 14.2 - Attributes +// +typedef enum { + UfsAttrBootLunEn = 0x00, + UfsAttrCurPowerMode = 0x02, + UfsAttrActiveIccLevel = 0x03, + UfsAttrOutOfOrderDataEn = 0x04, + UfsAttrBgOpStatus = 0x05, + UfsAttrPurgeStatus = 0x06, + UfsAttrMaxDataInSize = 0x07, + UfsAttrMaxDataOutSize = 0x08, + UfsAttrDynCapNeeded = 0x09, + UfsAttrRefClkFreq = 0x0a, + UfsAttrConfigDescLock = 0x0b, + UfsAttrMaxNumOfRtt = 0x0c, + UfsAttrExceptionEvtCtrl = 0x0d, + UfsAttrExceptionEvtSts = 0x0e, + UfsAttrSecondsPassed = 0x0f, + UfsAttrContextConf = 0x10, + UfsAttrCorrPrgBlkNum = 0x11 +} UFS_ATTR_IDN; + +typedef enum { + UfsNoData = 0, + UfsDataOut = 1, + UfsDataIn = 2, + UfsDdReserved +} UFS_DATA_DIRECTION; + + +#pragma pack() + +#endif + diff --git a/MdeModulePkg/Include/Ppi/UfsHostController.h b/MdeModulePkg/Include/Ppi/UfsHostController.h new file mode 100644 index 0000000000..129636580e --- /dev/null +++ b/MdeModulePkg/Include/Ppi/UfsHostController.h @@ -0,0 +1,60 @@ +/** @file + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+ +This program and the accompanying materials +are licensed and made available under the terms and conditions +of the BSD License which accompanies this distribution. The +full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _EDKII_PEI_UFS_HOST_CONTROLLER_PPI_H_ +#define _EDKII_PEI_UFS_HOST_CONTROLLER_PPI_H_ + +/// +/// Global ID for the EDKII_UFS_HOST_CONTROLLER_PPI. +/// +#define EDKII_UFS_HOST_CONTROLLER_PPI_GUID \ + { \ + 0xdc54b283, 0x1a77, 0x4cd6, { 0x83, 0xbb, 0xfd, 0xda, 0x46, 0x9a, 0x2e, 0xc6 } \ + } + +/// +/// Forward declaration for the UFS_HOST_CONTROLLER_PPI. +/// +typedef struct _EDKII_UFS_HOST_CONTROLLER_PPI EDKII_UFS_HOST_CONTROLLER_PPI; + +/** + Get the MMIO base address of UFS host controller. + + @param[in] This The protocol instance pointer. + @param[in] ControllerId The ID of the UFS host controller. + @param[out] MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_GET_MMIO_BAR)( + IN EDKII_UFS_HOST_CONTROLLER_PPI *This, + IN UINT8 ControllerId, + OUT UINTN *MmioBar + ); + +/// +/// This PPI contains a set of services to interact with the UFS host controller. +/// +struct _EDKII_UFS_HOST_CONTROLLER_PPI { + EDKII_UFS_HC_GET_MMIO_BAR GetUfsHcMmioBar; +}; + +extern EFI_GUID gEdkiiPeiUfsHostControllerPpiGuid; + +#endif diff --git a/MdeModulePkg/Include/Protocol/UfsHostController.h b/MdeModulePkg/Include/Protocol/UfsHostController.h new file mode 100644 index 0000000000..83db1a7bbd --- /dev/null +++ b/MdeModulePkg/Include/Protocol/UfsHostController.h @@ -0,0 +1,205 @@ +/** @file + + EDKII Universal Flash Storage Host Controller Protocol. + +Copyright (c) 2014, Intel Corporation. All rights reserved.
+This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef __EDKII_UFS_HC_PROTOCOL_H__ +#define __EDKII_UFS_HC_PROTOCOL_H__ + +// +// UFS Host Controller Protocol GUID value +// +#define EDKII_UFS_HOST_CONTROLLER_PROTOCOL_GUID \ + { \ + 0xebc01af5, 0x7a9, 0x489e, { 0xb7, 0xce, 0xdc, 0x8, 0x9e, 0x45, 0x9b, 0x2f } \ + } + +// +// Forward reference for pure ANSI compatability +// +typedef struct _EDKII_UFS_HOST_CONTROLLER_PROTOCOL EDKII_UFS_HOST_CONTROLLER_PROTOCOL; + + +/** + Get the MMIO base address of UFS host controller. + + @param This The protocol instance pointer. + @param MmioBar Pointer to the UFS host controller MMIO base address. + + @retval EFI_SUCCESS The operation succeeds. + @retval EFI_INVALID_PARAMETER The parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_GET_MMIO_BAR)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + OUT UINTN *MmioBar + ); + +/// +/// ******************************************************* +/// EFI_UFS_HOST_CONTROLLER_OPERATION +/// ******************************************************* +/// +typedef enum { + /// + /// A read operation from system memory by a bus master. + /// + EdkiiUfsHcOperationBusMasterRead, + /// + /// A write operation from system memory by a bus master. + /// + EdkiiUfsHcOperationBusMasterWrite, + /// + /// Provides both read and write access to system memory by both the processor and a + /// bus master. The buffer is coherent from both the processor's and the bus master's point of view. + /// + EdkiiUfsHcOperationBusMasterCommonBuffer, + EdkiiUfsHcOperationMaximum +} EDKII_UFS_HOST_CONTROLLER_OPERATION; + +/** + Provides the UFS controller-specific addresses needed to access system memory. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @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 UFS 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 UFS 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. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_MAP)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EDKII_UFS_HOST_CONTROLLER_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 This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_UNMAP)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an EfiUfsHcOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @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. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_ALLOCATE_BUFFER)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @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(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_FREE_BUFFER)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Flushes all posted write transactions from the UFS bus to attached UFS device. + + @param This A pointer to the EFI_UFS_HOST_CONTROLLER_PROTOCOL instance. + + @retval EFI_SUCCESS The posted write transactions were flushed from the UFS bus + to attached UFS device. + @retval EFI_DEVICE_ERROR The posted write transactions were not flushed from the UFS + bus to attached UFS device due to a hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EDKII_UFS_HC_FLUSH)( + IN EDKII_UFS_HOST_CONTROLLER_PROTOCOL *This + ); + +/// +/// UFS Host Controller Protocol structure. +/// +struct _EDKII_UFS_HOST_CONTROLLER_PROTOCOL { + EDKII_UFS_HC_GET_MMIO_BAR GetUfsHcMmioBar; + EDKII_UFS_HC_ALLOCATE_BUFFER AllocateBuffer; + EDKII_UFS_HC_FREE_BUFFER FreeBuffer; + EDKII_UFS_HC_MAP Map; + EDKII_UFS_HC_UNMAP Unmap; + EDKII_UFS_HC_FLUSH Flush; +}; + +/// +/// UFS Host Controller Protocol GUID variable. +/// +extern EFI_GUID gEdkiiUfsHostControllerProtocolGuid; + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 421c6977ab..5591b2f068 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -324,6 +324,9 @@ ## Include/Ppi/SerialPortPei.h gPeiSerialPortPpiGuid = { 0x490e9d85, 0x8aef, 0x4193, { 0x8e, 0x56, 0xf7, 0x34, 0xa9, 0xff, 0xac, 0x8b}} + ## Include/Ppi/UfsHostController.h + gEdkiiPeiUfsHostControllerPpiGuid = { 0xdc54b283, 0x1a77, 0x4cd6, { 0x83, 0xbb, 0xfd, 0xda, 0x46, 0x9a, 0x2e, 0xc6 }} + [Protocols] ## Load File protocol provides capability to load and unload EFI image into memory and execute it. # Include/Protocol/LoadPe32Image.h @@ -401,6 +404,9 @@ ## Include/Protocol/FormBrowserEx2.h gEdkiiFormBrowserEx2ProtocolGuid = { 0xa770c357, 0xb693, 0x4e6d, { 0xa6, 0xcf, 0xd2, 0x1c, 0x72, 0x8e, 0x55, 0xb } } + ## Include/Protocol/UfsHostController.h + gEdkiiUfsHostControllerProtocolGuid = { 0xebc01af5, 0x7a9, 0x489e, { 0xb7, 0xce, 0xdc, 0x8, 0x9e, 0x45, 0x9b, 0x2f } } + # # [Error.gEfiMdeModulePkgTokenSpaceGuid] # 0x80000001 | Invalid value provided. @@ -1214,6 +1220,12 @@ # @Prompt Disk I/O - Number of Data Buffer block. gEfiMdeModulePkgTokenSpaceGuid.PcdDiskIoDataBufferBlockNum|64|UINT32|0x30001039 + ## This PCD specifies the PCI-based UFS host controller mmio base address. + # Define the mmio base address of the pci-based UFS host controller. If there are multiple UFS + # host controllers, their mmio base addresses are calculated one by one from this base address. + # @Prompt Mmio base address of pci-based UFS host controller. + gEfiMdeModulePkgTokenSpaceGuid.PcdUfsPciHostControllerMmioBase|0xd0000000|UINT32|0x10000061 + [PcdsPatchableInModule] ## Specify memory size with page number for PEI code when # Loading Module at Fixed Address feature is enabled. diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index b9df820d6d..0bf3cca871 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -203,6 +203,10 @@ MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf + MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf + MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf + MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf + MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf diff --git a/MdeModulePkg/MdeModulePkg.uni b/MdeModulePkg/MdeModulePkg.uni index add9a51e56..1527453712 100644 Binary files a/MdeModulePkg/MdeModulePkg.uni and b/MdeModulePkg/MdeModulePkg.uni differ