From patchwork Fri Aug 10 08:49:15 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 143893 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp3088229ljj; Fri, 10 Aug 2018 01:49:34 -0700 (PDT) X-Google-Smtp-Source: AA+uWPzKuwVJS7zsDMF6fGiUWe1DXKcYxFVokxTA0HNQ0kHXfIjNC/rAoDHuCHHPzTf6IQCJmHCr X-Received: by 2002:ac8:5144:: with SMTP id h4-v6mr5481542qtn.290.1533890974334; Fri, 10 Aug 2018 01:49:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533890974; cv=none; d=google.com; s=arc-20160816; b=PYwurV01KerkBaNBxci04dY4VLc6lbUESF8AGGHJeiU2um/NreUD9+cy1ti7bsruiq 0h2gSh4Cs66jQK3dMsmgSxknxCLCVaAfwh0q6w6O76XqxcKojjop9LQ2Z8QiaAAOciDB 36cFxszw23pscVGcuwbcvJVmYQHnmC0nqajHSJ/BFhkGn6NY0/o5LzmMgg3oLjhCSBIO uLR1fpIuzNQgeBbj3NFXsewvoTOfHlTH9k4gxPikqwR74lqepAzOYQ0XE/zk7i7t7t0j 7c3fnCJ9HZdDTFgMIrotNm61JRUNtv4cnjuq0Yz5qX/Q0VVsYO6XqM6N7WqcQExaiONQ qq1Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=m9i5mxfd/yqgh9PXtHUeqh1sjqbq+jocovOK7fy1Ayo=; b=OkBKUQ+qsYZBr8W5I2CT07hQZ+6OCyZCGPoh31/bF/xdWw506/8vXLzeds9ISqpZZ4 s0x6o/j6KD8e2mu1+YZ/RB2TOueQTKFPHtCAIErU8LvVQbHfX8acE5yzAz8V0PIuqHFB pHbQr3Mpckmxmi+thL0ZVQsQMHTDtOxGa3VDyPOLItd7xQAuK46k+5tIP0UrPpeAWdtT ntlV+0fWwP9AWNFO2u77402xDQgcMwDsvfUXD2FKwK+Jnb+yKB1IAVpoAVoDgH/wriLJ laLlLToCEvGxZX8YBpuyay0b1sNS93k4g/kZo+OOOJQUsQJc5bIhSvXmXUwq3gS5KNpi cYtg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=Q5i7vDmg; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 198.145.21.10 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from ml01.01.org (ml01.01.org. [198.145.21.10]) by mx.google.com with ESMTPS id r4-v6si315347qkc.173.2018.08.10.01.49.33 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 10 Aug 2018 01:49:34 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 198.145.21.10 as permitted sender) client-ip=198.145.21.10; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=Q5i7vDmg; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 198.145.21.10 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org 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) X-Original-To: edk2-devel@lists.01.org Delivered-To: edk2-devel@lists.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 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) 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-Received: by 2002:a17:902:246a:: with SMTP id m39-v6mr5270150plg.57.1533890969226; Fri, 10 Aug 2018 01:49:29 -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) 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 Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" 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(+) -- 2.7.4 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.dec b/EmbeddedPkg/Drivers/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 Industry 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 available under +# the terms and conditions of the BSD License which accompanies this distribution. +# The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + DEC_SPECIFICATION = 0x00010019 + PACKAGE_NAME = DwMmcHcDxePkg + PACKAGE_GUID = e73097ce-1fe2-41a6-a930-3136bc6d23ef + PACKAGE_VERSION = 0.1 + + +################################################################################ +# +# Include Section - list of Include Paths that are provided by this package. +# 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_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION +# +################################################################################ + +[Guids.common] + gDwMmcHcDxeTokenSpaceGuid = { 0x576c132e, 0x7d51, 0x4abb, { 0xbc, 0x60, 0x13, 0x08, 0x04, 0x0e, 0x90, 0x92 }} + +[Protocols.common] + gPlatformDwMmcProtocolGuid = { 0x1d6dfde5, 0x76a7, 0x4404, { 0x85, 0x74, 0x7a, 0xdf, 0x1a, 0x8a, 0xa2, 0x0d }} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.inf b/EmbeddedPkg/Drivers/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 comply with +# Designware SD Host Controller. +# +# It will produce EFI_SD_MMC_PASS_THRU_PROTOCOL to allow sending SD/MMC/eMMC cmds +# to specified devices from upper layer. +# +# Copyright (c) 2015, Intel Corporation. All rights reserved.
+# 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 BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = DwMmcHcDxe + MODULE_UNI_FILE = DwMmcHcDxe.uni + FILE_GUID = 9be4d260-208c-4efe-a524-0b5d3bf77f9d + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = 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/Drivers/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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _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 instance. + + @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 returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is + returned. + + If Slot is not in a valid range for the SD controller, then + EFI_INVALID_PARAMETER is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are + NULL, EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot The slot number of the SD card to send the + command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SD command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is + invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both + InDataBuffer and OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet + is not supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength + exceeds the limit supported by SD card ( i.e. if + the number of bytes exceed the Last LBA). + +**/ +EFI_STATUS +EFIAPI +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 controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD + controller is returned. + + If Slot is a slot number that was returned on a previous call to + GetNextSlot(), then the slot number of the next slot on the SD controller is + returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to + GetNextSlot(), EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL + instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD + controller. + On output, a pointer to the next slot number on + the SD controller. + An input value of 0xFF retrieves the first slot + number on the SD controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was + returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a + previous call to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +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, then + EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then + EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), the + contents of DevicePath are initialized to describe the SD card specified by + Slot, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card for + which a device path node is to be allocated and + built. + @param[in,out] DevicePath A pointer to a single device path node that + describes the SD card specified by Slot. This + function is responsible for allocating the + buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsibi- + 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 returned in + DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on + the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +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 specified + by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is + returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver + supports, EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] DevicePath A pointer to the device path node that describes + a SD card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD + card on the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that + the SD Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +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_PROTOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card to be + reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device + reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +EFI_STATUS +EFIAPI +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 device is + provided, it further tests to see if this driver supports creating a handle + for the specified child device. + + This function checks to see if the driver specified by This supports the + device specified by ControllerHandle. Drivers will typically use the device + path attached to ControllerHandle and/or the services from the bus I/O + abstraction attached to ControllerHandle to determine if the driver supports + ControllerHandle. This function may be called many times during platform + initialization. In order to reduce boot times, the tests performed by this + function must be very small, and take as little time as possible to execute. + This function must not change the state of any hardware devices, and this + function must be aware that the device specified by ControllerHandle may + already be managed by the same driver or a different driver. This function + must match its calls to AllocatePages() with FreePages(), AllocatePool() with + FreePool(), and OpenProtocol() with CloseProtocol(). + Since ControllerHandle may have been previously started by the same driver, if + a protocol is already in the opened state, then it must not be closed with + CloseProtocol(). This is required to guarantee the state of ControllerHandle + is not modified by this function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to test. This + handle must support a protocol interface that + supplies an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus + drivers. For bus drivers, if this parameter + is not NULL, then the bus driver must deter- + mine if the bus controller specified by + ControllerHandle and the child controller + specified by RemainingDevicePath are both + supported by this bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the + driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by the driver specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by a different driver or an application that + requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the + driver specified by This. +**/ +EFI_STATUS +EFIAPI +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 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 natural- + ly aligned EFI_DEVICE_PATH_PROTOCOL. + 3. Prior to calling Start(), the Supported() function for the driver specified + by This must have been called with the same calling parameters, and + Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to start. This + handle must support a protocol interface that + supplies an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus dri- + vers. For a bus driver, if this parameter is + NULL, then handles for all the children of + Controller are created by this driver. + If this parameter is not NULL and the first + Device Path Node is not the End of Device + Path Node, then only the handle for the + child device specified by the first Device + Path Node of RemainingDevicePath is created + by this driver. + If the first Device Path Node of + RemainingDevicePath is the End of Device Path + Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a + device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +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 other + locations, but the following calling restrictions must be followed or the + system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous + call to this same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in + this driver's Start() function, and the Start() function must have called + OpenProtocol() on ControllerHandle with an Attribute of + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle + must support a bus specific I/O protocol for the + driver to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in + ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be + NULL if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device + error. + +**/ +EFI_STATUS +EFIAPI +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 of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +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 controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +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 instance. + @param[in] Slot The slot number of the SD card to send the command + to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. + If Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @return Created Trb or NULL. + +**/ +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 instance. + @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 instance. + @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 instance. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the + host controller. + +**/ +EFI_STATUS +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 instance. + @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 instance. + @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 instance. + + @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 instance. + + @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/Drivers/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 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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _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-bit Err */ +#define DW_MMC_INT_HLE (1 << 12) /* Hardware-lock Err */ +#define DW_MMC_INT_FRUN (1 << 11) /* FIFO UN/OV RUN */ +#define DW_MMC_INT_DRT (1 << 9) /* Data timeout */ +#define DW_MMC_INT_RTO (1 << 8) /* Response timeout */ +#define DW_MMC_INT_DCRC (1 << 7) /* Data CRC err */ +#define DW_MMC_INT_RCRC (1 << 6) /* Response CRC err */ +#define DW_MMC_INT_RXDR (1 << 5) /* Receive FIFO data request */ +#define DW_MMC_INT_TXDR (1 << 4) /* Transmit FIFO data request */ +#define DW_MMC_INT_DTO (1 << 3) /* Data trans over */ +#define DW_MMC_INT_CMD_DONE (1 << 2) /* Command done */ +#define DW_MMC_INT_RE (1 << 1) /* Response 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_MMC_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) /* Software Reset */ +#define DW_MMC_BMOD_FB (1 << 1) /* Fix Burst */ +#define DW_MMC_BMOD_DE (1 << 7) /* IDMAC Enable */ + +#define DW_MMC_IDSTS_TI (1 << 0) /* Transmit Interrupt */ +#define DW_MMC_IDSTS_RI (1 << 1) /* Receive 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 command 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 Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Read A boolean to indicate it's read or write operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +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 operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in, out] Data For read operations, the destination buffer to store + the results. For write operations, the source buffer + to write data from. The caller is responsible for + having ownership of the data buffer and ensuring its + size not less than Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or Data is NULL or the Count is not valid. + @retval EFI_SUCCESS The read/write operation succeeds. + @retval Others The read/write operation fails. + +**/ +EFI_STATUS +EFIAPI +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 mmio register. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +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 mmio register. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] BarIndex The BAR index of the standard PCI Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or OrData is NULL or the Count is not valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +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 Configuration + header to use as the base address for the memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2 , 4 or 8 bytes. + @param[in] AndData The pointer to the data used to do AND operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +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 operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The PciIo or AndData is NULL or the Count is not valid. + @retval EFI_SUCCESS The AND operation succeeds. + @retval Others The AND operation fails. + +**/ +EFI_STATUS +EFIAPI +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 memory + operation to perform. + @param[in] Offset The offset within the selected BAR to start the + memory operation. + @param[in] Count The width of the mmio register in bytes. + Must be 1, 2, 4 or 8 bytes. + @param[in] MaskValue The mask value of memory. + @param[in] TestValue The test value of memory. + @param[in] Timeout The time out value for wait memory set, uses 1 + microsecond as a unit. + + @retval EFI_TIMEOUT The MMIO register hasn't expected value in timeout + range. + @retval EFI_SUCCESS The MMIO register has expected value. + @retval Others The MMIO operation fails. + +**/ +EFI_STATUS +EFIAPI +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 timeout + 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 command 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 command 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 command 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 command to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +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 command to. + @param[out] Capability The buffer to store the capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +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 command to. + @param[out] MaxCurrent The buffer to store the maximum current capability data. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +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 host controller + slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +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 host 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 command to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +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 details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS Succeed to stop SD/MMC clock. + @retval Others Fail to stop SD/MMC clock. + +**/ +EFI_STATUS +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 details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +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 details. + + @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 details. + + @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 command to. + @param[in] PowerCtrl The value setting to the power control register. + + @retval TRUE There is a SD/MMC card attached. + @retval FALSE There is no a SD/MMC card attached. + +**/ +EFI_STATUS +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 command to. + @param[in] BusWidth The bus width used by the SD/MMC device, it must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +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 must be 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +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 command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The clock is supplied successfully. + @retval Others The clock isn't supplied successfully. + +**/ +EFI_STATUS +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 command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The voltage is supplied successfully. + @retval Others The voltage isn't supplied successfully. + +**/ +EFI_STATUS +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 details. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +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 details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + + @retval EFI_SUCCESS The timeout control register is configured successfully. + @retval Others The timeout control register isn't configured successfully. + +**/ +EFI_STATUS +DwMmcHcInitTimeoutCtrl ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo + ); +#endif + +#ifdef DWMMC_PCI +/** + Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value + at initialization. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +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 successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +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/Include/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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __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/Drivers/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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "DwMmcHcDxe.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gDwMmcHcComponentName = { + DwMmcHcComponentNameGetDriverName, + DwMmcHcComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gDwMmcHcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) DwMmcHcComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) DwMmcHcComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcDriverNameTable[] = { + { "eng;en", L"Designware Sd/Mmc Host Controller Driver" }, + { NULL , NULL } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mDwMmcHcControllerNameTable[] = { + { "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 of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDwMmcHcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gDwMmcHcComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +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 == NULL || ControllerName == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllerHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + gDwMmcHcDriverBinding.DriverBindingHandle, + &gEfiPciIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mDwMmcHcControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gDwMmcHcComponentName) + ); +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHcDxe.c b/EmbeddedPkg/Drivers/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 BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "DwMmcHcDxe.h" + +// +// Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gDwMmcHcDriverBinding = { + DwMmcHcDriverBindingSupported, + DwMmcHcDriverBindingStart, + DwMmcHcDriverBindingStop, + 0x10, + NULL, + NULL +}; + +// +// Template for Designware SD/MMC host controller private data. +// +DW_MMC_HC_PRIVATE_DATA gDwMmcHcTemplate = { + 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 = { + { + MESSAGING_DEVICE_PATH, + MSG_SD_DP, + { + (UINT8) (sizeof (SD_DEVICE_PATH)), + (UINT8) ((sizeof (SD_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +EMMC_DEVICE_PATH mEmmcDpTemplate = { + { + MESSAGING_DEVICE_PATH, + MSG_EMMC_DP, + { + (UINT8) (sizeof (EMMC_DEVICE_PATH)), + (UINT8) ((sizeof (EMMC_DEVICE_PATH)) >> 8) + } + }, + 0 +}; + +// +// Prioritized function list to detect card type. +// User could add other card detection logic here. +// +DWMMC_CARD_TYPE_DETECT_ROUTINE mCardTypeDetectRoutineTable[] = { + EmmcIdentification, + SdCardIdentification, + NULL +}; + +/** + The entry point for SD host controller driver, used to install this driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS Driver loaded. + @retval other Driver not loaded. + +**/ +EFI_STATUS +EFIAPI +InitializeDwMmcHcDxe ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = 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 = (DW_MMC_HC_PRIVATE_DATA *)Context; + + // + // Check if the first entry in the async I/O queue is done or not. + // + Status = EFI_SUCCESS; + Trb = NULL; + Link = GetFirstNode (&Private->Queue); + if (!IsNull (&Private->Queue, Link)) { + Trb = DW_MMC_HC_TRB_FROM_THIS (Link); + if (!Private->Slot[Trb->Slot].MediaPresent) { + Status = EFI_NO_MEDIA; + goto Done; + } + if (!Trb->Started) { + // + // Check whether the cmd/data line is ready for transfer. + // + Status = DwMmcCheckTrbEnv (Private, Trb); + if (!EFI_ERROR (Status)) { + Trb->Started = TRUE; + Status = DwMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + } else { + goto Done; + } + } + Status = DwMmcCheckTrbResult (Private, Trb); + } + +Done: + if ((Trb != NULL) && (Status == EFI_NOT_READY)) { + Packet = Trb->Packet; + if (Packet->Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + if ((!InfiniteWait) && (Trb->Timeout-- == 0)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = EFI_TIMEOUT; + TrbEvent = Trb->Event; + DwMmcFreeTrb (Trb); + DEBUG (( + DEBUG_VERBOSE, + "ProcessAsyncTaskList(): Signal Event %p EFI_TIMEOUT\n", + TrbEvent + )); + gBS->SignalEvent (TrbEvent); + return; + } + } + if ((Trb != NULL) && (Status != EFI_NOT_READY)) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = Status; + TrbEvent = Trb->Event; + 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 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 +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 = (DW_MMC_HC_PRIVATE_DATA *)Context; + + if ((Private->Slot[0].Enable) && + (Private->Slot[0].SlotType == RemovableSlot)) { + Status = DwMmcHcCardDetect ( + Private->DevIo, + Private->ControllerHandle, + 0, + &MediaPresent + ); + if ((Status == EFI_MEDIA_CHANGED) && !MediaPresent) { + DEBUG (( + DEBUG_INFO, + "DwMmcHcEnumerateDevice: device disconnected at %p\n", + Private->DevIo + )); + Private->Slot[0].MediaPresent = FALSE; + // + // Signal all async task events at the slot with EFI_NO_MEDIA status. + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + Trb = DW_MMC_HC_TRB_FROM_THIS (Link); + if (Trb->Slot == 0) { + RemoveEntryList (Link); + Trb->Packet->TransactionStatus = 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 == 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 = DwMmcHcInitHost (Private->DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + return; + } + // + // Reset the specified slot of the SD/MMC Pci Host Controller + // + Status = DwMmcHcReset (Private->DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + return; + } + + Private->Slot[0].MediaPresent = TRUE; + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / + sizeof (DWMMC_CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private); + if (!EFI_ERROR (Status)) { + break; + } + } + } + // + // This card doesn't get initialized correctly. + // + if (Index == 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 = DwMmcHcEnableInterrupt (DevIo); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcReset: enable interrupts fail: %r\n", Status)); + return Status; + } + Status = DwMmcHcInitTimeoutCtrl (DevIo); + if (EFI_ERROR (Status)) { + return Status; + } + + BlkSize = DW_MMC_BLOCK_SIZE; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_BLKSIZ, + FALSE, + sizeof (BlkSize), + &BlkSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcReset: set block size fails: %r\n", Status)); + return Status; + } + + Status = DwMmcHcInitClockFreq (DevIo, Capability); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DwMmcHcSetBusWidth (DevIo, FALSE, 1); + + return Status; +} + +/** + Tests to see if this driver supports a given controller. If a child device + is provided, it further tests to see if this driver supports creating a + handle for the specified child device. + + This function checks to see if the driver specified by This supports the + device specified by ControllerHandle. Drivers will typically use the device + path attached to ControllerHandle and/or the services from the bus I/O + abstraction attached to ControllerHandle to determine if the driver supports + ControllerHandle. This function may be called many times during platform + initialization. In order to reduce boot times, the tests performed by this + function must be very small, and take as little time as possible to execute. + This function must not change the state of any hardware devices, and this + function must be aware that the device specified by ControllerHandle may + already be managed by the same driver or a different driver. This function + must match its calls to AllocatePages() with FreePages(), AllocatePool() with + FreePool(), and OpenProtocol() with CloseProtocol(). Since ControllerHandle + may have been previously started by the same driver, if a protocol is already + in the opened state, then it must not be closed with CloseProtocol(). This is + required to guarantee the state of ControllerHandle is not modified by this + function. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to test. This + handle must support a protocol interface that + supplies an I/O abstraction to the driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus + drivers. For bus drivers, if this parameter + is not NULL, then the bus driver must deter- + mine if the bus controller specified by + ControllerHandle and the child controller + specified by RemainingDevicePath are both + supported by this bus driver. + + @retval EFI_SUCCESS The device specified by ControllerHandle and + RemainingDevicePath is supported by the + driver specified by This. + @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by the driver specified by This. + @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and + RemainingDevicePath is already being managed + by a different driver or an application that + requires exclusive access. + Currently not implemented. + @retval EFI_UNSUPPORTED The device specified by ControllerHandle and + RemainingDevicePath is not supported by the + driver specified by This. +**/ +EFI_STATUS +EFIAPI +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 = NULL; + + Status = gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID *) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + // + // EFI_ALREADY_STARTED is also an error. + // + return Status; + } + // + // Close the protocol because we don't use it here. + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Now test the EmbeddedNonDiscoverableIoProtocol. + // + Status = 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 parameters, + and Supported() must have returned EFI_SUCCESS. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle The handle of the controller to start. This + handle must support a protocol interface + that supplies an I/O abstraction to the + driver. + @param[in] RemainingDevicePath A pointer to the remaining portion of a + device path. This parameter is ignored by + device drivers, and is optional for bus + drivers. + For a bus driver, if this parameter is NULL, + then handles for all the children of + Controller are created by this driver. + If this parameter is not NULL and the first + Device Path Node is not the End of Device + Path Node, then only the handle for the + child device specified by the first Device + Path Node of RemainingDevicePath is created + by this driver. + If the first Device Path Node of + RemainingDevicePath is the End of Device Path + Node, no child handle is created by this + driver. + + @retval EFI_SUCCESS The device was started. + @retval EFI_DEVICE_ERROR The device could not be started due to a + device error. Currently not implemented. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a + lack of resources. + @retval Others The driver failded to start the device. + +**/ +EFI_STATUS +EFIAPI +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 = gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Controller, + &gEmbeddedNonDiscoverableIoProtocolGuid, + (VOID **) &DevIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = AllocateCopyPool (sizeof (DW_MMC_HC_PRIVATE_DATA), &gDwMmcHcTemplate); + if (Private == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Private->ControllerHandle = Controller; + Private->DevIo = DevIo; + Private->PlatformDwMmc = PlatformDwMmc; + InitializeListHead (&Private->Queue); + + Status = DwMmcHcGetCapability (DevIo, Controller, 0, &Private->Capability[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (Private->Capability[0].BaseClkFreq == 0) { + goto Done; + } + + DumpCapabilityReg (0, &Private->Capability[0]); + + MediaPresent = FALSE; + Status = DwMmcHcCardDetect (Private->DevIo, Controller, 0, &MediaPresent); + if (MediaPresent == FALSE) { + goto Done; + } + + // + // Initialize slot and start identification process for the new attached device + // + Status = DwMmcHcInitHost (DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Reset HC + // + Status = DwMmcHcReset (DevIo, Private->Capability[0]); + if (EFI_ERROR (Status)) { + goto Done; + } + + Private->Slot[0].CardType = Private->Capability[0].CardType; + Private->Slot[0].Enable = TRUE; + Private->Slot[0].MediaPresent = TRUE; + + RoutineNum = sizeof (mCardTypeDetectRoutineTable) / sizeof (DWMMC_CARD_TYPE_DETECT_ROUTINE); + for (Index = 0; Index < RoutineNum; Index++) { + Routine = &mCardTypeDetectRoutineTable[Index]; + if (*Routine != NULL) { + Status = (*Routine) (Private); + if (!EFI_ERROR (Status)) { + break; + } + } + } + + // + // Start the asynchronous I/O monitor + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + ProcessAsyncTaskList, + Private, + &Private->TimerEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->TimerEvent, TimerPeriodic, DW_MMC_HC_ASYNC_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Start the Sd removable device connection enumeration + // + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + DwMmcHcEnumerateDevice, + Private, + &Private->ConnectEvent + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = gBS->SetTimer (Private->ConnectEvent, TimerPeriodic, DW_MMC_HC_ENUM_TIMER); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = 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 != NULL) && (Private->TimerEvent != NULL)) { + gBS->CloseEvent (Private->TimerEvent); + } + + if ((Private != NULL) && (Private->ConnectEvent != NULL)) { + gBS->CloseEvent (Private->ConnectEvent); + } + + if (Private != NULL) { + FreePool (Private); + } + } + + return Status; +} + +/** + Stops a device controller or a bus controller. + + The Stop() function is designed to be invoked from the EFI boot service + DisconnectController(). + As a result, much of the error checking on the parameters to Stop() has been + moved into this common boot service. It is legal to call Stop() from other + locations, but the following calling restrictions must be followed or the + system behavior will not be deterministic. + 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous + call to this same driver's Start() function. + 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid + EFI_HANDLE. In addition, all of these handles must have been created in + this driver's Start() function, and the Start() function must have called + OpenProtocol() on ControllerHandle with an Attribute of + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL + instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle + must support a bus specific I/O protocol for the + driver to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in + ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be + NULL if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device + error. + +**/ +EFI_STATUS +EFIAPI +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 = gBS->OpenProtocol ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + (VOID**) &PassThru, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Private = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Close Non-Blocking timer and free Task list. + // + if (Private->TimerEvent != NULL) { + gBS->CloseEvent (Private->TimerEvent); + Private->TimerEvent = NULL; + } + if (Private->ConnectEvent != NULL) { + gBS->CloseEvent (Private->ConnectEvent); + Private->ConnectEvent = NULL; + } + // + // As the timer is closed, there is no needs to use TPL lock to + // protect the critical region "queue". + // + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = DW_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + DwMmcFreeTrb (Trb); + } + + // + // Uninstall Block I/O protocol from the device handle + // + Status = gBS->UninstallProtocolInterface ( + Controller, + &gEfiSdMmcPassThruProtocolGuid, + &(Private->PassThru) + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->CloseProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + 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 returned. + + If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is + returned. + + If Slot is not in a valid range for the SD controller, then + EFI_INVALID_PARAMETER is returned. + + If Packet defines a data command but both InDataBuffer and OutDataBuffer are + NULL, EFI_INVALID_PARAMETER is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot The slot number of the SD card to send the + command to. + @param[in,out] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @retval EFI_SUCCESS The SD Command Packet was sent by the host. + @retval EFI_DEVICE_ERROR A device error occurred while attempting to send + the SD command Packet. + @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is + invalid. + @retval EFI_INVALID_PARAMETER Packet defines a data command but both + InDataBuffer and OutDataBuffer are NULL. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_UNSUPPORTED The command described by the SD Command Packet + is not supported by the host controller. + @retval EFI_BAD_BUFFER_SIZE The InTransferLength or OutTransferLength + exceeds the limit supported by SD card + ( i.e. if the number of bytes exceed the Last + LBA). + +**/ +EFI_STATUS +EFIAPI +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 == NULL) || (Packet == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->SdMmcCmdBlk == NULL) || (Packet->SdMmcStatusBlk == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) { + return EFI_INVALID_PARAMETER; + } + + Private = 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 = DwMmcCreateTrb (Private, Slot, Packet, Event); + if (Trb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + // + // Immediately return for async I/O. + // + if (Event != NULL) { + return EFI_SUCCESS; + } + + // + // Wait async I/O list is empty before execute sync I/O operation. + // + while (TRUE) { + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + if (IsListEmpty (&Private->Queue)) { + gBS->RestoreTPL (OldTpl); + break; + } + gBS->RestoreTPL (OldTpl); + } + + Status = DwMmcWaitTrbEnv (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = DwMmcExecTrb (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + + Status = DwMmcWaitTrbResult (Private, Trb); + if (EFI_ERROR (Status)) { + goto Done; + } + +Done: + if (Trb != 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 controller. + If on input Slot is 0xFF, then the slot number of the first slot on the SD + controller is returned. + + If Slot is a slot number that was returned on a previous call to + GetNextSlot(), then the slot number of the next slot on the SD controller is + returned. + + If Slot is not 0xFF and Slot was not returned on a previous call to + GetNextSlot(), EFI_INVALID_PARAMETER is returned. + + If Slot is the slot number of the last slot on the SD controller, then + EFI_NOT_FOUND is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL + instance. + @param[in,out] Slot On input, a pointer to a slot number on the SD + controller. + On output, a pointer to the next slot number on + the SD controller. + An input value of 0xFF retrieves the first slot + number on the SD controller. + + @retval EFI_SUCCESS The next slot number on the SD controller was + returned in Slot. + @retval EFI_NOT_FOUND There are no more slots on this SD controller. + @retval EFI_INVALID_PARAMETER Slot is not 0xFF and Slot was not returned on a + previous call to GetNextSlot(). + +**/ +EFI_STATUS +EFIAPI +DwMmcPassThruGetNextSlot ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *This, + IN OUT UINT8 *Slot + ) +{ + DW_MMC_HC_PRIVATE_DATA *Private; + + if ((This == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = DW_MMC_HC_PRIVATE_FROM_THIS (This); + + if (*Slot == 0xFF) { + if (Private->Slot[0].Enable) { + *Slot = 0; + Private->PreviousSlot = 0; + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; + } else if (*Slot == 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, then + EFI_NOT_FOUND is returned. + + If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. + + If there are not enough resources to allocate the device path node, then + EFI_OUT_OF_RESOURCES is returned. + + Otherwise, DevicePath is allocated with the boot service AllocatePool(), + the contents of DevicePath are initialized to describe the SD card specified + by Slot, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card for + which a device path node is to be allocated and + built. + @param[in,out] DevicePath A pointer to a single device path node that + describes the SD card specified by Slot. This + function is responsible for allocating the + buffer DevicePath with the boot service + AllocatePool(). It is the caller's responsi- + 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 returned in + DevicePath. + @retval EFI_NOT_FOUND The SD card specified by Slot does not exist on + the SD controller. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate + DevicePath. + +**/ +EFI_STATUS +EFIAPI +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 == NULL) || (DevicePath == NULL) || (Slot >= DW_MMC_HC_MAX_SLOT)) { + return EFI_INVALID_PARAMETER; + } + + Private = 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 == SdCardType) { + SdNode = AllocateCopyPool (sizeof (SD_DEVICE_PATH), &mSdDpTemplate); + if (SdNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SdNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) SdNode; + } else if (Private->Slot[Slot].CardType == EmmcCardType) { + EmmcNode = AllocateCopyPool (sizeof (EMMC_DEVICE_PATH), &mEmmcDpTemplate); + if (EmmcNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + EmmcNode->SlotNumber = Slot; + + *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) EmmcNode; + } else { + // + // Currently we only support SD and EMMC two device nodes. + // + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + This function retrieves an SD card slot number based on the input device path. + + The GetSlotNumber() function retrieves slot number for the SD card specified + by the DevicePath node. If DevicePath is NULL, EFI_INVALID_PARAMETER is + returned. + + If DevicePath is not a device path node type that the SD Pass Thru driver + supports, EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] DevicePath A pointer to the device path node that describes + a SD card on the SD controller. + @param[out] Slot On return, points to the slot number of an SD + card on the SD controller. + + @retval EFI_SUCCESS SD card slot number is returned in Slot. + @retval EFI_INVALID_PARAMETER Slot or DevicePath is NULL. + @retval EFI_UNSUPPORTED DevicePath is not a device path node type that + the SD Pass Thru driver supports. + +**/ +EFI_STATUS +EFIAPI +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 == NULL) || (DevicePath == NULL) || (Slot == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Private = DW_MMC_HC_PRIVATE_FROM_THIS (This); + + // + // Check whether the DevicePath belongs to SD_DEVICE_PATH or EMMC_DEVICE_PATH + // + if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || + ((DevicePath->SubType != MSG_SD_DP) && + (DevicePath->SubType != MSG_EMMC_DP)) || + (DevicePathNodeLength(DevicePath) != sizeof(SD_DEVICE_PATH)) || + (DevicePathNodeLength(DevicePath) != sizeof(EMMC_DEVICE_PATH))) { + return EFI_UNSUPPORTED; + } + + if (DevicePath->SubType == MSG_SD_DP) { + SdNode = (SD_DEVICE_PATH *) DevicePath; + SlotNumber = SdNode->SlotNumber; + } else { + EmmcNode = (EMMC_DEVICE_PATH *) DevicePath; + SlotNumber = EmmcNode->SlotNumber; + } + + if (SlotNumber >= DW_MMC_HC_MAX_SLOT) { + return EFI_NOT_FOUND; + } + + if (Private->Slot[SlotNumber].Enable) { + *Slot = SlotNumber; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Resets an SD card that is connected to the SD controller. + + The ResetDevice() function resets the SD card specified by Slot. + + If this SD controller does not support a device reset operation, + EFI_UNSUPPORTED is returned. + + If Slot is not in a valid slot number for this SD controller, + EFI_INVALID_PARAMETER is returned. + + If the device reset operation is completed, EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot Specifies the slot number of the SD card to be + reset. + + @retval EFI_SUCCESS The SD card specified by Slot was reset. + @retval EFI_UNSUPPORTED The SD controller does not support a device + reset operation. + @retval EFI_INVALID_PARAMETER Slot number is invalid. + @retval EFI_NO_MEDIA SD Device not present in the Slot. + @retval EFI_DEVICE_ERROR The reset command failed due to a device error + +**/ +EFI_STATUS +EFIAPI +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 == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = 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 = gBS->RaiseTPL (TPL_NOTIFY); + + for (Link = GetFirstNode (&Private->Queue); + !IsNull (&Private->Queue, Link); + Link = NextLink) { + NextLink = GetNextNode (&Private->Queue, Link); + RemoveEntryList (Link); + Trb = DW_MMC_HC_TRB_FROM_THIS (Link); + Trb->Packet->TransactionStatus = EFI_ABORTED; + gBS->SignalEvent (Trb->Event); + DwMmcFreeTrb (Trb); + } + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/DwMmcHci.c b/EmbeddedPkg/Drivers/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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include +#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, + " == Slot [%d] Capability is 0x%x ==\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 == 0x00) { + DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot")); + } else if (Capability->SlotType == 0x01) { + DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot")); + } else if (Capability->SlotType == 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 buffer to + store the results. For write operations, the + source buffer to write data from. The caller is + responsible for having ownership of the data + buffer and ensuring its size not less than + Count bytes. + + @retval EFI_INVALID_PARAMETER The 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 == NULL) || (Data == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Count != 4) && (Count != 8)) { + return EFI_INVALID_PARAMETER; + } + + // + // Since there's FIFO in Designware controller, map it to 32-bit word only. + // + Count = Count / sizeof (UINT32); + if (Read) { + Status = DevIo->Mem.Read ( + DevIo, + IO_UINT32, + (UINT64) Offset, + Count, + Data + ); + } else { + Status = 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 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] OrData The pointer to the data used to do OR operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less + than Count bytes. + + @retval EFI_INVALID_PARAMETER The DevIo or OrData is NULL or the Count is not + valid. + @retval EFI_SUCCESS The OR operation succeeds. + @retval Others The OR operation fails. + +**/ +EFI_STATUS +EFIAPI +DwMmcHcOrMmio ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN UINT32 Offset, + IN UINT8 Count, + IN VOID *OrData + ) +{ + EFI_STATUS Status; + UINT64 Data; + UINT64 Or; + + Status = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + Or = *(UINT8*) OrData; + } else if (Count == 2) { + Or = *(UINT16*) OrData; + } else if (Count == 4) { + Or = *(UINT32*) OrData; + } else if (Count == 8) { + Or = *(UINT64*) OrData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data |= Or; + Status = 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 operation. + The caller is responsible for having ownership of + the data buffer and ensuring its size not less + than Count bytes. + + @retval EFI_INVALID_PARAMETER The 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 = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Count == 1) { + And = *(UINT8*) AndData; + } else if (Count == 2) { + And = *(UINT16*) AndData; + } else if (Count == 4) { + And = *(UINT32*) AndData; + } else if (Count == 8) { + And = *(UINT64*) AndData; + } else { + return EFI_INVALID_PARAMETER; + } + + Data &= And; + Status = 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 value. + @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 = 0; + Status = DwMmcHcRwMmio (DevIo, Offset, TRUE, Count, &Value); + if (EFI_ERROR (Status)) { + return Status; + } + + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Wait for the value of the specified MMIO register set to the test value. + + @param[in] 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 timeout + 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 == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + Status = DwMmcHcCheckMmioSet ( + DevIo, + Offset, + Count, + MaskValue, + TestValue + ); + if (Status != 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 = 0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_INTMASK, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Clear status in Interrupt Status Register + // + IntStatus = ~0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + IdIntEn = ~0; + Status = 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 = ~0; + Status = 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 == NULL) { + return EFI_INVALID_PARAMETER; + } + Status = gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = PlatformDwMmc->GetCapability (Controller, Slot, Capacity); + return Status; +} + +/** + Detect whether there is a SD/MMC card attached at the specified SD/MMC host + controller slot. + + Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details. + + @param[in] DevIo The DEVICE IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command + to. + @param[out] MediaPresent The pointer to the media present boolean value. + + @retval EFI_SUCCESS There is no media change happened. + @retval EFI_MEDIA_CHANGED There is media change happened. + @retval Others The detection fails. + +**/ +EFI_STATUS +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 == NULL) { + return EFI_INVALID_PARAMETER; + } + Status = gBS->LocateProtocol ( + &gPlatformDwMmcProtocolGuid, + NULL, + (VOID **) &PlatformDwMmc + ); + if (EFI_ERROR (Status)) { + return Status; + } + *MediaPresent = 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 = BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_UPDATE_CLOCK_ONLY | + BIT_CMD_START; + Status = DwMmcHcRwMmio (DevIo, DW_MMC_CMD, FALSE, sizeof (Cmd), &Cmd); + if (EFI_ERROR (Status)) { + return Status; + } + while (1) { + Status = DwMmcHcRwMmio (DevIo, DW_MMC_CMD, TRUE, sizeof (Cmd), &Cmd); + if (EFI_ERROR (Status)) { + return Status; + } + if (!(Cmd & CMD_START_BIT)) { + break; + } + Status = 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 = 0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKENA, + FALSE, + sizeof (ClkEna), + &ClkEna + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = 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 != 0); + + BaseClkFreq = Capability.BaseClkFreq; + if (ClockFreq == 0) { + return EFI_INVALID_PARAMETER; + } + + if (ClockFreq > BaseClkFreq) { + ClockFreq = BaseClkFreq; + } + + // + // Calculate the divisor of base frequency. + // + Divisor = 0; + SettingFreq = BaseClkFreq; + while (ClockFreq < SettingFreq) { + Divisor++; + + SettingFreq = BaseClkFreq / (2 * Divisor); + Remainder = BaseClkFreq % (2 * Divisor); + if ((ClockFreq == SettingFreq) && (Remainder == 0)) { + break; + } + if ((ClockFreq == SettingFreq) && (Remainder != 0)) { + SettingFreq ++; + } + } + + DEBUG (( + DEBUG_INFO, + "BaseClkFreq %dKHz Divisor %d ClockFreq %dKhz\n", + BaseClkFreq, + Divisor, + ClockFreq + )); + + // + // Wait until MMC is idle + // + do { + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + do { + Status = DwMmcHcStopClock (DevIo); + } while (EFI_ERROR (Status)); + + do { + ClkSrc = 0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKSRC, + FALSE, + sizeof (ClkSrc), + &ClkSrc + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // Set clock divisor + // + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKDIV, + FALSE, + sizeof (Divisor), + &Divisor + ); + if (EFI_ERROR (Status)) { + continue; + } + // + // Enable MMC clock + // + ClkEna = 1; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CLKENA, + FALSE, + sizeof (ClkEna), + &ClkEna + ); + if (EFI_ERROR (Status)) { + continue; + } + Status = 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 not. + @param[in] BusWidth The bus width used by the SD/MMC device, it must be + 1, 4 or 8. + + @retval EFI_SUCCESS The bus width is set successfully. + @retval Others The bus width isn't set successfully. + +**/ +EFI_STATUS +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 = MMC_1BIT_MODE; + break; + case 4: + Ctype = MMC_4BIT_MODE; + break; + case 8: + Ctype = MMC_8BIT_MODE; + break; + default: + return EFI_INVALID_PARAMETER; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTYPE, + FALSE, + sizeof (Ctype), + &Ctype + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = DwMmcHcRwMmio (DevIo, DW_MMC_UHSREG, TRUE, sizeof (Uhs), &Uhs); + if (EFI_ERROR (Status)) { + return Status; + } + if (IsDdr) { + Uhs |= UHS_DDR_MODE; + } else { + Uhs &= ~(UHS_DDR_MODE); + } + Status = 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 == 0) { + // + // Don't support get Base Clock Frequency information via another method + // + return EFI_UNSUPPORTED; + } + // + // Supply 400KHz clock frequency at initialization phase. + // + InitFreq = DWMMC_INIT_CLOCK_FREQ; + Status = 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 = 0x1; + Status = 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 = DW_MMC_CTRL_RESET_ALL; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Data), + &Data + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwMmcHcInitPowerVoltage: reset fails: %r\n", Status)); + return Status; + } + Status = 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 = DW_MMC_CTRL_INT_EN; + Status = 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 = ~0; + Status = 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 = 0x00FFFFFF; + Status = 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 command + to. + @param[in] Capability The capability of the slot. + + @retval EFI_SUCCESS The host controller is initialized successfully. + @retval Others The host controller isn't initialized successfully. + +**/ +EFI_STATUS +DwMmcHcInitHost ( + IN EFI_DEVICE_IO_PROTOCOL *DevIo, + IN DW_MMC_HC_SLOT_CAP Capability + ) +{ + EFI_STATUS Status; + + Status = 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 = Trb->Private->DevIo; + + // + // Reset DMA + // + Ctrl = DW_MMC_CTRL_DMA_RESET; + Status = 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 = 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 = DW_MMC_IDMAC_SWRESET; + Status = 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 = DW_MMC_CTRL_IDMAC_EN; + Status = 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 = DW_MMC_IDMAC_ENABLE | DW_MMC_IDMAC_FB; + Status = 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 = Trb->Private->DevIo; + + // + // Disable and reset IDMAC + // + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + TRUE, + sizeof (Ctrl), + &Ctrl + ); + if (EFI_ERROR (Status)) { + return Status; + } + Ctrl &= ~DW_MMC_CTRL_IDMAC_EN; + Ctrl |= DW_MMC_CTRL_DMA_RESET; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CTRL, + FALSE, + sizeof (Ctrl), + &Ctrl + ); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Stop IDMAC + // + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_BMOD, + TRUE, + sizeof (Bmod), + &Bmod + ); + if (EFI_ERROR (Status)) { + return Status; + } + Bmod &= ~(DW_MMC_BMOD_FB | DW_MMC_BMOD_DE); + Bmod |= DW_MMC_BMOD_SWR; + Status = 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 successfully. + @retval Others The DMA descriptor table isn't created successfully. + +**/ +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 = Trb->DataPhy; + DataLen = Trb->DataLen; + DevIo = Trb->Private->DevIo; + // + // Only support 32bit DMA Descriptor Table + // + if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + return EFI_INVALID_PARAMETER; + } + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always set + // to 0) for 32-bit address descriptor table. + // + if ((Data & (BIT0 | BIT1)) != 0) { + DEBUG (( + DEBUG_INFO, + "The buffer [0x%x] to construct DMA desc is not aligned to 4 bytes!\n", + Data + )); + } + + Entries = (DataLen + DWMMC_DMA_BUF_SIZE - 1) / DWMMC_DMA_BUF_SIZE; + TableSize = Entries * sizeof (DW_MMC_HC_DMA_DESC_LINE); + Blocks = (DataLen + DW_MMC_BLOCK_SIZE - 1) / DW_MMC_BLOCK_SIZE; + + Trb->DmaDescPages = (UINT32)EFI_SIZE_TO_PAGES (Entries * DWMMC_DMA_BUF_SIZE); + Status = 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 = TableSize; + Status = DevIo->Map ( + DevIo, + EfiBusMasterCommonBuffer, + (EFI_PHYSICAL_ADDRESS *)Trb->DmaDesc, + &Bytes, + &Trb->DmaDescPhy, + &Trb->DmaMap + ); + + if (EFI_ERROR (Status) || (Bytes != 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 = DataLen; + BytCnt = DataLen; + Remaining = DataLen; + } else { + BlkSize = DW_MMC_BLOCK_SIZE; + BytCnt = DW_MMC_BLOCK_SIZE * Blocks; + Remaining = DW_MMC_BLOCK_SIZE * Blocks; + } + Status = 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 = DwMmcHcRwMmio ( + DevIo, + DW_MMC_BYTCNT, + FALSE, + sizeof (BytCnt), + &BytCnt + ); + if (EFI_ERROR (Status)) { + return Status; + } + DmaDesc = Trb->DmaDesc; + for (Index = 0; Index < Entries; Index++, DmaDesc++) { + DmaDesc->Des0 = DW_MMC_IDMAC_DES0_OWN | DW_MMC_IDMAC_DES0_CH | + DW_MMC_IDMAC_DES0_DIC; + DmaDesc->Des1 = DW_MMC_IDMAC_DES1_BS1 (DWMMC_DMA_BUF_SIZE); + // + // Buffer Address + // + DmaDesc->Des2 = (UINT32)((UINTN)Trb->DataPhy + + (DWMMC_DMA_BUF_SIZE * Index)); + // + // Next Descriptor Address + // + DmaDesc->Des3 = (UINT32)((UINTN)Trb->DmaDescPhy + + sizeof (DW_MMC_HC_DMA_DESC_LINE) * (Index + 1)); + Remaining = Remaining - DWMMC_DMA_BUF_SIZE; + } + // + // First Descriptor + // + Trb->DmaDesc[0].Des0 |= DW_MMC_IDMAC_DES0_FS; + // + // Last Descriptor + // + Trb->DmaDesc[Entries - 1].Des0 &= ~(DW_MMC_IDMAC_DES0_CH | + DW_MMC_IDMAC_DES0_DIC); + Trb->DmaDesc[Entries - 1].Des0 |= DW_MMC_IDMAC_DES0_OWN | + DW_MMC_IDMAC_DES0_LD; + Trb->DmaDesc[Entries - 1].Des1 = DW_MMC_IDMAC_DES1_BS1 (Remaining + + DWMMC_DMA_BUF_SIZE); + // + // Set the next field of the Last Descriptor + // + Trb->DmaDesc[Entries - 1].Des3 = 0; + DmaDescPhy = (UINT32)Trb->DmaDescPhy; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_DBADDR, + FALSE, + sizeof (DmaDescPhy), + &DmaDescPhy + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Clear interrupts + // + Idsts = ~0; + Status = 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 = Trb->Private->DevIo; + Received = 0; + Count = 0; + Index = 0; + Ascending = 0; + Descending = ((Trb->DataLen + 3) & ~3) - 4; + do { + Status = 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 = 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 = GET_STS_FIFO_COUNT (Sts) << 2; + if ((FifoCount == 0) && (Received < Trb->DataLen)) { + continue; + } + Index = 0; + Count = (MIN (FifoCount, Trb->DataLen) + 3) & ~3; + while (Index < Count) { + Status = 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) = SwapBytes32 (Data); + Descending = Descending - 4; + } else { + *(UINT32 *)((UINTN)Trb->Data + Ascending) = Data; + Ascending += 4; + } + Index += 4; + Received += 4; + } /* while */ + } /* if */ + } while (((Intsts & DW_MMC_INT_CMD_DONE) == 0) || (Received < Trb->DataLen)); + // + // Clear RINTSTS + // + Intsts = ~0; + Status = 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 instance. + @param[in] Slot The slot number of the SD card to send the command + to. + @param[in] Packet A pointer to the SD command data structure. + @param[in] Event If Event is NULL, blocking I/O is performed. If + Event is not NULL, then nonblocking I/O is + performed, and Event will be signaled when the + Packet completes. + + @return Created Trb or NULL. + +**/ +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 = AllocateZeroPool (sizeof (DW_MMC_HC_TRB)); + if (Trb == NULL) { + return NULL; + } + + Trb->Signature = DW_MMC_HC_TRB_SIG; + Trb->Slot = Slot; + Trb->BlockSize = 0x200; + Trb->Packet = Packet; + Trb->Event = Event; + Trb->Started = FALSE; + Trb->Timeout = Packet->Timeout; + Trb->Private = Private; + + if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { + Trb->Data = Packet->InDataBuffer; + Trb->DataLen = Packet->InTransferLength; + Trb->Read = TRUE; + ZeroMem (Trb->Data, Trb->DataLen); + } else if (Packet->OutTransferLength && (Packet->OutDataBuffer != NULL)) { + Trb->Data = Packet->OutDataBuffer; + Trb->DataLen = Packet->OutTransferLength; + Trb->Read = FALSE; + } else if (!Packet->InTransferLength && !Packet->OutTransferLength) { + Trb->Data = NULL; + Trb->DataLen = 0; + } else { + goto Error; + } + + if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || + ((Private->Slot[Trb->Slot].CardType == SdCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { + Trb->Mode = SdMmcPioMode; + } else { + if (Trb->Read) { + Flag = EfiBusMasterWrite; + } else { + Flag = EfiBusMasterRead; + } + + DevIo = Private->DevIo; + if (Private->Slot[Trb->Slot].CardType == SdCardType) { + Trb->UseFifo = TRUE; + } else { + Trb->UseFifo = FALSE; + if (Trb->DataLen) { + MapLength = Trb->DataLen; + Status = DevIo->Map ( + DevIo, + Flag, + Trb->Data, + &MapLength, + &Trb->DataPhy, + &Trb->DataMap + ); + if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + Status = BuildDmaDescTable (Trb); + if (EFI_ERROR (Status)) { + DevIo->Unmap (DevIo, Trb->DataMap); + goto Error; + } + Status = DwMmcHcStartDma (Private, Trb); + if (EFI_ERROR (Status)) { + DevIo->Unmap (DevIo, Trb->DataMap); + goto Error; + } + } + } + } /* TuningBlock */ + + if (Event != NULL) { + OldTpl = 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 = Trb->Private->DevIo; + + if (Trb->DmaMap != NULL) { + DevIo->Unmap (DevIo, Trb->DmaMap); + } + if (Trb->DataMap != 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 instance. + @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 instance. + @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 = Trb->Packet; + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = DwMmcCheckTrbEnv (Private, Trb); + if (Status != 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 = Trb->Packet; + DevIo = Trb->Private->DevIo; + + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Wait until MMC is idle + // + do { + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + IntStatus = ~0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); + if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) || + (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case EMMC_SET_RELATIVE_ADDR: + Cmd |= BIT_CMD_SEND_INIT; + break; + case EMMC_SEND_STATUS: + Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE; + break; + case EMMC_STOP_TRANSMISSION: + Cmd |= BIT_CMD_STOP_ABORT_CMD; + break; + } + if (Packet->InTransferLength) { + Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_READ; + } else if (Packet->OutTransferLength) { + Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_WRITE; + } + Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; + } else { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case EMMC_GO_IDLE_STATE: + Cmd |= BIT_CMD_SEND_INIT; + break; + case EMMC_SEND_OP_COND: + Cmd |= BIT_CMD_RESPONSE_EXPECT; + break; + case EMMC_ALL_SEND_CID: + Cmd |= 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 |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | + BIT_CMD_LONG_RESPONSE; + break; + case SdMmcResponseTypeR3: + Cmd |= BIT_CMD_RESPONSE_EXPECT; + break; + } + Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START; + + Argument = Packet->SdMmcCmdBlk->CommandArgument; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMDARG, + FALSE, + sizeof (Argument), + &Argument + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMD, + FALSE, + sizeof (Cmd), + &Cmd + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + + ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | + DW_MMC_INT_RCRC | DW_MMC_INT_RE; + ErrMask |= DW_MMC_INT_DCRC | DW_MMC_INT_DRT | DW_MMC_INT_SBE; + do { + Timeout = 10000; + if (--Timeout == 0) { + break; + } + Status = 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) == 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 = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + case SdMmcResponseTypeR2: + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP1, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp1), + &Packet->SdMmcStatusBlk->Resp1 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP2, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp2), + &Packet->SdMmcStatusBlk->Resp2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = 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 == 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 = Trb->Packet; + DevIo = Trb->Private->DevIo; + + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + // + // Wait until MMC is idle + // + do { + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_STATUS, + TRUE, + sizeof (MmcStatus), + &MmcStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while (MmcStatus & DW_MMC_STS_DATA_BUSY); + + IntStatus = ~0; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RINTSTS, + FALSE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + Cmd = CMD_INDEX (Packet->SdMmcCmdBlk->CommandIndex); + if ((Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAc) || + (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc)) { + switch (Packet->SdMmcCmdBlk->CommandIndex) { + case SD_SET_RELATIVE_ADDR: + Cmd |= BIT_CMD_SEND_INIT; + break; + case SD_STOP_TRANSMISSION: + Cmd |= BIT_CMD_STOP_ABORT_CMD; + break; + case SD_SEND_SCR: + Trb->UseBE = TRUE; + break; + } + if (Packet->InTransferLength) { + Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_READ; + } else if (Packet->OutTransferLength) { + Cmd |= BIT_CMD_WAIT_PRVDATA_COMPLETE | BIT_CMD_DATA_EXPECTED | + BIT_CMD_WRITE; + } + Cmd |= 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 |= BIT_CMD_SEND_INIT; + break; + } + } + switch (Packet->SdMmcCmdBlk->ResponseType) { + case SdMmcResponseTypeR2: + Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC | + BIT_CMD_LONG_RESPONSE; + break; + case SdMmcResponseTypeR3: + Cmd |= BIT_CMD_RESPONSE_EXPECT; + break; + case SdMmcResponseTypeR1b: + case SdMmcResponseTypeR4: + case SdMmcResponseTypeR6: + case SdMmcResponseTypeR7: + Cmd |= BIT_CMD_RESPONSE_EXPECT | BIT_CMD_CHECK_RESPONSE_CRC; + break; + } + Cmd |= BIT_CMD_USE_HOLD_REG | BIT_CMD_START; + + if (Trb->UseFifo == TRUE) { + BytCnt = Packet->InTransferLength; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_BYTCNT, + FALSE, + sizeof (BytCnt), + &BytCnt + ); + if (EFI_ERROR (Status)) { + return Status; + } + if (Packet->InTransferLength > DW_MMC_BLOCK_SIZE) { + BlkSize = DW_MMC_BLOCK_SIZE; + } else { + BlkSize = Packet->InTransferLength; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_BLKSIZ, + FALSE, + sizeof (BlkSize), + &BlkSize + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "DwSdExecTrb: set block size fails: %r\n", Status)); + return Status; + } + } + + Argument = Packet->SdMmcCmdBlk->CommandArgument; + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMDARG, + FALSE, + sizeof (Argument), + &Argument + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_CMD, + FALSE, + sizeof (Cmd), + &Cmd + ); + if (EFI_ERROR (Status)) { + return Status; + } + ArmDataSynchronizationBarrier (); + ArmInstructionSynchronizationBarrier (); + + ErrMask = DW_MMC_INT_EBE | DW_MMC_INT_HLE | DW_MMC_INT_RTO | + DW_MMC_INT_RCRC | DW_MMC_INT_RE; + ErrMask |= DW_MMC_INT_DRT | DW_MMC_INT_SBE; + if (Packet->InTransferLength || Packet->OutTransferLength) { + ErrMask |= DW_MMC_INT_DCRC; + } + if (Trb->UseFifo == TRUE) { + Status = ReadFifo (Trb); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + Timeout = 10000; + do { + if (--Timeout == 0) { + break; + } + Status = 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) == 0)) { + // + // Transfer not Done + // + MicroSecondDelay (10); + continue; + } + MicroSecondDelay (10); + } while (!(IntStatus & DW_MMC_INT_CMD_DONE)); + if (Packet->InTransferLength) { + do { + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & DW_MMC_IDSTS_RI) == 0); + Status = DwMmcHcStopDma (Private, Trb); + if (EFI_ERROR (Status)) { + return Status; + } + } else if (Packet->OutTransferLength) { + do { + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & DW_MMC_IDSTS_TI) == 0); + Status = 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 = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + break; + case SdMmcResponseTypeR2: + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP0, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp0), + &Packet->SdMmcStatusBlk->Resp0 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP1, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp1), + &Packet->SdMmcStatusBlk->Resp1 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = DwMmcHcRwMmio ( + DevIo, + DW_MMC_RESP2, + TRUE, + sizeof (Packet->SdMmcStatusBlk->Resp2), + &Packet->SdMmcStatusBlk->Resp2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = 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 == 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 instance. + @param[in] Trb The pointer to the DW_MMC_HC_TRB instance. + + @retval EFI_SUCCESS The TRB is sent to host controller successfully. + @retval Others Some erros happen when sending this request to the + host controller. + +**/ +EFI_STATUS +DwMmcExecTrb ( + IN DW_MMC_HC_PRIVATE_DATA *Private, + IN DW_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINT32 Slot; + + Slot = Trb->Slot; + if (Private->Slot[Slot].CardType == EmmcCardType) { + Status = DwEmmcExecTrb (Private, Trb); + } else if (Private->Slot[Slot].CardType == SdCardType) { + Status = 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 instance. + @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 = Trb->Packet; + if (Trb->UseFifo == TRUE) { + return EFI_SUCCESS; + } + if (Packet->InTransferLength) { + do { + Status = DwMmcHcRwMmio ( + Private->DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & BIT1) == 0); + } else if (Packet->OutTransferLength) { + do { + Status = DwMmcHcRwMmio ( + Private->DevIo, + DW_MMC_IDSTS, + TRUE, + sizeof (Idsts), + &Idsts + ); + if (EFI_ERROR (Status)) { + return Status; + } + } while ((Idsts & BIT0) == 0); + } else { + return EFI_SUCCESS; + } + Idsts = ~0; + Status = 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 instance. + @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 = Trb->Packet; + // + // Wait Command Complete Interrupt Status bit in Normal Interrupt Status + // Register + // + Timeout = Packet->Timeout; + if (Timeout == 0) { + InfiniteWait = TRUE; + } else { + InfiniteWait = FALSE; + } + + while (InfiniteWait || (Timeout > 0)) { + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = DwMmcCheckTrbResult (Private, Trb); + if (Status != 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/Drivers/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 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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#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 <= 2GB, 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 device to + make it go to Idle State. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Slot The slot number of the SD card to send the command + to. + + @retval EFI_SUCCESS The EMMC device is reset correctly. + @retval Others The device reset fails. + +**/ +EFI_STATUS +EmmcReset ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc; + SdMmcCmdBlk.ResponseType = 0; + SdMmcCmdBlk.CommandArgument = 0; + + gBS->Stall (1000); + + Status = 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 send + to the device. + On output, the argument is the value of OCR + register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetOcr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN OUT UINT32 *Argument + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3; + SdMmcCmdBlk.CommandArgument = *Argument; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + // + // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12. + // + *Argument = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to send + the data of their CID registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_ALL_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = 0; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative device + Address (RCA). + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SEND_CSD to the EMMC device to get the data of the CSD register. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of + this buffer as the content of this byte is + meaningless even if the operation succeeds. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcGetCsd ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT EMMC_CSD *Csd + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_EXT_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0x00000000; + + Packet.InDataBuffer = ExtCsd; + Packet.InTransferLength = sizeof (EMMC_EXT_CSD); + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + return Status; +} + +/** + Send command SWITCH to the EMMC device to switch the mode of operation of the + selected Device or modifies the EXT_CSD registers. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SWITCH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + SdMmcCmdBlk.CommandArgument = (Access << 24) | (Index << 16) | \ + (Value << 8) | CmdSet; + + Status = 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 device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal sampling + point detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = EMMC_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + if (BusWidth == 8) { + Packet.InTransferLength = sizeof (TuningBlock); + } else { + Packet.InTransferLength = 64; + } + + Status = 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 finishes + 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 instance. + @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 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] IsDdr If TRUE, use dual data rate data simpling method. + Otherwise use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchBusWidth ( + IN EFI_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 = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, BusWidth); + if (BusWidth == 1) { + Value = 0; + } else { + if (BusWidth == 4) { + Value = 1; + } else if (BusWidth == 8) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + + if (IsDdr) { + Value += 4; + } + } + + CmdSet = 0; + Status = EmmcSwitch (PassThru, 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 = 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) == EMMC_STATE_PRG); + + Status = 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 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] 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 = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + // + // Write Byte, the Value field is written into the byte pointed by Index. + // + Access = 0x03; + Index = OFFSET_OF (EMMC_EXT_CSD, HsTiming); + Value = HsTiming; + CmdSet = 0; + + Status = EmmcSwitch (PassThru, 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 = 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) != 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 = DwMmcHcClockSupply (DevIo, ClockFreq * 1000, Private->Capability[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 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] ClockFreq The max clock frequency to be set. + @param[in] IsDdr If TRUE, use dual data rate data simpling method. + Otherwise use single data rate data simpling method. + @param[in] BusWidth The bus width to be set, it could be 4 or 8. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +EmmcSwitchToHighSpeed ( + IN EFI_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 = 1; + Status = EmmcSwitchClockFreq (DevIo, PassThru, Rca, HsTiming, ClockFreq); + if (EFI_ERROR (Status)) { + return Status; + } + Status = 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 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] 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 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. + + @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 = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + ASSERT (Private->Capability[0].BaseClkFreq != 0); + + Status = EmmcGetCsd (PassThru, Rca, &Csd); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetCsd fails with %r\n", Status)); + return Status; + } + + Status = EmmcSelect (PassThru, Rca); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: Select fails with %r\n", Status)); + return Status; + } + + do { + Status = 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) != EMMC_STATE_TRAN); + + BusWidth = 1; + Status = EmmcSwitchBusWidth (DevIo, PassThru, Rca, FALSE, BusWidth); + if (EFI_ERROR (Status)) { + return Status; + } + + BusWidth = Private->Capability[0].BusWidth; + // + // Get Deivce_Type from EXT_CSD register. + // + Status = EmmcGetExtCsd (PassThru, &ExtCsd); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetExtCsd fails with %r\n", Status)); + return Status; + } + + // + // Calculate supported bus speed/bus width/clock frequency. + // + HsTiming = 0; + IsDdr = FALSE; + ClockFreq = 0; + if (((ExtCsd.DeviceType & (BIT4 | BIT5)) != 0) && + (Private->Capability[0].Sdr104 != 0)) { + HsTiming = 2; + IsDdr = FALSE; + ClockFreq = 200; + } else if (((ExtCsd.DeviceType & (BIT2 | BIT3)) != 0) && + (Private->Capability[0].Ddr50 != 0)) { + HsTiming = 1; + IsDdr = TRUE; + ClockFreq = 52; + } else if (((ExtCsd.DeviceType & BIT1) != 0) && + (Private->Capability[0].HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 52; + } else if (((ExtCsd.DeviceType & BIT0) != 0) && + (Private->Capability[0].HighSpeed != 0)) { + HsTiming = 1; + IsDdr = FALSE; + ClockFreq = 26; + } + + if ((ClockFreq == 0) || (HsTiming == 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 == 2) { + // + // Execute HS200 timing switch procedure + // + Status = EmmcSwitchToHS200 (DevIo, PassThru, Rca, ClockFreq, BusWidth); + } else { + // + // Execute High Speed timing switch procedure + // + Status = EmmcSwitchToHighSpeed ( + DevIo, + PassThru, + Rca, + ClockFreq, + IsDdr, + BusWidth + ); + } + + DEBUG (( + DEBUG_INFO, + "EmmcSetBusMode: Switch to %a %r\n", + (HsTiming == 3) ? "HS400" : ((HsTiming == 2) ? "HS200" : "HighSpeed"), + Status + )); + + return Status; +} + +/** + Execute EMMC device identification procedure. + + Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details. + + @param[in] Private A pointer to the DW_MMC_HC_PRIVATE_DATA instance. + + @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 = Private->DevIo; + PassThru = &Private->PassThru; + + Status = EmmcReset (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd0 fails with %r\n", + Status + )); + return Status; + } + + Timeout = 100; + do { + Ocr = EMMC_CMD1_CAPACITY_GREATER_THAN_2GB; + Status = EmmcGetOcr (PassThru, &Ocr); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcIdentification: Executing Cmd1 fails with %r\n", + Status + )); + return Status; + } + if (--Timeout <= 0) { + return EFI_DEVICE_ERROR; + } + MicroSecondDelay (100); + } while ((Ocr & BIT31) == 0); + + Status = 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 = 1; + Status = 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 = EmmcCardType; + + Status = EmmcSetBusMode (DevIo, PassThru, Rca); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Exit DATA Mode. + // + do { + Status = EmmcSendStatus (PassThru, Rca, &DevStatus); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "EmmcSwitchBusWidth: Send status fails with %r\n", + Status + )); + return Status; + } + } while ((DevStatus & 0xf) == EMMC_STATE_DATA); + + return Status; +} diff --git a/EmbeddedPkg/Drivers/DwMmcHcDxe/SdDevice.c b/EmbeddedPkg/Drivers/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 available + under the terms and conditions of the BSD License which accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_GO_IDLE_STATE; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBc; + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_IF_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR7; + SdMmcCmdBlk.CommandArgument = (SupplyVoltage << 8) | CheckPattern; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + if (!EFI_ERROR (Status)) { + if (SdMmcStatusBlk.Resp0 != SdMmcCmdBlk.CommandArgument) { + return EFI_DEVICE_ERROR; + } + } + + return Status; +} + +/** + Send command SDIO_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SDIO Simplified Spec 3 Section 3.2 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.8v. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdioSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SDIO_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR4; + + Switch = S18R ? BIT24 : 0; + + SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SD_SEND_OP_COND to the device to see whether it is SDIO device. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of addressed device. + @param[in] VoltageWindow The supply voltage window. + @param[in] S18R The boolean to show if it should switch to 1.8v. + @param[in] Xpc The boolean to show if it should provide 0.36w + power control. + @param[in] Hcs The boolean to show if it support host capacity + info. + @param[out] Ocr The buffer to store returned OCR register value. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendOpCond ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT32 VoltageWindow, + IN BOOLEAN S18R, + IN BOOLEAN Xpc, + IN BOOLEAN Hcs, + OUT UINT32 *Ocr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT32 Switch; + UINT32 MaxPower; + UINT32 HostCapacity; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SEND_OP_COND; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR3; + + Switch = S18R ? BIT24 : 0; + MaxPower = Xpc ? BIT28 : 0; + HostCapacity = Hcs ? BIT30 : 0; + + SdMmcCmdBlk.CommandArgument = (VoltageWindow & 0xFFFFFF) | Switch | \ + MaxPower | HostCapacity; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *Ocr = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Broadcast command ALL_SEND_CID to the bus to ask all the SD devices to send + the data of their CID registers. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_ALL_SEND_CID; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + + return Status; +} + +/** + Send command SET_RELATIVE_ADDR to the SD device to assign a Relative device + Address (RCA). + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SET_RELATIVE_ADDR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeBcr; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR6; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *Rca = (UINT16)(SdMmcStatusBlk.Resp0 >> 16); + } + + return Status; +} + +/** + Send command SEND_CSD to the SD device to get the data of the CSD register. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + @param[in] Rca The relative device address of selected device. + @param[out] Csd The buffer to store the content of the CSD register. + Note the caller should ignore the lowest byte of + this buffer as the content of this byte is meaning- + 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_CSD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR2; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = 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 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 selected device. + @param[out] Scr The buffer to store the content of the SCR register. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardGetScr ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT SD_SCR *Scr + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SEND_SCR; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + Packet.InDataBuffer = Scr; + Packet.InTransferLength = sizeof (SD_SCR); + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SELECT_DESELECT_CARD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + if (Rca != 0) { + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b; + } + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_VOLTAGE_SWITCH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Status = 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 device. + @param[in] BusWidth The bus width to be set, it could be 1 or 4. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSetBusWidth ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + IN UINT8 BusWidth + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + UINT8 Value; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_APP_CMD; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + SdMmcCmdBlk.CommandIndex = SD_SET_BUS_WIDTH; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + if (BusWidth == 1) { + Value = 0; + } else if (BusWidth == 4) { + Value = 2; + } else { + return EFI_INVALID_PARAMETER; + } + + SdMmcCmdBlk.CommandArgument = Value & 0x3; + + Status = 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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SWITCH_FUNC; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + + ModeValue = Mode ? BIT31 : 0; + SdMmcCmdBlk.CommandArgument = (AccessMode & 0xF) | \ + ((PowerLimit & 0xF) << 4) | \ + ((DriveStrength & 0xF) << 8) | \ + ((DriveStrength & 0xF) << 12) | \ + ModeValue; + + Packet.InDataBuffer = SwitchResp; + Packet.InTransferLength = 64; + + Status = 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 device. + @param[out] DevStatus The returned device status. + + @retval EFI_SUCCESS The operation is done correctly. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdCardSendStatus ( + IN EFI_SD_MMC_PASS_THRU_PROTOCOL *PassThru, + IN UINT16 Rca, + OUT UINT32 *DevStatus + ) +{ + EFI_SD_MMC_COMMAND_BLOCK SdMmcCmdBlk; + EFI_SD_MMC_STATUS_BLOCK SdMmcStatusBlk; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET Packet; + EFI_STATUS Status; + + ZeroMem (&SdMmcCmdBlk, sizeof (SdMmcCmdBlk)); + ZeroMem (&SdMmcStatusBlk, sizeof (SdMmcStatusBlk)); + ZeroMem (&Packet, sizeof (Packet)); + + Packet.SdMmcCmdBlk = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_STATUS; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = (UINT32)Rca << 16; + + Status = DwMmcPassThruPassThru (PassThru, 0, &Packet, NULL); + if (!EFI_ERROR (Status)) { + *DevStatus = SdMmcStatusBlk.Resp0; + } + + return Status; +} + +/** + Send command SEND_TUNING_BLOCK to the SD device for HS200 optimal sampling + point detection. + + It may be sent up to 40 times until the host finishes the tuning procedure. + + Refer to SD Physical Layer Simplified Spec 4.1 Section 4.7 for details. + + @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL + instance. + + @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 = &SdMmcCmdBlk; + Packet.SdMmcStatusBlk = &SdMmcStatusBlk; + Packet.Timeout = DW_MMC_HC_GENERIC_TIMEOUT; + + SdMmcCmdBlk.CommandIndex = SD_SEND_TUNING_BLOCK; + SdMmcCmdBlk.CommandType = SdMmcCommandTypeAdtc; + SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1; + SdMmcCmdBlk.CommandArgument = 0; + + Packet.InDataBuffer = TuningBlock; + Packet.InTransferLength = sizeof (TuningBlock); + + Status = 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 = 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 = 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) != 0) { + DEBUG (( + DEBUG_ERROR, + "SdCardSwitchBusWidth: The switch operation fails as DevStatus 0x%08x\n", + DevStatus + )); + return EFI_DEVICE_ERROR; + } + + Status = 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 = DW_MMC_HC_PRIVATE_FROM_THIS (PassThru); + + Capability = &Private->Capability[0]; + + if ((Capability->BusWidth == 1) || (Capability->BusWidth == 4)) { + BusWidths &= Capability[0].BusWidth; + } else { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: BusWidths (%d) in capability are wrong\n", + Capability->BusWidth + )); + return EFI_INVALID_PARAMETER; + } + + if (BusWidths == 0) { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: Get wrong BusWidths:%d\n", + BusWidths + )); + return EFI_INVALID_PARAMETER; + } + + if (Private->Capability[0].Ddr50) { + IsDdr = TRUE; + } else { + IsDdr = FALSE; + } + + Status = 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 = 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 device + // capability. + // + ClockFreq = 0; + if (S18A && (Capability->Sdr104 != 0) && ((SwitchResp[13] & BIT3) != 0)) { + ClockFreq = 208; + AccessMode = 3; + } else if (S18A && (Capability->Sdr50 != 0) && + ((SwitchResp[13] & BIT2) != 0)) { + ClockFreq = 100; + AccessMode = 2; + } else if (S18A && (Capability->Ddr50 != 0) && + ((SwitchResp[13] & BIT4) != 0)) { + ClockFreq = 50; + AccessMode = 4; + } else if ((SwitchResp[13] & BIT1) != 0) { + ClockFreq = 50; + AccessMode = 1; + } else { + ClockFreq = 25; + AccessMode = 0; + } + + Status = SdCardSwitch (PassThru, AccessMode, 0xF, 0xF, 0xF, TRUE, SwitchResp); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((SwitchResp[16] & 0xF) != AccessMode) { + DEBUG (( + DEBUG_ERROR, + "SdCardSetBusMode: Switch to AccessMode %d ClockFreq %d fails! The Switch 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 = 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 = Private->DevIo; + PassThru = &Private->PassThru; + // + // 1. Send Cmd0 to the device + // + Status = 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 = 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 = 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 != 0) { + // + // Support 3.3V + // + MaxCurrent = ((UINT32)Private->MaxCurrent[0] & 0xFF) * 4; + S18r = FALSE; + } else if (Private->Capability[0].Voltage30 != 0) { + // + // Support 3.0V + // + MaxCurrent = (((UINT32)Private->MaxCurrent[0] >> 8) & 0xFF) * 4; + S18r = FALSE; + } else if (Private->Capability[0].Voltage18 != 0) { + // + // Support 1.8V + // + MaxCurrent = (((UINT32)Private->MaxCurrent[0] >> 16) & 0xFF) * 4; + S18r = TRUE; + } else { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + if (MaxCurrent >= 150) { + Xpc = TRUE; + } else { + Xpc = 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 = 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) == 0); + + Status = SdCardAllSendCid (PassThru); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardAllSendCid fails with %r\n", + Status + )); + return Status; + } + + Status = SdCardSetRca (PassThru, &Rca); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardSetRca fails with %r\n", + Status + )); + return Status; + } + + Status = SdCardGetCsd (PassThru, Rca, &Csd); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Executing SdCardGetCsd fails with %r\n", + Status + )); + return Status; + } + + Status = SdCardSelect (PassThru, Rca); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "SdCardIdentification: Selecting card fails with %r\n", + Status + )); + return Status; + } + + Status = 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 = SdCardType; + + Status = SdCardSetBusMode (DevIo, PassThru, Rca, S18r, Scr.SdBusWidths); + if (EFI_ERROR (Status)) { + return Status; + } + + Private->Slot[0].Initialized = TRUE; + + return Status; +}