From nobody Fri Mar 29 13:46:40 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1533890968091793.5362876877518; Fri, 10 Aug 2018 01:49:28 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id E489F210EB129; Fri, 10 Aug 2018 01:49:26 -0700 (PDT) Received: from mail-pg1-x529.google.com (mail-pg1-x529.google.com [IPv6:2607:f8b0:4864:20::529]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 08C3E210EB126 for ; Fri, 10 Aug 2018 01:49:25 -0700 (PDT) Received: by mail-pg1-x529.google.com with SMTP id a11-v6so4088572pgw.6 for ; Fri, 10 Aug 2018 01:49:25 -0700 (PDT) Received: from localhost.localdomain ([64.64.108.24]) by smtp.gmail.com with ESMTPSA id 82-v6sm23569724pfw.159.2018.08.10.01.49.21 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 10 Aug 2018 01:49:23 -0700 (PDT) X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::529; helo=mail-pg1-x529.google.com; envelope-from=haojian.zhuang@linaro.org; receiver=edk2-devel@lists.01.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references; bh=0tX/xkgIgpqfq7/D3E9E6o00DyQ+OBl0Xvw7+1wGnzk=; b=HSfCbmFERNS81LjiJIVuKXYlq34mpgcGpYXVUpuLKhRWijRku0fdU6axlcWRiuAVFH nJRNaXRqQc/G/fktANu3/wqkHKzLxLof1qmtr0lBDU9QHGu8HqRu4nLVgFl6gfAFiXU2 lmze8x0LGxyuH3gz0vNX4OADUVI4/GCBZLClE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=0tX/xkgIgpqfq7/D3E9E6o00DyQ+OBl0Xvw7+1wGnzk=; b=DmQpJMZQR/h3kBLb7mCrI3aMpyCpeZzbybrPp5MfOszLNXHyYBs1gOA/eLz4iWRA4v 8j29q6AfbQUDSgJquHeLxWMAD8DE4H0Sh0xQoCQj+jQuy1aNG2t4g82cV4sqInwkCPmD 9XfXYueUPZY3R+WISpq60p43vWYF2G/qVi1v0mhr5Tb57uOuwp/Dpz0RInS4eUexvBwZ a0PfmR/ZtGEndo/KWdGzv+McNh0cz/vrJZ8DlJUGz7MCh6koKYuVssqzY4UhGAVcmus3 x8W1jOAaRRF2MqHCnGSXrffb0uQxTyiH0SNDwsTvj/B/VfpMex308F89Hcn5SGQg29ez qIig== X-Gm-Message-State: AOUpUlFftlaYwqYE4SKeyl2DBeXMOqAqP3AZLk00oS+1wBBbxKcwH+vu dgAnCf8a9i2htpuPQcis20+yNGFp7lk= X-Google-Smtp-Source: AA+uWPz0N9tU0SkMtdINU5zabgVV/4m+3daJceyq15x6cTR7586rOL5V7jiZugyYlwVB50whs8MoOw== X-Received: by 2002:a62:9541:: with SMTP id p62-v6mr6070570pfd.152.1533890964394; Fri, 10 Aug 2018 01:49:24 -0700 (PDT) From: Haojian Zhuang To: edk2-devel@lists.01.org Date: Fri, 10 Aug 2018 16:49:14 +0800 Message-Id: <1533890955-13005-2-git-send-email-haojian.zhuang@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1533890955-13005-1-git-send-email-haojian.zhuang@linaro.org> References: <1533890955-13005-1-git-send-email-haojian.zhuang@linaro.org> Subject: [edk2] [PATCH v1 1/2] EmbeddedPkg: add NonDiscoverableDeviceDxe driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDMRC_1 RDKM_2 RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" It's used to create NonDiscoverableDevice in embedded platform. Since there's no PCI bus. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Haojian Zhuang --- EmbeddedPkg/EmbeddedPkg.dec = | 1 + EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.in= f | 52 ++ EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h = | 92 ++ EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c = | 124 +++ EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c = | 243 +++++ EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c = | 972 ++++++++++++++++++++ 6 files changed, 1484 insertions(+) diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec index 28a143865d0e..6a80f31e95c0 100644 --- a/EmbeddedPkg/EmbeddedPkg.dec +++ b/EmbeddedPkg/EmbeddedPkg.dec @@ -85,6 +85,7 @@ [Protocols.common] gPlatformGpioProtocolGuid =3D { 0x52ce9845, 0x5af4, 0x43e2, {0xba, 0xfd,= 0x23, 0x08, 0x12, 0x54, 0x7a, 0xc2 }} gPlatformVirtualKeyboardProtocolGuid =3D { 0x0e3606d2, 0x1dc3, 0x4e6f, {= 0xbe, 0x65, 0x39, 0x49, 0x82, 0xa2, 0x65, 0x47 }} gAndroidBootImgProtocolGuid =3D { 0x9859bb19, 0x407c, 0x4f8b, {0xbc, 0xe= 1, 0xf8, 0xda, 0x65, 0x65, 0xf4, 0xa5 }} + gEmbeddedNonDiscoverableIoProtocolGuid =3D { 0x6937742f, 0xf611, 0x4a40,= { 0xb1, 0xc6, 0xe7, 0xb4, 0x6e, 0x3c, 0x6e, 0x32 }} =20 [Ppis] gEdkiiEmbeddedGpioPpiGuid =3D { 0x21c3b115, 0x4e0b, 0x470c, { 0x85, 0xc7= , 0xe1, 0x05, 0xa5, 0x75, 0xc9, 0x7b }} diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceDxe.inf b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscovera= bleDeviceDxe.inf new file mode 100644 index 000000000000..b3f7c8bc2976 --- /dev/null +++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceD= xe.inf @@ -0,0 +1,52 @@ +## @file +# I/O driver for non-discoverable devices. +# +# Copyright (C) 2016-2018, Linaro Ltd. +# +# This program and the accompanying materials are licensed and made availa= ble +# 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, WI= THOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION =3D 0x0001001a + BASE_NAME =3D NonDiscoverableDeviceDxe + FILE_GUID =3D 66c8ca38-4c1e-4730-8c77-6c248ad89abd + MODULE_TYPE =3D UEFI_DRIVER + VERSION_STRING =3D 1.0 + ENTRY_POINT =3D NonDiscoverableDeviceDxeEntryPoint + +[Sources] + ComponentName.c + NonDiscoverableDeviceDxe.c + NonDiscoverableDeviceIo.c + NonDiscoverableDeviceIo.h + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + DxeServicesTableLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEdkiiNonDiscoverableDeviceProtocolGuid ## TO_START + gEfiCpuArchProtocolGuid ## CONSUMES + gEmbeddedNonDiscoverableIoProtocolGuid + +[Guids] + gEdkiiNonDiscoverableNvmeDeviceGuid ## CONSUMES ## GUID + gEdkiiNonDiscoverableSdhciDeviceGuid ## CONSUMES ## GUID + gEdkiiNonDiscoverableUfsDeviceGuid ## CONSUMES ## GUID diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceIo.h b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceIo.h new file mode 100644 index 000000000000..faa0bfcc17d4 --- /dev/null +++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceI= o.h @@ -0,0 +1,92 @@ +/** @file + + Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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, WI= THOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __NON_DISCOVERABLE_DEVICE_IO_H__ +#define __NON_DISCOVERABLE_DEVICE_IO_H__ + +#include + +#include +#include +#include +#include +#include + +#define NON_DISCOVERABLE_IO_DEVICE_SIG SIGNATURE_32 ('N', 'D', 'I', 'D') + +#define NON_DISCOVERABLE_IO_DEVICE_FROM_IO(IoPointer) \ + CR (IoPointer, NON_DISCOVERABLE_IO_DEVICE, Io, \ + NON_DISCOVERABLE_IO_DEVICE_SIG) + +extern EFI_CPU_ARCH_PROTOCOL *mCpu; + +typedef struct { + // + // The linked-list next pointer + // + LIST_ENTRY List; + // + // The address of the uncached allocation + // + VOID *HostAddress; + // + // The number of pages in the allocation + // + UINTN NumPages; + // + // The attributes of the allocation + // + UINT64 Attributes; +} NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION; + +typedef struct { + UINT32 Signature; + // + // The bound non-discoverable device protocol instance + // + NON_DISCOVERABLE_DEVICE *Device; + // + // The exposed I/O protocol instance. + // + EFI_DEVICE_IO_PROTOCOL Io; + // + // The I/O attributes for this device + // + UINT64 Attributes; + // + // Whether this device has been enabled + // + BOOLEAN Enabled; + // + // Linked list to keep track of uncached allocations performed + // on behalf of this device + // + LIST_ENTRY UncachedAllocationList; +} NON_DISCOVERABLE_IO_DEVICE; + +/** + Initialize Io Protocol. + + @param Device Point to NON_DISCOVERABLE_IO_DEVICE instance. + +**/ +VOID +InitializeIoProtocol ( + NON_DISCOVERABLE_IO_DEVICE *Device + ); + +extern EFI_COMPONENT_NAME_PROTOCOL gComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2; + +#endif /* __NON_DISCOVERABLE_DEVICE_IO_H__ */ diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c= b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c new file mode 100644 index 000000000000..613938697ee4 --- /dev/null +++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c @@ -0,0 +1,124 @@ +/** @file + + Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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, WI= THOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include "NonDiscoverableDeviceIo.h" + +// +// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL a= nd +// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's = name +// in English, for display on standard console devices. This is recommende= d for +// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Wri= ter's +// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. +// + +STATIC +EFI_UNICODE_STRING_TABLE mDriverNameTable[] =3D { + { "eng;en", L"I/O protocol emulation driver for non-discoverable devices= " }, + { NULL, NULL } +}; + +EFI_COMPONENT_NAME_PROTOCOL gComponentName; + +/** + Retrieves a Unicode string that is the user readable name of the UEFI Dr= iver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL insta= nce. + @param Language A pointer to a three character ISO 639-2 language = identifier. + This is the language of the driver name that that = the caller + is requesting, and it must match one of the langua= ges specified + in SupportedLanguages. The number of languages su= pported by a + driver is up to the driver writer. + @param DriverName A pointer to the Unicode string to return. This U= nicode 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 specifie= d 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 supp= ort the + language specified by Language. +**/ +STATIC +EFI_STATUS +EFIAPI +NonDiscoverableGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDriverNameTable, + DriverName, + (BOOLEAN)(This =3D=3D &gComponentName) // Iso639Language + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the control= ler + that is being managed by an UEFI Driver. + + @param This A pointer to the EFI_COMPONENT_NAME_PROTOC= OL instance. + @param DeviceHandle The handle of a controller that the driver= specified by + This is managing. This handle specifies t= he controller + whose name is to be returned. + @param ChildHandle The handle of the child controller to retr= ieve the name + of. This is an optional parameter that ma= y be NULL. It + will be NULL for device drivers. It will = also be NULL + for a bus drivers that wish to retrieve th= e name of the + bus controller. It will not be NULL for a= bus driver + that wishes to retrieve the name of a chil= d controller. + @param Language A pointer to a three character ISO 639-2 l= anguage + identifier. This is the language of the c= ontroller name + that that the caller is requesting, and it= must match one + of the languages specified in SupportedLan= guages. The + number of languages supported by a driver = is up to the + driver writer. + @param ControllerName A pointer to the Unicode string to return.= This Unicode + string is the name of the controller speci= fied by + ControllerHandle and ChildHandle in the la= nguage + specified by Language from the point of vi= ew of the + driver specified by This. +**/ +STATIC +EFI_STATUS +EFIAPI +NonDiscoverableGetDeviceName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_HANDLE ChildHandle, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_COMPONENT_NAME_PROTOCOL gComponentName =3D { + &NonDiscoverableGetDriverName, + &NonDiscoverableGetDeviceName, + "eng" // SupportedLanguages, ISO 639-2 language codes +}; + +EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 =3D { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &NonDiscoverableGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &NonDiscoverableGetDeviceName, + "en" // SupportedLanguages, RFC 4646 language codes +}; diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceDxe.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverabl= eDeviceDxe.c new file mode 100644 index 000000000000..654b33002346 --- /dev/null +++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceD= xe.c @@ -0,0 +1,243 @@ +/** @file + + Copyright (C) 2016-2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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, WI= THOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include + +#include + +#include "NonDiscoverableDeviceIo.h" + + +EFI_CPU_ARCH_PROTOCOL *mCpu; + +// +// We only support the following device types +// +STATIC +CONST EFI_GUID * CONST +SupportedNonDiscoverableDevices[] =3D { + &gEdkiiNonDiscoverableSdhciDeviceGuid, + &gEdkiiNonDiscoverableUfsDeviceGuid, +}; + +// +// Probe, start and stop functions of this driver, called by the DXE core = for +// specific devices. +// +// The following specifications document these interfaces: +// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol +// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol +// +// The implementation follows: +// - Driver Writer's Guide for UEFI 2.3.1 v1.01 +// - 5.1.3.4 OpenProtocol() and CloseProtocol() +// - UEFI Spec 2.3.1 + Errata C +// - 6.3 Protocol Handler Services +// + +/** + Supported function of Driver Binding protocol for this driver. + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param DeviceHandle Handle of device to test. + @param RemainingDevicePath A pointer to the device path. + it should be ignored by device driver. + + @retval EFI_SUCCESS This driver supports this device. + @retval other This driver does not support this device. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonDiscoverableIoDeviceSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + NON_DISCOVERABLE_DEVICE *Device; + EFI_STATUS Status; + INTN Idx; + + Status =3D gBS->OpenProtocol (DeviceHandle, + &gEdkiiNonDiscoverableDeviceProtocolGuid, (VOID **)&Devi= ce, + This->DriverBindingHandle, DeviceHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D EFI_UNSUPPORTED; + for (Idx =3D 0; Idx < ARRAY_SIZE (SupportedNonDiscoverableDevices); Idx+= +) { + if (CompareGuid (Device->Type, SupportedNonDiscoverableDevices [Idx]))= { + Status =3D EFI_SUCCESS; + break; + } + } + + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + +CloseProtocol: + gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGu= id, + This->DriverBindingHandle, DeviceHandle); + + return Status; +} + +/** + This routine is called right after the .Supported() called and + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param DeviceHandle Handle of device to bind driver to. + @param RemainingDevicePath A pointer to the device path. + it should be ignored by device driver. + + @retval EFI_SUCCESS This driver is added to this device. + @retval other Some error occurs when binding this driver= to this device. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonDiscoverableIoDeviceStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + NON_DISCOVERABLE_IO_DEVICE *Dev; + + Dev =3D AllocateZeroPool (sizeof *Dev); + if (Dev =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status =3D gBS->OpenProtocol (DeviceHandle, + &gEdkiiNonDiscoverableDeviceProtocolGuid, + (VOID **)&Dev->Device, This->DriverBindingHandle, + DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR (Status)) { + goto FreeDev; + } + + Dev->Signature =3D NON_DISCOVERABLE_IO_DEVICE_SIG; + + InitializeIoProtocol (Dev); + + Status =3D gBS->InstallProtocolInterface ( + &DeviceHandle, + &gEmbeddedNonDiscoverableIoProtocolGuid, + EFI_NATIVE_INTERFACE, + &Dev->Io + ); + if (EFI_ERROR (Status)) { + goto CloseProtocol; + } + return EFI_SUCCESS; + +CloseProtocol: + gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGu= id, + This->DriverBindingHandle, DeviceHandle); +FreeDev: + FreePool (Dev); + + return Status; +} + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param DeviceHandle Handle of device to stop driver on. + @param NumberOfChildren Not used. + @param ChildHandleBuffer Not used. + + @retval EFI_SUCCESS This driver is removed from this device. + @retval other Some error occurs when removing this driver from t= his + device. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonDiscoverableIoDeviceStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE DeviceHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + + gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGu= id, + This->DriverBindingHandle, DeviceHandle); + + return EFI_SUCCESS; +} + + +// +// The static object that groups the Supported() (ie. probe), Start() and +// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Err= ata +// C, 10.1 EFI Driver Binding Protocol. +// +STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding =3D { + &NonDiscoverableIoDeviceSupported, + &NonDiscoverableIoDeviceStart, + &NonDiscoverableIoDeviceStop, + 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed driv= ers + NULL, + NULL +}; + +/** + Entry point of this driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to the System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurred when executing this entry po= int. + +**/ +EFI_STATUS +EFIAPI +NonDiscoverableDeviceDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status =3D gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **= )&mCpu); + ASSERT_EFI_ERROR(Status); + + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDriverBinding, + ImageHandle, + &gComponentName, + &gComponentName2 + ); +} diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceIo.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverable= DeviceIo.c new file mode 100644 index 000000000000..178f10b216b9 --- /dev/null +++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceI= o.c @@ -0,0 +1,972 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2016-2018, Linaro, Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BS= D License + which accompanies this distribution. The full text of the license may b= e 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 IMP= LIED. + +**/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "NonDiscoverableDeviceIo.h" + +typedef struct { + EFI_PHYSICAL_ADDRESS AllocAddress; + VOID *HostAddress; + EFI_IO_OPERATION_TYPE Operation; + UINTN NumberOfBytes; +} NON_DISCOVERABLE_IO_DEVICE_MAP_INFO; + +/** + Enable a driver to access controller registers in the memory or I/O spac= e. + + @param Width Signifies the width of the memory or I/O operation= s. + @param Count The number of memory or I/O operations to perform. + @param DstStride The stride of the destination buffer. + @param Dst For read operations, the destination buffer to sto= re + the results. For write operations, the destination + buffer to write data to. + @param SrcStride The stride of the source buffer. + @param Src For read operations, the source buffer to read data + from. For write operations, the source buffer to w= rite + data from. + + @retval EFI_SUCCESS The data was read from or written to the + controller. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +STATIC +EFI_STATUS +EFIAPI +IoMemRW ( + IN EFI_IO_WIDTH Width, + IN UINTN Count, + IN UINTN DstStride, + IN VOID *Dst, + IN UINTN SrcStride, + OUT CONST VOID *Src + ) +{ + volatile UINT8 *Dst8; + volatile UINT16 *Dst16; + volatile UINT32 *Dst32; + volatile CONST UINT8 *Src8; + volatile CONST UINT16 *Src16; + volatile CONST UINT32 *Src32; + + // + // Loop for each iteration and move the data + // + switch (Width & 0x3) { + case IO_UINT8: + Dst8 =3D (UINT8 *)Dst; + Src8 =3D (UINT8 *)Src; + for (;Count > 0; Count--, Dst8 +=3D DstStride, Src8 +=3D SrcStride) { + *Dst8 =3D *Src8; + } + break; + case IO_UINT16: + Dst16 =3D (UINT16 *)Dst; + Src16 =3D (UINT16 *)Src; + for (;Count > 0; Count--, Dst16 +=3D DstStride, Src16 +=3D SrcStride) { + *Dst16 =3D *Src16; + } + break; + case IO_UINT32: + Dst32 =3D (UINT32 *)Dst; + Src32 =3D (UINT32 *)Src; + for (;Count > 0; Count--, Dst32 +=3D DstStride, Src32 +=3D SrcStride) { + *Dst32 =3D *Src32; + } + break; + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Enable a driver to access controller registers in the memory or I/O spac= e. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Width Signifies the width of the memory or I/O + operations. + @param Offset The offset to start the memory or I/O oper= ation. + @param Count The number of memory or I/O operations to + perform. + @param Buffer For read operations, the destination buffe= r to + store the results. For write operations, t= he + source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the + controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Wid= th, + and Count is not valid. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +STATIC +EFI_STATUS +EFIAPI +IoMemRead ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_WIDTH Width, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + NON_DISCOVERABLE_IO_DEVICE *Dev; + UINTN AlignMask; + UINT64 Address; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; + + if (Buffer =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + + Desc =3D Dev->Device->Resources; + Address =3D Desc->AddrRangeMin + Offset; + + if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) { + return EFI_UNSUPPORTED; + } + + AlignMask =3D (1 << (Width & 0x03)) - 1; + if ((UINTN)Address & AlignMask) { + return EFI_INVALID_PARAMETER; + } + + switch (Width) { + case IO_UINT8: + case IO_UINT16: + case IO_UINT32: + case IO_UINT64: + return IoMemRW (Width, Count, 1, Buffer, 1, (VOID *)Address); + default: + break; + } + return EFI_INVALID_PARAMETER; +} + +/** + Enable a driver to access controller registers in the memory or I/O spac= e. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Width Signifies the width of the memory or I/O + operations. + @param Offset The offset to start the memory or I/O oper= ation. + @param Count The number of memory or I/O operations to + perform. + @param Buffer For read operations, the destination buffe= r to + store the results. For write operations, t= he + source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the + controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Wid= th, + and Count is not valid. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +STATIC +EFI_STATUS +EFIAPI +IoMemWrite ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_WIDTH Width, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + NON_DISCOVERABLE_IO_DEVICE *Dev; + UINTN AlignMask; + UINT64 Address; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Desc; + + if (Buffer =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + + Desc =3D Dev->Device->Resources; + + Address =3D Desc->AddrRangeMin + Offset; + if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) { + return EFI_UNSUPPORTED; + } + + AlignMask =3D (1 << (Width & 0x03)) - 1; + if ((UINTN)Address & AlignMask) { + return EFI_INVALID_PARAMETER; + } + + switch (Width) { + case IO_UINT8: + case IO_UINT16: + case IO_UINT32: + case IO_UINT64: + return IoMemRW (Width, Count, 1, (VOID *)Address, 1, Buffer); + default: + break; + } + return EFI_INVALID_PARAMETER; +} + +/** + Enable a driver to access controller registers in the memory or I/O spac= e. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Width Signifies the width of the memory or I/O + operations. + @param Offset The offset to start the memory or I/O oper= ation. + @param Count The number of memory or I/O operations to + perform. + @param Buffer For read operations, the destination buffer + to store the results. For write operations, + the source buffer to write data from. + +**/ +STATIC +EFI_STATUS +EFIAPI +IoIoRead ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_WIDTH Width, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + ASSERT (FALSE); + return EFI_UNSUPPORTED; +} + +/** + Enable a driver to access controller registers in the memory or I/O spac= e. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Width Signifies the width of the memory or I/O + operations. + @param Offset The offset to start the memory or I/O oper= ation. + @param Count The number of memory or I/O operations to + perform. + @param Buffer For read operations, the destination buffe= r to + store the results. For write operations, t= he + source buffer to write data from. + +**/ +STATIC +EFI_STATUS +EFIAPI +IoIoWrite ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + ASSERT (FALSE); + return EFI_UNSUPPORTED; +} + +/** + Provides the controller-specific addresses needed to access system memor= y. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Operation Indicates if the bus master is going to re= ad + or write to system memory. + @param HostAddress The system memory address to map to the + controller. + @param NumberOfBytes On input the number of bytes to map. On ou= tput + the number of bytes that were mapped. + @param DeviceAddress The resulting map address for the bus mast= er + 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 comm= on + 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 requ= ested + address. + +**/ +STATIC +EFI_STATUS +EFIAPI +CoherentIoMap ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_OPERATION_TYPE Operation, + IN EFI_PHYSICAL_ADDRESS *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + NON_DISCOVERABLE_IO_DEVICE *Dev; + NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo; + + // + // If HostAddress exceeds 4 GB, and this device does not support 64-bit = DMA + // addressing, we need to allocate a bounce buffer and copy over the dat= a. + // + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + if ((EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB= ) { + // + // Bounce buffering is not possible for consistent mappings + // + if (Operation =3D=3D EfiBusMasterCommonBuffer) { + return EFI_UNSUPPORTED; + } + + MapInfo =3D AllocatePool (sizeof *MapInfo); + if (MapInfo =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MapInfo->AllocAddress =3D MAX_UINT32; + MapInfo->HostAddress =3D HostAddress; + MapInfo->Operation =3D Operation; + MapInfo->NumberOfBytes =3D *NumberOfBytes; + + Status =3D gBS->AllocatePages ( + AllocateMaxAddress, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), + &MapInfo->AllocAddress + ); + if (EFI_ERROR (Status)) { + // + // If we fail here, it is likely because the system has no memory be= low + // 4 GB to begin with. There is not much we can do about that other = than + // fail the map request. + // + FreePool (MapInfo); + return EFI_DEVICE_ERROR; + } + if (Operation =3D=3D EfiBusMasterRead) { + gBS->CopyMem ( + (VOID *)(UINTN)MapInfo->AllocAddress, + HostAddress, + *NumberOfBytes + ); + } + *DeviceAddress =3D MapInfo->AllocAddress; + *Mapping =3D MapInfo; + } else { + *DeviceAddress =3D (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; + *Mapping =3D NULL; + } + return EFI_SUCCESS; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + +**/ +STATIC +EFI_STATUS +EFIAPI +CoherentIoUnmap ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN VOID *Mapping + ) +{ + NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo; + + MapInfo =3D Mapping; + if (MapInfo !=3D NULL) { + if (MapInfo->Operation =3D=3D EfiBusMasterWrite) { + gBS->CopyMem ( + MapInfo->HostAddress, + (VOID *)(UINTN)MapInfo->AllocAddress, + MapInfo->NumberOfBytes + ); + } + gBS->FreePages ( + MapInfo->AllocAddress, + EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes) + ); + FreePool (MapInfo); + } + return EFI_SUCCESS; +} + +/** + Allocates pages. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL insta= nce. + @param Type This parameter is not used and must be ign= ored. + @param MemoryType The type of memory to allocate, + EfiBootServicesData or EfiRuntimeServicesD= ata. + @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 t= he + 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. + +**/ +STATIC +EFI_STATUS +EFIAPI +CoherentIoAllocateBuffer ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT EFI_PHYSICAL_ADDRESS *HostAddress + ) +{ + EFI_STATUS Status; + NON_DISCOVERABLE_IO_DEVICE *Dev; + EFI_ALLOCATE_TYPE AllocType; + EFI_PHYSICAL_ADDRESS AllocAddress; + + // + // Allocate below 4 GB if the dual address cycle attribute has not + // been set. If the system has no memory available below 4 GB, there + // is little we can do except propagate the error. + // + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + if ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) =3D=3D = 0) { + AllocAddress =3D MAX_UINT32; + AllocType =3D AllocateMaxAddress; + } else { + AllocType =3D AllocateAnyPages; + } + + Status =3D gBS->AllocatePages (AllocType, MemoryType, Pages, &AllocAddre= ss); + if (!EFI_ERROR (Status)) { + *HostAddress =3D AllocAddress; + } + return Status; +} + +/** + Frees memory that was allocated in function CoherentIoAllocateBuffer (). + + @param This A pointer to the EFI_DEVICE_IO_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. + +**/ +STATIC +EFI_STATUS +EFIAPI +CoherentIoFreeBuffer ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN UINTN Pages, + IN EFI_PHYSICAL_ADDRESS HostAddress + ) +{ + FreePages ((VOID *)HostAddress, Pages); + return EFI_SUCCESS; +} + +/** + Frees memory that was allocated in function NonCoherentIoAllocateBuffer = (). + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allo= cated + range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval others The operation contain some errors. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoFreeBuffer ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN UINTN Pages, + IN EFI_PHYSICAL_ADDRESS HostAddress + ) +{ + NON_DISCOVERABLE_IO_DEVICE *Dev; + LIST_ENTRY *Entry; + EFI_STATUS Status; + NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc; + BOOLEAN Found; + + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + + Found =3D FALSE; + Alloc =3D NULL; + + // + // Find the uncached allocation list entry associated + // with this allocation + // + for (Entry =3D Dev->UncachedAllocationList.ForwardLink; + Entry !=3D &Dev->UncachedAllocationList; + Entry =3D Entry->ForwardLink) { + + Alloc =3D BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION,= List); + if (Alloc->HostAddress =3D=3D (VOID *)HostAddress && Alloc->NumPages = =3D=3D Pages) { + // + // We are freeing the exact allocation we were given + // before by AllocateBuffer() + // + Found =3D TRUE; + break; + } + } + + if (!Found) { + ASSERT_EFI_ERROR (EFI_NOT_FOUND); + return EFI_NOT_FOUND; + } + + RemoveEntryList (&Alloc->List); + + Status =3D gDS->SetMemorySpaceAttributes ( + (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, + EFI_PAGES_TO_SIZE (Pages), + Alloc->Attributes); + if (EFI_ERROR (Status)) { + goto FreeAlloc; + } + + // + // If we fail to restore the original attributes, it is better to leak t= he + // memory than to return it to the heap + // + FreePages ((VOID *)HostAddress, Pages); + +FreeAlloc: + FreePool (Alloc); + return Status; +} + +/** + Allocates pages. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Type This parameter is not used and must be ign= ored. + @param MemoryType The type of memory to allocate, + EfiBootServicesData or EfiRuntimeServicesD= ata. + @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 t= he + 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. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoAllocateBuffer ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT EFI_PHYSICAL_ADDRESS *HostAddress + ) +{ + NON_DISCOVERABLE_IO_DEVICE *Dev; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; + EFI_STATUS Status; + UINT64 MemType; + NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc; + EFI_PHYSICAL_ADDRESS AllocAddress; + + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + + Status =3D CoherentIoAllocateBuffer ( + This, + Type, + MemoryType, + Pages, + &AllocAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D gDS->GetMemorySpaceDescriptor ( + (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, + &GcdDescriptor + ); + if (EFI_ERROR (Status)) { + goto FreeBuffer; + } + + if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) =3D= =3D 0) { + Status =3D EFI_UNSUPPORTED; + goto FreeBuffer; + } + + // + // Set the preferred memory attributes + // + if ((GcdDescriptor.Capabilities & EFI_MEMORY_UC) =3D=3D 0) { + // + // Use write combining if it was requested, or if it is the only + // type supported by the region. + // + MemType =3D EFI_MEMORY_WC; + } else { + MemType =3D EFI_MEMORY_UC; + } + + Alloc =3D AllocatePool (sizeof *Alloc); + if (Alloc =3D=3D NULL) { + goto FreeBuffer; + } + + Alloc->HostAddress =3D (VOID *)AllocAddress; + Alloc->NumPages =3D Pages; + Alloc->Attributes =3D GcdDescriptor.Attributes; + + // + // Record this allocation in the linked list, so we + // can restore the memory space attributes later + // + InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List); + + Status =3D gDS->SetMemorySpaceAttributes ( + (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, + EFI_PAGES_TO_SIZE (Pages), + MemType + ); + if (EFI_ERROR (Status)) { + goto RemoveList; + } + + Status =3D mCpu->FlushDataCache ( + mCpu, + (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress, + EFI_PAGES_TO_SIZE (Pages), + EfiCpuFlushTypeInvalidate); + if (EFI_ERROR (Status)) { + goto RemoveList; + } + + *HostAddress =3D AllocAddress; + + return EFI_SUCCESS; + +RemoveList: + RemoveEntryList (&Alloc->List); + FreePool (Alloc); + +FreeBuffer: + CoherentIoFreeBuffer (This, Pages, AllocAddress); + return Status; +} + +/** + Provides the controller-specific addresses needed to access system memor= y. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Operation Indicates if the bus master is going to re= ad or + write to system memory. + @param HostAddress The system memory address to map to the + controller. + @param NumberOfBytes On input the number of bytes to map. On ou= tput + the number of bytes that were mapped. + @param DeviceAddress The resulting map address for the bus mast= er + 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 comm= on + 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 requ= ested + address. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoMap ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN EFI_IO_OPERATION_TYPE Operation, + IN EFI_PHYSICAL_ADDRESS *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + NON_DISCOVERABLE_IO_DEVICE *Dev; + EFI_STATUS Status; + NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo; + UINTN AlignMask; + EFI_PHYSICAL_ADDRESS AllocAddress; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; + BOOLEAN Bounce; + + MapInfo =3D AllocatePool (sizeof *MapInfo); + if (MapInfo =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + MapInfo->HostAddress =3D HostAddress; + MapInfo->Operation =3D Operation; + MapInfo->NumberOfBytes =3D *NumberOfBytes; + + Dev =3D NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This); + + // + // If this device does not support 64-bit DMA addressing, we need to all= ocate + // a bounce buffer and copy over the data in case HostAddress >=3D 4 GB. + // + Bounce =3D ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE)= =3D=3D 0 && + (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SI= ZE_4GB); + + if (!Bounce) { + switch (Operation) { + case EfiBusMasterRead: + case EfiBusMasterWrite: + // + // For streaming DMA, it is sufficient if the buffer is aligned to + // the CPUs DMA buffer alignment. + // + AlignMask =3D mCpu->DmaBufferAlignment - 1; + if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) =3D=3D 0) { + break; + } + // fall through + + case EfiBusMasterCommonBuffer: + // + // Check whether the host address refers to an uncached mapping. + // + Status =3D gDS->GetMemorySpaceDescriptor ( + (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, + &GcdDescriptor + ); + if (EFI_ERROR (Status) || + (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) !=3D = 0) { + Bounce =3D TRUE; + } + break; + + default: + ASSERT (FALSE); + } + } + + if (Bounce) { + if (Operation =3D=3D EfiBusMasterCommonBuffer) { + Status =3D EFI_DEVICE_ERROR; + goto FreeMapInfo; + } + + Status =3D NonCoherentIoAllocateBuffer ( + This, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), + &AllocAddress); + if (EFI_ERROR (Status)) { + goto FreeMapInfo; + } + MapInfo->AllocAddress =3D AllocAddress; + if (Operation =3D=3D EfiBusMasterRead) { + gBS->CopyMem ((VOID *)AllocAddress, HostAddress, *NumberOfBytes); + } + *DeviceAddress =3D MapInfo->AllocAddress; + } else { + MapInfo->AllocAddress =3D 0; + *DeviceAddress =3D (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress; + + // + // We are not using a bounce buffer: the mapping is sufficiently + // aligned to allow us to simply flush the caches. Note that cleaning + // the caches is necessary for both data directions: + // - for bus master read, we want the latest data to be present + // in main memory + // - for bus master write, we don't want any stale dirty cachelines th= at + // may be written back unexpectedly, and clobber the data written to + // main memory by the device. + // + mCpu->FlushDataCache ( + mCpu, + (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, + *NumberOfBytes, + EfiCpuFlushTypeWriteBack + ); + } + + *Mapping =3D MapInfo; + return EFI_SUCCESS; + +FreeMapInfo: + FreePool (MapInfo); + + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_DEVICE_IO_PROTOCOL + instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + +**/ +STATIC +EFI_STATUS +EFIAPI +NonCoherentIoUnmap ( + IN EFI_DEVICE_IO_PROTOCOL *This, + IN VOID *Mapping + ) +{ + NON_DISCOVERABLE_IO_DEVICE_MAP_INFO *MapInfo; + + if (Mapping =3D=3D NULL) { + return EFI_DEVICE_ERROR; + } + + MapInfo =3D Mapping; + if (MapInfo->AllocAddress !=3D 0) { + // + // We are using a bounce buffer: copy back the data if necessary, + // and free the buffer. + // + if (MapInfo->Operation =3D=3D EfiBusMasterWrite) { + gBS->CopyMem ( + MapInfo->HostAddress, + (VOID *)(UINTN)MapInfo->AllocAddress, + MapInfo->NumberOfBytes + ); + } + NonCoherentIoFreeBuffer ( + This, + EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes), + MapInfo->AllocAddress + ); + } else { + // + // We are *not* using a bounce buffer: if this is a bus master write, + // we have to invalidate the caches so the CPU will see the uncached + // data written by the device. + // + if (MapInfo->Operation =3D=3D EfiBusMasterWrite) { + mCpu->FlushDataCache ( + mCpu, + (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress, + MapInfo->NumberOfBytes, + EfiCpuFlushTypeInvalidate + ); + } + } + FreePool (MapInfo); + return EFI_SUCCESS; +} + +STATIC CONST EFI_DEVICE_IO_PROTOCOL IoTemplate =3D +{ + { IoMemRead, IoMemWrite }, + { IoIoRead, IoIoWrite }, + { 0, 0 }, + CoherentIoMap, + 0, + CoherentIoUnmap, + CoherentIoAllocateBuffer, + 0, + CoherentIoFreeBuffer, +}; + +/** + Initialize DevIo Protocol. + + @param Dev Point to NON_DISCOVERABLE_IO_DEVICE instance. + +**/ +VOID +InitializeIoProtocol ( + NON_DISCOVERABLE_IO_DEVICE *Dev + ) +{ + InitializeListHead (&Dev->UncachedAllocationList); + + // + // Copy protocol structure + // + CopyMem(&Dev->Io, &IoTemplate, sizeof (IoTemplate)); + + if (Dev->Device->DmaType =3D=3D NonDiscoverableDeviceDmaTypeNonCoherent)= { + Dev->Io.AllocateBuffer =3D NonCoherentIoAllocateBuffer; + Dev->Io.FreeBuffer =3D NonCoherentIoFreeBuffer; + Dev->Io.Map =3D NonCoherentIoMap; + Dev->Io.Unmap =3D NonCoherentIoUnmap; + } else { + Dev->Io.AllocateBuffer =3D CoherentIoAllocateBuffer; + Dev->Io.FreeBuffer =3D CoherentIoFreeBuffer; + Dev->Io.Map =3D CoherentIoMap; + Dev->Io.Unmap =3D CoherentIoUnmap; + } +} --=20 2.7.4 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel From nobody Fri Mar 29 13:46:40 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from ml01.01.org (ml01.01.org [198.145.21.10]) by mx.zohomail.com with SMTPS id 1533890974605750.9984866606648; Fri, 10 Aug 2018 01:49:34 -0700 (PDT) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 22EA5210EB131; Fri, 10 Aug 2018 01:49:33 -0700 (PDT) Received: from mail-pl0-x22c.google.com (mail-pl0-x22c.google.com [IPv6:2607:f8b0:400e:c01::22c]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id F0142210EB122 for ; Fri, 10 Aug 2018 01:49:31 -0700 (PDT) Received: by mail-pl0-x22c.google.com with SMTP id x6-v6so3763296plv.10 for ; Fri, 10 Aug 2018 01:49:31 -0700 (PDT) Received: from localhost.localdomain ([64.64.108.24]) by smtp.gmail.com with ESMTPSA id 82-v6sm23569724pfw.159.2018.08.10.01.49.24 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 10 Aug 2018 01:49:28 -0700 (PDT) X-Original-To: edk2-devel@lists.01.org Received-SPF: none (zoho.com: 198.145.21.10 is neither permitted nor denied by domain of lists.01.org) client-ip=198.145.21.10; envelope-from=edk2-devel-bounces@lists.01.org; helo=ml01.01.org; Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:400e:c01::22c; helo=mail-pl0-x22c.google.com; envelope-from=haojian.zhuang@linaro.org; receiver=edk2-devel@lists.01.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=KiX+b9gi3u7pictc12yddSRN17d2+HOx1VOPWy+jt2I=; b=Q5i7vDmg6WEvfGInyIoJK4gmvRtsbELCFpO5VbByQy0cMUkXkFpTHuz4K7CPCbJxzk K/zR1+LGNGJZDk3RftJxUxcDTmGfF0ay88K+/351AkLKLm2ZQcF4eyKKnwgHfNLlUEIt 2bbbQlGT/sKVXqjznax5bk5viZoxR+LNOMcCk= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=KiX+b9gi3u7pictc12yddSRN17d2+HOx1VOPWy+jt2I=; b=KKgOZe7x3CK7vbmxiD04jp+DBaht525AtdyPlttBLEOG9Y7bCUvF+YxQ2hfOixsRM5 0FtHc4AZWn9R4L4yDJKgtNywzvi2IAPskSRObBuhCIvyq8YWB3IJQZORc+8VxC9836hF aR/VYWvxrr0EzoL3sIQInBRr7g1ledvIe4eAONVsOUD1N0P07JY3Dn2xR3wFLaiSbcNE G6GxffmXXD7ba+OTemWlgUerQpQOsCNt6fRtPV1QZyApyTBByrnHBNbEgbqYU8rl83AP UjJdN66MMRdHwFVq4OTEpMkA0kANvQDqdjuYNsYf7HNVByG17YXa2kY2lHBdQwSdpVKp YQmA== X-Gm-Message-State: AOUpUlEecFIjxmcdV6skH+ogoQbNKScoH52HI1yW+RdIUzSS/qyvCono CPpAxwPwtPOHxYnB2A+gYg6SUw/TRqY= X-Google-Smtp-Source: AA+uWPzvqA7bd6RV55M7L90LcptAzErKToK3DelHGOIuepR88YaqHfiT/wu08bCfhjz7c0FmSqb3dg== X-Received: by 2002:a17:902:246a:: with SMTP id m39-v6mr5270150plg.57.1533890969226; Fri, 10 Aug 2018 01:49:29 -0700 (PDT) From: Haojian Zhuang To: edk2-devel@lists.01.org Date: Fri, 10 Aug 2018 16:49:15 +0800 Message-Id: <1533890955-13005-3-git-send-email-haojian.zhuang@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1533890955-13005-1-git-send-email-haojian.zhuang@linaro.org> References: <1533890955-13005-1-git-send-email-haojian.zhuang@linaro.org> Subject: [edk2] [PATCH v1 2/2] EmbeddedPkg: add DwMmcHcDxe driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDMRC_1 RDKM_2 RSF_4 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" DwMmcHcDxe driver is for Designware SD and MMMC controller. Cc: Leif Lindholm Cc: Ard Biesheuvel Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Haojian Zhuang --- EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec | 40 + EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf | 69 + EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h | 815 +++++++ EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h | 983 ++++++++ EmbeddedPkg/Include/Protocol/PlatformDwMmc.h | 79 + EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c | 214 ++ EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c | 1295 +++++++++++ EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c | 2366 ++++++++++++++++++++ EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c | 1042 +++++++++ EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c | 1104 +++++++++ 10 files changed, 8007 insertions(+) diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec b/EmbeddedPkg/Dr= ivers/DwMmcHcDxe/DwMmcHcDxe.dec new file mode 100644 index 000000000000..cf85ccb1a030 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec @@ -0,0 +1,40 @@ +#/** @file +# Framework Module Development Environment Industry Standards +# +# This Package provides headers and libraries that conform to EFI/PI Indus= try standards. +# Copyright (c) 2007, Intel Corporation. All rights reserved.
+# Copyright (c) 2012-2014, ARM Ltd. All rights reserved.
+# Copyright (c) 2018, Linaro. All rights reserved.
+# +# This program and the accompanying materials are licensed and made ava= ilable under +# the terms and conditions of the BSD License which accompanies this di= stribution. +# 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] + DEC_SPECIFICATION =3D 0x00010019 + PACKAGE_NAME =3D DwMmcHcDxePkg + PACKAGE_GUID =3D e73097ce-1fe2-41a6-a930-3136bc6d23ef + PACKAGE_VERSION =3D 0.1 + + +##########################################################################= ###### +# +# Include Section - list of Include Paths that are provided by this packag= e. +# Comments are used for Keywords and Module Types. +# +# Supported Module Types: +# BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_D= RIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION +# +##########################################################################= ###### + +[Guids.common] + gDwMmcHcDxeTokenSpaceGuid =3D { 0x576c132e, 0x7d51, 0x4abb, { 0xbc, = 0x60, 0x13, 0x08, 0x04, 0x0e, 0x90, 0x92 }} + +[Protocols.common] + gPlatformDwMmcProtocolGuid =3D { 0x1d6dfde5, 0x76a7, 0x4404, { 0x85, = 0x74, 0x7a, 0xdf, 0x1a, 0x8a, 0xa2, 0x0d }} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf b/EmbeddedPkg/Dr= ivers/DwMmcHcDxe/DwMmcHcDxe.inf new file mode 100644 index 000000000000..699de99e22b1 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf @@ -0,0 +1,69 @@ +## @file +# DwSdMmcHcDxe driver is used to manage those host controllers which comp= ly with +# Designware SD Host Controller. +# +# It will produce EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC/e= MMC cmds +# to specified devices from upper layer. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# Copyright (C) 2016, Marvell International Ltd. All rights reserved.
+# Copyright (c) 2018, Linaro Ltd. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the B= SD License +# which accompanies this distribution. The full text of the license may b= e 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 IM= PLIED. +# +# +## + +[Defines] + INF_VERSION =3D 0x00010019 + BASE_NAME =3D DwMmcHcDxe + MODULE_UNI_FILE =3D DwMmcHcDxe.uni + FILE_GUID =3D 9be4d260-208c-4efe-a524-0b5d3bf77f9d + MODULE_TYPE =3D UEFI_DRIVER + VERSION_STRING =3D 1.0 + ENTRY_POINT =3D InitializeDwMmcHcDxe + +[Sources] + ComponentName.c + DwMmcHcDxe.c + DwMmcHcDxe.h + DwMmcHci.c + DwMmcHci.h + EmmcDevice.c + SdDevice.c + +[Packages] + ArmPkg/ArmPkg.dec + EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec + EmbeddedPkg/EmbeddedPkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + ArmLib + BaseLib + BaseMemoryLib + CacheMaintenanceLib + DebugLib + DevicePathLib + MemoryAllocationLib + TimerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + UefiRuntimeServicesTableLib + +[Protocols] + gEfiDevicePathProtocolGuid ## TO_START + gEfiPciIoProtocolGuid ## TO_START + gEfiSdMmcPassThruProtocolGuid ## BY_START + gEmbeddedNonDiscoverableIoProtocolGuid + gPlatformDwMmcProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + DwMmcHcDxeExtra.uni diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h b/EmbeddedPkg/Driv= ers/DwMmcHcDxe/DwMmcHcDxe.h new file mode 100644 index 000000000000..d17604d51304 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.h @@ -0,0 +1,815 @@ +/** @file + + Provides some data structure definitions used by the Designware SD/MMC + host controller driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ Copyright (C) 2018, Linaro Ltd. All rigths reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#ifndef _DW_MMC_HC_DXE_H_ +#define _DW_MMC_HC_DXE_H_ + +#include + +#include + +#include +#include +#include +#include +#include + +#include "DwMmcHci.h" + +extern EFI_COMPONENT_NAME_PROTOCOL gDwMmcHcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gDwMmcHcComponentName2; +extern EFI_DRIVER_BINDING_PROTOCOL gDwMmcHcDriverBinding; + +#define DW_MMC_HC_PRIVATE_SIGNATURE SIGNATURE_32 ('d', 'w', 's', 'd') + +#define DW_MMC_HC_PRIVATE_FROM_THIS(a) \ + CR(a, DW_MMC_HC_PRIVATE_DATA, PassThru, DW_MMC_HC_PRIVATE_SIGNATURE) + +// +// Generic time out value, 1 microsecond as unit. +// +#define DW_MMC_HC_GENERIC_TIMEOUT (1 * 1000 * 1000) + +// +// SD/MMC async transfer timer interval, set by experience. +// The unit is 100us, takes 1ms as interval. +// +#define DW_MMC_HC_ASYNC_TIMER EFI_TIMER_PERIOD_MILLISECONDS(1) +// +// SD/MMC removable device enumeration timer interval, set by experience. +// The unit is 100us, takes 100ms as interval. +// +#define DW_MMC_HC_ENUM_TIMER EFI_TIMER_PERIOD_MILLISECONDS(100) + +typedef struct { + BOOLEAN Enable; + EFI_SD_MMC_SLOT_TYPE SlotType; + BOOLEAN MediaPresent; + BOOLEAN Initialized; + SD_MMC_CARD_TYPE CardType; +} DW_MMC_HC_SLOT; + +typedef struct { + UINTN Signature; + + EFI_HANDLE ControllerHandle; + EFI_DEVICE_IO_PROTOCOL *DevIo; + + EFI_SD_MMC_PASS_THRU_PROTOCOL PassThru; + + PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; + // + // The field is used to record the previous slot in GetNextSlot(). + // + UINT8 PreviousSlot; + // + // For Non-blocking operation. + // + EFI_EVENT TimerEvent; + // + // For Sd removable device enumeration. + // + EFI_EVENT ConnectEvent; + LIST_ENTRY Queue; + + DW_MMC_HC_SLOT Slot[DW_MMC_HC_MAX_SLOT]; + DW_MMC_HC_SLOT_CAP Capability[DW_MMC_HC_MAX_SLOT]; + UINT64 MaxCurrent[DW_MMC_HC_MAX_SLOT]; + + UINT32 ControllerVersion; +} DW_MMC_HC_PRIVATE_DATA; + +#define DW_MMC_HC_TRB_SIG SIGNATURE_32 ('D', 'T', 'R', 'B') + +// +// TRB (Transfer Request Block) contains information for the cmd request. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY TrbList; + + UINT8 Slot; + UINT16 BlockSize; + + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + VOID *Data; + UINT32 DataLen; + BOOLEAN Read; + EFI_PHYSICAL_ADDRESS DataPhy; + VOID *DataMap; + DW_MMC_HC_TRANSFER_MODE Mode; + + EFI_EVENT Event; + BOOLEAN Started; + UINT64 Timeout; + + DW_MMC_HC_DMA_DESC_LINE *DmaDesc; + EFI_PHYSICAL_ADDRESS DmaDescPhy; + UINT32 DmaDescPages; + VOID *DmaMap; + + BOOLEAN UseFifo; + BOOLEAN UseBE; // Big-endian + + DW_MMC_HC_PRIVATE_DATA *Private; +} DW_MMC_HC_TRB; + +#define DW_MMC_HC_TRB_FROM_THIS(a) \ + CR(a, DW_MMC_HC_TRB, TrbList, DW_MMC_HC_TRB_SIG) + +// +// Task for Non-blocking mode. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + UINT8 Slot; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + BOOLEAN IsStart; + EFI_EVENT Event; + UINT64 RetryTimes; + BOOLEAN InfiniteWait; + VOID *Map; + VOID *MapAddress; +} DW_MMC_HC_QUEUE; + +// +// Prototypes +// +/** + Execute card identification procedure. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + + @retval EFI_SUCCESS The card is identified correctly. + @retval Others The card can't be identified. + +**/ +typedef +EFI_STATUS +(*DWMMC_CARD_TYPE_DETECT_ROUTINE) ( + IN DW_MMC_HC_PRIVATE_DATA *Private + ); + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the = SD + card specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is retur= ned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR= is + returned. + + If Slot is not in a valid range for the SD controller, then + EFI_INVALID_PARAMETER is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer= are + NULL, EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] Slot The slot number of the SD card to send the + command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performe= d. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when= the + Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting t= o send + the SD command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packe= t is + invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both + InDataBuffer and OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Pa= cket + is not supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength + exceeds the limit supported by SD card ( i= .e. if + the number of bytes exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruPassThru ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ); + +/** + Used to retrieve next slot numbers supported by the SD controller. The + function returns information about all available slots (populated or + not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD contr= oller. + If on input Slot is 0xFF, then the slot number of the first slot on the = SD + controller is returned. + + If Slot is a slot number that was returned on a previous call to + GetNextSlot(), then the slot number of the next slot on the SD controlle= r is + returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to + GetNextSlot(), EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PRO= TOCOL + instance. + @param[in,out] Slot On input, a pointer to a slot number on th= e SD + controller. + On output, a pointer to the next slot numb= er on + the SD controller. + An input value of 0xFF retrieves the first= slot + number on the SD controller. + + @retval EFI_SUCCESS The next slot number on the SD controller = was + returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD control= ler. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned= on a + previous call to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ); + +/** + Used to allocate and build a device path node for an SD card on the SD + controller. + + The BuildDevicePath() function allocates and builds a single device node + for the SD + card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, th= en + EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then + EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(),= the + contents of DevicePath are initialized to describe the SD card specified= by + Slot, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PRO= TOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card f= or + which a device path node is to be allocate= d and + built. + @param[in,out] DevicePath A pointer to a single device path node that + describes the SD card specified by Slot. T= his + function is responsible for allocating the + buffer DevicePath with the boot service + AllocatePool(). It is the caller's respons= ibi- + lity to free DevicePath when the caller is + finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD= card + specified by Slot was allocated and return= ed in + DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exi= st on + the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruBuildDevicePath ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +/** + This function retrieves an SD card slot number based on the input device= path. + + The GetSlotNumber() function retrieves slot number for the SD card speci= fied + by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is + returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver + supports, EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] DevicePath A pointer to the device path node that des= cribes + a SD card on the SD controller. + @param[out] Slot On return, points to the slot number of an= SD + card on the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type = that + the SD Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruGetSlotNumber ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 *Slot + ); + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, + EFI_UNSUPPORTED is returned. + + If Slot is not in a valid slot number for this SD controller, + EFI_INVALID_PARAMETER is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] Slot Specifies the slot number of the SD card t= o be + reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device + reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device e= rror + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruResetDevice ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot + ); + +// +// Driver model protocol interfaces +// +/** + Tests to see if this driver supports a given controller. If a child devi= ce is + provided, it further tests to see if this driver supports creating a han= dle + 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 dev= ice + path attached to ControllerHandle and/or the services from the bus I/O + abstraction attached to ControllerHandle to determine if the driver supp= orts + ControllerHandle. This function may be called many times during platform + initialization. In order to reduce boot times, the tests performed by th= is + function must be very small, and take as little time as possible to exec= ute. + 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 functi= on + 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 driv= er, if + a protocol is already in the opened state, then it must not be closed wi= th + CloseProtocol(). This is required to guarantee the state of ControllerHa= ndle + is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PRO= TOCOL + instance. + @param[in] ControllerHandle The handle of the controller to test. T= his + handle must support a protocol interfac= e that + supplies an I/O abstraction to the driv= er. + @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 param= eter + is not NULL, then the bus driver must d= eter- + mine if the bus controller specified by + ControllerHandle and the child controll= er + specified by RemainingDevicePath are bo= th + supported by this bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandl= e and + RemainingDevicePath is supported by the + driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandl= e and + RemainingDevicePath is already being ma= naged + by the driver specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandl= e and + RemainingDevicePath is already being ma= naged + by a different driver or an application= that + requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandl= e and + RemainingDevicePath is not supported by= the + driver specified by This. +**/ +EFI_STATUS +EFIAPI +DwMmcHcDriverBindingSupported ( + 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 ot= her + locations, + but the following calling restrictions must be followed or the system be= havior + 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 na= tural- + ly aligned EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver spe= cified + 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_PRO= TOCOL + instance. + @param[in] ControllerHandle The handle of the controller to start. = This + handle must support a protocol interfac= e that + supplies an I/O abstraction to the driv= er. + @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= dri- + vers. For a bus driver, if this paramet= er is + NULL, then handles for all the children= of + Controller are created by this driver. + If this parameter is not NULL and the f= irst + Device Path Node is not the End of Devi= ce + Path Node, then only the handle for the + child device specified by the first Dev= ice + Path Node of RemainingDevicePath is cre= ated + by this driver. + If the first Device Path Node of + RemainingDevicePath is the End of Devic= e 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 +DwMmcHcDriverBindingStart ( + 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 oth= er + 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 previo= us + 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 ca= lled + OpenProtocol() on ControllerHandle with an Attribute of + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOC= OL + instance. + @param[in] ControllerHandle A handle to the device being stopped. The = handle + must support a bus specific I/O protocol f= or 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 d= evice + error. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcDriverBindingStop ( + 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 o= f 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 speci= fied + 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_PROTO= COL 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 calle= r 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 for= mat. + + @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 specifie= d by + This and the language specified by Languag= e 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 supp= ort + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user readable name of the control= ler + that is being managed by a driver. + + This function retrieves the user readable name of the controller specifi= ed 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 specif= ied by + Language, then a pointer to the controller name is returned in Controlle= rName, + and EFI_SUCCESS is returned. If the driver specified by This is not cur= rently + 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 retu= rned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTO= COL 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 retr= ieve + the name of. This is an optional paramete= r that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus d= rivers + 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 calle= r 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 specifie= d 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 a= nd + ChildHandle in the language specified by + Language from the point of view of the dri= ver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable n= ame in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a va= lid + 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 curren= tly + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not supp= ort + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle, OPTIONAL + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/** + Create a new TRB for the SD/MMC cmd request. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Slot The slot number of the SD card to send the com= mand + to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. + If Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @return Created Trb or NULL. + +**/ +DW_MMC_HC_TRB * +DwMmcCreateTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event + ); + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + +**/ +VOID +DwMmcFreeTrb ( + IN DW_MMC_HC_TRB *Trb + ); + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +DwMmcCheckTrbEnv ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ); + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +DwMmcWaitTrbEnv ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ); + +/** + Execute the specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfull= y. + @retval Others Some erros happen when sending this request to= the + host controller. + +**/ +EFI_STATUS +DwMmcExecTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ); + +/** + Check the TRB execution result. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +DwMmcCheckTrbResult ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ); + +/** + Wait for the TRB execution result. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +DwMmcWaitTrbResult ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ); + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcIdentification ( + IN DW_MMC_HC_PRIVATE_DATA *Private + ); + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +SdCardIdentification ( + IN DW_MMC_HC_PRIVATE_DATA *Private + ); + +#endif /* _DW_MMC_HC_DXE_H_ */ diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h b/EmbeddedPkg/Driver= s/DwMmcHcDxe/DwMmcHci.h new file mode 100644 index 000000000000..e4fe26e7d2e8 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.h @@ -0,0 +1,983 @@ +/** @file + + Provides some data structure definitions used by the SD/MMC host control= ler + driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#ifndef _DW_MMC_HCI_H_ +#define _DW_MMC_HCI_H_ + +#include +#include + +#include + +// +// SD Host Controller SlotInfo Register Offset +// +#define DW_MMC_HC_SLOT_OFFSET 0x40 + +#define DW_MMC_HC_MAX_SLOT 1 + +// +// SD Host Controller MMIO Register Offset +// +#define DW_MMC_CTRL 0x000 +#define DW_MMC_PWREN 0x004 +#define DW_MMC_CLKDIV 0x008 +#define DW_MMC_CLKSRC 0x00c +#define DW_MMC_CLKENA 0x010 +#define DW_MMC_TMOUT 0x014 +#define DW_MMC_CTYPE 0x018 +#define DW_MMC_BLKSIZ 0x01c +#define DW_MMC_BYTCNT 0x020 +#define DW_MMC_INTMASK 0x024 +#define DW_MMC_CMDARG 0x028 +#define DW_MMC_CMD 0x02c +#define DW_MMC_RESP0 0x030 +#define DW_MMC_RESP1 0x034 +#define DW_MMC_RESP2 0x038 +#define DW_MMC_RESP3 0x03c +#define DW_MMC_RINTSTS 0x044 +#define DW_MMC_STATUS 0x048 +#define DW_MMC_FIFOTH 0x04c +#define DW_MMC_GPIO 0x058 +#define DW_MMC_DEBNCE 0x064 +#define DW_MMC_USRID 0x068 +#define DW_MMC_VERID 0x06c +#define DW_MMC_HCON 0x070 +#define DW_MMC_UHSREG 0x074 +#define DW_MMC_BMOD 0x080 +#define DW_MMC_DBADDR 0x088 +#define DW_MMC_IDSTS 0x08c +#define DW_MMC_IDINTEN 0x090 +#define DW_MMC_DSCADDR 0x094 +#define DW_MMC_BUFADDR 0x098 +#define DW_MMC_CARDTHRCTL 0x100 +#define DW_MMC_UHSREG_EXT 0x108 +#define DW_MMC_ENABLE_SHIFT 0x110 +#define DW_MMC_FIFO_START 0x200 + +#define GET_IDSTS_DMAC_FSM(x) (((x) >> 13) & 0xf) +#define IDSTS_FSM_DMA_IDLE 0 +#define IDSTS_FSM_DMA_SUSPEND 1 +#define IDSTS_FSM_DESC_RD 2 +#define IDSTS_FSM_DESC_CHK 3 +#define IDSTS_FSM_DMA_RD_REQ_WAIT 4 +#define IDSTS_FSM_DMA_WR_REQ_WAIT 5 +#define IDSTS_FSM_DMA_RD 6 +#define IDSTS_FSM_DMA_WR 7 +#define IDSTS_FSM_DESC_CLOSE 8 +#define IDSTS_FSM_MASK 0xf + +#define CMD_UPDATE_CLK 0x80202000 +#define CMD_START_BIT (1 << 31) + +#define MMC_8BIT_MODE (1 << 16) +#define MMC_4BIT_MODE (1 << 0) +#define MMC_1BIT_MODE 0 + +#define DW_MMC_BLOCK_SIZE 512 + +#define CMD_INDEX_MASK 0x3F +#define BIT_CMD_RESPONSE_EXPECT (1 << 6) +#define BIT_CMD_LONG_RESPONSE (1 << 7) +#define BIT_CMD_CHECK_RESPONSE_CRC (1 << 8) +#define BIT_CMD_DATA_EXPECTED (1 << 9) +#define BIT_CMD_READ (0 << 10) +#define BIT_CMD_WRITE (1 << 10) +#define BIT_CMD_BLOCK_TRANSFER (0 << 11) +#define BIT_CMD_STREAM_TRANSFER (1 << 11) +#define BIT_CMD_SEND_AUTO_STOP (1 << 12) +#define BIT_CMD_WAIT_PRVDATA_COMPLETE (1 << 13) +#define BIT_CMD_STOP_ABORT_CMD (1 << 14) +#define BIT_CMD_SEND_INIT (1 << 15) +#define BIT_CMD_UPDATE_CLOCK_ONLY (1 << 21) +#define BIT_CMD_READ_CEATA_DEVICE (1 << 22) +#define BIT_CMD_CCS_EXPECTED (1 << 23) +#define BIT_CMD_ENABLE_BOOT (1 << 24) +#define BIT_CMD_EXPECT_BOOT_ACK (1 << 25) +#define BIT_CMD_DISABLE_BOOT (1 << 26) +#define BIT_CMD_MANDATORY_BOOT (0 << 27) +#define BIT_CMD_ALTERNATE_BOOT (1 << 27) +#define BIT_CMD_VOLT_SWITCH (1 << 28) +#define BIT_CMD_USE_HOLD_REG (1 << 29) +#define BIT_CMD_START (1 << 31) + +#define CMD_INDEX(x) ((x) & CMD_INDEX_MASK) + +#define DW_MMC_INT_EBE (1 << 15) /* End-bit= Err */ +#define DW_MMC_INT_SBE (1 << 13) /* Start-b= it Err */ +#define DW_MMC_INT_HLE (1 << 12) /* Hardwar= e-lock Err */ +#define DW_MMC_INT_FRUN (1 << 11) /* FIFO UN= /OV RUN */ +#define DW_MMC_INT_DRT (1 << 9) /* Data ti= meout */ +#define DW_MMC_INT_RTO (1 << 8) /* Respons= e timeout */ +#define DW_MMC_INT_DCRC (1 << 7) /* Data CR= C err */ +#define DW_MMC_INT_RCRC (1 << 6) /* Respons= e CRC err */ +#define DW_MMC_INT_RXDR (1 << 5) /* Receive= FIFO data request */ +#define DW_MMC_INT_TXDR (1 << 4) /* Transmi= t FIFO data request */ +#define DW_MMC_INT_DTO (1 << 3) /* Data tr= ans over */ +#define DW_MMC_INT_CMD_DONE (1 << 2) /* Command= done */ +#define DW_MMC_INT_RE (1 << 1) /* Respons= e error */ + +#define DW_MMC_IDMAC_DES0_DIC (1 << 1) +#define DW_MMC_IDMAC_DES0_LD (1 << 2) +#define DW_MMC_IDMAC_DES0_FS (1 << 3) +#define DW_MMC_IDMAC_DES0_CH (1 << 4) +#define DW_MMC_IDMAC_DES0_ER (1 << 5) +#define DW_MMC_IDMAC_DES0_CES (1 << 30) +#define DW_MMC_IDMAC_DES0_OWN (1 << 31) +#define DW_MMC_IDMAC_DES1_BS1(x) ((x) & 0x1fff) +#define DW_MMC_IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13) +#define DW_MMC_IDMAC_SWRESET (1 << 0) +#define DW_MMC_IDMAC_FB (1 << 1) +#define DW_MMC_IDMAC_ENABLE (1 << 7) + +#define DW_MMC_CTRL_RESET (1 << 0) +#define DW_MMC_CTRL_FIFO_RESET (1 << 1) +#define DW_MMC_CTRL_DMA_RESET (1 << 2) +#define DW_MMC_CTRL_INT_EN (1 << 4) +#define DW_MMC_CTRL_DMA_EN (1 << 5) +#define DW_MMC_CTRL_IDMAC_EN (1 << 25) +#define DW_MMC_CTRL_RESET_ALL (DW_MMC_CTRL_RESET | DW_MM= C_CTRL_FIFO_RESET | DW_MMC_CTRL_DMA_RESET) + +#define DW_MMC_STS_DATA_BUSY (1 << 9) +#define DW_MMC_STS_FIFO_COUNT(x) (((x) & 0x1fff) << 17) /= * Number of filled locations in FIFO */ +#define GET_STS_FIFO_COUNT(x) (((x) >> 17) & 0x1fff) + +#define DW_MMC_BMOD_SWR (1 << 0) /* Softwa= re Reset */ +#define DW_MMC_BMOD_FB (1 << 1) /* Fix Bu= rst */ +#define DW_MMC_BMOD_DE (1 << 7) /* IDMAC = Enable */ + +#define DW_MMC_IDSTS_TI (1 << 0) /* Transm= it Interrupt */ +#define DW_MMC_IDSTS_RI (1 << 1) /* Receiv= e Interrupt */ + +#define DW_MMC_FIFO_TWMARK(x) ((x) & 0xfff) +#define DW_MMC_FIFO_RWMARK(x) (((x) & 0x1ff) << 16) +#define DW_MMC_DMA_BURST_SIZE(x) (((x) & 0x7) << 28) + +#define DW_MMC_CARD_RD_THR(x) (((x) & 0xfff) << 16) +#define DW_MMC_CARD_RD_THR_EN (1 << 0) + +#define UHS_DDR_MODE (1 << 16) + +#define GENCLK_DIV 7 + +#define DW_MMC_GPIO_CLK_DIV(x) (((x) & 0xf) << 8) +#define DW_MMC_GPIO_USE_SAMPLE_DLY(x) (((x) & 1) << 13) +#define DW_MMC_GPIO_CLK_ENABLE BIT16 + +#define UHSEXT_SAMPLE_PHASE(x) (((x) & 0x1f) << 16) +#define UHSEXT_SAMPLE_DRVPHASE(x) (((x) & 0x1f) << 21) +#define UHSEXT_SAMPLE_DLY(x) (((x) & 0x1f) << 26) + +#define DWMMC_DMA_BUF_SIZE (512 * 8) +#define DWMMC_FIFO_THRESHOLD 16 + +#define DWMMC_INIT_CLOCK_FREQ 400 /* KHz */ + +// +// The transfer modes supported by SD Host Controller +// Simplified Spec 3.0 Table 1-2 +// +typedef enum { + SdMmcNoData, + SdMmcPioMode, + SdMmcSdmaMode, + SdMmcAdmaMode +} DW_MMC_HC_TRANSFER_MODE; + +// +// The maximum data length of each descriptor line +// +#define ADMA_MAX_DATA_PER_LINE 0x10000 + +typedef struct { + UINT32 Des0; + UINT32 Des1; + UINT32 Des2; + UINT32 Des3; +} DW_MMC_HC_DMA_DESC_LINE; + +#define SD_MMC_SDMA_BOUNDARY 512 * 1024 +#define SD_MMC_SDMA_ROUND_UP(x, n) (((x) + n) & ~(n - 1)) + +typedef struct { + UINT8 FirstBar:3; // bit 0:2 + UINT8 Reserved:1; // bit 3 + UINT8 SlotNum:3; // bit 4:6 + UINT8 Reserved1:1; // bit 7 +} DW_MMC_HC_SLOT_INFO; + +/** + Dump the content of SD/MMC host controller's Capability Register. + + @param[in] Slot The slot number of the SD card to send the c= ommand to. + @param[in] Capability The buffer to store the capability data. + +**/ +VOID +DumpCapabilityReg ( + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP *Capability + ); + +#if 0 +/** + Read SlotInfo register from SD/MMC host controller pci config space. + + @param[in] PciIo The PCI IO protocol instance. + @param[out] FirstBar The buffer to store the first BAR value. + @param[out] SlotNum The buffer to store the supported slot number. + + @retval EFI_SUCCESS The operation succeeds. + @retval Others The operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcGetSlotInfo ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + OUT UINT8 *FirstBar, + OUT UINT8 *SlotNum + ); +#endif + +#ifdef DWMMC_PCI +/** + Read/Write specified SD/MMC host controller mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configura= tion + header to use as the base address for the m= emory + operation to perform. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Read A boolean to indicate it's read or write op= eration. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer= to store + the results. For write operations, the sour= ce buffer + to write data from. The caller is responsib= le for + having ownership of the data buffer and ens= uring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is = not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcRwMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ); +#else +/** + Read/Write specified SD/MMC host controller mmio register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Read A boolean to indicate it's read or write op= eration. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer= to store + the results. For write operations, the sour= ce buffer + to write data from. The caller is responsib= le for + having ownership of the data buffer and ens= uring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is = not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcRwMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ); +#endif + +#ifdef DWMMC_PCI +/** + Do OR operation with the value of the specified SD/MMC host controller m= mio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configura= tion + header to use as the base address for the m= emory + operation to perform. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR opera= tion. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count i= s not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcOrMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ); +#else +/** + Do OR operation with the value of the specified SD/MMC host controller m= mio register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configura= tion + header to use as the base address for the m= emory + operation to perform. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR opera= tion. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count i= s not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcOrMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ); +#endif + +#ifdef DWMMC_PCI +/** + Do AND operation with the value of the specified SD/MMC host controller = mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configura= tion + header to use as the base address for the m= emory + operation to perform. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND oper= ation. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count = is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcAndMmio ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *AndData + ); +#else +/** + Do AND operation with the value of the specified SD/MMC host controller = mmio register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset within the selected BAR to start= the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND oper= ation. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count = is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcAndMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *AndData + ); +#endif + +#ifdef DWMMC_PCI +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memo= ry + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in tim= eout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcWaitMmioSet ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 BarIndex, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ); +#else +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in tim= eout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcWaitMmioSet ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ); +#endif + +#ifdef DWMMC_PCI +/** + Software reset the specified SD/MMC host controller. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +DwMmcHcReset ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#else +/** + Software reset the specified SD/MMC host controller. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +DwMmcHcReset ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#endif + +#ifdef DWMMC_PCI +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcEnableInterrupt ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); +#else +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcEnableInterrupt ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ); +#endif + +#ifdef DWMMC_PCI +/** + Get the capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the c= ommand to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcGetCapability ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT DW_MMC_HC_SLOT_CAP *Capability + ); +#else +/** + Get the capability data from the specified slot. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the c= ommand to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcGetCapability ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT DW_MMC_HC_SLOT_CAP *Capability + ); +#endif + +#if 0 +/** + Get the maximum current capability data from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the c= ommand to. + @param[out] MaxCurrent The buffer to store the maximum current capa= bility data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcGetMaxCurrent ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT UINT64 *MaxCurrent + ); +#endif + +#ifdef DWMMC_PCI +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC h= ost controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +DwMmcHcCardDetect ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT BOOLEAN *MediaPresent + ); +#else +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC h= ost controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +DwMmcHcCardDetect ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT BOOLEAN *MediaPresent + ); +#endif + +#ifdef DWMMC_PCI +/** + Stop SD/MMC card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for detail= s. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +DwMmcHcStopClock ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); + +/** + SD/MMC card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for detail= s. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] ClockFreq The max clock frequency to be set. The unit is= KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcClockSupply ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT64 ClockFreq, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#else +/** + Stop SD/MMC card clock. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for detail= s. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +DwMmcHcStopClock ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ); + +/** + SD/MMC card clock supply. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for detail= s. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] ClockFreq The max clock frequency to be set. The unit is= KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcClockSupply ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT64 ClockFreq, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#endif + +#if 0 +/** + SD/MMC bus power control. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] PowerCtrl The value setting to the power control registe= r. + + @retval TRUE There is a SD/MMC card attached. + @retval FALSE There is no a SD/MMC card attached. + +**/ +EFI_STATUS +DwMmcHcPowerControl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN UINT8 PowerCtrl + ); +#endif + +#ifdef DWMMC_PCI +/** + Set the SD/MMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] BusWidth The bus width used by the SD/MMC device, it mu= st be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +DwMmcHcSetBusWidth ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN BOOLEAN IsDdr, + IN UINT16 BusWidth + ); +#else +/** + Set the SD/MMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] BusWidth The bus width used by the SD/MMC device, it mu= st be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +DwMmcHcSetBusWidth ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN BOOLEAN IsDdr, + IN UINT16 BusWidth + ); +#endif + +#ifdef DWMMC_PCI +/** + Supply SD/MMC card with lowest clock frequency at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitClockFreq ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#else +/** + Supply SD/MMC card with lowest clock frequency at initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitClockFreq ( + IN EFI_DEVICE_IO_PROTOCOL *PciIo, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#endif + +#ifdef DWMMC_PCI +/** + Supply SD/MMC card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitPowerVoltage ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#else +/** + Supply SD/MMC card with maximum voltage at initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitPowerVoltage ( + IN EFI_DEVICE_IO_PROTOCOL *PciIo, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#endif + +#ifdef DWMMC_PCI +/** + Initialize the Timeout Control register with most conservative value at = initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for detai= ls. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS The timeout control register is configured suc= cessfully. + @retval Others The timeout control register isn't configured = successfully. + +**/ +EFI_STATUS +DwMmcHcInitTimeoutCtrl ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ); +#else +/** + Initialize the Timeout Control register with most conservative value at = initialization. + + Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for detai= ls. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + + @retval EFI_SUCCESS The timeout control register is configured suc= cessfully. + @retval Others The timeout control register isn't configured = successfully. + +**/ +EFI_STATUS +DwMmcHcInitTimeoutCtrl ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ); +#endif + +#ifdef DWMMC_PCI +/** + Initial SD/MMC host controller with lowest clock frequency, max power an= d max timeout value + at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfull= y. + @retval Others The host controller isn't initialized successf= ully. + +**/ +EFI_STATUS +DwMmcHcInitHost ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#else +/** + Initial SD/MMC host controller with lowest clock frequency, max power and + max timeout value at initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfull= y. + @retval Others The host controller isn't initialized successf= ully. + +**/ +EFI_STATUS +DwMmcHcInitHost ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ); +#endif + +#endif /* _DW_MMC_HCI_H_ */ diff --git a/EmbeddedPkg/Include/Protocol/PlatformDwMmc.h b/EmbeddedPkg/Inc= lude/Protocol/PlatformDwMmc.h new file mode 100644 index 000000000000..54a44928a7e1 --- /dev/null +++ b/EmbeddedPkg/Include/Protocol/PlatformDwMmc.h @@ -0,0 +1,79 @@ +/** @file + + Copyright (c) 2018, Linaro. All rights reserved. + + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#ifndef __PLATFORM_DW_MMC_H__ +#define __PLATFORM_DW_MMC_H__ + +typedef enum { + RemovableSlot, + EmbeddedSlot, + SharedBusSlot, + UnknownSlot +} EFI_SD_MMC_SLOT_TYPE; + +typedef enum { + UnknownCardType, + SdCardType, + SdioCardType, + MmcCardType, + EmmcCardType +} SD_MMC_CARD_TYPE; + +typedef struct { + UINT32 DefaultSpeed:1; // bit 0 + UINT32 HighSpeed:1; // bit 1 + UINT32 Sdr12:1; // bit 2 + UINT32 Sdr25:1; // bit 3 + UINT32 Sdr50:1; // bit 4 + UINT32 Sdr104:1; // bit 5 + UINT32 Ddr50:1; // bit 6 + UINT32 SysBus64:1; // bit 7 + UINT32 BusWidth:4; // bit 11:8 + UINT32 SlotType:2; // bit 13:12 + UINT32 CardType:3; // bit 16:14 + UINT32 Voltage18:1; // bit 17 + UINT32 Voltage30:1; // bit 18 + UINT32 Voltage33:1; // bit 19 + UINT32 BaseClkFreq; + EFI_HANDLE Controller; +} DW_MMC_HC_SLOT_CAP; + +// +// Protocol interface structure +// +typedef struct _PLATFORM_DW_MMC_PROTOCOL PLATFORM_DW_MMC_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *PLATFORM_DW_MMC_GET_CAPABILITY) ( + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT DW_MMC_HC_SLOT_CAP *Capability + ); + +typedef +BOOLEAN +(EFIAPI *PLATFORM_DW_MMC_CARD_DETECT) ( + IN EFI_HANDLE Controller, + IN UINT8 Slot + ); + +struct _PLATFORM_DW_MMC_PROTOCOL { + PLATFORM_DW_MMC_GET_CAPABILITY GetCapability; + PLATFORM_DW_MMC_CARD_DETECT CardDetect; +}; + +extern EFI_GUID gPlatformDwMmcProtocolGuid; + +#endif /* __PLATFORM_DW_MMC_H__ */ diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c b/EmbeddedPkg/D= rivers/DwMmcHcDxe/ComponentName.c new file mode 100644 index 000000000000..1edade69d091 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/ComponentName.c @@ -0,0 +1,214 @@ +/** @file + UEFI Component Name(2) protocol implementation for Designware SD/MMC host + controller driver. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#include "DwMmcHcDxe.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDwMmcHcComponen= tName =3D { + DwMmcHcComponentNameGetDriverName, + DwMmcHcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDwMmcHcCompone= ntName2 =3D { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DwMmcHcComponentNameGetDriverN= ame, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DwMmcHcComponentNameGetControl= lerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcDriverNameT= able[] =3D { + { "eng;en", L"Designware Sd/Mmc Host Controller Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcControllerN= ameTable[] =3D { + { "eng;en", L"Designware Sd/Mmc Host Controller" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form o= f 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 speci= fied + 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_PROTO= COL 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 calle= r 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 for= mat. + + @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 specifie= d by + This and the language specified by Languag= e 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 supp= ort + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDwMmcHcDriverNameTable, + DriverName, + (BOOLEAN)(This =3D=3D &gDwMmcHcComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the control= ler + that is being managed by a driver. + + This function retrieves the user readable name of the controller specifi= ed 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 specif= ied by + Language, then a pointer to the controller name is returned in Controlle= rName, + and EFI_SUCCESS is returned. If the driver specified by This is not cur= rently + 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 retu= rned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTO= COL 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 retr= ieve + the name of. This is an optional paramete= r that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus d= rivers + 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 calle= r 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 specifie= d 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 a= nd + ChildHandle in the language specified by + Language from the point of view of the dri= ver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable n= ame in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a va= lid + 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 curren= tly + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not supp= ort + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcComponentNameGetControllerName ( + 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 =3D=3D NULL || ControllerName =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle !=3D NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status =3D EfiTestManagedDevice ( + ControllerHandle, + gDwMmcHcDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDwMmcHcControllerNameTable, + ControllerName, + (BOOLEAN)(This =3D=3D &gDwMmcHcComponentName) + ); +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c b/EmbeddedPkg/Driv= ers/DwMmcHcDxe/DwMmcHcDxe.c new file mode 100644 index 000000000000..6e8e3fac8d75 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c @@ -0,0 +1,1295 @@ +/** @file + This driver is used to manage Designware SD/MMC host controller. + + It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (C) 2016 Marvell International Ltd. All rigths reserved.
+ Copyright (C) 2018, Linaro Ltd. All rigths reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BS= D License + which accompanies this distribution. The full text of the license may b= e 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 IMP= LIED. + +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "DwMmcHcDxe.h" + +// +// Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gDwMmcHcDriverBinding =3D { + DwMmcHcDriverBindingSupported, + DwMmcHcDriverBindingStart, + DwMmcHcDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for Designware SD/MMC host controller private data. +// +DW_MMC_HC_PRIVATE_DATA gDwMmcHcTemplate =3D { + DW_MMC_HC_PRIVATE_SIGNATURE, // Signature + NULL, // ControllerHandle + NULL, // DevIo + { // PassThru + sizeof (UINT32), + DwMmcPassThruPassThru, + DwMmcPassThruGetNextSlot, + DwMmcPassThruBuildDevicePath, + DwMmcPassThruGetSlotNumber, + DwMmcPassThruResetDevice + }, + NULL, // PlatformDwMmc + 0, // PreviousSlot + NULL, // TimerEvent + NULL, // ConnectEvent + // Queue + INITIALIZE_LIST_HEAD_VARIABLE (gDwMmcHcTemplate.Queue), + { // Slot + {0, UnknownSlot, 0, 0, 0} + }, + { // Capability + {0} + }, + { // MaxCurrent + 0 + }, + 0 // ControllerVersion +}; + +SD_DEVICE_PATH mSdDpTemplate =3D { + { + MESSAGING_DEVICE_PATH, + MSG_SD_DP, + { + (UINT8) (sizeof (SD_DEVICE_PATH)), + (UINT8) ((sizeof (SD_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +EMMC_DEVICE_PATH mEmmcDpTemplate =3D { + { + MESSAGING_DEVICE_PATH, + MSG_EMMC_DP, + { + (UINT8) (sizeof (EMMC_DEVICE_PATH)), + (UINT8) ((sizeof (EMMC_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +// +// Prioritized function list to detect card type. +// User could add other card detection logic here. +// +DWMMC_CARD_TYPE_DETECT_ROUTINE mCardTypeDetectRoutineTable[] =3D { + EmmcIdentification, + SdCardIdentification, + NULL +}; + +/** + The entry point for SD host controller driver, used to install this driv= er 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 +InitializeDwMmcHcDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status =3D EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gDwMmcHcDriverBinding, + ImageHandle, + &gDwMmcHcComponentName, + &gDwMmcHcComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Call back function when the timer event is signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +ProcessAsyncTaskList ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + DW_MMC_HC_TRB *Trb; + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + BOOLEAN InfiniteWait; + EFI_EVENT TrbEvent; + + Private =3D (DW_MMC_HC_PRIVATE_DATA *)Context; + + // + // Check if the first entry in the async I/O queue is done or not. + // + Status =3D EFI_SUCCESS; + Trb =3D NULL; + Link =3D GetFirstNode (&Private->Queue); + if (!IsNull (&Private->Queue, Link)) { + Trb =3D DW_MMC_HC_TRB_FROM_THIS (Link); + if (!Private->Slot[Trb->Slot].MediaPresent) { + Status =3D EFI_NO_MEDIA; + goto Done; + } + if (!Trb->Started) { + // + // Check whether the cmd/data line is ready for transfer. + // + Status =3D DwMmcCheckTrbEnv (Private, Trb); + if (!EFI_ERROR (Status)) { + Trb->Started =3D TRUE; + Status =3D DwMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + goto Done; + } + } + Status =3D DwMmcCheckTrbResult (Private, Trb); + } + +Done: + if ((Trb !=3D NULL) && (Status =3D=3D EFI_NOT_READY)) { + Packet =3D Trb->Packet; + if (Packet->Timeout =3D=3D 0) { + InfiniteWait =3D TRUE; + } else { + InfiniteWait =3D FALSE; + } + if ((!InfiniteWait) && (Trb->Timeout-- =3D=3D 0)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus =3D EFI_TIMEOUT; + TrbEvent =3D Trb->Event; + DwMmcFreeTrb (Trb); + DEBUG (( + DEBUG_VERBOSE, + "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n", + TrbEvent + )); + gBS->SignalEvent (TrbEvent); + return; + } + } + if ((Trb !=3D NULL) && (Status !=3D EFI_NOT_READY)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus =3D Status; + TrbEvent =3D Trb->Event; + DwMmcFreeTrb (Trb); + DEBUG (( + DEBUG_VERBOSE, + "ProcessAsyncTaskList(): Signal Event %p with %r\n", + TrbEvent, + Status + )); + gBS->SignalEvent (TrbEvent); + } + return; +} + +/** + Sd removable device enumeration callback function when the timer event i= s signaled. + + @param[in] Event The Event this notify function registered to. + @param[in] Context Pointer to the context data registered to the + Event. + +**/ +VOID +EFIAPI +DwMmcHcEnumerateDevice ( + IN EFI_EVENT Event, + IN VOID* Context + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + EFI_STATUS Status; + BOOLEAN MediaPresent; + UINT32 RoutineNum; + DWMMC_CARD_TYPE_DETECT_ROUTINE *Routine; + UINTN Index; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + DW_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + Private =3D (DW_MMC_HC_PRIVATE_DATA *)Context; + + if ((Private->Slot[0].Enable) && + (Private->Slot[0].SlotType =3D=3D RemovableSlot)) { + Status =3D DwMmcHcCardDetect ( + Private->DevIo, + Private->ControllerHandle, + 0, + &MediaPresent + ); + if ((Status =3D=3D EFI_MEDIA_CHANGED) && !MediaPresent) { + DEBUG (( + DEBUG_INFO, + "DwMmcHcEnumerateDevice: device disconnected at %p\n", + Private->DevIo + )); + Private->Slot[0].MediaPresent =3D FALSE; + // + // Signal all async task events at the slot with EFI_NO_MEDIA status. + // + OldTpl =3D gBS->RaiseTPL (TPL_NOTIFY); + for (Link =3D GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link =3D NextLink) { + NextLink =3D GetNextNode (&Private->Queue, Link); + Trb =3D DW_MMC_HC_TRB_FROM_THIS (Link); + if (Trb->Slot =3D=3D 0) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus =3D EFI_NO_MEDIA; + gBS->SignalEvent (Trb->Event); + DwMmcFreeTrb (Trb); + } + } + gBS->RestoreTPL (OldTpl); + // + // Notify the upper layer the connect state change through + // ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + if ((Status =3D=3D EFI_MEDIA_CHANGED) && MediaPresent) { + DEBUG (( + DEBUG_INFO, + "DwMmcHcEnumerateDevice: device connected at %p\n", + Private->DevIo + )); + // + // Initialize slot and start identification process for the new + // attached device + // + Status =3D DwMmcHcInitHost (Private->DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + return; + } + // + // Reset the specified slot of the SD/MMC Pci Host Controller + // + Status =3D DwMmcHcReset (Private->DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + return; + } + + Private->Slot[0].MediaPresent =3D TRUE; + RoutineNum =3D sizeof (mCardTypeDetectRoutineTable) / + sizeof (DWMMC_CARD_TYPE_DETECT_ROUTINE); + for (Index =3D 0; Index < RoutineNum; Index++) { + Routine =3D &mCardTypeDetectRoutineTable[Index]; + if (*Routine !=3D NULL) { + Status =3D (*Routine) (Private); + if (!EFI_ERROR (Status)) { + break; + } + } + } + // + // This card doesn't get initialized correctly. + // + if (Index =3D=3D RoutineNum) { + return; + } + + // + // Notify the upper layer the connect state change through + // ReinstallProtocolInterface. + // + gBS->ReinstallProtocolInterface ( + Private->ControllerHandle, + &gEfiSdMmcPassThruProtocolGuid, + &Private->PassThru, + &Private->PassThru + ); + } + } + + return; +} + +/** + Reset the specified SD/MMC host controller and enable all interrupts. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS The software reset executes successfully. + @retval Others The software reset fails. + +**/ +EFI_STATUS +DwMmcHcReset ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 BlkSize; + + // + // Enable all interrupt after reset all. + // + Status =3D DwMmcHcEnableInterrupt (DevIo); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcReset: enable interrupts fail: %r\n", Sta= tus)); + return Status; + } + Status =3D DwMmcHcInitTimeoutCtrl (DevIo); + if (EFI_ERROR (Status)) { + return Status; + } + + BlkSize =3D DW_MMC_BLOCK_SIZE; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BLKSIZ, + FALSE, + sizeof (BlkSize), + &BlkSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set block size fails: %r\n", Statu= s)); + return Status; + } + + Status =3D DwMmcHcInitClockFreq (DevIo, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D DwMmcHcSetBusWidth (DevIo, FALSE, 1); + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child devi= ce + 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 dev= ice + path attached to ControllerHandle and/or the services from the bus I/O + abstraction attached to ControllerHandle to determine if the driver supp= orts + ControllerHandle. This function may be called many times during platform + initialization. In order to reduce boot times, the tests performed by th= is + function must be very small, and take as little time as possible to exec= ute. + 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 functi= on + must match its calls to AllocatePages() with FreePages(), AllocatePool()= with + FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHan= dle + may have been previously started by the same driver, if a protocol is al= ready + in the opened state, then it must not be closed with CloseProtocol(). Th= is is + required to guarantee the state of ControllerHandle is not modified by t= his + function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PRO= TOCOL + instance. + @param[in] ControllerHandle The handle of the controller to test. T= his + handle must support a protocol interfac= e that + supplies an I/O abstraction to the driv= er. + @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 param= eter + is not NULL, then the bus driver must d= eter- + mine if the bus controller specified by + ControllerHandle and the child controll= er + specified by RemainingDevicePath are bo= th + supported by this bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandl= e and + RemainingDevicePath is supported by the + driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandl= e and + RemainingDevicePath is already being ma= naged + by the driver specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandl= e and + RemainingDevicePath is already being ma= naged + by a different driver or an application= that + requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandl= e and + RemainingDevicePath is not supported by= the + driver specified by This. +**/ +EFI_STATUS +EFIAPI +DwMmcHcDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_IO_PROTOCOL *DevIo; + PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; + + ParentDevicePath =3D NULL; + + Status =3D gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D 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 EmbeddedNonDiscoverableIoProtocol. + // + Status =3D gBS->OpenProtocol ( + Controller, + &gEmbeddedNonDiscoverableIoProtocolGuid, + (VOID **) &DevIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + gBS->CloseProtocol ( + Controller, + &gEmbeddedNonDiscoverableIoProtocolGuid, + 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 paramet= ers, + and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PRO= TOCOL + 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 f= irst + Device Path Node is not the End of Devi= ce + Path Node, then only the handle for the + child device specified by the first Dev= ice + Path Node of RemainingDevicePath is cre= ated + by this driver. + If the first Device Path Node of + RemainingDevicePath is the End of Devic= e 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 +DwMmcHcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + DW_MMC_HC_PRIVATE_DATA *Private; + EFI_DEVICE_IO_PROTOCOL *DevIo; + BOOLEAN MediaPresent; + DWMMC_CARD_TYPE_DETECT_ROUTINE *Routine; + UINT8 Index; + UINT32 RoutineNum; + PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; + + Status =3D gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D gBS->OpenProtocol ( + Controller, + &gEmbeddedNonDiscoverableIoProtocolGuid, + (VOID **) &DevIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private =3D AllocateCopyPool (sizeof (DW_MMC_HC_PRIVATE_DATA), &gDwMmcHc= Template); + if (Private =3D=3D NULL) { + Status =3D EFI_OUT_OF_RESOURCES; + goto Done; + } + + Private->ControllerHandle =3D Controller; + Private->DevIo =3D DevIo; + Private->PlatformDwMmc =3D PlatformDwMmc; + InitializeListHead (&Private->Queue); + + Status =3D DwMmcHcGetCapability (DevIo, Controller, 0, &Private->Capabil= ity[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Private->Capability[0].BaseClkFreq =3D=3D 0) { + goto Done; + } + + DumpCapabilityReg (0, &Private->Capability[0]); + + MediaPresent =3D FALSE; + Status =3D DwMmcHcCardDetect (Private->DevIo, Controller, 0, &MediaPrese= nt); + if (MediaPresent =3D=3D FALSE) { + goto Done; + } + + // + // Initialize slot and start identification process for the new attached= device + // + Status =3D DwMmcHcInitHost (DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Reset HC + // + Status =3D DwMmcHcReset (DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + Private->Slot[0].CardType =3D Private->Capability[0].CardType; + Private->Slot[0].Enable =3D TRUE; + Private->Slot[0].MediaPresent =3D TRUE; + + RoutineNum =3D sizeof (mCardTypeDetectRoutineTable) / sizeof (DWMMC_CARD= _TYPE_DETECT_ROUTINE); + for (Index =3D 0; Index < RoutineNum; Index++) { + Routine =3D &mCardTypeDetectRoutineTable[Index]; + if (*Routine !=3D NULL) { + Status =3D (*Routine) (Private); + if (!EFI_ERROR (Status)) { + break; + } + } + } + + // + // Start the asynchronous I/O monitor + // + Status =3D gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ProcessAsyncTaskList, + Private, + &Private->TimerEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status =3D gBS->SetTimer (Private->TimerEvent, TimerPeriodic, DW_MMC_HC_= ASYNC_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Start the Sd removable device connection enumeration + // + Status =3D gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DwMmcHcEnumerateDevice, + Private, + &Private->ConnectEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status =3D gBS->SetTimer (Private->ConnectEvent, TimerPeriodic, DW_MMC_H= C_ENUM_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status =3D gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru), + NULL + ); + + DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStart: %r End on %x\n", Status,= Controller)); + +Done: + if (EFI_ERROR (Status)) { + if ((Private !=3D NULL) && (Private->TimerEvent !=3D NULL)) { + gBS->CloseEvent (Private->TimerEvent); + } + + if ((Private !=3D NULL) && (Private->ConnectEvent !=3D NULL)) { + gBS->CloseEvent (Private->ConnectEvent); + } + + if (Private !=3D 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 oth= er + 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 previo= us + 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 ca= lled + OpenProtocol() on ControllerHandle with an Attribute of + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOC= OL + instance. + @param[in] ControllerHandle A handle to the device being stopped. The = handle + must support a bus specific I/O protocol f= or 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 d= evice + error. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + DW_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + DW_MMC_HC_TRB *Trb; + + DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStop: Start\n")); + + Status =3D gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Close Non-Blocking timer and free Task list. + // + if (Private->TimerEvent !=3D NULL) { + gBS->CloseEvent (Private->TimerEvent); + Private->TimerEvent =3D NULL; + } + if (Private->ConnectEvent !=3D NULL) { + gBS->CloseEvent (Private->ConnectEvent); + Private->ConnectEvent =3D NULL; + } + // + // As the timer is closed, there is no needs to use TPL lock to + // protect the critical region "queue". + // + for (Link =3D GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link =3D NextLink) { + NextLink =3D GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb =3D DW_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus =3D EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + DwMmcFreeTrb (Trb); + } + + // + // Uninstall Block I/O protocol from the device handle + // + Status =3D gBS->UninstallProtocolInterface ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru) + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + FreePool (Private); + + DEBUG ((DEBUG_INFO, "DwMmcHcDriverBindingStop: End with %r\n", Status)); + + return Status; +} + +/** + Sends SD command to an SD card that is attached to the SD controller. + + The PassThru() function sends the SD command specified by Packet to the = SD + card specified by Slot. + + If Packet is successfully sent to the SD card, then EFI_SUCCESS is retur= ned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR= is + returned. + + If Slot is not in a valid range for the SD controller, then + EFI_INVALID_PARAMETER is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer= are + NULL, EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] Slot The slot number of the SD card to send the + command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performe= d. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when= the + Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting t= o send + the SD command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packe= t is + invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both + InDataBuffer and OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Pa= cket + is not supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength + exceeds the limit supported by SD card + ( i.e. if the number of bytes exceed the L= ast + LBA). + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruPassThru ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event OPTIONAL + ) +{ + EFI_STATUS Status; + DW_MMC_HC_PRIVATE_DATA *Private; + DW_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if ((This =3D=3D NULL) || (Packet =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SdMmcCmdBlk =3D=3D NULL) || (Packet->SdMmcStatusBlk =3D=3D = NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer =3D=3D NULL) && (Packet->OutTransferLength != =3D 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer =3D=3D NULL) && (Packet->InTransferLength !=3D= 0)) { + return EFI_INVALID_PARAMETER; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + + Trb =3D DwMmcCreateTrb (Private, Slot, Packet, Event); + if (Trb =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Immediately return for async I/O. + // + if (Event !=3D NULL) { + return EFI_SUCCESS; + } + + // + // Wait async I/O list is empty before execute sync I/O operation. + // + while (TRUE) { + OldTpl =3D gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&Private->Queue)) { + gBS->RestoreTPL (OldTpl); + break; + } + gBS->RestoreTPL (OldTpl); + } + + Status =3D DwMmcWaitTrbEnv (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status =3D DwMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status =3D DwMmcWaitTrbResult (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if (Trb !=3D NULL) { + DwMmcFreeTrb (Trb); + } + + return Status; +} + +/** + Used to retrieve next slot numbers supported by the SD controller. The + function returns information about all available slots (populated or + not-populated). + + The GetNextSlot() function retrieves the next slot number on an SD contr= oller. + If on input Slot is 0xFF, then the slot number of the first slot on the = SD + controller is returned. + + If Slot is a slot number that was returned on a previous call to + GetNextSlot(), then the slot number of the next slot on the SD controlle= r is + returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to + GetNextSlot(), EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PRO= TOCOL + instance. + @param[in,out] Slot On input, a pointer to a slot number on th= e SD + controller. + On output, a pointer to the next slot numb= er on + the SD controller. + An input value of 0xFF retrieves the first= slot + number on the SD controller. + + @retval EFI_SUCCESS The next slot number on the SD controller = was + returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD control= ler. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned= on a + previous call to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + + if ((This =3D=3D NULL) || (Slot =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (This); + + if (*Slot =3D=3D 0xFF) { + if (Private->Slot[0].Enable) { + *Slot =3D 0; + Private->PreviousSlot =3D 0; + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; + } else if (*Slot =3D=3D Private->PreviousSlot) { + return EFI_NOT_FOUND; + } else { + return EFI_INVALID_PARAMETER; + } +} + +/** + Used to allocate and build a device path node for an SD card on the SD + controller. + + The BuildDevicePath() function allocates and builds a single device node + for the SD card specified by Slot. + + If the SD card specified by Slot is not present on the SD controller, th= en + EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then + EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), + the contents of DevicePath are initialized to describe the SD card speci= fied + by Slot, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PRO= TOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card f= or + which a device path node is to be allocate= d and + built. + @param[in,out] DevicePath A pointer to a single device path node that + describes the SD card specified by Slot. T= his + function is responsible for allocating the + buffer DevicePath with the boot service + AllocatePool(). It is the caller's respons= i- + bility to free DevicePath when the caller = is + finished with DevicePath. + + @retval EFI_SUCCESS The device path node that describes the SD= card + specified by Slot was allocated and return= ed in + DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exi= st on + the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruBuildDevicePath ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + + if ((This =3D=3D NULL) || (DevicePath =3D=3D NULL) || (Slot >=3D DW_MMC_= HC_MAX_SLOT)) { + return EFI_INVALID_PARAMETER; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (This); + + if ((!Private->Slot[Slot].Enable) || (!Private->Slot[Slot].MediaPresent)= ) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[Slot].CardType =3D=3D SdCardType) { + SdNode =3D AllocateCopyPool (sizeof (SD_DEVICE_PATH), &mSdDpTemplate); + if (SdNode =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + SdNode->SlotNumber =3D Slot; + + *DevicePath =3D (EFI_DEVICE_PATH_PROTOCOL *) SdNode; + } else if (Private->Slot[Slot].CardType =3D=3D EmmcCardType) { + EmmcNode =3D AllocateCopyPool (sizeof (EMMC_DEVICE_PATH), &mEmmcDpTemp= late); + if (EmmcNode =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + EmmcNode->SlotNumber =3D Slot; + + *DevicePath =3D (EFI_DEVICE_PATH_PROTOCOL *) EmmcNode; + } else { + // + // Currently we only support SD and EMMC two device nodes. + // + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + This function retrieves an SD card slot number based on the input device= path. + + The GetSlotNumber() function retrieves slot number for the SD card speci= fied + by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is + returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver + supports, EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] DevicePath A pointer to the device path node that des= cribes + a SD card on the SD controller. + @param[out] Slot On return, points to the slot number of an= SD + card on the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type = that + the SD Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruGetSlotNumber ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT UINT8 *Slot + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + SD_DEVICE_PATH *SdNode; + EMMC_DEVICE_PATH *EmmcNode; + UINT8 SlotNumber; + + if ((This =3D=3D NULL) || (DevicePath =3D=3D NULL) || (Slot =3D=3D NULL)= ) { + return EFI_INVALID_PARAMETER; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (This); + + // + // Check whether the DevicePath belongs to SD_DEVICE_PATH or EMMC_DEVICE= _PATH + // + if ((DevicePath->Type !=3D MESSAGING_DEVICE_PATH) || + ((DevicePath->SubType !=3D MSG_SD_DP) && + (DevicePath->SubType !=3D MSG_EMMC_DP)) || + (DevicePathNodeLength(DevicePath) !=3D sizeof(SD_DEVICE_PATH)) || + (DevicePathNodeLength(DevicePath) !=3D sizeof(EMMC_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + if (DevicePath->SubType =3D=3D MSG_SD_DP) { + SdNode =3D (SD_DEVICE_PATH *) DevicePath; + SlotNumber =3D SdNode->SlotNumber; + } else { + EmmcNode =3D (EMMC_DEVICE_PATH *) DevicePath; + SlotNumber =3D EmmcNode->SlotNumber; + } + + if (SlotNumber >=3D DW_MMC_HC_MAX_SLOT) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[SlotNumber].Enable) { + *Slot =3D SlotNumber; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, + EFI_UNSUPPORTED is returned. + + If Slot is not in a valid slot number for this SD controller, + EFI_INVALID_PARAMETER is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROT= OCOL + instance. + @param[in] Slot Specifies the slot number of the SD card t= o be + reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device + reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device e= rror + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruResetDevice ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN UINT8 Slot + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + DW_MMC_HC_TRB *Trb; + EFI_TPL OldTpl; + + if (This =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (This); + + if (!Private->Slot[Slot].Enable) { + return EFI_INVALID_PARAMETER; + } + + if (!Private->Slot[Slot].MediaPresent) { + return EFI_NO_MEDIA; + } + + // + // Free all async I/O requests in the queue + // + OldTpl =3D gBS->RaiseTPL (TPL_NOTIFY); + + for (Link =3D GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link =3D NextLink) { + NextLink =3D GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb =3D DW_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus =3D EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + DwMmcFreeTrb (Trb); + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c b/EmbeddedPkg/Driver= s/DwMmcHcDxe/DwMmcHci.c new file mode 100644 index 000000000000..c261495387e6 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c @@ -0,0 +1,2366 @@ +/** @file + This driver is used to manage Designware SD/MMC PCI host controllers. + + It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro Ltd. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#include +#include + +#include +#include +#include +#include +#include + +#include "DwMmcHcDxe.h" + +/** + Dump the content of SD/MMC host controller's Capability Register. + + @param[in] Slot The slot number of the SD card to send the + command to. + @param[in] Capability The buffer to store the capability data. + +**/ +VOID +DumpCapabilityReg ( + IN UINT8 Slot, + IN DW_MMC_HC_SLOT_CAP *Capability + ) +{ + // + // Dump Capability Data + // + DEBUG (( + DEBUG_INFO, + " =3D=3D Slot [%d] Capability is 0x%x =3D=3D\n", + Slot, + Capability + )); + DEBUG (( + DEBUG_INFO, + " Base Clk Freq %dKHz\n", + Capability->BaseClkFreq + )); + DEBUG (( + DEBUG_INFO, + " BusWidth %d\n", + Capability->BusWidth + )); + DEBUG (( + DEBUG_INFO, + " HighSpeed Support %a\n", + Capability->HighSpeed ? "TRUE" : "FALSE" + )); + DEBUG (( + DEBUG_INFO, + " Voltage 1.8 %a\n", + Capability->Voltage18 ? "TRUE" : "FALSE" + )); + DEBUG (( + DEBUG_INFO, + " 64-bit Sys Bus %a\n", + Capability->SysBus64 ? "TRUE" : "FALSE" + )); + DEBUG ((DEBUG_INFO, " SlotType ")); + if (Capability->SlotType =3D=3D 0x00) { + DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot")); + } else if (Capability->SlotType =3D=3D 0x01) { + DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot")); + } else if (Capability->SlotType =3D=3D 0x02) { + DEBUG ((DEBUG_INFO, "%a\n", "Shared Bus Slot")); + } else { + DEBUG ((DEBUG_INFO, "%a\n", "Reserved")); + } + DEBUG (( + DEBUG_INFO, + " SDR50 Support %a\n", + Capability->Sdr50 ? "TRUE" : "FALSE" + )); + DEBUG (( + DEBUG_INFO, + " SDR104 Support %a\n", + Capability->Sdr104 ? "TRUE" : "FALSE" + )); + DEBUG (( + DEBUG_INFO, + " DDR50 Support %a\n", + Capability->Ddr50 ? "TRUE" : "FALSE" + )); + return; +} + +/** + Read/Write specified SD/MMC host controller mmio register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset to start the memory operation. + @param[in] Read A boolean to indicate it's read or write + operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffe= r to + store the results. For write operations, t= he + source buffer to write data from. The call= er is + responsible for having ownership of the da= ta + buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The DevIo or Data is NULL or the Count is = not + valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcRwMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN BOOLEAN Read, + IN UINT8 Count, + IN OUT VOID *Data + ) +{ + EFI_STATUS Status; + + if ((DevIo =3D=3D NULL) || (Data =3D=3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Count !=3D 4) && (Count !=3D 8)) { + return EFI_INVALID_PARAMETER; + } + + // + // Since there's FIFO in Designware controller, map it to 32-bit word on= ly. + // + Count =3D Count / sizeof (UINT32); + if (Read) { + Status =3D DevIo->Mem.Read ( + DevIo, + IO_UINT32, + (UINT64) Offset, + Count, + Data + ); + } else { + Status =3D DevIo->Mem.Write ( + DevIo, + IO_UINT32, + (UINT64) Offset, + Count, + Data + ); + } + + return Status; +} + +/** + Do OR operation with the value of the specified SD/MMC host controller m= mio + register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset to start the memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR opera= tion. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess + than Count bytes. + + @retval EFI_INVALID_PARAMETER The DevIo or OrData is NULL or the Count i= s not + valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcOrMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 Or; + + Status =3D DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count =3D=3D 1) { + Or =3D *(UINT8*) OrData; + } else if (Count =3D=3D 2) { + Or =3D *(UINT16*) OrData; + } else if (Count =3D=3D 4) { + Or =3D *(UINT32*) OrData; + } else if (Count =3D=3D 8) { + Or =3D *(UINT64*) OrData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data |=3D Or; + Status =3D DwMmcHcRwMmio (DevIo, Offset, FALSE, Count, &Data); + + return Status; +} + +/** + Do AND operation with the value of the specified SD/MMC host controller = mmio + register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset to start the memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND oper= ation. + The caller is responsible for having owners= hip of + the data buffer and ensuring its size not l= ess + than Count bytes. + + @retval EFI_INVALID_PARAMETER The DevIo or AndData is NULL or the Count = is + not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcAndMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *AndData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 And; + + Status =3D DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count =3D=3D 1) { + And =3D *(UINT8*) AndData; + } else if (Count =3D=3D 2) { + And =3D *(UINT16*) AndData; + } else if (Count =3D=3D 4) { + And =3D *(UINT32*) AndData; + } else if (Count =3D=3D 8) { + And =3D *(UINT64*) AndData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data &=3D And; + Status =3D DwMmcHcRwMmio (DevIo, Offset, FALSE, Count, &Data); + + return Status; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset to start the memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + + @retval EFI_NOT_READY The MMIO register hasn't set to the expected v= alue. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcCheckMmioSet ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue + ) +{ + EFI_STATUS Status; + UINT64 Value; + + Value =3D 0; + Status =3D DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + + Value &=3D MaskValue; + + if (Value =3D=3D TestValue) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Offset The offset to start the memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in tim= eout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcWaitMmioSet ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN UINT64 MaskValue, + IN UINT64 TestValue, + IN UINT64 Timeout + ) +{ + EFI_STATUS Status; + BOOLEAN InfiniteWait; + + if (Timeout =3D=3D 0) { + InfiniteWait =3D TRUE; + } else { + InfiniteWait =3D FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + Status =3D DwMmcHcCheckMmioSet ( + DevIo, + Offset, + Count, + MaskValue, + TestValue + ); + if (Status !=3D EFI_NOT_READY) { + return Status; + } + + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +/** + Set all interrupt status bits in Normal and Error Interrupt Status Enable + register. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +DwMmcHcEnableInterrupt ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ) +{ + EFI_STATUS Status; + UINT32 IntStatus; + UINT32 IdIntEn; + UINT32 IdSts; + + // + // Enable all bits in Interrupt Mask Register + // + IntStatus =3D 0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_INTMASK, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Clear status in Interrupt Status Register + // + IntStatus =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + IdIntEn =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDINTEN, + FALSE, + sizeof (IdIntEn), + &IdIntEn + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "DwMmcHcEnableInterrupt: init dma interrupts fail: %r\n", + Status + )); + return Status; + } + + IdSts =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + FALSE, + sizeof (IdSts), + &IdSts + ); + return Status; +} + +EFI_STATUS +DwMmcHcGetCapability ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT DW_MMC_HC_SLOT_CAP *Capacity + ) +{ + PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; + EFI_STATUS Status; + + if (Capacity =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + Status =3D gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D PlatformDwMmc->GetCapability (Controller, Slot, Capacity); + return Status; +} + +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC h= ost + controller slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand + to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +DwMmcHcCardDetect ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_HANDLE Controller, + IN UINT8 Slot, + OUT BOOLEAN *MediaPresent + ) +{ + PLATFORM_DW_MMC_PROTOCOL *PlatformDwMmc; + EFI_STATUS Status; + + if (MediaPresent =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + Status =3D gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + *MediaPresent =3D PlatformDwMmc->CardDetect (Controller, Slot); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +DwMmcHcUpdateClock ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ) +{ + EFI_STATUS Status; + UINT32 Cmd; + UINT32 IntStatus; + + Cmd =3D BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY | + BIT_CMD_START; + Status =3D DwMmcHcRwMmio (DevIo, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd); + if (EFI_ERROR (Status)) { + return Status; + } + while (1) { + Status =3D DwMmcHcRwMmio (DevIo, DW_MMC_CMD, TRUE, sizeof (Cmd), &Cmd); + if (EFI_ERROR (Status)) { + return Status; + } + if (!(Cmd & CMD_START_BIT)) { + break; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + if (IntStatus & DW_MMC_INT_HLE) { + DEBUG (( + DEBUG_ERROR, + "DwMmcHcUpdateClock: failed to update mmc clock frequency\n" + )); + return EFI_DEVICE_ERROR; + } + } + return EFI_SUCCESS; + +} + +/** + Stop SD/MMC card clock. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +DwMmcHcStopClock ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ) +{ + EFI_STATUS Status; + UINT32 ClkEna; + + // + // Disable MMC clock first + // + ClkEna =3D 0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKENA, + FALSE, + sizeof (ClkEna), + &ClkEna + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcUpdateClock (DevIo); + if (EFI_ERROR (Status)) { + return Status; + } + return Status; +} + +/** + SD/MMC card clock supply. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] ClockFreq The max clock frequency to be set. The unit is= KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcClockSupply ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT64 ClockFreq, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 BaseClkFreq; + UINT32 SettingFreq; + UINT32 Divisor; + UINT32 Remainder; + UINT32 MmcStatus; + UINT32 ClkEna; + UINT32 ClkSrc; + + // + // Calculate a divisor for SD clock frequency + // + ASSERT (Capability.BaseClkFreq !=3D 0); + + BaseClkFreq =3D Capability.BaseClkFreq; + if (ClockFreq =3D=3D 0) { + return EFI_INVALID_PARAMETER; + } + + if (ClockFreq > BaseClkFreq) { + ClockFreq =3D BaseClkFreq; + } + + // + // Calculate the divisor of base frequency. + // + Divisor =3D 0; + SettingFreq =3D BaseClkFreq; + while (ClockFreq < SettingFreq) { + Divisor++; + + SettingFreq =3D BaseClkFreq / (2 * Divisor); + Remainder =3D BaseClkFreq % (2 * Divisor); + if ((ClockFreq =3D=3D SettingFreq) && (Remainder =3D=3D 0)) { + break; + } + if ((ClockFreq =3D=3D SettingFreq) && (Remainder !=3D 0)) { + SettingFreq ++; + } + } + + DEBUG (( + DEBUG_INFO, + "BaseClkFreq %dKHz Divisor %d ClockFreq %dKhz\n", + BaseClkFreq, + Divisor, + ClockFreq + )); + + // + // Wait until MMC is idle + // + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + do { + Status =3D DwMmcHcStopClock (DevIo); + } while (EFI_ERROR (Status)); + + do { + ClkSrc =3D 0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKSRC, + FALSE, + sizeof (ClkSrc), + &ClkSrc + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // Set clock divisor + // + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKDIV, + FALSE, + sizeof (Divisor), + &Divisor + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // Enable MMC clock + // + ClkEna =3D 1; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKENA, + FALSE, + sizeof (ClkEna), + &ClkEna + ); + if (EFI_ERROR (Status)) { + continue; + } + Status =3D DwMmcHcUpdateClock (DevIo); + } while (EFI_ERROR (Status)); + + return EFI_SUCCESS; +} + +/** + Set the SD/MMC bus width. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] IsDdr A boolean to indicate it's dual data rate or n= ot. + @param[in] BusWidth The bus width used by the SD/MMC device, it mu= st be + 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +DwMmcHcSetBusWidth ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN BOOLEAN IsDdr, + IN UINT16 BusWidth + ) +{ + EFI_STATUS Status; + UINT32 Ctype; + UINT32 Uhs; + + switch (BusWidth) { + case 1: + Ctype =3D MMC_1BIT_MODE; + break; + case 4: + Ctype =3D MMC_4BIT_MODE; + break; + case 8: + Ctype =3D MMC_8BIT_MODE; + break; + default: + return EFI_INVALID_PARAMETER; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTYPE, + FALSE, + sizeof (Ctype), + &Ctype + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio (DevIo, DW_MMC_UHSREG, TRUE, sizeof (Uhs), &Uhs= ); + if (EFI_ERROR (Status)) { + return Status; + } + if (IsDdr) { + Uhs |=3D UHS_DDR_MODE; + } else { + Uhs &=3D ~(UHS_DDR_MODE); + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_UHSREG, + FALSE, + sizeof (Uhs), + &Uhs + ); + return Status; +} + +/** + Supply SD/MMC card with lowest clock frequency at initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitClockFreq ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 InitFreq; + + // + // Calculate a divisor for SD clock frequency + // + if (Capability.BaseClkFreq =3D=3D 0) { + // + // Don't support get Base Clock Frequency information via another meth= od + // + return EFI_UNSUPPORTED; + } + // + // Supply 400KHz clock frequency at initialization phase. + // + InitFreq =3D DWMMC_INIT_CLOCK_FREQ; + Status =3D DwMmcHcClockSupply (DevIo, InitFreq, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + MicroSecondDelay (100); + return Status; +} + +/** + Supply SD/MMC card with maximum voltage at initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +DwMmcHcInitPowerVoltage ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + UINT32 Data; + + Data =3D 0x1; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_PWREN, + FALSE, + sizeof (Data), + &Data + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "DwMmcHcInitPowerVoltage: enable power fails: %r\n", + Status + )); + return Status; + } + + Data =3D DW_MMC_CTRL_RESET_ALL; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Data), + &Data + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: reset fails: %r\n", Sta= tus)); + return Status; + } + Status =3D DwMmcHcWaitMmioSet ( + DevIo, + DW_MMC_CTRL, + sizeof (Data), + DW_MMC_CTRL_RESET_ALL, + 0x00, + DW_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "DwMmcHcInitPowerVoltage: reset done with %r\n", + Status + )); + return Status; + } + + Data =3D DW_MMC_CTRL_INT_EN; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Data), + &Data + ); + return Status; +} + +/** + Initialize the Timeout Control register with most conservative value at + initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + + @retval EFI_SUCCESS The timeout control register is configured + successfully. + @retval Others The timeout control register isn't configured + successfully. + +**/ +EFI_STATUS +DwMmcHcInitTimeoutCtrl ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ) +{ + EFI_STATUS Status; + UINT32 Data; + + Data =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_TMOUT, + FALSE, + sizeof (Data), + &Data + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "DwMmcHcInitTimeoutCtrl: set timeout fails: %r\n", + Status + )); + return Status; + } + + Data =3D 0x00FFFFFF; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_DEBNCE, + FALSE, + sizeof (Data), + &Data + ); + return Status; +} + +/** + Initial SD/MMC host controller with lowest clock frequency, max power and + max timeout value at initialization. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the com= mand + to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfull= y. + @retval Others The host controller isn't initialized successf= ully. + +**/ +EFI_STATUS +DwMmcHcInitHost ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + + Status =3D DwMmcHcInitPowerVoltage (DevIo, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + return Status; +} + +EFI_STATUS +DwMmcHcStartDma ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINT32 Ctrl; + UINT32 Bmod; + + DevIo =3D Trb->Private->DevIo; + + // + // Reset DMA + // + Ctrl =3D DW_MMC_CTRL_DMA_RESET; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Ctrl), + &Ctrl + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: reset fails: %r\n", Status)); + return Status; + } + Status =3D DwMmcHcWaitMmioSet ( + DevIo, + DW_MMC_CTRL, + sizeof (Ctrl), + DW_MMC_CTRL_DMA_RESET, + 0x00, + DW_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "DwMmcHcStartDma: reset done with %r\n", Status)); + return Status; + } + Bmod =3D DW_MMC_IDMAC_SWRESET; + Status =3D DwMmcHcOrMmio (DevIo, DW_MMC_BMOD, sizeof (Bmod), &Bmod); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD fail: %r\n", Status)); + return Status; + } + + // + // Select IDMAC + // + Ctrl =3D DW_MMC_CTRL_IDMAC_EN; + Status =3D DwMmcHcOrMmio (DevIo, DW_MMC_CTRL, sizeof (Ctrl), &Ctrl); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: init IDMAC fail: %r\n", Status)= ); + return Status; + } + + // + // Enable IDMAC + // + Bmod =3D DW_MMC_IDMAC_ENABLE | DW_MMC_IDMAC_FB; + Status =3D DwMmcHcOrMmio (DevIo, DW_MMC_BMOD, sizeof (Bmod), &Bmod); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcStartDma: set BMOD failure: %r\n", Status= )); + return Status; + } + return Status; +} + +EFI_STATUS +DwMmcHcStopDma ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINT32 Ctrl; + UINT32 Bmod; + + DevIo =3D Trb->Private->DevIo; + + // + // Disable and reset IDMAC + // + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + TRUE, + sizeof (Ctrl), + &Ctrl + ); + if (EFI_ERROR (Status)) { + return Status; + } + Ctrl &=3D ~DW_MMC_CTRL_IDMAC_EN; + Ctrl |=3D DW_MMC_CTRL_DMA_RESET; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Ctrl), + &Ctrl + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Stop IDMAC + // + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BMOD, + TRUE, + sizeof (Bmod), + &Bmod + ); + if (EFI_ERROR (Status)) { + return Status; + } + Bmod &=3D ~(DW_MMC_BMOD_FB | DW_MMC_BMOD_DE); + Bmod |=3D DW_MMC_BMOD_SWR; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BMOD, + FALSE, + sizeof (Bmod), + &Bmod + ); + if (EFI_ERROR (Status)) { + return Status; + } + return Status; +} + +/** + Build DMA descriptor table for transfer. + + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The DMA descriptor table is created successful= ly. + @retval Others The DMA descriptor table isn't created success= fully. + +**/ +EFI_STATUS +BuildDmaDescTable ( + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_PHYSICAL_ADDRESS Data; + UINT64 DataLen; + UINT64 Entries; + UINT32 Index; + UINT64 Remaining; + UINTN TableSize; + EFI_DEVICE_IO_PROTOCOL *DevIo; + EFI_STATUS Status; + UINTN Bytes; + UINTN Blocks; + DW_MMC_HC_DMA_DESC_LINE *DmaDesc; + UINT32 DmaDescPhy; + UINT32 Idsts; + UINT32 BytCnt; + UINT32 BlkSize; + + Data =3D Trb->DataPhy; + DataLen =3D Trb->DataLen; + DevIo =3D Trb->Private->DevIo; + // + // Only support 32bit DMA Descriptor Table + // + if ((Data >=3D 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + return EFI_INVALID_PARAMETER; + } + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always = set + // to 0) for 32-bit address descriptor table. + // + if ((Data & (BIT0 | BIT1)) !=3D 0) { + DEBUG (( + DEBUG_INFO, + "The buffer [0x%x] to construct DMA desc is not aligned to 4 bytes!\= n", + Data + )); + } + + Entries =3D (DataLen + DWMMC_DMA_BUF_SIZE - 1) / DWMMC_DMA_BUF_SIZE; + TableSize =3D Entries * sizeof (DW_MMC_HC_DMA_DESC_LINE); + Blocks =3D (DataLen + DW_MMC_BLOCK_SIZE - 1) / DW_MMC_BLOCK_SIZE; + + Trb->DmaDescPages =3D (UINT32)EFI_SIZE_TO_PAGES (Entries * DWMMC_DMA_BUF= _SIZE); + Status =3D DevIo->AllocateBuffer ( + DevIo, + AllocateAnyPages, + EfiBootServicesData, + EFI_SIZE_TO_PAGES (TableSize), + (EFI_PHYSICAL_ADDRESS *)&Trb->DmaDesc + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + ZeroMem (Trb->DmaDesc, TableSize); + Bytes =3D TableSize; + Status =3D DevIo->Map ( + DevIo, + EfiBusMasterCommonBuffer, + (EFI_PHYSICAL_ADDRESS *)Trb->DmaDesc, + &Bytes, + &Trb->DmaDescPhy, + &Trb->DmaMap + ); + + if (EFI_ERROR (Status) || (Bytes !=3D TableSize)) { + // + // Map error or unable to map the whole RFis buffer into a contiguous + // region. + // + DevIo->FreeBuffer ( + DevIo, + EFI_SIZE_TO_PAGES (TableSize), + (EFI_PHYSICAL_ADDRESS)Trb->DmaDesc + ); + return EFI_OUT_OF_RESOURCES; + } + + if ((UINT64)(UINTN)Trb->DmaDescPhy > 0x100000000ul) { + // + // The DMA doesn't support 64bit addressing. + // + DevIo->Unmap ( + DevIo, + Trb->DmaMap + ); + return EFI_DEVICE_ERROR; + } + + if (DataLen < DW_MMC_BLOCK_SIZE) { + BlkSize =3D DataLen; + BytCnt =3D DataLen; + Remaining =3D DataLen; + } else { + BlkSize =3D DW_MMC_BLOCK_SIZE; + BytCnt =3D DW_MMC_BLOCK_SIZE * Blocks; + Remaining =3D DW_MMC_BLOCK_SIZE * Blocks; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BLKSIZ, + FALSE, + sizeof (BlkSize), + &BlkSize + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "BuildDmaDescTable: set block size fails: %r\n", + Status + )); + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BYTCNT, + FALSE, + sizeof (BytCnt), + &BytCnt + ); + if (EFI_ERROR (Status)) { + return Status; + } + DmaDesc =3D Trb->DmaDesc; + for (Index =3D 0; Index < Entries; Index++, DmaDesc++) { + DmaDesc->Des0 =3D DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_CH | + DW_MMC_IDMAC_DES0_DIC; + DmaDesc->Des1 =3D DW_MMC_IDMAC_DES1_BS1 (DWMMC_DMA_BUF_SIZE); + // + // Buffer Address + // + DmaDesc->Des2 =3D (UINT32)((UINTN)Trb->DataPhy + + (DWMMC_DMA_BUF_SIZE * Index)); + // + // Next Descriptor Address + // + DmaDesc->Des3 =3D (UINT32)((UINTN)Trb->DmaDescPhy + + sizeof (DW_MMC_HC_DMA_DESC_LINE) * (Index + 1)); + Remaining =3D Remaining - DWMMC_DMA_BUF_SIZE; + } + // + // First Descriptor + // + Trb->DmaDesc[0].Des0 |=3D DW_MMC_IDMAC_DES0_FS; + // + // Last Descriptor + // + Trb->DmaDesc[Entries - 1].Des0 &=3D ~(DW_MMC_IDMAC_DES0_CH | + DW_MMC_IDMAC_DES0_DIC); + Trb->DmaDesc[Entries - 1].Des0 |=3D DW_MMC_IDMAC_DES0_OWN | + DW_MMC_IDMAC_DES0_LD; + Trb->DmaDesc[Entries - 1].Des1 =3D DW_MMC_IDMAC_DES1_BS1 (Remaining + + DWMMC_DMA_BUF_SIZE); + // + // Set the next field of the Last Descriptor + // + Trb->DmaDesc[Entries - 1].Des3 =3D 0; + DmaDescPhy =3D (UINT32)Trb->DmaDescPhy; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_DBADDR, + FALSE, + sizeof (DmaDescPhy), + &DmaDescPhy + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Clear interrupts + // + Idsts =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + FALSE, + sizeof (Idsts), + &Idsts + ); + return Status; +} + +EFI_STATUS +ReadFifo ( + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINT32 Data; + UINT32 Received; + UINT32 Count; + UINT32 Intsts; + UINT32 Sts; + UINT32 FifoCount; + UINT32 Index; /* count with bytes */ + UINT32 Ascending; + UINT32 Descending; + + DevIo =3D Trb->Private->DevIo; + Received =3D 0; + Count =3D 0; + Index =3D 0; + Ascending =3D 0; + Descending =3D ((Trb->DataLen + 3) & ~3) - 4; + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + TRUE, + sizeof (Intsts), + &Intsts + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ReadFifo: failed to read RINTSTS, Status:%r\n", + Status + )); + return Status; + } + if (Trb->DataLen && ((Intsts & DW_MMC_INT_RXDR) || + (Intsts & DW_MMC_INT_DTO))) { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (Sts), + &Sts + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ReadFifo: failed to read STATUS, Status:%r\n", + Status + )); + return Status; + } + // + // Convert to bytes + // + FifoCount =3D GET_STS_FIFO_COUNT (Sts) << 2; + if ((FifoCount =3D=3D 0) && (Received < Trb->DataLen)) { + continue; + } + Index =3D 0; + Count =3D (MIN (FifoCount, Trb->DataLen) + 3) & ~3; + while (Index < Count) { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_FIFO_START, + TRUE, + sizeof (Data), + &Data + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ReadFifo: failed to read FIFO, Status:%r\n", + Status + )); + return Status; + } + if (Trb->UseBE) { + *(UINT32 *)((UINTN)Trb->Data + Descending) =3D SwapBytes32 (Data= ); + Descending =3D Descending - 4; + } else { + *(UINT32 *)((UINTN)Trb->Data + Ascending) =3D Data; + Ascending +=3D 4; + } + Index +=3D 4; + Received +=3D 4; + } /* while */ + } /* if */ + } while (((Intsts & DW_MMC_INT_CMD_DONE) =3D=3D 0) || (Received < Trb->D= ataLen)); + // + // Clear RINTSTS + // + Intsts =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (Intsts), + &Intsts + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "ReadFifo: failed to write RINTSTS, Status:%r\n", + Status + )); + return Status; + } + return EFI_SUCCESS; +} + +/** + Create a new TRB for the SD/MMC cmd request. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Slot The slot number of the SD card to send the com= mand + to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @return Created Trb or NULL. + +**/ +DW_MMC_HC_TRB * +DwMmcCreateTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet, + IN EFI_EVENT Event + ) +{ + DW_MMC_HC_TRB *Trb; + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_IO_OPERATION_TYPE Flag; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINTN MapLength; + + Trb =3D AllocateZeroPool (sizeof (DW_MMC_HC_TRB)); + if (Trb =3D=3D NULL) { + return NULL; + } + + Trb->Signature =3D DW_MMC_HC_TRB_SIG; + Trb->Slot =3D Slot; + Trb->BlockSize =3D 0x200; + Trb->Packet =3D Packet; + Trb->Event =3D Event; + Trb->Started =3D FALSE; + Trb->Timeout =3D Packet->Timeout; + Trb->Private =3D Private; + + if ((Packet->InTransferLength !=3D 0) && (Packet->InDataBuffer !=3D NULL= )) { + Trb->Data =3D Packet->InDataBuffer; + Trb->DataLen =3D Packet->InTransferLength; + Trb->Read =3D TRUE; + ZeroMem (Trb->Data, Trb->DataLen); + } else if (Packet->OutTransferLength && (Packet->OutDataBuffer !=3D NULL= )) { + Trb->Data =3D Packet->OutDataBuffer; + Trb->DataLen =3D Packet->OutTransferLength; + Trb->Read =3D FALSE; + } else if (!Packet->InTransferLength && !Packet->OutTransferLength) { + Trb->Data =3D NULL; + Trb->DataLen =3D 0; + } else { + goto Error; + } + + if (((Private->Slot[Trb->Slot].CardType =3D=3D EmmcCardType) && + (Packet->SdMmcCmdBlk->CommandIndex =3D=3D EMMC_SEND_TUNING_BLOCK)) = || + ((Private->Slot[Trb->Slot].CardType =3D=3D SdCardType) && + (Packet->SdMmcCmdBlk->CommandIndex =3D=3D SD_SEND_TUNING_BLOCK))) { + Trb->Mode =3D SdMmcPioMode; + } else { + if (Trb->Read) { + Flag =3D EfiBusMasterWrite; + } else { + Flag =3D EfiBusMasterRead; + } + + DevIo =3D Private->DevIo; + if (Private->Slot[Trb->Slot].CardType =3D=3D SdCardType) { + Trb->UseFifo =3D TRUE; + } else { + Trb->UseFifo =3D FALSE; + if (Trb->DataLen) { + MapLength =3D Trb->DataLen; + Status =3D DevIo->Map ( + DevIo, + Flag, + Trb->Data, + &MapLength, + &Trb->DataPhy, + &Trb->DataMap + ); + if (EFI_ERROR (Status) || (Trb->DataLen !=3D MapLength)) { + Status =3D EFI_BAD_BUFFER_SIZE; + goto Error; + } + + Status =3D BuildDmaDescTable (Trb); + if (EFI_ERROR (Status)) { + DevIo->Unmap (DevIo, Trb->DataMap); + goto Error; + } + Status =3D DwMmcHcStartDma (Private, Trb); + if (EFI_ERROR (Status)) { + DevIo->Unmap (DevIo, Trb->DataMap); + goto Error; + } + } + } + } /* TuningBlock */ + + if (Event !=3D NULL) { + OldTpl =3D gBS->RaiseTPL (TPL_NOTIFY); + InsertTailList (&Private->Queue, &Trb->TrbList); + gBS->RestoreTPL (OldTpl); + } + + return Trb; + +Error: + return NULL; +} + +/** + Free the resource used by the TRB. + + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + +**/ +VOID +DwMmcFreeTrb ( + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_DEVICE_IO_PROTOCOL *DevIo; + + DevIo =3D Trb->Private->DevIo; + + if (Trb->DmaMap !=3D NULL) { + DevIo->Unmap (DevIo, Trb->DmaMap); + } + if (Trb->DataMap !=3D NULL) { + DevIo->Unmap (DevIo, Trb->DataMap); + } + FreePool (Trb); +} + +/** + Check if the env is ready for execute specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_NOT_READY The env is not ready for TRB execution. + @retval Others Some erros happen. + +**/ +EFI_STATUS +DwMmcCheckTrbEnv ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + return EFI_SUCCESS; +} + +/** + Wait for the env to be ready for execute specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The env is ready for TRB execution. + @retval EFI_TIMEOUT The env is not ready for TRB execution in time. + @retval Others Some erros happen. + +**/ +EFI_STATUS +DwMmcWaitTrbEnv ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status + // Register + // + Packet =3D Trb->Packet; + Timeout =3D Packet->Timeout; + if (Timeout =3D=3D 0) { + InfiniteWait =3D TRUE; + } else { + InfiniteWait =3D FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status regis= ter. + // + Status =3D DwMmcCheckTrbEnv (Private, Trb); + if (Status !=3D EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} + +EFI_STATUS +DwEmmcExecTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINT32 Cmd; + UINT32 MmcStatus; + UINT32 IntStatus; + UINT32 Argument; + UINT32 ErrMask; + UINT32 Timeout; + + Packet =3D Trb->Packet; + DevIo =3D Trb->Private->DevIo; + + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Wait until MMC is idle + // + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + IntStatus =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + Cmd =3D CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); + if ((Packet->SdMmcCmdBlk->CommandType =3D=3D SdMmcCommandTypeAc) || + (Packet->SdMmcCmdBlk->CommandType =3D=3D SdMmcCommandTypeAdtc)) { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case EMMC_SET_RELATIVE_ADDR: + Cmd |=3D BIT_CMD_SEND_INIT; + break; + case EMMC_SEND_STATUS: + Cmd |=3D BIT_CMD_WAIT_PRVDATA_COMPLETE; + break; + case EMMC_STOP_TRANSMISSION: + Cmd |=3D BIT_CMD_STOP_ABORT_CMD; + break; + } + if (Packet->InTransferLength) { + Cmd |=3D BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_READ; + } else if (Packet->OutTransferLength) { + Cmd |=3D BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_WRITE; + } + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; + } else { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case EMMC_GO_IDLE_STATE: + Cmd |=3D BIT_CMD_SEND_INIT; + break; + case EMMC_SEND_OP_COND: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT; + break; + case EMMC_ALL_SEND_CID: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_LONG_RESPONSE | + BIT_CMD_CHECK_RESPONSE_CRC | BIT_CMD_SEND_INIT; + break; + } + } + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR2: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | + BIT_CMD_LONG_RESPONSE; + break; + case SdMmcResponseTypeR3: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT; + break; + } + Cmd |=3D BIT_CMD_USE_HOLD_REG | BIT_CMD_START; + + Argument =3D Packet->SdMmcCmdBlk->CommandArgument; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMDARG, + FALSE, + sizeof (Argument), + &Argument + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMD, + FALSE, + sizeof (Cmd), + &Cmd + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + + ErrMask =3D DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | + DW_MMC_INT_RCRC | DW_MMC_INT_RE; + ErrMask |=3D DW_MMC_INT_DCRC | DW_MMC_INT_DRT | DW_MMC_INT_SBE; + do { + Timeout =3D 10000; + if (--Timeout =3D=3D 0) { + break; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + if (IntStatus & ErrMask) { + return EFI_DEVICE_ERROR; + } + if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) =3D=3D 0)) { + // + // Transfer Not Done + // + MicroSecondDelay (10); + continue; + } + MicroSecondDelay (10); + } while (!(IntStatus & DW_MMC_INT_CMD_DONE)); + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR1: + case SdMmcResponseTypeR1b: + case SdMmcResponseTypeR3: + case SdMmcResponseTypeR4: + case SdMmcResponseTypeR5: + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + case SdMmcResponseTypeR2: + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP1, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp1), + &Packet->SdMmcStatusBlk->Resp1 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP2, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp2), + &Packet->SdMmcStatusBlk->Resp2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP3, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp3), + &Packet->SdMmcStatusBlk->Resp3 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + } + + // + // The workaround on EMMC_SEND_CSD is used to be compatible with SDHC. + // + if (Packet->SdMmcCmdBlk->CommandIndex =3D=3D EMMC_SEND_CSD) { + { + UINT32 Buf[4]; + ZeroMem (Buf, sizeof (Buf)); + CopyMem ( + (UINT8 *)Buf, + (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, + sizeof (Buf) - 1 + ); + CopyMem ( + (UINT8 *)&Packet->SdMmcStatusBlk->Resp0, + (UINT8 *)Buf, + sizeof (Buf) - 1 + ); + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +DwSdExecTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + EFI_DEVICE_IO_PROTOCOL *DevIo; + UINT32 Cmd; + UINT32 MmcStatus; + UINT32 IntStatus; + UINT32 Argument; + UINT32 ErrMask; + UINT32 Timeout; + UINT32 Idsts; + UINT32 BytCnt; + UINT32 BlkSize; + + Packet =3D Trb->Packet; + DevIo =3D Trb->Private->DevIo; + + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Wait until MMC is idle + // + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + IntStatus =3D ~0; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + Cmd =3D CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); + if ((Packet->SdMmcCmdBlk->CommandType =3D=3D SdMmcCommandTypeAc) || + (Packet->SdMmcCmdBlk->CommandType =3D=3D SdMmcCommandTypeAdtc)) { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case SD_SET_RELATIVE_ADDR: + Cmd |=3D BIT_CMD_SEND_INIT; + break; + case SD_STOP_TRANSMISSION: + Cmd |=3D BIT_CMD_STOP_ABORT_CMD; + break; + case SD_SEND_SCR: + Trb->UseBE =3D TRUE; + break; + } + if (Packet->InTransferLength) { + Cmd |=3D BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_READ; + } else if (Packet->OutTransferLength) { + Cmd |=3D BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_WRITE; + } + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | + BIT_CMD_SEND_AUTO_STOP; + } else { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case SD_GO_IDLE_STATE: + Cmd |=3D BIT_CMD_SEND_INIT; + break; + } + } + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR2: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | + BIT_CMD_LONG_RESPONSE; + break; + case SdMmcResponseTypeR3: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT; + break; + case SdMmcResponseTypeR1b: + case SdMmcResponseTypeR4: + case SdMmcResponseTypeR6: + case SdMmcResponseTypeR7: + Cmd |=3D BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; + break; + } + Cmd |=3D BIT_CMD_USE_HOLD_REG | BIT_CMD_START; + + if (Trb->UseFifo =3D=3D TRUE) { + BytCnt =3D Packet->InTransferLength; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BYTCNT, + FALSE, + sizeof (BytCnt), + &BytCnt + ); + if (EFI_ERROR (Status)) { + return Status; + } + if (Packet->InTransferLength > DW_MMC_BLOCK_SIZE) { + BlkSize =3D DW_MMC_BLOCK_SIZE; + } else { + BlkSize =3D Packet->InTransferLength; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_BLKSIZ, + FALSE, + sizeof (BlkSize), + &BlkSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwSdExecTrb: set block size fails: %r\n", Stat= us)); + return Status; + } + } + + Argument =3D Packet->SdMmcCmdBlk->CommandArgument; + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMDARG, + FALSE, + sizeof (Argument), + &Argument + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMD, + FALSE, + sizeof (Cmd), + &Cmd + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + + ErrMask =3D DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | + DW_MMC_INT_RCRC | DW_MMC_INT_RE; + ErrMask |=3D DW_MMC_INT_DRT | DW_MMC_INT_SBE; + if (Packet->InTransferLength || Packet->OutTransferLength) { + ErrMask |=3D DW_MMC_INT_DCRC; + } + if (Trb->UseFifo =3D=3D TRUE) { + Status =3D ReadFifo (Trb); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Timeout =3D 10000; + do { + if (--Timeout =3D=3D 0) { + break; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + if (IntStatus & ErrMask) { + return EFI_DEVICE_ERROR; + } + if (Trb->DataLen && ((IntStatus & DW_MMC_INT_DTO) =3D=3D 0)) { + // + // Transfer not Done + // + MicroSecondDelay (10); + continue; + } + MicroSecondDelay (10); + } while (!(IntStatus & DW_MMC_INT_CMD_DONE)); + if (Packet->InTransferLength) { + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & DW_MMC_IDSTS_RI) =3D=3D 0); + Status =3D DwMmcHcStopDma (Private, Trb); + if (EFI_ERROR (Status)) { + return Status; + } + } else if (Packet->OutTransferLength) { + do { + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & DW_MMC_IDSTS_TI) =3D=3D 0); + Status =3D DwMmcHcStopDma (Private, Trb); + if (EFI_ERROR (Status)) { + return Status; + } + } /* Packet->InTransferLength */ + } /* UseFifo */ + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR1: + case SdMmcResponseTypeR1b: + case SdMmcResponseTypeR3: + case SdMmcResponseTypeR4: + case SdMmcResponseTypeR5: + case SdMmcResponseTypeR6: + case SdMmcResponseTypeR7: + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + case SdMmcResponseTypeR2: + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP1, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp1), + &Packet->SdMmcStatusBlk->Resp1 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP2, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp2), + &Packet->SdMmcStatusBlk->Resp2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP3, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp3), + &Packet->SdMmcStatusBlk->Resp3 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + } + + // + // The workaround on SD_SEND_CSD is used to be compatible with SDHC. + // + if (Packet->SdMmcCmdBlk->CommandIndex =3D=3D SD_SEND_CSD) { + { + UINT32 Buf[4]; + ZeroMem (Buf, sizeof (Buf)); + CopyMem ( + (UINT8 *)Buf, + (UINT8 *)&Packet->SdMmcStatusBlk->Resp0 + 1, + sizeof (Buf) - 1 + ); + CopyMem ( + (UINT8 *)&Packet->SdMmcStatusBlk->Resp0, + (UINT8 *)Buf, + sizeof (Buf) - 1 + ); + } + } + + return EFI_SUCCESS; +} + +/** + Execute the specified TRB. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfull= y. + @retval Others Some erros happen when sending this request to= the + host controller. + +**/ +EFI_STATUS +DwMmcExecTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status =3D EFI_SUCCESS; + UINT32 Slot; + + Slot =3D Trb->Slot; + if (Private->Slot[Slot].CardType =3D=3D EmmcCardType) { + Status =3D DwEmmcExecTrb (Private, Trb); + } else if (Private->Slot[Slot].CardType =3D=3D SdCardType) { + Status =3D DwSdExecTrb (Private, Trb); + } else { + ASSERT (0); + } + return Status; +} + +/** + Check the TRB execution result. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval EFI_NOT_READY The TRB is not completed for execution. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +DwMmcCheckTrbResult ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT32 Idsts; + + Packet =3D Trb->Packet; + if (Trb->UseFifo =3D=3D TRUE) { + return EFI_SUCCESS; + } + if (Packet->InTransferLength) { + do { + Status =3D DwMmcHcRwMmio ( + Private->DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & BIT1) =3D=3D 0); + } else if (Packet->OutTransferLength) { + do { + Status =3D DwMmcHcRwMmio ( + Private->DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & BIT0) =3D=3D 0); + } else { + return EFI_SUCCESS; + } + Idsts =3D ~0; + Status =3D DwMmcHcRwMmio ( + Private->DevIo, + DW_MMC_IDSTS, + FALSE, + sizeof (Idsts), &Idsts); + if (EFI_ERROR (Status)) { + return Status; + } + return EFI_SUCCESS; +} + +/** + Wait for the TRB execution result. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is executed successfully. + @retval Others Some erros happen when executing this request. + +**/ +EFI_STATUS +DwMmcWaitTrbResult ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT64 Timeout; + BOOLEAN InfiniteWait; + + Packet =3D Trb->Packet; + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status + // Register + // + Timeout =3D Packet->Timeout; + if (Timeout =3D=3D 0) { + InfiniteWait =3D TRUE; + } else { + InfiniteWait =3D FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status regis= ter. + // + Status =3D DwMmcCheckTrbResult (Private, Trb); + if (Status !=3D EFI_NOT_READY) { + return Status; + } + // + // Stall for 1 microsecond. + // + gBS->Stall (1); + + Timeout--; + } + + return EFI_TIMEOUT; +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c b/EmbeddedPkg/Driv= ers/DwMmcHcDxe/EmmcDevice.c new file mode 100644 index 000000000000..cf7c7195a569 --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/EmmcDevice.c @@ -0,0 +1,1042 @@ +/** @file + This file provides some helper functions which are specific for EMMC dev= ice. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#include + +#include +#include +#include + +#include "DwMmcHcDxe.h" + +#define EMMC_GET_STATE(x) (((x) >> 9) & 0xf) +#define EMMC_STATE_IDLE 0 +#define EMMC_STATE_READY 1 +#define EMMC_STATE_IDENT 2 +#define EMMC_STATE_STBY 3 +#define EMMC_STATE_TRAN 4 +#define EMMC_STATE_DATA 5 +#define EMMC_STATE_RCV 6 +#define EMMC_STATE_PRG 7 +#define EMMC_STATE_DIS 8 +#define EMMC_STATE_BTST 9 +#define EMMC_STATE_SLP 10 + +#define EMMC_CMD1_CAPACITY_LESS_THAN_2GB 0x00FF8080 // Capacity <=3D 2G= B, byte addressing used +#define EMMC_CMD1_CAPACITY_GREATER_THAN_2GB 0x40FF8080 // Capacity > 2GB, = 512-byte sector addressing used + +/** + Send command GO_IDLE_STATE (CMD0 with argument of 0x00000000) to the dev= ice to + make it go to Idle State. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot The slot number of the SD card to send the com= mand + to. + + @retval EFI_SUCCESS The EMMC device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +EmmcReset ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBc; + SdMmcCmdBlk.ResponseType =3D 0; + SdMmcCmdBlk.CommandArgument =3D 0; + + gBS->Stall (1000); + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_OP_COND to the EMMC device to get the data of the OCR + register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in, out] Argument On input, the argument of SEND_OP_COND is to s= end + to the device. + On output, the argument is the value of OCR + register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetOcr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN OUT UINT32 *Argument + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SEND_OP_COND; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR3; + SdMmcCmdBlk.CommandArgument =3D *Argument; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table = 2-12. + // + *Argument =3D SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to= send + the data of their CID registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetAllCid ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_ALL_SEND_CID; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument =3D 0; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative d= evice + Address (RCA). + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSetRca ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_CSD to the EMMC device to get the data of the CSD regi= ster. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD reg= ister. + Note the caller should ignore the lowest byte = of + this buffer as the content of this byte is + meaningless even if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT EMMC_CSD *Csd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SEND_CSD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // Copy 128bit data for CSD structure. + // + CopyMem ((VOID *)Csd + 1, &SdMmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1= ); + } + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the EMMC device to select/deselect = it. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSelect ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_EXT_CSD to the EMMC device to get the data of the EXT_= CSD + register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[out] ExtCsd The buffer to store the content of the EXT_CSD + register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetExtCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + OUT EMMC_EXT_CSD *ExtCsd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SEND_EXT_CSD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D 0x00000000; + + Packet.InDataBuffer =3D ExtCsd; + Packet.InTransferLength =3D sizeof (EMMC_EXT_CSD); + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + return Status; +} + +/** + Send command SWITCH to the EMMC device to switch the mode of operation o= f the + selected Device or modifies the EXT_CSD registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Access The access mode of SWTICH command. + @param[in] Index The offset of the field to be access. + @param[in] Value The value to be set to the specified field of + EXT_CSD register. + @param[in] CmdSet The value of CmdSet field of EXT_CSD register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 Access, + IN UINT8 Index, + IN UINT8 Value, + IN UINT8 CmdSet + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SWITCH; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1b; + SdMmcCmdBlk.CommandArgument =3D (Access << 24) | (Index << 16) | \ + (Value << 8) | CmdSet; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed EMMC device to get its status + register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of addressed devic= e. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SEND_STATUS; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus =3D SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal samp= ling + point detection. + + It may be sent up to 40 times until the host finishes the tuning procedu= re. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSendTuningBlk ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 BusWidth + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[128]; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D EMMC_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D 0; + + Packet.InDataBuffer =3D TuningBlock; + if (BusWidth =3D=3D 8) { + Packet.InTransferLength =3D sizeof (TuningBlock); + } else { + Packet.InTransferLength =3D 64; + } + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Tunning the clock to get HS200 optimal sampling point. + + Command SEND_TUNING_BLOCK may be sent up to 40 times until the host fini= shes + the tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] BusWidth The bus width to work. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcTuningClkForHs200 ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 BusWidth + ) +{ + return EFI_SUCCESS; +} + +/** + Switch the bus width to specified width. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] IsDdr If TRUE, use dual data rate data simpling meth= od. + Otherwise use single data rate data simpling m= ethod. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchBusWidth ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access =3D 0x03; + Index =3D OFFSET_OF (EMMC_EXT_CSD, BusWidth); + if (BusWidth =3D=3D 1) { + Value =3D 0; + } else { + if (BusWidth =3D=3D 4) { + Value =3D 1; + } else if (BusWidth =3D=3D 8) { + Value =3D 2; + } else { + return EFI_INVALID_PARAMETER; + } + + if (IsDdr) { + Value +=3D 4; + } + } + + CmdSet =3D 0; + Status =3D EmmcSwitch (PassThru, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "EmmcSwitchBusWidth: Switch to bus width %d fails with %r\n", + BusWidth, + Status + )); + return Status; + } + + do { + Status =3D EmmcSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "EmmcSwitchBusWidth: Send status fails with %r\n", + Status + )); + return Status; + } + // + // Check the switch operation is really successful or not. + // + } while ((DevStatus & 0xf) =3D=3D EMMC_STATE_PRG); + + Status =3D DwMmcHcSetBusWidth (DevIo, IsDdr, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +/** + Switch the clock frequency to the specified value. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] HsTiming The value to be written to HS_TIMING field of + EXT_CSD register. + @param[in] ClockFreq The max clock frequency to be set, the unit is= MHz. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchClockFreq ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT8 HsTiming, + IN UINT32 ClockFreq + ) +{ + EFI_STATUS Status; + UINT8 Access; + UINT8 Index; + UINT8 Value; + UINT8 CmdSet; + UINT32 DevStatus; + DW_MMC_HC_PRIVATE_DATA *Private; + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access =3D 0x03; + Index =3D OFFSET_OF (EMMC_EXT_CSD, HsTiming); + Value =3D HsTiming; + CmdSet =3D 0; + + Status =3D EmmcSwitch (PassThru, Access, Index, Value, CmdSet); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "EmmcSwitchClockFreq: Switch to hstiming %d fails with %r\n", + HsTiming, + Status + )); + return Status; + } + + Status =3D EmmcSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "EmmcSwitchClockFreq: Send status fails with %r\n", + Status + )); + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus & BIT7) !=3D 0) { + DEBUG (( + DEBUG_ERROR, + "EmmcSwitchClockFreq: The switch operation fails as DevStatus 0x%08x= \n", + DevStatus + )); + return EFI_DEVICE_ERROR; + } + // + // Convert the clock freq unit from MHz to KHz. + // + Status =3D DwMmcHcClockSupply (DevIo, ClockFreq * 1000, Private->Capabil= ity[0]); + + return Status; +} + +/** + Switch to the High Speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] IsDdr If TRUE, use dual data rate data simpling meth= od. + Otherwise use single data rate data simpling m= ethod. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHighSpeed ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT32 ClockFreq, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT8 HsTiming; + + HsTiming =3D 1; + Status =3D EmmcSwitchClockFreq (DevIo, PassThru, Rca, HsTiming, ClockFre= q); + if (EFI_ERROR (Status)) { + return Status; + } + Status =3D EmmcSwitchBusWidth (DevIo, PassThru, Rca, IsDdr, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + return EFI_SUCCESS; +} + +/** + Switch to the HS200 timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] ClockFreq The max clock frequency to be set. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHS200 ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT32 ClockFreq, + IN UINT8 BusWidth + ) +{ + return EFI_SUCCESS; +} + +/** + Switch the high speed timing according to request. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8. + + @param[in] DevIo A pointer to the EFI_DEVICE_IO_PROTOCOL instan= ce. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSetBusMode ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca + ) +{ + EFI_STATUS Status; + EMMC_CSD Csd; + EMMC_EXT_CSD ExtCsd; + UINT8 HsTiming; + BOOLEAN IsDdr; + UINT32 DevStatus; + UINT32 ClockFreq; + UINT8 BusWidth; + DW_MMC_HC_PRIVATE_DATA *Private; + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + ASSERT (Private->Capability[0].BaseClkFreq !=3D 0); + + Status =3D EmmcGetCsd (PassThru, Rca, &Csd); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetCsd fails with %r\n", Status)= ); + return Status; + } + + Status =3D EmmcSelect (PassThru, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: Select fails with %r\n", Status)= ); + return Status; + } + + do { + Status =3D EmmcSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "EmmcSetBusMode: Get Status fails with %r\n", + Status + )); + return Status; + } + } while (EMMC_GET_STATE (DevStatus) !=3D EMMC_STATE_TRAN); + + BusWidth =3D 1; + Status =3D EmmcSwitchBusWidth (DevIo, PassThru, Rca, FALSE, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + BusWidth =3D Private->Capability[0].BusWidth; + // + // Get Deivce_Type from EXT_CSD register. + // + Status =3D EmmcGetExtCsd (PassThru, &ExtCsd); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetExtCsd fails with %r\n", Stat= us)); + return Status; + } + + // + // Calculate supported bus speed/bus width/clock frequency. + // + HsTiming =3D 0; + IsDdr =3D FALSE; + ClockFreq =3D 0; + if (((ExtCsd.DeviceType & (BIT4 | BIT5)) !=3D 0) && + (Private->Capability[0].Sdr104 !=3D 0)) { + HsTiming =3D 2; + IsDdr =3D FALSE; + ClockFreq =3D 200; + } else if (((ExtCsd.DeviceType & (BIT2 | BIT3)) !=3D 0) && + (Private->Capability[0].Ddr50 !=3D 0)) { + HsTiming =3D 1; + IsDdr =3D TRUE; + ClockFreq =3D 52; + } else if (((ExtCsd.DeviceType & BIT1) !=3D 0) && + (Private->Capability[0].HighSpeed !=3D 0)) { + HsTiming =3D 1; + IsDdr =3D FALSE; + ClockFreq =3D 52; + } else if (((ExtCsd.DeviceType & BIT0) !=3D 0) && + (Private->Capability[0].HighSpeed !=3D 0)) { + HsTiming =3D 1; + IsDdr =3D FALSE; + ClockFreq =3D 26; + } + + if ((ClockFreq =3D=3D 0) || (HsTiming =3D=3D 0)) { + // + // Continue using default setting. + // + return EFI_SUCCESS; + } + + DEBUG (( + DEBUG_INFO, + "EmmcSetBusMode: HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n", + HsTiming, + ClockFreq, + BusWidth, + IsDdr ? "TRUE" : "FALSE" + )); + + if (HsTiming =3D=3D 2) { + // + // Execute HS200 timing switch procedure + // + Status =3D EmmcSwitchToHS200 (DevIo, PassThru, Rca, ClockFreq, BusWidt= h); + } else { + // + // Execute High Speed timing switch procedure + // + Status =3D EmmcSwitchToHighSpeed ( + DevIo, + PassThru, + Rca, + ClockFreq, + IsDdr, + BusWidth + ); + } + + DEBUG (( + DEBUG_INFO, + "EmmcSetBusMode: Switch to %a %r\n", + (HsTiming =3D=3D 3) ? "HS400" : ((HsTiming =3D=3D 2) ? "HS200" : "High= Speed"), + Status + )); + + return Status; +} + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instan= ce. + + @retval EFI_SUCCESS There is a EMMC card. + @retval Others There is not a EMMC card. + +**/ +EFI_STATUS +EmmcIdentification ( + IN DW_MMC_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DEVICE_IO_PROTOCOL *DevIo; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT32 Ocr; + UINT16 Rca; + UINT32 DevStatus; + UINT32 Timeout; + + DevIo =3D Private->DevIo; + PassThru =3D &Private->PassThru; + + Status =3D EmmcReset (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd0 fails with %r\n", + Status + )); + return Status; + } + + Timeout =3D 100; + do { + Ocr =3D EMMC_CMD1_CAPACITY_GREATER_THAN_2GB; + Status =3D EmmcGetOcr (PassThru, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd1 fails with %r\n", + Status + )); + return Status; + } + if (--Timeout <=3D 0) { + return EFI_DEVICE_ERROR; + } + MicroSecondDelay (100); + } while ((Ocr & BIT31) =3D=3D 0); + + Status =3D EmmcGetAllCid (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd2 fails with %r\n", + Status + )); + return Status; + } + // + // valid RCA starts from 1. + // Here we takes a simple formula to calculate the RCA. + // Don't support multiple devices on the slot, that is + // shared bus slot feature. + // + Rca =3D 1; + Status =3D EmmcSetRca (PassThru, Rca); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd3 fails with %r\n", + Status + )); + return Status; + } + // + // Enter Data Tranfer Mode. + // + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Found a EMMC device at RCA [%d]\n", + Rca + )); + Private->Slot[0].CardType =3D EmmcCardType; + + Status =3D EmmcSetBusMode (DevIo, PassThru, Rca); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Exit DATA Mode. + // + do { + Status =3D EmmcSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcSwitchBusWidth: Send status fails with %r\n", + Status + )); + return Status; + } + } while ((DevStatus & 0xf) =3D=3D EMMC_STATE_DATA); + + return Status; +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c b/EmbeddedPkg/Driver= s/DwMmcHcDxe/SdDevice.c new file mode 100644 index 000000000000..58900b03417f --- /dev/null +++ b/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c @@ -0,0 +1,1104 @@ +/** @file + This file provides some helper functions which are specific for SD card + device. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ Copyright (c) 2018, Linaro. All rights reserved.
+ + This program and the accompanying materials are licensed and made availa= ble + 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 IMP= LIED. + +**/ + +#include + +#include +#include + +#include "DwMmcHcDxe.h" + +/** + Send command GO_IDLE_STATE to the device to make it go to Idle State. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @retval EFI_SUCCESS The SD device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +SdCardReset ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBc; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_IF_COND to the device to inquiry the SD Memory Card + interface condition. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] SupplyVoltage The supplied voltage by the host. + @param[in] CheckPattern The check pattern to be sent to the device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardVoltageCheck ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 SupplyVoltage, + IN UINT8 CheckPattern + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_IF_COND; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR7; + SdMmcCmdBlk.CommandArgument =3D (SupplyVoltage << 8) | CheckPattern; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + if (!EFI_ERROR (Status)) { + if (SdMmcStatusBlk.Resp0 !=3D SdMmcCmdBlk.CommandArgument) { + return EFI_DEVICE_ERROR; + } + } + + return Status; +} + +/** + Send command SDIO_SEND_OP_COND to the device to see whether it is SDIO d= evice. + + Refer to SDIO Simplified Spec 3 Section 3.2 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.8= v. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdioSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SDIO_SEND_OP_COND; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR4; + + Switch =3D S18R ? BIT24 : 0; + + SdMmcCmdBlk.CommandArgument =3D (VoltageWindow & 0xFFFFFF) | Switch; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SD_SEND_OP_COND to the device to see whether it is SDIO dev= ice. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of addressed devi= ce. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.= 8v. + @param[in] Xpc The boolean to show if it should provide 0.36w + power control. + @param[in] Hcs The boolean to show if it support host capaci= ty + info. + @param[out] Ocr The buffer to store returned OCR register val= ue. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R, + IN BOOLEAN Xpc, + IN BOOLEAN Hcs, + OUT UINT32 *Ocr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + UINT32 MaxPower; + UINT32 HostCapacity; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_APP_CMD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_OP_COND; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR3; + + Switch =3D S18R ? BIT24 : 0; + MaxPower =3D Xpc ? BIT28 : 0; + HostCapacity =3D Hcs ? BIT30 : 0; + + SdMmcCmdBlk.CommandArgument =3D (VoltageWindow & 0xFFFFFF) | Switch | \ + MaxPower | HostCapacity; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *Ocr =3D SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the SD devices to s= end + the data of their CID registers. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardAllSendCid ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_ALL_SEND_CID; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR2; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the SD device to assign a Relative dev= ice + Address (RCA). + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[out] Rca The relative device address to assign. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetRca ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + OUT UINT16 *Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR6; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *Rca =3D (UINT16)(SdMmcStatusBlk.Resp0 >> 16); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD regist= er. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD reg= ister. + Note the caller should ignore the lowest byte = of + this buffer as the content of this byte is mea= ning- + less even if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardGetCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT SD_CSD *Csd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_CSD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + CopyMem (((UINT8*)Csd) + 1, &SdMmcStatusBlk.Resp0, sizeof (SD_CSD) - 1= ); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD regist= er. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + @param[out] Scr The buffer to store the content of the SCR reg= ister. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardGetScr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT SD_SCR *Scr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_APP_CMD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_SCR; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + + Packet.InDataBuffer =3D Scr; + Packet.InTransferLength =3D sizeof (SD_SCR); + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SELECT_DESELECT_CARD to the SD device to select/deselect it. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSelect ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + if (Rca !=3D 0) { + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1b; + } + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command VOLTAGE_SWITCH to the SD device to switch the voltage of the + device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardVoltageSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_VOLTAGE_SWITCH; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D 0; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SET_BUS_WIDTH to the SD device to set the bus width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of addressed devic= e. + @param[in] BusWidth The bus width to be set, it could be 1 or 4. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetBusWidth ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 Value; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_APP_CMD; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex =3D SD_SET_BUS_WIDTH; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + + if (BusWidth =3D=3D 1) { + Value =3D 0; + } else if (BusWidth =3D=3D 4) { + Value =3D 2; + } else { + return EFI_INVALID_PARAMETER; + } + + SdMmcCmdBlk.CommandArgument =3D Value & 0x3; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + return Status; +} + +/** + Send command SWITCH_FUNC to the SD device to check switchable function or + switch card function. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] AccessMode The value for access mode group. + @param[in] CommandSystem The value for command set group. + @param[in] DriveStrength The value for drive length group. + @param[in] PowerLimit The value for power limit group. + @param[in] Mode Switch or check function. + @param[out] SwitchResp The return switch function status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSwitch ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT8 AccessMode, + IN UINT8 CommandSystem, + IN UINT8 DriveStrength, + IN UINT8 PowerLimit, + IN BOOLEAN Mode, + OUT UINT8 *SwitchResp + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 ModeValue; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SWITCH_FUNC; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + + ModeValue =3D Mode ? BIT31 : 0; + SdMmcCmdBlk.CommandArgument =3D (AccessMode & 0xF) | \ + ((PowerLimit & 0xF) << 4) | \ + ((DriveStrength & 0xF) << 8) | \ + ((DriveStrength & 0xF) << 12) | \ + ModeValue; + + Packet.InDataBuffer =3D SwitchResp; + Packet.InTransferLength =3D 64; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_STATUS to the addressed SD device to get its status + register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of addressed devic= e. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_STATUS; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D (UINT32)Rca << 16; + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus =3D SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the SD device for HS200 optimal sampli= ng + point detection. + + It may be sent up to 40 times until the host finishes the tuning procedu= re. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendTuningBlk ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 TuningBlock[64]; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk =3D &SdMmcCmdBlk; + Packet.SdMmcStatusBlk =3D &SdMmcStatusBlk; + Packet.Timeout =3D DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex =3D SD_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType =3D SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType =3D SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument =3D 0; + + Packet.InDataBuffer =3D TuningBlock; + Packet.InTransferLength =3D sizeof (TuningBlock); + + Status =3D DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Switch the bus width to specified width. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 and + SD Host Controller Simplified Spec 3.0 section Figure 3-7 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSwitchBusWidth ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN BOOLEAN IsDdr, + IN UINT8 BusWidth + ) +{ + EFI_STATUS Status; + UINT32 DevStatus; + + Status =3D SdCardSetBusWidth (PassThru, Rca, BusWidth); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardSwitchBusWidth: Switch to bus width %d fails with %r\n", + BusWidth, + Status + )); + return Status; + } + + Status =3D SdCardSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardSwitchBusWidth: Send status fails with %r\n", + Status + )); + return Status; + } + // + // Check the switch operation is really successful or not. + // + if ((DevStatus >> 16) !=3D 0) { + DEBUG (( + DEBUG_ERROR, + "SdCardSwitchBusWidth: The switch operation fails as DevStatus 0x%08= x\n", + DevStatus + )); + return EFI_DEVICE_ERROR; + } + + Status =3D DwMmcHcSetBusWidth (DevIo, IsDdr, BusWidth); + + return Status; +} + +/** + Switch the high speed timing according to request. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PciIo A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address to be assigned. + @param[in] S18A The boolean to show if it's a UHS-I SD card. + @param[in] BusWidths The bus width of the SD card. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetBusMode ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN BOOLEAN S18A, + IN UINT32 BusWidths + ) +{ + EFI_STATUS Status; + DW_MMC_HC_SLOT_CAP *Capability; + UINT32 ClockFreq; + UINT8 AccessMode; + UINT8 SwitchResp[64]; + DW_MMC_HC_PRIVATE_DATA *Private; + BOOLEAN IsDdr; + + Private =3D DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + + Capability =3D &Private->Capability[0]; + + if ((Capability->BusWidth =3D=3D 1) || (Capability->BusWidth =3D=3D 4)) { + BusWidths &=3D Capability[0].BusWidth; + } else { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: BusWidths (%d) in capability are wrong\n", + Capability->BusWidth + )); + return EFI_INVALID_PARAMETER; + } + + if (BusWidths =3D=3D 0) { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: Get wrong BusWidths:%d\n", + BusWidths + )); + return EFI_INVALID_PARAMETER; + } + + if (Private->Capability[0].Ddr50) { + IsDdr =3D TRUE; + } else { + IsDdr =3D FALSE; + } + + Status =3D SdCardSwitchBusWidth (DevIo, PassThru, Rca, IsDdr, BusWidths); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: Executing SdCardSwitchBusWidth fails with %r\n", + Status + )); + return Status; + } + + // + // Get the supported bus speed from SWITCH cmd return data group #1. + // + Status =3D SdCardSwitch (PassThru, 0xF, 0xF, 0xF, 0xF, FALSE, SwitchResp= ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Calculate supported bus speed/bus width/clock frequency by host and d= evice + // capability. + // + ClockFreq =3D 0; + if (S18A && (Capability->Sdr104 !=3D 0) && ((SwitchResp[13] & BIT3) !=3D= 0)) { + ClockFreq =3D 208; + AccessMode =3D 3; + } else if (S18A && (Capability->Sdr50 !=3D 0) && + ((SwitchResp[13] & BIT2) !=3D 0)) { + ClockFreq =3D 100; + AccessMode =3D 2; + } else if (S18A && (Capability->Ddr50 !=3D 0) && + ((SwitchResp[13] & BIT4) !=3D 0)) { + ClockFreq =3D 50; + AccessMode =3D 4; + } else if ((SwitchResp[13] & BIT1) !=3D 0) { + ClockFreq =3D 50; + AccessMode =3D 1; + } else { + ClockFreq =3D 25; + AccessMode =3D 0; + } + + Status =3D SdCardSwitch (PassThru, AccessMode, 0xF, 0xF, 0xF, TRUE, Swit= chResp); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((SwitchResp[16] & 0xF) !=3D AccessMode) { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: Switch to AccessMode %d ClockFreq %d fails! The S= witch response is 0x%1x\n", + AccessMode, + ClockFreq, + SwitchResp[16] & 0xF + )); + return EFI_DEVICE_ERROR; + } + + DEBUG (( + DEBUG_INFO, + "SdCardSetBusMode: Switch to AccessMode %d ClockFreq %d \n", + AccessMode, + ClockFreq + )); + + Status =3D DwMmcHcClockSupply (DevIo, ClockFreq * 1000, *Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + return Status; +} + +EFI_STATUS +SdCardIdentification ( + IN DW_MMC_HC_PRIVATE_DATA *Private + ) +{ + EFI_STATUS Status; + EFI_DEVICE_IO_PROTOCOL *DevIo; + EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru; + UINT32 Ocr; + UINT16 Rca; + BOOLEAN Xpc; + BOOLEAN S18r; + UINT64 MaxCurrent; + SD_SCR Scr; + SD_CSD Csd; + + DevIo =3D Private->DevIo; + PassThru =3D &Private->PassThru; + // + // 1. Send Cmd0 to the device + // + Status =3D SdCardReset (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "SdCardIdentification: Executing Cmd0 fails with %r\n", + Status + )); + return Status; + } + MicroSecondDelay (10000); + // + // 2. Send Cmd8 to the device + // + Status =3D SdCardVoltageCheck (PassThru, 0x1, 0xFF); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "SdCardIdentification: Executing Cmd8 fails with %r\n", + Status + )); + return Status; + } + // + // 3. Send Acmd41 with voltage window 0 to the device + // + Status =3D SdCardSendOpCond (PassThru, 0, 0, FALSE, FALSE, FALSE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "SdCardIdentification: Executing SdCardSendOpCond fails with %r\n", + Status + )); + return EFI_DEVICE_ERROR; + } + + if (Private->Capability[0].Voltage33 !=3D 0) { + // + // Support 3.3V + // + MaxCurrent =3D ((UINT32)Private->MaxCurrent[0] & 0xFF) * 4; + S18r =3D FALSE; + } else if (Private->Capability[0].Voltage30 !=3D 0) { + // + // Support 3.0V + // + MaxCurrent =3D (((UINT32)Private->MaxCurrent[0] >> 8) & 0xFF) * 4; + S18r =3D FALSE; + } else if (Private->Capability[0].Voltage18 !=3D 0) { + // + // Support 1.8V + // + MaxCurrent =3D (((UINT32)Private->MaxCurrent[0] >> 16) & 0xFF) * 4; + S18r =3D TRUE; + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + if (MaxCurrent >=3D 150) { + Xpc =3D TRUE; + } else { + Xpc =3D FALSE; + } + + // + // 4. Repeatly send Acmd41 with supply voltage window to the device. + // Note here we only support the cards complied with SD physical + // layer simplified spec version 2.0 and version 3.0 and above. + // + do { + Status =3D SdCardSendOpCond (PassThru, 0, Ocr, S18r, Xpc, TRUE, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: SdCardSendOpCond fails with %r Ocr %x, S18r= %x, Xpc %x\n", + Status, + Ocr, + S18r, + Xpc + )); + return EFI_DEVICE_ERROR; + } + } while ((Ocr & BIT31) =3D=3D 0); + + Status =3D SdCardAllSendCid (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardAllSendCid fails with %r\n", + Status + )); + return Status; + } + + Status =3D SdCardSetRca (PassThru, &Rca); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardSetRca fails with %r\n", + Status + )); + return Status; + } + + Status =3D SdCardGetCsd (PassThru, Rca, &Csd); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardGetCsd fails with %r\n", + Status + )); + return Status; + } + + Status =3D SdCardSelect (PassThru, Rca); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Selecting card fails with %r\n", + Status + )); + return Status; + } + + Status =3D SdCardGetScr (PassThru, Rca, &Scr); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardGetScr fails with %r\n", + Status + )); + return Status; + } + + // + // Enter Data Tranfer Mode. + // + DEBUG ((DEBUG_INFO, "SdCardIdentification: Found a SD device\n")); + Private->Slot[0].CardType =3D SdCardType; + + Status =3D SdCardSetBusMode (DevIo, PassThru, Rca, S18r, Scr.SdBusWidths= ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->Slot[0].Initialized =3D TRUE; + + return Status; +} --=20 2.7.4 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel