From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: None (no SPF record) identity=mailfrom; client-ip=2a00:1450:4864:20::542; helo=mail-ed1-x542.google.com; envelope-from=pete@akeo.ie; receiver=edk2-devel@lists.01.org Received: from mail-ed1-x542.google.com (mail-ed1-x542.google.com [IPv6:2a00:1450:4864:20::542]) (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 BA8942194D3B9 for ; Mon, 10 Dec 2018 04:39:33 -0800 (PST) Received: by mail-ed1-x542.google.com with SMTP id b14so9329392edt.6 for ; Mon, 10 Dec 2018 04:39:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akeo-ie.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=dhuGZEUDedVFnuWdqVjV19B5yZh+z5eVzD+ACqetLVA=; b=w1EoJoa6U43iJySJ0jOCxIbK+DmsH8Ufts4qjQgJU0TDkMjBMih0RU4uUL8ZFkCL46 74f5/9q5wZAAQlCv5G2S4/OSDv+L3m9nRRZa8Ez6AAqG7AdIpEY2oMFTzK96wkTBg5Mn VR5MugP8V/PeBpytf/smCHyrJtArKe5QDyXz6lLkPVZKNP58gyGQTd3k2x0GkD7rbac5 PVuJgxFViH5jucuWWJ9PNZhijr4z1DKDlgWrw5KlG/A2Nf5FLJTbEEQiGMpKP+Urhxiq h60SR/R7tyIZNrUePtmSre5bu1qb4IbSfcgG9PRWn9JztSGTnWld2rBwqMD1TIXDn0ni /KcA== 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=dhuGZEUDedVFnuWdqVjV19B5yZh+z5eVzD+ACqetLVA=; b=RR+4hy5q3uAHZMs79DYj6mOH3UXHE6THzm/QeDT+xCfiq3+SvIRAbyBo/iXbKd2Crd GHe0Z++vLyGm6AXYyXrCtwhEkowBOHiQ4F6biwXTt+rVzwjPnE8jGIh7LgegJFGx2iWa RufSIoHp6VLH559L8g7OCfYQOSw3oeJxlHuRYvLYChjAuwvUGjGkndqHHXwjHp/2G1nO t/Af4wYMFKkwq2kp68z3YUun3pTku1knOONDMdhdVMvTn8eIYwZN9fFv62S3+WaiL1e/ 1EC9CBTLupspROjIeVqE5DxAG4tVQh2Yr3J8FSQMAEPGz8DjgN1KZZN13Q7jQ41etNnQ wA/Q== X-Gm-Message-State: AA+aEWbTL5D6eqd+JqrE91cGqPU+gaML+Sl3+5ZC8BqE4FTLZR2w9Hsl vlmToo1zOmOnAxFdjbi/syx7OpztxWk= X-Google-Smtp-Source: AFSGD/X4HLhbhVFH7Mhy9uEXXjEeg2RtxIUl4ahUVqXJWcvfIXcQ/bB5yxggltDOHGqfPnBQQzBFTw== X-Received: by 2002:a50:8f86:: with SMTP id y6mr11645807edy.131.1544445570478; Mon, 10 Dec 2018 04:39:30 -0800 (PST) Received: from localhost.localdomain ([84.203.68.105]) by smtp.gmail.com with ESMTPSA id e14sm3296949edb.79.2018.12.10.04.39.26 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 10 Dec 2018 04:39:29 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Mon, 10 Dec 2018 12:38:43 +0000 Message-Id: <20181210123853.4864-11-pete@akeo.ie> X-Mailer: git-send-email 2.17.0.windows.1 In-Reply-To: <20181210123853.4864-1-pete@akeo.ie> References: <20181210123853.4864-1-pete@akeo.ie> Subject: [PATCH v2 edk2-platforms 10/20] Platform/Broadcom/RPi3: Add Base MMC driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 X-List-Received-Date: Mon, 10 Dec 2018 12:39:34 -0000 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Pete Batard --- Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c | 163 ++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c | 256 +++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c | 458 +++++++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h | 533 +++++++++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c | 473 ++++++++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c | 169 ++++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf | 58 ++ Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c | 993 ++++++++++++++++++++ Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h | 53 ++ Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h | 187 ++++ 10 files changed, 3343 insertions(+) diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c new file mode 100644 index 000000000000..80eb4ff9a870 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/ComponentName.c @@ -0,0 +1,163 @@ +/** @file + * + * Component Name Protocol implementation for the MMC DXE driver + * + * Copyright (c) 2011, ARM Limited. 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 "Mmc.h" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMmcComponentName = { + MmcGetDriverName, + MmcGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) MmcGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) MmcGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE +mMmcDriverNameTable[] = { + {"eng;en", L"MMC/SD Card Interface Driver"}, + {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 A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param Language 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 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 +MmcGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mMmcDriverNameTable, + DriverName, + (BOOLEAN)(This == &gMmcComponentName) + ); +} + +/** + 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 A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + @param ControllerHandle 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 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 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 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 +MmcGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c new file mode 100644 index 000000000000..f0ff16708b67 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Diagnostics.c @@ -0,0 +1,256 @@ +/** @file + * + * Diagnostics Protocol implementation for the MMC DXE driver + * + * Copyright (c) 2011-2014, ARM Limited. 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 "Mmc.h" + +#define DIAGNOSTIC_LOGBUFFER_MAXCHAR 1024 + +CHAR16* mLogBuffer = NULL; +UINTN mLogRemainChar = 0; + +CHAR16* +DiagnosticInitLog ( + UINTN MaxBufferChar + ) +{ + mLogRemainChar = MaxBufferChar; + mLogBuffer = AllocatePool ((UINTN)MaxBufferChar * sizeof (CHAR16)); + return mLogBuffer; +} + +UINTN +DiagnosticLog ( + CONST CHAR16* Str + ) +{ + UINTN len = StrLen (Str); + if (len < mLogRemainChar) { + StrCpyS (mLogBuffer, mLogRemainChar, Str); + mLogRemainChar -= len; + mLogBuffer += len; + return len; + } else { + return 0; + } +} + +VOID +GenerateRandomBuffer ( + VOID* Buffer, + UINTN BufferSize + ) +{ + UINT64 i; + UINT64* Buffer64 = (UINT64*)Buffer; + + for (i = 0; i < (BufferSize >> 3); i++) { + *Buffer64 = i | (~i << 32); + Buffer64++; + } +} + +BOOLEAN +CompareBuffer ( + VOID *BufferA, + VOID *BufferB, + UINTN BufferSize + ) +{ + UINTN i; + UINT64* BufferA64 = (UINT64*)BufferA; + UINT64* BufferB64 = (UINT64*)BufferB; + + for (i = 0; i < (BufferSize >> 3); i++) { + if (*BufferA64 != *BufferB64) { + DEBUG ((DEBUG_ERROR, "CompareBuffer: Error at %i", i)); + DEBUG ((DEBUG_ERROR, "(0x%lX) != (0x%lX)\n", *BufferA64, *BufferB64)); + return FALSE; + } + BufferA64++; + BufferB64++; + } + return TRUE; +} + +EFI_STATUS +MmcReadWriteDataTest ( + MMC_HOST_INSTANCE *MmcHostInstance, + EFI_LBA Lba, + UINTN BufferSize + ) +{ + VOID *BackBuffer; + VOID *WriteBuffer; + VOID *ReadBuffer; + EFI_STATUS Status; + + // Check if a Media is Present + if (!MmcHostInstance->BlockIo.Media->MediaPresent) { + DiagnosticLog (L"ERROR: No Media Present\n"); + return EFI_NO_MEDIA; + } + + if (MmcHostInstance->State != MmcTransferState) { + DiagnosticLog (L"ERROR: Not ready for Transfer state\n"); + return EFI_NOT_READY; + } + + BackBuffer = AllocatePool (BufferSize); + WriteBuffer = AllocatePool (BufferSize); + ReadBuffer = AllocatePool (BufferSize); + + // Read (and save) buffer at a specific location + Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer); + if (Status != EFI_SUCCESS) { + DiagnosticLog (L"ERROR: Fail to Read Block (1)\n"); + return Status; + } + + // Write buffer at the same location + GenerateRandomBuffer (WriteBuffer,BufferSize); + Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,WriteBuffer); + if (Status != EFI_SUCCESS) { + DiagnosticLog (L"ERROR: Fail to Write Block (1)\n"); + return Status; + } + + // Read the buffer at the same location + Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer); + if (Status != EFI_SUCCESS) { + DiagnosticLog (L"ERROR: Fail to Read Block (2)\n"); + return Status; + } + + // Check that is conform + if (!CompareBuffer (ReadBuffer,WriteBuffer,BufferSize)) { + DiagnosticLog (L"ERROR: Fail to Read/Write Block (1)\n"); + return EFI_INVALID_PARAMETER; + } + + // Restore content at the original location + Status = MmcWriteBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,BackBuffer); + if (Status != EFI_SUCCESS) { + DiagnosticLog (L"ERROR: Fail to Write Block (2)\n"); + return Status; + } + + // Read the restored content + Status = MmcReadBlocks (&(MmcHostInstance->BlockIo), MmcHostInstance->BlockIo.Media->MediaId,Lba,BufferSize,ReadBuffer); + if (Status != EFI_SUCCESS) { + DiagnosticLog (L"ERROR: Fail to Read Block (3)\n"); + return Status; + } + + // Check the content is correct + if (!CompareBuffer (ReadBuffer,BackBuffer,BufferSize)) { + DiagnosticLog (L"ERROR: Fail to Read/Write Block (2)\n"); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +MmcDriverDiagnosticsRunDiagnostics ( + IN EFI_DRIVER_DIAGNOSTICS_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN EFI_DRIVER_DIAGNOSTIC_TYPE DiagnosticType, + IN CHAR8 *Language, + OUT EFI_GUID **ErrorType, + OUT UINTN *BufferSize, + OUT CHAR16 **Buffer + ) +{ + LIST_ENTRY *CurrentLink; + MMC_HOST_INSTANCE *MmcHostInstance; + EFI_STATUS Status; + + if ((Language == NULL) || + (ErrorType == NULL) || + (Buffer == NULL) || + (ControllerHandle == NULL) || + (BufferSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Check Language is supported (i.e. is "en-*" - only English is supported) + if (AsciiStrnCmp (Language, "en", 2) != 0) { + return EFI_UNSUPPORTED; + } + + Status = EFI_SUCCESS; + *ErrorType = NULL; + *BufferSize = DIAGNOSTIC_LOGBUFFER_MAXCHAR; + *Buffer = DiagnosticInitLog (DIAGNOSTIC_LOGBUFFER_MAXCHAR); + + DiagnosticLog (L"MMC Driver Diagnostics\n"); + + // Find the MMC Host instance on which we have been asked to run diagnostics + MmcHostInstance = NULL; + CurrentLink = mMmcHostPool.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) { + MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink); + ASSERT(MmcHostInstance != NULL); + if (MmcHostInstance->MmcHandle == ControllerHandle) { + break; + } + CurrentLink = CurrentLink->ForwardLink; + } + + // If we didn't find the controller, return EFI_UNSUPPORTED + if ((MmcHostInstance == NULL) + || (MmcHostInstance->MmcHandle != ControllerHandle)) { + return EFI_UNSUPPORTED; + } + + // LBA=1 Size=BlockSize + DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block\n"); + Status = MmcReadWriteDataTest (MmcHostInstance, 1, MmcHostInstance->BlockIo.Media->BlockSize); + + // LBA=2 Size=BlockSize + DiagnosticLog (L"MMC Driver Diagnostics - Test: Second Block\n"); + Status = MmcReadWriteDataTest (MmcHostInstance, 2, MmcHostInstance->BlockIo.Media->BlockSize); + + // LBA=10 Size=BlockSize + DiagnosticLog (L"MMC Driver Diagnostics - Test: Any Block\n"); + Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock >> 1, MmcHostInstance->BlockIo.Media->BlockSize); + + // LBA=LastBlock Size=BlockSize + DiagnosticLog (L"MMC Driver Diagnostics - Test: Last Block\n"); + Status = MmcReadWriteDataTest (MmcHostInstance, MmcHostInstance->BlockIo.Media->LastBlock, MmcHostInstance->BlockIo.Media->BlockSize); + + // LBA=1 Size=2*BlockSize + DiagnosticLog (L"MMC Driver Diagnostics - Test: First Block / 2 BlockSSize\n"); + Status = MmcReadWriteDataTest (MmcHostInstance, 1, 2 * MmcHostInstance->BlockIo.Media->BlockSize); + + return Status; +} + +// +// EFI Driver Diagnostics 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2 = { + (EFI_DRIVER_DIAGNOSTICS2_RUN_DIAGNOSTICS) MmcDriverDiagnosticsRunDiagnostics, + "en" +}; diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c new file mode 100644 index 000000000000..d90cf3018251 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.c @@ -0,0 +1,458 @@ +/** @file + * + * Main file of the MMC Dxe driver. The driver entrypoint is defined into this file. + * + * Copyright (c) 2011-2013, ARM Limited. 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 "Mmc.h" + +EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = { + SIGNATURE_32('m','m','c','o'), // MediaId + TRUE, // RemovableMedia + FALSE, // MediaPresent + FALSE, // LogicalPartition + FALSE, // ReadOnly + FALSE, // WriteCaching + 512, // BlockSize + 4, // IoAlign + 0, // Pad + 0 // LastBlock +}; + +// +// This device structure is serviced as a header. +// Its next field points to the first root bridge device node. +// +LIST_ENTRY mMmcHostPool; + +/** + Event triggered by the timer to check if any cards have been removed + or if new ones have been plugged in +**/ + +EFI_EVENT gCheckCardsEvent; + +/** + Initialize the MMC Host Pool to support multiple MMC devices +**/ +VOID +InitializeMmcHostPool ( + VOID + ) +{ + InitializeListHead (&mMmcHostPool); +} + +/** + Insert a new Mmc Host controller to the pool +**/ +VOID +InsertMmcHost ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link)); +} + +/* + Remove a new Mmc Host controller to the pool +*/ +VOID +RemoveMmcHost ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + RemoveEntryList (&(MmcHostInstance->Link)); +} + +MMC_HOST_INSTANCE* CreateMmcHostInstance ( + IN EFI_MMC_HOST_PROTOCOL* MmcHost + ) +{ + EFI_STATUS Status; + MMC_HOST_INSTANCE* MmcHostInstance; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE)); + if (MmcHostInstance == NULL) { + return NULL; + } + + MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE; + + MmcHostInstance->State = MmcHwInitializationState; + + MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate); + if (MmcHostInstance->BlockIo.Media == NULL) { + goto FREE_INSTANCE; + } + + MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION; + MmcHostInstance->BlockIo.Reset = MmcReset; + MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks; + MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks; + MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks; + + MmcHostInstance->MmcHost = MmcHost; + + // Create DevicePath for the new MMC Host + Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode); + if (EFI_ERROR (Status)) { + goto FREE_MEDIA; + } + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH); + if (DevicePath == NULL) { + goto FREE_MEDIA; + } + + SetDevicePathEndNode (DevicePath); + MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode); + + // Publish BlockIO protocol interface + Status = gBS->InstallMultipleProtocolInterfaces ( + &MmcHostInstance->MmcHandle, + &gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo, + &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath, + NULL + ); + if (EFI_ERROR(Status)) { + goto FREE_DEVICE_PATH; + } + + return MmcHostInstance; + +FREE_DEVICE_PATH: + FreePool(DevicePath); + +FREE_MEDIA: + FreePool(MmcHostInstance->BlockIo.Media); + +FREE_INSTANCE: + FreePool(MmcHostInstance); + + return NULL; +} + +EFI_STATUS DestroyMmcHostInstance ( + IN MMC_HOST_INSTANCE* MmcHostInstance + ) +{ + EFI_STATUS Status; + + // Uninstall Protocol Interfaces + Status = gBS->UninstallMultipleProtocolInterfaces ( + MmcHostInstance->MmcHandle, + &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo), + &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // Free Memory allocated for the instance + if (MmcHostInstance->BlockIo.Media) { + FreePool(MmcHostInstance->BlockIo.Media); + } + if (MmcHostInstance->CardInfo.ECSDData) { + FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD))); + } + FreePool (MmcHostInstance); + + return Status; +} + +/** + This function checks if the controller implement the Mmc Host and the Device Path Protocols +**/ +EFI_STATUS +EFIAPI +MmcDriverBindingSupported ( + 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_MMC_HOST_PROTOCOL *MmcHost; + EFI_DEV_PATH_PTR Node; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, go on checking other conditions + // + if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + Node.DevPath = RemainingDevicePath; + if (Node.DevPath->Type != HARDWARE_DEVICE_PATH || + Node.DevPath->SubType != HW_VENDOR_DP || + DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) { + return EFI_UNSUPPORTED; + } + } + } + + // + // Check if Mmc Host protocol is installed by platform + // + Status = gBS->OpenProtocol ( + Controller, + &gRaspberryPiMmcHostProtocolGuid, + (VOID **) &MmcHost, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the Mmc Host used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gRaspberryPiMmcHostProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + +**/ +EFI_STATUS +EFIAPI +MmcDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + MMC_HOST_INSTANCE *MmcHostInstance; + EFI_MMC_HOST_PROTOCOL *MmcHost; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + } + + // + // Get the Mmc Host protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gRaspberryPiMmcHostProtocolGuid, + (VOID **) &MmcHost, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + return Status; + } + + MmcHostInstance = CreateMmcHostInstance(MmcHost); + if (MmcHostInstance != NULL) { + // Add the handle to the pool + InsertMmcHost (MmcHostInstance); + + MmcHostInstance->Initialized = FALSE; + + // Detect card presence now + CheckCardsCallback (NULL, NULL); + } + + return EFI_SUCCESS; +} + +/** + +**/ +EFI_STATUS +EFIAPI +MmcDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + LIST_ENTRY *CurrentLink; + MMC_HOST_INSTANCE *MmcHostInstance; + + MMC_TRACE("MmcDriverBindingStop()"); + + // For each MMC instance + CurrentLink = mMmcHostPool.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) { + MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink); + ASSERT(MmcHostInstance != NULL); + + // Close gRaspberryPiMmcHostProtocolGuid + Status = gBS->CloseProtocol ( + Controller, + &gRaspberryPiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost, + This->DriverBindingHandle + ); + + // Remove MMC Host Instance from the pool + RemoveMmcHost (MmcHostInstance); + + // Destroy MmcHostInstance + DestroyMmcHostInstance (MmcHostInstance); + } + + return Status; +} + +VOID +EFIAPI +CheckCardsCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *CurrentLink; + MMC_HOST_INSTANCE *MmcHostInstance; + EFI_STATUS Status; + + CurrentLink = mMmcHostPool.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) { + MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink); + ASSERT(MmcHostInstance != NULL); + + if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) { + MmcHostInstance->State = MmcHwInitializationState; + MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized; + MmcHostInstance->Initialized = !MmcHostInstance->Initialized; + + if (MmcHostInstance->BlockIo.Media->MediaPresent) { + InitializeMmcDevice (MmcHostInstance); + } + + Status = gBS->ReinstallProtocolInterface ( + (MmcHostInstance->MmcHandle), + &gEfiBlockIoProtocolGuid, + &(MmcHostInstance->BlockIo), + &(MmcHostInstance->BlockIo) + ); + + if (EFI_ERROR(Status)) { + Print(L"MMC Card: Error reinstalling BlockIo interface\n"); + } + } + + CurrentLink = CurrentLink->ForwardLink; + } +} + + +EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = { + MmcDriverBindingSupported, + MmcDriverBindingStart, + MmcDriverBindingStop, + 0xa, + NULL, + NULL +}; + +/** + +**/ +EFI_STATUS +EFIAPI +MmcDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Initializes MMC Host pool + // + InitializeMmcHostPool (); + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gMmcDriverBinding, + ImageHandle, + &gMmcComponentName, + &gMmcComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + // Install driver diagnostics + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // Use a timer to detect if a card has been plugged in or removed + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + CheckCardsCallback, + NULL, + &gCheckCardsEvent); + ASSERT_EFI_ERROR (Status); + + Status = gBS->SetTimer( + gCheckCardsEvent, + TimerPeriodic, + (UINT64)(10*1000*200)); // 200 ms + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h new file mode 100644 index 000000000000..a18f4a82d903 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/Mmc.h @@ -0,0 +1,533 @@ +/** @file + * + * Main Header file for the MMC DXE driver + * + * Copyright (c) 2011-2015, ARM Limited. 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 __MMC_H +#define __MMC_H + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define MMC_TRACE(txt) DEBUG((DEBUG_BLKIO, "MMC: " txt "\n")) + +#define MMC_IOBLOCKS_READ 0 +#define MMC_IOBLOCKS_WRITE 1 + +#define MMC_OCR_POWERUP 0x80000000 + +#define MMC_OCR_ACCESS_MASK 0x3 /* bit[30-29] */ +#define MMC_OCR_ACCESS_BYTE 0x1 /* bit[29] */ +#define MMC_OCR_ACCESS_SECTOR 0x2 /* bit[30] */ + +#define MMC_CSD_GET_CCC(Response) (Response[2] >> 20) +#define MMC_CSD_GET_TRANSPEED(Response) (Response[3] & 0xFF) +#define MMC_CSD_GET_READBLLEN(Response) ((Response[2] >> 16) & 0xF) +#define MMC_CSD_GET_WRITEBLLEN(Response) ((Response[0] >> 22) & 0xF) +#define MMC_CSD_GET_FILEFORMAT(Response) ((Response[0] >> 10) & 0x3) +#define MMC_CSD_GET_FILEFORMATGRP(Response) ((Response[0] >> 15) & 0x1) +#define MMC_CSD_GET_DEVICESIZE(csd) (((Response[1] >> 30) & 0x3) | ((Response[2] & 0x3FF) << 2)) +#define HC_MMC_CSD_GET_DEVICESIZE(Response) ((Response[1] >> 16) | ((Response[2] & 0x3F) << 16)); +#define MMC_CSD_GET_DEVICESIZEMULT(csd) ((Response[1] >> 15) & 0x7) + +#define MMC_R0_READY_FOR_DATA (1 << 8) + +#define MMC_R0_CURRENTSTATE(Response) ((Response[0] >> 9) & 0xF) + +#define MMC_R0_STATE_IDLE 0 +#define MMC_R0_STATE_READY 1 +#define MMC_R0_STATE_IDENT 2 +#define MMC_R0_STATE_STDBY 3 +#define MMC_R0_STATE_TRAN 4 +#define MMC_R0_STATE_DATA 5 +#define MMC_R0_STATE_RECV 6 +#define MMC_R0_STATE_PROG 7 +#define MMC_R0_STATE_DIS 8 + + +#define EMMC_CMD6_ARG_ACCESS(x) (((x) & 0x3) << 24) +#define EMMC_CMD6_ARG_INDEX(x) (((x) & 0xFF) << 16) +#define EMMC_CMD6_ARG_VALUE(x) (((x) & 0xFF) << 8) +#define EMMC_CMD6_ARG_CMD_SET(x) (((x) & 0x7) << 0) + +#define SWITCH_CMD_DATA_LENGTH 64 +#define SD_HIGH_SPEED_SUPPORTED 0x200 +#define SD_DEFAULT_SPEED 25000000 +#define SD_HIGH_SPEED 50000000 +#define SWITCH_CMD_SUCCESS_MASK 0xf + +#define BUSWIDTH_4 4 + +typedef enum { + UNKNOWN_CARD, + MMC_CARD, //MMC card + MMC_CARD_HIGH, //MMC Card with High capacity + EMMC_CARD, //eMMC 4.41 card + SD_CARD, //SD 1.1 card + SD_CARD_2, //SD 2.0 or above standard card + SD_CARD_2_HIGH //SD 2.0 or above high capacity card +} CARD_TYPE; + +typedef struct { + UINT32 Reserved0: 7; // 0 + UINT32 V170_V195: 1; // 1.70V - 1.95V + UINT32 V200_V260: 7; // 2.00V - 2.60V + UINT32 V270_V360: 9; // 2.70V - 3.60V + UINT32 RESERVED_1: 5; // Reserved + UINT32 AccessMode: 2; // 00b (byte mode), 10b (sector mode) + UINT32 PowerUp: 1; // This bit is set to LOW if the card has not finished the power up routine +} OCR; + +typedef struct { + UINT8 SD_SPEC: 4; // SD Memory Card - Spec. Version [59:56] + UINT8 SCR_STRUCTURE: 4; // SCR Structure [63:60] + UINT8 SD_BUS_WIDTHS: 4; // DAT Bus widths supported [51:48] + UINT8 DATA_STAT_AFTER_ERASE: 1; // Data Status after erases [55] + UINT8 SD_SECURITY: 3; // CPRM Security Support [54:52] + UINT8 EX_SECURITY_1: 1; // Extended Security Support [43] + UINT8 SD_SPEC4: 1; // Spec. Version 4.00 or higher [42] + UINT8 RESERVED_1: 2; // Reserved [41:40] + UINT8 SD_SPEC3: 1; // Spec. Version 3.00 or higher [47] + UINT8 EX_SECURITY_2: 3; // Extended Security Support [46:44] + UINT8 CMD_SUPPORT: 4; // Command Support bits [35:32] + UINT8 RESERVED_2: 4; // Reserved [39:36] + UINT32 RESERVED_3; // Manufacturer Usage [31:0] +} SCR; + +typedef struct { + UINT32 NOT_USED; // 1 [0:0] + UINT32 CRC; // CRC7 checksum [7:1] + + UINT32 MDT; // Manufacturing date [19:8] + UINT32 RESERVED_1; // Reserved [23:20] + UINT32 PSN; // Product serial number [55:24] + UINT8 PRV; // Product revision [63:56] + UINT8 PNM[5]; // Product name [64:103] + UINT16 OID; // OEM/Application ID [119:104] + UINT8 MID; // Manufacturer ID [127:120] +} CID; + +typedef struct { + UINT8 NOT_USED: 1; // Not used, always 1 [0:0] + UINT8 CRC: 7; // CRC [7:1] + + UINT8 RESERVED_1: 2; // Reserved [9:8] + UINT8 FILE_FORMAT: 2; // File format [11:10] + UINT8 TMP_WRITE_PROTECT: 1; // Temporary write protection [12:12] + UINT8 PERM_WRITE_PROTECT: 1; // Permanent write protection [13:13] + UINT8 COPY: 1; // Copy flag (OTP) [14:14] + UINT8 FILE_FORMAT_GRP: 1; // File format group [15:15] + + UINT16 RESERVED_2: 5; // Reserved [20:16] + UINT16 WRITE_BL_PARTIAL: 1; // Partial blocks for write allowed [21:21] + UINT16 WRITE_BL_LEN: 4; // Max. write data block length [25:22] + UINT16 R2W_FACTOR: 3; // Write speed factor [28:26] + UINT16 RESERVED_3: 2; // Reserved [30:29] + UINT16 WP_GRP_ENABLE: 1; // Write protect group enable [31:31] + + UINT32 WP_GRP_SIZE: 7; // Write protect group size [38:32] + UINT32 SECTOR_SIZE: 7; // Erase sector size [45:39] + UINT32 ERASE_BLK_EN: 1; // Erase single block enable [46:46] + UINT32 C_SIZE_MULT: 3; // Device size multiplier [49:47] + UINT32 VDD_W_CURR_MAX: 3; // Max. write current @ VDD max [52:50] + UINT32 VDD_W_CURR_MIN: 3; // Max. write current @ VDD min [55:53] + UINT32 VDD_R_CURR_MAX: 3; // Max. read current @ VDD max [58:56] + UINT32 VDD_R_CURR_MIN: 3; // Max. read current @ VDD min [61:59] + UINT32 C_SIZELow2: 2; // Device size [63:62] + + UINT32 C_SIZEHigh10: 10;// Device size [73:64] + UINT32 RESERVED_4: 2; // Reserved [75:74] + UINT32 DSR_IMP: 1; // DSR implemented [76:76] + UINT32 READ_BLK_MISALIGN: 1; // Read block misalignment [77:77] + UINT32 WRITE_BLK_MISALIGN: 1; // Write block misalignment [78:78] + UINT32 READ_BL_PARTIAL: 1; // Partial blocks for read allowed [79:79] + UINT32 READ_BL_LEN: 4; // Max. read data block length [83:80] + UINT32 CCC: 12;// Card command classes [95:84] + + UINT8 TRAN_SPEED ; // Max. bus clock frequency [103:96] + UINT8 NSAC ; // Data read access-time 2 in CLK cycles (NSAC*100) [111:104] + UINT8 TAAC ; // Data read access-time 1 [119:112] + + UINT8 RESERVED_5: 2; // Reserved [121:120] + UINT8 SPEC_VERS: 4; // System specification version [125:122] + UINT8 CSD_STRUCTURE: 2; // CSD structure [127:126] +} CSD; + +typedef struct { + UINT8 RESERVED_1[16]; // Reserved [15:0] + UINT8 SECURE_REMOVAL_TYPE; // Secure Removal Type [16:16] + UINT8 PRODUCT_STATE_AWARENESS_ENABLEMENT; // Product state awareness enablement [17:17] + UINT8 MAX_PRE_LOADING_DATA_SIZE[4]; // MAX pre loading data size [21:18] + UINT8 PRE_LOADING_DATA_SIZE[4]; // Pre loading data size [25:22] + UINT8 FFU_STATUS; // FFU Status [26:26] + UINT8 RESERVED_2[2]; // Reserved [28:27] + UINT8 MODE_OPERATION_CODES; // Mode operation codes [29:29] + UINT8 MODE_CONFIG; // Mode config [30:30] + UINT8 RESERVED_3; // Reserved [31:31] + UINT8 FLUSH_CACHE; // Flushing of the cache [32:32] + UINT8 CACHE_CTRL; // Control to turn the cache ON/OFF [33:33] + UINT8 POWER_OFF_NOTIFICATION; // Power Off Notification [34:34] + UINT8 PACKED_FAILURE_INDEX; // Packed command failure index [35:35] + UINT8 PACKED_COMMAND_STATUS; // Packed command status [36:36] + UINT8 CONTEXT_CONF[15]; // Context configuration [51:37] + UINT8 EXT_PARTITIONS_ATTRIBUTE[2]; // Extended partitions attribute [53:52] + UINT8 EXCEPTION_EVENTS_STATUS[2]; // Exception events status [55:54] + UINT8 EXCEPTION_EVENTS_CTRL[2]; // Exception events control [57:56] + UINT8 DYNCAP_NEEDED; // Number of addressed group to be released [58:58] + UINT8 CLASS_6_CTRL; // Class 6 commands control [59:59] + UINT8 INI_TIMEOUT_EMU; // 1st initialization after disabling sector size emulation [60:60] + UINT8 DATA_SECTOR_SIZE; // Sector size [61:61] + UINT8 USE_NATIVE_SECTOR; // Sector size emulation [62:62] + UINT8 NATIVE_SECTOR_SIZE; // Native sector size [63:63] + UINT8 VENDOR_SPECIFIC_FIELD[64]; // Vendor specific fields [127:64] + UINT8 RESERVED_4[2]; // Reserved [129:128] + UINT8 PROGRAM_CID_CSD_DDR_SUPPORT; // Program CID/CSD in DDR mode support [130:130] + UINT8 PERIODIC_WAKEUP; // Periodic wake-up [131:131] + UINT8 TCASE_SUPPORT; // Package case temperature is controlled [132:132] + UINT8 PRODUCTION_STATE_AWARENESS; // Production state awareness [133:133] + UINT8 SECTOR_BAD_BLK_MGMNT; // Bad block management mode [134:134] + UINT8 RESERVED_5; // Reserved [135:135] + UINT8 ENH_START_ADDR[4]; // Enhanced user data start address [139:136] + UINT8 ENH_SIZE_MULT[3]; // Enhanced user data area size [142:140] + UINT8 GP_SIZE_MULT[12]; // General purpose partition size [154:143] + UINT8 PARTITION_SETTING_COMPLETED; // Partitioning setting [155:155] + UINT8 PARTITIONS_ATTRIBUTE; // Partitions attribute [156:156] + UINT8 MAX_ENH_SIZE_MULT[3]; // Max enhanced area size [159:157] + UINT8 PARTITIONING_SUPPORT; // Partitioning [160:160] + UINT8 HPI_MGMT; // HPI management [161:161] + UINT8 RST_N_FUNCTION; // H/W reset function [162:162] + UINT8 BKOPS_EN; // Enable background operations handshake [163:163] + UINT8 BKOPS_START; // Manually start background operations [164:164] + UINT8 SANITIZE_START; // Start sanitize operation [165:165] + UINT8 WR_REL_PARAM; // Write reliability parameter register [166:166] + UINT8 WR_REL_SET; // Write reliability setting register [167:167] + UINT8 RPMB_SIZE_MULT; // RPMB size [168:168] + UINT8 FW_CONFIG; // FW configuration [169:169] + UINT8 RESERVED_6; // Reserved [170:170] + UINT8 USER_WP; // User area write protection register [171:171] + UINT8 RESERVED_7; // Reserved [172:172] + UINT8 BOOT_WP; // Boot area write protection register [173:173] + UINT8 BOOT_WP_STATUS; // Boot write protection register [174:174] + UINT8 ERASE_GROUP_DEF; // High-density erase group definition [175:175] + UINT8 RESERVED_8; // Reserved [176:176] + UINT8 BOOT_BUS_CONDITIONS; // Boot bus conditions [177:177] + UINT8 BOOT_CONFIG_PROT; // Boot config protection [178:178] + UINT8 PARTITION_CONFIG; // Partition config [179:179] + UINT8 RESERVED_9; // Reserved [180:180] + UINT8 ERASED_MEM_CONT; // Erased memory content [181:181] + UINT8 RESERVED_10; // Reserved [182:182] + UINT8 BUS_WIDTH; // Bus width mode [183:183] + UINT8 RESERVED_11; // Reserved [184:184] + UINT8 HS_TIMING; // High-speed interface timing [185:185] + UINT8 RESERVED_12; // Reserved [186:186] + UINT8 POWER_CLASS; // Power class [187:187] + UINT8 RESERVED_13; // Reserved [188:188] + UINT8 CMD_SET_REV; // Command set revision [189:189] + UINT8 RESERVED_14; // Reserved [190:190] + UINT8 CMD_SET; // Command set [191:191] + UINT8 EXT_CSD_REV; // Extended CSD revision [192:192] + UINT8 RESERVED_15; // Reserved [193:193] + UINT8 CSD_STRUCTURE; // CSD Structure [194:194] + UINT8 RESERVED_16; // Reserved [195:195] + UINT8 DEVICE_TYPE; // Device type [196:196] + UINT8 DRIVER_STRENGTH; // I/O Driver strength [197:197] + UINT8 OUT_OF_INTERRUPT_TIME; // Out-of-interrupt busy timing [198:198] + UINT8 PARTITION_SWITCH_TIME; // Partition switching timing [199:199] + UINT8 PWR_CL_52_195; // Power class for 52MHz at 1.95V 1 R [200:200] + UINT8 PWR_CL_26_195; // Power class for 26MHz at 1.95V 1 R [201:201] + UINT8 PWR_CL_52_360; // Power class for 52MHz at 3.6V 1 R [202:202] + UINT8 PWR_CL_26_360; // Power class for 26MHz at 3.6V 1 R [203:203] + UINT8 RESERVED_17; // Reserved [204:204] + UINT8 MIN_PERF_R_4_26; // Minimum read performance for 4bit at 26MHz [205:205] + UINT8 MIN_PERF_W_4_26; // Minimum write performance for 4bit at 26MHz [206:206] + UINT8 MIN_PERF_R_8_26_4_52; // Minimum read performance for 8bit at 26MHz, for 4bit at 52MHz [207:207] + UINT8 MIN_PERF_W_8_26_4_52; // Minimum write performance for 8bit at 26MHz, for 4bit at 52MHz [208:208] + UINT8 MIN_PERF_R_8_52; // Minimum read performance for 8bit at 52MHz [209:209] + UINT8 MIN_PERF_W_8_52; // Minimum write performance for 8bit at 52MHz [210:210] + UINT8 RESERVED_18; // Reserved [211:211] + UINT32 SECTOR_COUNT; // Sector count [215:212] + UINT8 SLEEP_NOTIFICATION_TIME; // Sleep notification timout [216:216] + UINT8 S_A_TIMEOUT; // Sleep/awake timeout [217:217] + UINT8 PRODUCTION_STATE_AWARENESS_TIMEOUT; // Production state awareness timeout [218:218] + UINT8 S_C_VCCQ; // Sleep current (VCCQ) [219:219] + UINT8 S_C_VCC; // Sleep current (VCC) [220:220] + UINT8 HC_WP_GRP_SIZE; // High-capacity write protect group size [221:221] + UINT8 REL_WR_SECTOR_C; // Reliable write sector count [222:222] + UINT8 ERASE_TIMEOUT_MULT; // High-capacity erase timeout [223:223] + UINT8 HC_ERASE_GRP_SIZE; // High-capacity erase unit size [224:224] + UINT8 ACC_SIZE; // Access size [225:225] + UINT8 BOOT_SIZE_MULTI; // Boot partition size [226:226] + UINT8 RESERVED_19; // Reserved [227:227] + UINT8 BOOT_INFO; // Boot information [228:228] + UINT8 SECURE_TRIM_MULT; // Secure TRIM Multiplier [229:229] + UINT8 SECURE_ERASE_MULT; // Secure Erase Multiplier [230:230] + UINT8 SECURE_FEATURE_SUPPORT; // Secure Feature Support [231:231] + UINT8 TRIM_MULT; // TRIM Multiplier [232:232] + UINT8 RESERVED_20; // Reserved [233:233] + UINT8 MIN_PREF_DDR_R_8_52; // Minimum read performance for 8bit at 52MHz in DDR mode [234:234] + UINT8 MIN_PREF_DDR_W_8_52; // Minimum write performance for 8bit at 52MHz in DDR mode [235:235] + UINT8 PWR_CL_200_130; // Power class for 200MHz at VCCQ=1.3V, VCC=3.6V [236:236] + UINT8 PWR_CL_200_195; // Power class for 200MHz at VCCQ=1.95V, VCC=3.6V [237:237] + UINT8 PWR_CL_DDR_52_195; // Power class for 52MHz, DDR at 1.95V [238:238] + UINT8 PWR_CL_DDR_52_360; // Power class for 52Mhz, DDR at 3.6V [239:239] + UINT8 RESERVED_21; // Reserved [240:240] + UINT8 INI_TIMEOUT_AP; // 1st initialization time after partitioning [241:241] + UINT8 CORRECTLY_PRG_SECTORS_NUM[4]; // Number of correctly programmed sectors [245:242] + UINT8 BKOPS_STATUS; // Background operations status [246:246] + UINT8 POWER_OFF_LONG_TIME; // Power off notification (long) timeout [247:247] + UINT8 GENERIC_CMD6_TIME; // Generic CMD6 timeout [248:248] + UINT8 CACHE_SIZE[4]; // Cache size [252:249] + UINT8 PWR_CL_DDR_200_360; // Power class for 200MHz, DDR at VCC=3.6V [253:253] + UINT8 FIRMWARE_VERSION[8]; // Firmware version [261:254] + UINT8 DEVICE_VERSION[2]; // Device version [263:262] + UINT8 OPTIMAL_TRIM_UNIT_SIZE; // Optimal trim unit size [264:264] + UINT8 OPTIMAL_WRITE_SIZE; // Optimal write size [265:265] + UINT8 OPTIMAL_READ_SIZE; // Optimal read size [266:266] + UINT8 PRE_EOL_INFO; // Pre EOL information [267:267] + UINT8 DEVICE_LIFE_TIME_EST_TYP_A; // Device life time estimation type A [268:268] + UINT8 DEVICE_LIFE_TIME_EST_TYP_B; // Device life time estimation type B [269:269] + UINT8 VENDOR_PROPRIETARY_HEALTH_REPORT[32]; // Vendor proprietary health report [301:270] + UINT8 NUMBER_OF_FW_SECTORS_CORRECTLY_PROGRAMMED[4]; // Number of FW sectors correctly programmed [305:302] + UINT8 RESERVED_22[181]; // Reserved [486:306] + UINT8 FFU_ARG[4]; // FFU argument [490:487] + UINT8 OPERATION_CODE_TIMEOUT; // Operation codes timeout [491:491] + UINT8 FFU_FEATURES; // FFU features [492:492] + UINT8 SUPPORTED_MODES; // Supported modes [493:493] + UINT8 EXT_SUPPORT; // Extended partitions attribute support [494:494] + UINT8 LARGE_UNIT_SIZE_M1; // Large unit size [495:495] + UINT8 CONTEXT_CAPABILITIES; // Context management capabilities [496:496] + UINT8 TAG_RES_SIZE; // Tag resource size [497:497] + UINT8 TAG_UNIT_SIZE; // Tag unit size [498:498] + UINT8 DATA_TAG_SUPPORT; // Data tag support [499:499] + UINT8 MAX_PACKED_WRITES; // Max packed write commands [500:500] + UINT8 MAX_PACKED_READS; // Max packed read commands [501:501] + UINT8 BKOPS_SUPPORT; // Background operations support [502:502] + UINT8 HPI_FEATURES; // HPI features [503:503] + UINT8 S_CMD_SET; // Supported command sets [504:504] + UINT8 EXT_SECURITY_ERR; // Extended security commands error [505:505] + UINT8 RESERVED_23[6]; // Reserved [511:506] +} ECSD; + +typedef struct { + UINT16 RCA; + CARD_TYPE CardType; + OCR OCRData; + CID CIDData; + CSD CSDData; + ECSD *ECSDData; // MMC V4 extended card specific +} CARD_INFO; + +typedef struct _MMC_HOST_INSTANCE { + UINTN Signature; + LIST_ENTRY Link; + EFI_HANDLE MmcHandle; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + MMC_STATE State; + EFI_BLOCK_IO_PROTOCOL BlockIo; + CARD_INFO CardInfo; + EFI_MMC_HOST_PROTOCOL *MmcHost; + + BOOLEAN Initialized; +} MMC_HOST_INSTANCE; + +#define MMC_HOST_INSTANCE_SIGNATURE SIGNATURE_32('m', 'm', 'c', 'h') +#define MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS(a) CR (a, MMC_HOST_INSTANCE, BlockIo, MMC_HOST_INSTANCE_SIGNATURE) +#define MMC_HOST_INSTANCE_FROM_LINK(a) CR (a, MMC_HOST_INSTANCE, Link, MMC_HOST_INSTANCE_SIGNATURE) + + +EFI_STATUS +EFIAPI +MmcGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +MmcGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +extern EFI_COMPONENT_NAME_PROTOCOL gMmcComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gMmcComponentName2; + +extern EFI_DRIVER_DIAGNOSTICS2_PROTOCOL gMmcDriverDiagnostics2; + +extern LIST_ENTRY mMmcHostPool; + +/** + Reset the block device. + + This function implements EFI_BLOCK_IO_PROTOCOL.Reset(). + It resets the block device hardware. + ExtendedVerification is ignored in this implementation. + + @param This Indicates a pointer to the calling context. + @param ExtendedVerification Indicates that the driver may perform a more exhaustive + verification operation of the device during reset. + + @retval EFI_SUCCESS The block device was reset. + @retval EFI_DEVICE_ERROR The block device is not functioning correctly and could not be reset. + +**/ +EFI_STATUS +EFIAPI +MmcReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the requested number of blocks from the device. + + This function implements EFI_BLOCK_IO_PROTOCOL.ReadBlocks(). + It reads the requested number of blocks from the device. + All the blocks are read, or an error is returned. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the read request is for. + @param Lba The starting logical block address to read from on the device. + @param BufferSize The size of the Buffer in bytes. + This must be a multiple of the intrinsic block size of the device. + @param Buffer A pointer to the destination buffer for the data. The caller is + responsible for either having implicit or explicit ownership of the buffer. + + @retval EFI_SUCCESS The data was read correctly from the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to perform the read operation. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the intrinsic block size of the device. + @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +MmcReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ); + +/** + Writes a specified number of blocks to the device. + + This function implements EFI_BLOCK_IO_PROTOCOL.WriteBlocks(). + It writes a specified number of blocks to the device. + All blocks are written, or an error is returned. + + @param This Indicates a pointer to the calling context. + @param MediaId The media ID that the write request is for. + @param Lba The starting logical block address to be written. + @param BufferSize The size of the Buffer in bytes. + This must be a multiple of the intrinsic block size of the device. + @param Buffer Pointer to the source buffer for the data. + + @retval EFI_SUCCESS The data were written correctly to the device. + @retval EFI_WRITE_PROTECTED The device cannot be written to. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to perform the write operation. + @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the intrinsic + block size of the device. + @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid, + or the buffer is not on proper alignment. + +**/ +EFI_STATUS +EFIAPI +MmcWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +/** + Flushes all modified data to a physical block device. + + @param This Indicates a pointer to the calling context. + + @retval EFI_SUCCESS All outstanding data were written correctly to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to write data. + @retval EFI_NO_MEDIA There is no media in the device. + +**/ +EFI_STATUS +EFIAPI +MmcFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ); + +EFI_STATUS +MmcNotifyState ( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN MMC_STATE State + ); + +EFI_STATUS +InitializeMmcDevice ( + IN MMC_HOST_INSTANCE *MmcHost + ); + +VOID +EFIAPI +CheckCardsCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +PrintCSD ( + IN UINT32* Csd + ); + +VOID +PrintRCA ( + IN UINT32 Rca + ); + +VOID +PrintOCR ( + IN UINT32 Ocr + ); + +VOID +PrintResponseR1 ( + IN UINT32 Response + ); + +VOID +PrintCID ( + IN UINT32* Cid + ); + +#endif diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c new file mode 100644 index 000000000000..54cdbde95292 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcBlockIo.c @@ -0,0 +1,473 @@ +/** @file + * + * Copyright (c) 2011-2015, ARM Limited. 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 "Mmc.h" + +#define MMCI0_BLOCKLEN 512 +#define MMCI0_TIMEOUT 1000 + +STATIC +EFI_STATUS +R1TranAndReady(UINT32 *Response) +{ + if ((*Response & MMC_R0_READY_FOR_DATA) != 0 && + MMC_R0_CURRENTSTATE(Response) == MMC_R0_STATE_TRAN) { + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +STATIC +EFI_STATUS +ValidateWrittenBlockCount( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN UINTN Count, + OUT UINTN *TransferredBlocks + ) +{ + UINT32 R1; + UINT8 Data[4]; + EFI_STATUS Status; + UINT32 BlocksWritten; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + if (MmcHostInstance->CardInfo.CardType == MMC_CARD || + MmcHostInstance->CardInfo.CardType == MMC_CARD_HIGH || + MmcHostInstance->CardInfo.CardType == EMMC_CARD) { + /* + * Not on MMC. + */ + return EFI_SUCCESS; + } + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, + MmcHostInstance->CardInfo.RCA << 16); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + Status = MmcHost->SendCommand (MmcHost, MMC_ACMD22, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, &R1); + Status = R1TranAndReady(&R1); + if (EFI_ERROR (Status)) { + return Status; + } + + // Read Data + Status = MmcHost->ReadBlockData (MmcHost, 0, sizeof(Data), + (VOID *) Data); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + /* + * Big Endian. + */ + BlocksWritten = ((UINT32) Data[0] << 24) | + ((UINT32) Data[1] << 16) | + ((UINT32) Data[2] << 8) | + ((UINT32) Data[3] << 0); + if (BlocksWritten != Count) { + DEBUG ((DEBUG_ERROR, "%a(%u): expected %u != gotten %u\n", + __FUNCTION__, __LINE__, Count, BlocksWritten)); + if (BlocksWritten == 0) { + return EFI_DEVICE_ERROR; + } + } + + *TransferredBlocks = BlocksWritten; + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +WaitUntilTran( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + INTN Timeout; + UINT32 Response[1]; + EFI_STATUS Status = EFI_SUCCESS; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + Timeout = MMCI0_TIMEOUT; + while(Timeout--) { + /* + * We expect CMD13 to timeout while card is programming, + * because the card holds DAT0 low (busy). + */ + Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, + MmcHostInstance->CardInfo.RCA << 16); + if (EFI_ERROR(Status) && Status != EFI_TIMEOUT) { + DEBUG ((DEBUG_ERROR, "%a(%u) CMD13 failed: %r\n", + __FUNCTION__, __LINE__, Status)); + break; + } + + if (Status == EFI_SUCCESS) { + MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response); + Status = R1TranAndReady(Response); + if (!EFI_ERROR(Status)) { + break; + } + } + + gBS->Stall(1000); + } + + if (0 == Timeout) { + DEBUG ((DEBUG_ERROR, "%a(%u) card is busy\n", __FUNCTION__, __LINE__)); + return EFI_NOT_READY; + } + + return Status; +} + +EFI_STATUS +MmcNotifyState ( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN MMC_STATE State + ) +{ + MmcHostInstance->State = State; + return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State); +} + +EFI_STATUS +EFIAPI +MmcReset ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ) +{ + MMC_HOST_INSTANCE *MmcHostInstance; + + MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This); + + if (MmcHostInstance->MmcHost == NULL) { + // Nothing to do + return EFI_SUCCESS; + } + + // If a card is not present then clear all media settings + if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) { + MmcHostInstance->BlockIo.Media->MediaPresent = FALSE; + MmcHostInstance->BlockIo.Media->LastBlock = 0; + MmcHostInstance->BlockIo.Media->BlockSize = 512; // Should be zero but there is a bug in DiskIo + MmcHostInstance->BlockIo.Media->ReadOnly = FALSE; + + // Indicate that the driver requires initialization + MmcHostInstance->State = MmcHwInitializationState; + + return EFI_SUCCESS; + } + + // Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn + // on power and restart Identification mode + return EFI_SUCCESS; +} + +EFI_STATUS +MmcDetectCard ( + EFI_MMC_HOST_PROTOCOL *MmcHost + ) +{ + if (!MmcHost->IsCardPresent (MmcHost)) { + return EFI_NO_MEDIA; + } else { + return EFI_SUCCESS; + } +} + +EFI_STATUS +MmcStopTransmission ( + EFI_MMC_HOST_PROTOCOL *MmcHost + ) +{ + EFI_STATUS Status; + UINT32 Response[4]; + // Command 12 - Stop transmission (ends read or write) + // Normally only needed for streaming transfers or after error. + Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0); + if (!EFI_ERROR (Status)) { + MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response); + } + return Status; +} + +STATIC +EFI_STATUS +MmcTransferBlock ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINTN Cmd, + IN UINTN Transfer, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer, + OUT UINTN *TransferredSize + ) +{ + EFI_STATUS Status; + MMC_HOST_INSTANCE *MmcHostInstance; + EFI_MMC_HOST_PROTOCOL *MmcHost; + UINTN CmdArg; + + MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This); + MmcHost = MmcHostInstance->MmcHost; + + //Set command argument based on the card access mode (Byte mode or Block mode) + if ((MmcHostInstance->CardInfo.OCRData.AccessMode & MMC_OCR_ACCESS_MASK) == + MMC_OCR_ACCESS_SECTOR) { + CmdArg = Lba; + } else { + CmdArg = Lba * This->Media->BlockSize; + } + + Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__, + MMC_INDX(Cmd), Status)); + return Status; + } + + if (Transfer == MMC_IOBLOCKS_READ) { + Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer); + } else { + Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer); + if (!EFI_ERROR (Status)) { + Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a() : Error MmcProgrammingState\n", __func__)); + return Status; + } + } + } + + if (EFI_ERROR (Status) || + BufferSize > This->Media->BlockSize) { + /* + * CMD12 needs to be set for multiblock (to transition from + * RECV to PROG) or for errors. + */ + EFI_STATUS Status2 = MmcStopTransmission (MmcHost); + if (EFI_ERROR (Status2)) { + DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : CMD12 error on Status %r: %r\n", + Status, Status2)); + return Status2; + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_BLKIO, "%a(): Error %a Block Data and Status = %r\n", + __func__, Transfer == MMC_IOBLOCKS_READ ? "Read" : "Write", + Status)); + return Status; + } + + ASSERT (Cmd == MMC_CMD25 || Cmd == MMC_CMD18); + } + + // + // For reads, should be already in TRAN. For writes, wait + // until programming finishes. + // + Status = WaitUntilTran(MmcHostInstance); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "WaitUntilTran after write failed\n")); + return Status; + } + + Status = MmcNotifyState (MmcHostInstance, MmcTransferState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIoBlocks() : Error MmcTransferState\n")); + return Status; + } + + if (Transfer != MMC_IOBLOCKS_READ) { + UINTN BlocksWritten = 0; + + Status = ValidateWrittenBlockCount (MmcHostInstance, + BufferSize / + This->Media->BlockSize, + &BlocksWritten); + *TransferredSize = BlocksWritten * + This->Media->BlockSize; + } else { + *TransferredSize = BufferSize; + } + + return Status; +} + +EFI_STATUS +MmcIoBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINTN Transfer, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN Cmd; + MMC_HOST_INSTANCE *MmcHostInstance; + EFI_MMC_HOST_PROTOCOL *MmcHost; + UINTN BytesRemainingToBeTransfered; + UINTN BlockCount; + UINTN ConsumeSize; + + BlockCount = 1; + MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This); + ASSERT (MmcHostInstance != NULL); + MmcHost = MmcHostInstance->MmcHost; + ASSERT (MmcHost); + + if (This->Media->MediaId != MediaId) { + return EFI_MEDIA_CHANGED; + } + + if ((MmcHost == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Check if a Card is Present + if (!MmcHostInstance->BlockIo.Media->MediaPresent) { + return EFI_NO_MEDIA; + } + + if (PcdGet32(PcdMmcDisableMulti) == 0 && + MMC_HOST_HAS_ISMULTIBLOCK(MmcHost) && + MmcHost->IsMultiBlock(MmcHost)) { + BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize; + } + + // All blocks must be within the device + if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) { + return EFI_INVALID_PARAMETER; + } + + if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) { + return EFI_WRITE_PROTECTED; + } + + // Reading 0 Byte is valid + if (BufferSize == 0) { + return EFI_SUCCESS; + } + + // The buffer size must be an exact multiple of the block size + if ((BufferSize % This->Media->BlockSize) != 0) { + return EFI_BAD_BUFFER_SIZE; + } + + // Check the alignment + if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) { + return EFI_INVALID_PARAMETER; + } + + BytesRemainingToBeTransfered = BufferSize; + while (BytesRemainingToBeTransfered > 0) { + Status = WaitUntilTran(MmcHostInstance); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "WaitUntilTran before IO failed")); + return Status; + } + + if (Transfer == MMC_IOBLOCKS_READ) { + if (BlockCount == 1) { + // Read a single block + Cmd = MMC_CMD17; + } else { + // Read multiple blocks + Cmd = MMC_CMD18; + } + } else { + if (BlockCount == 1) { + // Write a single block + Cmd = MMC_CMD24; + } else { + // Write multiple blocks + Cmd = MMC_CMD25; + } + } + + ConsumeSize = BlockCount * This->Media->BlockSize; + if (BytesRemainingToBeTransfered < ConsumeSize) { + ConsumeSize = BytesRemainingToBeTransfered; + } + + Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer, &ConsumeSize); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status)); + return Status; + } + + BytesRemainingToBeTransfered -= ConsumeSize; + if (BytesRemainingToBeTransfered > 0) { + Lba += BlockCount; + Buffer = (UINT8 *)Buffer + ConsumeSize; + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +MmcReadBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + OUT VOID *Buffer + ) +{ + return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer); +} + +EFI_STATUS +EFIAPI +MmcWriteBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This, + IN UINT32 MediaId, + IN EFI_LBA Lba, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer); +} + +EFI_STATUS +EFIAPI +MmcFlushBlocks ( + IN EFI_BLOCK_IO_PROTOCOL *This + ) +{ + return EFI_SUCCESS; +} diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c new file mode 100644 index 000000000000..ea53700caaf8 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDebug.c @@ -0,0 +1,169 @@ +/** @file + * + * Copyright (c) 2011-2013, ARM Limited. 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 "Mmc.h" + +#if !defined(MDEPKG_NDEBUG) +CONST CHAR8* mStrUnit[] = { "100kbit/s", "1Mbit/s", "10Mbit/s", "100MBit/s", + "Unknown", "Unknown", "Unknown", "Unknown" }; +CONST CHAR8* mStrValue[] = { "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", + "3.0", "3.5", "4.0", "4.5", "5.0", "5.5", + "6.0", "7.0", "8.0" }; +#endif + +VOID +PrintCID ( + IN UINT32* Cid + ) +{ + DEBUG ((DEBUG_ERROR, "- PrintCID\n")); + DEBUG ((DEBUG_ERROR, "\t- Manufacturing date: %d/%d\n", (Cid[0] >> 8) & 0xF, (Cid[0] >> 12) & 0xFF)); + DEBUG ((DEBUG_ERROR, "\t- Product serial number: 0x%X%X\n", Cid[1] & 0xFFFFFF, (Cid[0] >> 24) & 0xFF)); + DEBUG ((DEBUG_ERROR, "\t- Product revision: %d\n", Cid[1] >> 24)); + //DEBUG ((DEBUG_ERROR, "\t- Product name: %s\n", (char*)(Cid + 2))); + DEBUG ((DEBUG_ERROR, "\t- OEM ID: %c%c\n", (Cid[3] >> 8) & 0xFF, (Cid[3] >> 16) & 0xFF)); +} + + +VOID +PrintCSD ( + IN UINT32* Csd + ) +{ + UINTN Value; + + if (((Csd[2] >> 30) & 0x3) == 0) { + DEBUG ((DEBUG_ERROR, "- PrintCSD Version 1.01-1.10/Version 2.00/Standard Capacity\n")); + } else if (((Csd[2] >> 30) & 0x3) == 1) { + DEBUG ((DEBUG_ERROR, "- PrintCSD Version 2.00/High Capacity\n")); + } else { + DEBUG ((DEBUG_ERROR, "- PrintCSD Version Higher than v3.3\n")); + } + + DEBUG ((DEBUG_ERROR, "\t- Supported card command class: 0x%X\n", MMC_CSD_GET_CCC (Csd))); + DEBUG ((DEBUG_ERROR, "\t- Max Speed: %a * %a\n",mStrValue[(MMC_CSD_GET_TRANSPEED (Csd) >> 3) & 0xF],mStrUnit[MMC_CSD_GET_TRANSPEED (Csd) & 7])); + DEBUG ((DEBUG_ERROR, "\t- Maximum Read Data Block: %d\n",2 << (MMC_CSD_GET_READBLLEN (Csd)-1))); + DEBUG ((DEBUG_ERROR, "\t- Maximum Write Data Block: %d\n",2 << (MMC_CSD_GET_WRITEBLLEN (Csd)-1))); + + if (!MMC_CSD_GET_FILEFORMATGRP (Csd)) { + Value = MMC_CSD_GET_FILEFORMAT (Csd); + if (Value == 0) { + DEBUG ((DEBUG_ERROR, "\t- Format (0): Hard disk-like file system with partition table\n")); + } else if (Value == 1) { + DEBUG ((DEBUG_ERROR, "\t- Format (1): DOS FAT (floppy-like) with boot sector only (no partition table)\n")); + } else if (Value == 2) { + DEBUG ((DEBUG_ERROR, "\t- Format (2): Universal File Format\n")); + } else { + DEBUG ((DEBUG_ERROR, "\t- Format (3): Others/Unknown\n")); + } + } else { + DEBUG ((DEBUG_ERROR, "\t- Format: Reserved\n")); + } +} + +VOID +PrintRCA ( + IN UINT32 Rca + ) +{ + DEBUG ((DEBUG_ERROR, "- PrintRCA: 0x%X\n", Rca)); + DEBUG ((DEBUG_ERROR, "\t- Status: 0x%X\n", Rca & 0xFFFF)); + DEBUG ((DEBUG_ERROR, "\t- RCA: 0x%X\n", (Rca >> 16) & 0xFFFF)); +} + +VOID +PrintOCR ( + IN UINT32 Ocr + ) +{ + UINTN MinV; + UINTN MaxV; + UINTN Volts; + UINTN Loop; + + MinV = 36; // 3.6 + MaxV = 20; // 2.0 + Volts = 20; // 2.0 + + // The MMC register bits [23:8] indicate the working range of the card + for (Loop = 8; Loop < 24; Loop++) { + if (Ocr & (1 << Loop)) { + if (MinV > Volts) { + MinV = Volts; + } + if (MaxV < Volts) { + MaxV = Volts + 1; + } + } + Volts++; + } + + DEBUG ((DEBUG_ERROR, "- PrintOCR Ocr (0x%X)\n",Ocr)); + DEBUG ((DEBUG_ERROR, "\t- Card operating voltage: %d.%d to %d.%d\n", MinV/10, MinV % 10, MaxV/10, MaxV % 10)); + if (((Ocr >> 29) & 3) == 0) { + DEBUG ((DEBUG_ERROR, "\t- AccessMode: Byte Mode\n")); + } else { + DEBUG ((DEBUG_ERROR, "\t- AccessMode: Block Mode (0x%X)\n", ((Ocr >> 29) & 3))); + } + + if (Ocr & MMC_OCR_POWERUP) { + DEBUG ((DEBUG_ERROR, "\t- PowerUp\n")); + } else { + DEBUG ((DEBUG_ERROR, "\t- Voltage Not Supported\n")); + } +} + +VOID +PrintResponseR1 ( + IN UINT32 Response + ) +{ + DEBUG ((DEBUG_INFO, "Response: 0x%X\n", Response)); + if (Response & MMC_R0_READY_FOR_DATA) { + DEBUG ((DEBUG_INFO, "\t- READY_FOR_DATA\n")); + } + + switch ((Response >> 9) & 0xF) { + case 0: + DEBUG ((DEBUG_INFO, "\t- State: Idle\n")); + break; + case 1: + DEBUG ((DEBUG_INFO, "\t- State: Ready\n")); + break; + case 2: + DEBUG ((DEBUG_INFO, "\t- State: Ident\n")); + break; + case 3: + DEBUG ((DEBUG_INFO, "\t- State: StandBy\n")); + break; + case 4: + DEBUG ((DEBUG_INFO, "\t- State: Tran\n")); + break; + case 5: + DEBUG ((DEBUG_INFO, "\t- State: Data\n")); + break; + case 6: + DEBUG ((DEBUG_INFO, "\t- State: Rcv\n")); + break; + case 7: + DEBUG ((DEBUG_INFO, "\t- State: Prg\n")); + break; + case 8: + DEBUG ((DEBUG_INFO, "\t- State: Dis\n")); + break; + default: + DEBUG ((DEBUG_INFO, "\t- State: Reserved\n")); + break; + } +} diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf new file mode 100644 index 000000000000..fd91f47aeaf0 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcDxe.inf @@ -0,0 +1,58 @@ +#/** @file +# +# Copyright (c) 2018, Andrei Warkentin +# Copyright (c) 2011-2015, ARM Limited. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PiMmcDxe + FILE_GUID = b6f44cc0-9e45-11df-be21-0002a5f5f51b + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = MmcDxeInitialize + +[Sources.common] + ComponentName.c + Mmc.c + MmcBlockIo.c + MmcIdentification.c + MmcDebug.c + Diagnostics.c + +[Packages] + Platform/Broadcom/Bcm283x/RaspberryPiPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + UefiLib + UefiDriverEntryPoint + BaseMemoryLib + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + gEfiDevicePathProtocolGuid + gEfiDriverDiagnostics2ProtocolGuid + gRaspberryPiMmcHostProtocolGuid + +[Pcd] + gRaspberryPiTokenSpaceGuid.PcdMmcForce1Bit + gRaspberryPiTokenSpaceGuid.PcdMmcForceDefaultSpeed + gRaspberryPiTokenSpaceGuid.PcdMmcSdDefaultSpeedMHz + gRaspberryPiTokenSpaceGuid.PcdMmcSdHighSpeedMHz + gRaspberryPiTokenSpaceGuid.PcdMmcDisableMulti + +[Depex] + TRUE diff --git a/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c new file mode 100644 index 000000000000..ef819fb753c6 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Drivers/PiMmcDxe/MmcIdentification.c @@ -0,0 +1,993 @@ +/** @file + * + * Copyright (c) 2011-2015, ARM Limited. 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 "Mmc.h" + +typedef union { + UINT32 Raw; + OCR Ocr; +} OCR_RESPONSE; + +#define MAX_RETRY_COUNT 1000 +#define CMD_RETRY_COUNT 20 +#define RCA_SHIFT_OFFSET 16 +#define EMMC_CARD_SIZE 512 +#define EMMC_ECSD_SIZE_OFFSET 53 + +#define EXTCSD_BUS_WIDTH 183 +#define EXTCSD_HS_TIMING 185 + +#define EMMC_TIMING_BACKWARD 0 +#define EMMC_TIMING_HS 1 +#define EMMC_TIMING_HS200 2 +#define EMMC_TIMING_HS400 3 + +#define EMMC_BUS_WIDTH_1BIT 0 +#define EMMC_BUS_WIDTH_4BIT 1 +#define EMMC_BUS_WIDTH_8BIT 2 +#define EMMC_BUS_WIDTH_DDR_4BIT 5 +#define EMMC_BUS_WIDTH_DDR_8BIT 6 + +#define EMMC_SWITCH_ERROR (1 << 7) + +#define SD_BUS_WIDTH_1BIT (1 << 0) +#define SD_BUS_WIDTH_4BIT (1 << 2) + +#define SD_CCC_SWITCH (1 << 10) + +#define DEVICE_STATE(x) (((x) >> 9) & 0xf) +typedef enum _EMMC_DEVICE_STATE { + EMMC_IDLE_STATE = 0, + EMMC_READY_STATE, + EMMC_IDENT_STATE, + EMMC_STBY_STATE, + EMMC_TRAN_STATE, + EMMC_DATA_STATE, + EMMC_RCV_STATE, + EMMC_PRG_STATE, + EMMC_DIS_STATE, + EMMC_BTST_STATE, + EMMC_SLP_STATE +} EMMC_DEVICE_STATE; + +UINT32 mEmmcRcaCount = 0; + +STATIC +EFI_STATUS +EFIAPI +EmmcGetDeviceState ( + IN MMC_HOST_INSTANCE *MmcHostInstance, + OUT EMMC_DEVICE_STATE *State + ) +{ + EFI_MMC_HOST_PROTOCOL *Host; + EFI_STATUS Status; + UINT32 Data, RCA; + + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + Host = MmcHostInstance->MmcHost; + RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET; + Status = Host->SendCommand (Host, MMC_CMD13, RCA); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get card status, Status=%r.\n", Status)); + return Status; + } + Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R1, &Data); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to get response of CMD13, Status=%r.\n", Status)); + return Status; + } + if (Data & EMMC_SWITCH_ERROR) { + DEBUG ((DEBUG_ERROR, "EmmcGetDeviceState(): Failed to switch expected mode, Status=%r.\n", Status)); + return EFI_DEVICE_ERROR; + } + *State = DEVICE_STATE(Data); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +EmmcSetEXTCSD ( + IN MMC_HOST_INSTANCE *MmcHostInstance, + UINT32 ExtCmdIndex, + UINT32 Value + ) +{ + EFI_MMC_HOST_PROTOCOL *Host; + EMMC_DEVICE_STATE State; + EFI_STATUS Status; + UINT32 Argument; + + Host = MmcHostInstance->MmcHost; + Argument = EMMC_CMD6_ARG_ACCESS(3) | EMMC_CMD6_ARG_INDEX(ExtCmdIndex) | + EMMC_CMD6_ARG_VALUE(Value) | EMMC_CMD6_ARG_CMD_SET(1); + Status = Host->SendCommand (Host, MMC_CMD6, Argument); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to send CMD6, Status=%r.\n", Status)); + return Status; + } + // Make sure device exiting prog mode + do { + Status = EmmcGetDeviceState (MmcHostInstance, &State); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcSetEXTCSD(): Failed to get device state, Status=%r.\n", Status)); + return Status; + } + } while (State == EMMC_PRG_STATE); + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +EmmcIdentificationMode ( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN OCR_RESPONSE Response + ) +{ + EFI_MMC_HOST_PROTOCOL *Host; + EFI_BLOCK_IO_MEDIA *Media; + EFI_STATUS Status; + EMMC_DEVICE_STATE State; + UINT32 RCA; + + Host = MmcHostInstance->MmcHost; + Media = MmcHostInstance->BlockIo.Media; + + // Fetch card identity register + Status = Host->SendCommand (Host, MMC_CMD2, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD2, Status=%r.\n", Status)); + return Status; + } + + Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CIDData)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CID retrieval error, Status=%r.\n", Status)); + return Status; + } + + // Assign a relative address value to the card + MmcHostInstance->CardInfo.RCA = ++mEmmcRcaCount; // TODO: might need a more sophisticated way of doing this + RCA = MmcHostInstance->CardInfo.RCA << RCA_SHIFT_OFFSET; + Status = Host->SendCommand (Host, MMC_CMD3, RCA); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): RCA set error, Status=%r.\n", Status)); + return Status; + } + + // Fetch card specific data + Status = Host->SendCommand (Host, MMC_CMD9, RCA); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to send CMD9, Status=%r.\n", Status)); + return Status; + } + + Status = Host->ReceiveResponse (Host, MMC_RESPONSE_TYPE_R2, (UINT32 *)&(MmcHostInstance->CardInfo.CSDData)); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): CSD retrieval error, Status=%r.\n", Status)); + return Status; + } + + // Select the card + Status = Host->SendCommand (Host, MMC_CMD7, RCA); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Card selection error, Status=%r.\n", Status)); + } + + if (MMC_HOST_HAS_SETIOS(Host)) { + // Set 1-bit bus width + Status = Host->SetIos (Host, 0, 1, EMMCBACKWARD); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set 1-bit bus width error, Status=%r.\n", Status)); + return Status; + } + + // Set 1-bit bus width for EXTCSD + Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, EMMC_BUS_WIDTH_1BIT); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Set extcsd bus width error, Status=%r.\n", Status)); + return Status; + } + } + + // Fetch ECSD + MmcHostInstance->CardInfo.ECSDData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ECSD))); + if (MmcHostInstance->CardInfo.ECSDData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = Host->SendCommand (Host, MMC_CMD8, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD fetch error, Status=%r.\n", Status)); + } + + Status = Host->ReadBlockData (Host, 0, 512, (UINT32 *)MmcHostInstance->CardInfo.ECSDData); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): ECSD read error, Status=%r.\n", Status)); + goto FreePageExit; + } + + // Make sure device exiting data mode + do { + Status = EmmcGetDeviceState (MmcHostInstance, &State); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "EmmcIdentificationMode(): Failed to get device state, Status=%r.\n", Status)); + goto FreePageExit; + } + } while (State == EMMC_DATA_STATE); + + // Set up media + Media->BlockSize = EMMC_CARD_SIZE; // 512-byte support is mandatory for eMMC cards + Media->MediaId = MmcHostInstance->CardInfo.CIDData.PSN; + Media->ReadOnly = MmcHostInstance->CardInfo.CSDData.PERM_WRITE_PROTECT; + Media->LogicalBlocksPerPhysicalBlock = 1; + Media->IoAlign = 4; + // Compute last block using bits [215:212] of the ECSD + Media->LastBlock = MmcHostInstance->CardInfo.ECSDData->SECTOR_COUNT - 1; // eMMC isn't supposed to report this for + // Cards <2GB in size, but the model does. + + // Setup card type + MmcHostInstance->CardInfo.CardType = EMMC_CARD; + return EFI_SUCCESS; + +FreePageExit: + FreePages (MmcHostInstance->CardInfo.ECSDData, EFI_SIZE_TO_PAGES (sizeof (ECSD))); + return Status; +} + +STATIC +EFI_STATUS +InitializeEmmcDevice ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + EFI_MMC_HOST_PROTOCOL *Host; + EFI_STATUS Status = EFI_SUCCESS; + ECSD *ECSDData; + UINT32 BusClockFreq, Idx, BusMode; + UINT32 BusWidth = 8; + UINT32 TimingMode[4] = {EMMCHS52DDR1V2, EMMCHS52DDR1V8, EMMCHS52, EMMCHS26}; + + Host = MmcHostInstance->MmcHost; + ECSDData = MmcHostInstance->CardInfo.ECSDData; + if (ECSDData->DEVICE_TYPE == EMMCBACKWARD){ + return EFI_SUCCESS; + } + + if (PcdGet32(PcdMmcForceDefaultSpeed)) { + DEBUG((DEBUG_WARN, "Forcing default speed mode\n")); + return EFI_SUCCESS; + } + + if (PcdGet32(PcdMmcForce1Bit)) { + DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n")); + BusWidth = 1; + } + + if (!MMC_HOST_HAS_SETIOS(Host)) { + DEBUG((DEBUG_ERROR, "Controller doesn't support speed / bus width change\n")); + return EFI_SUCCESS; + } + + Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_HS_TIMING, EMMC_TIMING_HS); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to switch high speed mode, Status:%r.\n", Status)); + return Status; + } + + for (Idx = 0; Idx < 4; Idx++) { + switch (TimingMode[Idx]) { + case EMMCHS52DDR1V2: + case EMMCHS52DDR1V8: + case EMMCHS52: + BusClockFreq = 52000000; + break; + case EMMCHS26: + BusClockFreq = 26000000; + break; + default: + return EFI_UNSUPPORTED; + } + Status = Host->SetIos (Host, BusClockFreq, BusWidth, TimingMode[Idx]); + if (!EFI_ERROR (Status)) { + switch (TimingMode[Idx]) { + case EMMCHS52DDR1V2: + case EMMCHS52DDR1V8: + BusMode = EMMC_BUS_WIDTH_DDR_8BIT; + break; + case EMMCHS52: + case EMMCHS26: + BusMode = EMMC_BUS_WIDTH_8BIT; + break; + default: + return EFI_UNSUPPORTED; + } + Status = EmmcSetEXTCSD (MmcHostInstance, EXTCSD_BUS_WIDTH, BusMode); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "InitializeEmmcDevice(): Failed to set EXTCSD bus width, Status:%r\n", Status)); + } + return Status; + } + } + return Status; +} + +STATIC +UINT32 +SdSwitchCmdArgument ( + IN UINT32 AccessMode, + IN UINT32 CommandSystem, + IN UINT32 DriveStrength, + IN UINT32 PowerLimit, + IN BOOLEAN Mode + ) +{ + return (AccessMode & 0xF) | ((PowerLimit & 0xF) << 4) | \ + ((DriveStrength & 0xF) << 8) | ((DriveStrength & 0xF) << 12) | \ + (Mode ? BIT31 : 0); +} + +STATIC +EFI_STATUS +SdSelect ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + /* + * Moves a card from standby to transfer state. + */ + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, + MmcHostInstance->CardInfo.RCA << 16); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a: error: %r\n", + __FUNCTION__, Status)); + } + + return Status; +} + +STATIC +EFI_STATUS +SdDeselect ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + /* + * Moves a card from transfer to standby. + */ + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD7, 0); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a: error: %r\n", + __FUNCTION__, Status)); + } + + return Status; +} + +STATIC +EFI_STATUS +SdGetCsd( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN UINT32 *Response, + IN BOOLEAN Print + ) +{ + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD9, + MmcHostInstance->CardInfo.RCA << 16); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", __FUNCTION__, + __LINE__, Status)); + return Status; + } + + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CSD, + Response); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error %r\n", __FUNCTION__, + __LINE__, Status)); + return Status; + } + + if (Print) { + PrintCSD (Response); + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SdSet4Bit( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + UINT32 CmdArg; + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + if (PcdGet32(PcdMmcForce1Bit)) { + DEBUG((DEBUG_WARN, "Forcing 1 bit mode\n")); + return EFI_SUCCESS; + } + + if (!MMC_HOST_HAS_SETIOS(MmcHost)) { + DEBUG((DEBUG_WARN, "Controller doesn't support bus width change\n")); + return EFI_SUCCESS; + } + + CmdArg = MmcHostInstance->CardInfo.RCA << 16; + Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, CmdArg); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + /* Width: 4 */ + Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, 2); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + Status = MmcHost->SetIos (MmcHost, 0, BUSWIDTH_4, EMMCBACKWARD); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SdSetSpeed( + IN MMC_HOST_INSTANCE *MmcHostInstance, + IN BOOLEAN CccSwitch + ) +{ + UINT32 Speed; + UINT32 CmdArg; + EFI_STATUS Status; + UINT32 Buffer[16]; + UINT32 Response[4]; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + if (!MMC_HOST_HAS_SETIOS(MmcHost)) { + DEBUG((DEBUG_WARN, "Controller doesn't support speed change\n")); + return EFI_SUCCESS; + } + + Speed = PcdGet32(PcdMmcSdDefaultSpeedMHz) * 1000000; + if (Speed == 0) { + Speed = SD_DEFAULT_SPEED; + } else { + DEBUG((DEBUG_INFO, "Using default speed override %u Hz\n", + Speed)); + } + + /* + * First set base speed. We'll then try HS. + */ + Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n", + __FUNCTION__, Speed, Status)); + return Status; + } + + if (PcdGet32(PcdMmcForceDefaultSpeed)) { + DEBUG((DEBUG_WARN, "Forcing default speed mode\n")); + return EFI_SUCCESS; + } + + if (!CccSwitch) { + return EFI_SUCCESS; + } + + /* Query. */ + CmdArg = SdSwitchCmdArgument(0xf, 0xf, 0xf, 0xf, FALSE); + Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } else { + Status = MmcHost->ReadBlockData (MmcHost, 0, SWITCH_CMD_DATA_LENGTH, + Buffer); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + } + + if (!(Buffer[3] & SD_HIGH_SPEED_SUPPORTED)) { + DEBUG((DEBUG_ERROR, "%a: High Speed not supported by Card\n")); + return EFI_SUCCESS; + } + + /* Switch to high speed. */ + CmdArg = SdSwitchCmdArgument(1, 0xf, 0xf, 0xf, TRUE); + Status = MmcHost->SendCommand (MmcHost, MMC_CMD6, CmdArg); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } else { + Status = MmcHost->ReadBlockData (MmcHost, 0, + SWITCH_CMD_DATA_LENGTH, Buffer); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a(%u): error: %r\n", + __FUNCTION__, __LINE__, Status)); + return Status; + } + + if ((Buffer[4] & SWITCH_CMD_SUCCESS_MASK) != 0x1) { + DEBUG((DEBUG_ERROR, "Problem switching SD card into HS mode\n")); + DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n", + Buffer[0], Buffer[1], Buffer[2], Buffer[3])); + DEBUG((DEBUG_ERROR, "%08x %08x %08x %08x\n", + Buffer[4], Buffer[5], Buffer[6], Buffer[8])); + return Status; + } + } + + DEBUG((DEBUG_ERROR, "Dumping CSD after high-speed switch\n")); + SdDeselect(MmcHostInstance); + SdGetCsd(MmcHostInstance, Response, TRUE); + SdSelect(MmcHostInstance); + + Speed = PcdGet32(PcdMmcSdHighSpeedMHz) * 1000000; + if (Speed == 0) { + Speed = SD_HIGH_SPEED; + } else { + DEBUG((DEBUG_INFO, "Using high speed override %u Hz\n", + Speed)); + } + + Status = MmcHost->SetIos (MmcHost, Speed, 0, EMMCBACKWARD); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "%a: error setting speed %u: %r\n", + __FUNCTION__, Speed, Status)); + return Status; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +InitializeSdMmcDevice ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + UINT32 Response[4]; + UINT32 Buffer[128]; + UINTN BlockSize; + UINTN CardSize; + UINTN NumBlocks; + BOOLEAN CccSwitch; + SCR Scr; + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost = MmcHostInstance->MmcHost; + + Status = SdGetCsd (MmcHostInstance, Response, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + if (MMC_CSD_GET_CCC(Response) & SD_CCC_SWITCH) { + CccSwitch = TRUE; + } else { + CccSwitch = FALSE; + } + + if (MmcHostInstance->CardInfo.CardType == SD_CARD_2_HIGH) { + CardSize = HC_MMC_CSD_GET_DEVICESIZE (Response); + NumBlocks = ((CardSize + 1) * 1024); + BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response); + } else { + CardSize = MMC_CSD_GET_DEVICESIZE (Response); + NumBlocks = (CardSize + 1) * (1 << (MMC_CSD_GET_DEVICESIZEMULT (Response) + 2)); + BlockSize = 1 << MMC_CSD_GET_READBLLEN (Response); + } + + // For >=2G card, BlockSize may be 1K, but the transfer size is 512 bytes. + if (BlockSize > 512) { + NumBlocks = MultU64x32 (NumBlocks, BlockSize / 512); + BlockSize = 512; + } + + MmcHostInstance->BlockIo.Media->LastBlock = (NumBlocks - 1); + MmcHostInstance->BlockIo.Media->BlockSize = BlockSize; + MmcHostInstance->BlockIo.Media->ReadOnly = MmcHost->IsReadOnly (MmcHost); + MmcHostInstance->BlockIo.Media->MediaPresent = TRUE; + MmcHostInstance->BlockIo.Media->MediaId++; + + Status = SdSelect(MmcHostInstance); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, + MmcHostInstance->CardInfo.RCA << 16); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status)); + return Status; + } + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a (MMC_CMD55): Error and Status = %r\n", __FUNCTION__, Status)); + return Status; + } + if ((Response[0] & MMC_STATUS_APP_CMD) == 0) { + return EFI_SUCCESS; + } + + /* SCR */ + Status = MmcHost->SendCommand (MmcHost, MMC_ACMD51, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): Error and Status = %r\n", __func__, Status)); + return Status; + } else { + Status = MmcHost->ReadBlockData (MmcHost, 0, 8, Buffer); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a(MMC_ACMD51): ReadBlockData Error and Status = %r\n", __func__, Status)); + return Status; + } + CopyMem (&Scr, Buffer, 8); + if (Scr.SD_SPEC == 2) { + if (Scr.SD_SPEC3 == 1) { + if (Scr.SD_SPEC4 == 1) { + DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 4.xx\n")); + } else { + DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 3.0x\n")); + } + } else { + if (Scr.SD_SPEC4 == 0) { + DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 2.0\n")); + } else { + DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n")); + } + } + } else { + if ((Scr.SD_SPEC3 == 0) && (Scr.SD_SPEC4 == 0)) { + if (Scr.SD_SPEC == 1) { + DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.10\n")); + } else { + DEBUG ((DEBUG_INFO, "Found SD Card for Spec Version 1.0\n")); + } + } else { + DEBUG ((DEBUG_ERROR, "Found invalid SD Card\n")); + } + } + } + + Status = SdSetSpeed(MmcHostInstance, CccSwitch); + if (EFI_ERROR(Status)) { + return Status; + } + + if (Scr.SD_BUS_WIDTHS & SD_BUS_WIDTH_4BIT) { + Status = SdSet4Bit(MmcHostInstance); + if (EFI_ERROR(Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +EFIAPI +MmcIdentificationMode ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + EFI_STATUS Status; + UINT32 Response[4]; + UINTN Timeout; + UINTN CmdArg; + BOOLEAN IsHCS; + EFI_MMC_HOST_PROTOCOL *MmcHost; + OCR_RESPONSE OcrResponse; + + MmcHost = MmcHostInstance->MmcHost; + CmdArg = 0; + IsHCS = FALSE; + + if (MmcHost == NULL) { + return EFI_INVALID_PARAMETER; + } + + // We can get into this function if we restart the identification mode + if (MmcHostInstance->State == MmcHwInitializationState) { + // Initialize the MMC Host HW + Status = MmcNotifyState (MmcHostInstance, MmcHwInitializationState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcHwInitializationState, Status=%r.\n", Status)); + return Status; + } + } + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD0, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD0): Error, Status=%r.\n", Status)); + return Status; + } + Status = MmcNotifyState (MmcHostInstance, MmcIdleState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdleState, Status=%r.\n", Status)); + return Status; + } + + // Send CMD1 to get OCR (MMC) + // This command only valid for MMC and eMMC + Timeout = MAX_RETRY_COUNT; + do { + Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, EMMC_CMD1_CAPACITY_GREATER_THAN_2GB); + if (EFI_ERROR (Status)) + break; + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, (UINT32 *)&OcrResponse); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status)); + return Status; + } + Timeout--; + } while (!OcrResponse.Ocr.PowerUp && (Timeout > 0)); + if (Status == EFI_SUCCESS) { + if (!OcrResponse.Ocr.PowerUp) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD1): Card initialisation failure, Status=%r.\n", Status)); + return EFI_DEVICE_ERROR; + } + OcrResponse.Ocr.PowerUp = 0; + if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB) { + MmcHostInstance->CardInfo.OCRData.AccessMode = BIT1; + } + else { + MmcHostInstance->CardInfo.OCRData.AccessMode = 0x0; + } + // Check whether MMC or eMMC + if (OcrResponse.Raw == EMMC_CMD1_CAPACITY_GREATER_THAN_2GB || + OcrResponse.Raw == EMMC_CMD1_CAPACITY_LESS_THAN_2GB) { + return EmmcIdentificationMode (MmcHostInstance, OcrResponse); + } + } + + // Are we using SDIO ? + Status = MmcHost->SendCommand (MmcHost, MMC_CMD5, 0); + if (Status == EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD5): Error - SDIO not supported, Status=%r.\n", Status)); + return EFI_UNSUPPORTED; + } + + // Check which kind of card we are using. Ver2.00 or later SD Memory Card (PL180 is SD v1.1) + CmdArg = (0x0UL << 12 | BIT8 | 0xCEUL << 0); + Status = MmcHost->SendCommand (MmcHost, MMC_CMD8, CmdArg); + if (Status == EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR, "Card is SD2.0 => Supports high capacity\n")); + IsHCS = TRUE; + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R7, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive response to CMD8, Status=%r.\n", Status)); + return Status; + } + PrintResponseR1 (Response[0]); + // Check if it is valid response + if (Response[0] != CmdArg) { + DEBUG ((DEBUG_ERROR, "The Card is not usable\n")); + return EFI_UNSUPPORTED; + } + } else { + DEBUG ((DEBUG_ERROR, "Not a SD2.0 Card\n")); + } + + // We need to wait for the MMC or SD card is ready => (gCardInfo.OCRData.PowerUp == 1) + Timeout = MAX_RETRY_COUNT; + while (Timeout > 0) { + // SD Card or MMC Card ? CMD55 indicates to the card that the next command is an application specific command + Status = MmcHost->SendCommand (MmcHost, MMC_CMD55, 0); + if (Status == EFI_SUCCESS) { + DEBUG ((DEBUG_INFO, "Card should be SD\n")); + if (IsHCS) { + MmcHostInstance->CardInfo.CardType = SD_CARD_2; + } else { + MmcHostInstance->CardInfo.CardType = SD_CARD; + } + + // Note: The first time CmdArg will be zero + CmdArg = ((UINTN *) &(MmcHostInstance->CardInfo.OCRData))[0]; + if (IsHCS) { + CmdArg |= BIT30; + } + Status = MmcHost->SendCommand (MmcHost, MMC_ACMD41, CmdArg); + if (!EFI_ERROR (Status)) { + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status)); + return Status; + } + ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0]; + } + } else { + DEBUG ((DEBUG_INFO, "Card should be MMC\n")); + MmcHostInstance->CardInfo.CardType = MMC_CARD; + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD1, 0x800000); + if (!EFI_ERROR (Status)) { + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_OCR, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive OCR, Status=%r.\n", Status)); + return Status; + } + ((UINT32 *) &(MmcHostInstance->CardInfo.OCRData))[0] = Response[0]; + } + } + + if (!EFI_ERROR (Status)) { + if (!MmcHostInstance->CardInfo.OCRData.PowerUp) { + gBS->Stall (1); + Timeout--; + } else { + if ((MmcHostInstance->CardInfo.CardType == SD_CARD_2) && (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1)) { + MmcHostInstance->CardInfo.CardType = SD_CARD_2_HIGH; + DEBUG ((DEBUG_ERROR, "High capacity card.\n")); + } + break; // The MMC/SD card is ready. Continue the Identification Mode + } + } else { + gBS->Stall (1); + Timeout--; + } + } + + if (Timeout == 0) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(): No Card\n")); + return EFI_NO_MEDIA; + } else { + PrintOCR (Response[0]); + } + + Status = MmcNotifyState (MmcHostInstance, MmcReadyState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcReadyState\n")); + return Status; + } + + Status = MmcHost->SendCommand (MmcHost, MMC_CMD2, 0); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD2): Error\n")); + return Status; + } + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_CID, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive CID, Status=%r.\n", Status)); + return Status; + } + + PrintCID (Response); + + Status = MmcHost->NotifyState (MmcHost, MmcIdentificationState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcIdentificationState\n")); + return Status; + } + + // + // Note, SD specifications say that "if the command execution causes a state change, it + // will be visible to the host in the response to the next command" + // The status returned for this CMD3 will be 2 - identification + // + CmdArg = 1; + Status = MmcHost->SendCommand (MmcHost, MMC_CMD3, CmdArg); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode(MMC_CMD3): Error\n")); + return Status; + } + + Status = MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_RCA, Response); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Failed to receive RCA, Status=%r.\n", Status)); + return Status; + } + PrintRCA (Response[0]); + + // For MMC card, RCA is assigned by CMD3 while CMD3 dumps the RCA for SD card + if (MmcHostInstance->CardInfo.CardType != MMC_CARD) { + MmcHostInstance->CardInfo.RCA = Response[0] >> 16; + } else { + MmcHostInstance->CardInfo.RCA = CmdArg; + } + Status = MmcNotifyState (MmcHostInstance, MmcStandByState); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "MmcIdentificationMode() : Error MmcStandByState\n")); + return Status; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +InitializeMmcDevice ( + IN MMC_HOST_INSTANCE *MmcHostInstance + ) +{ + EFI_STATUS Status; + EFI_MMC_HOST_PROTOCOL *MmcHost; + UINTN BlockCount; + + BlockCount = 1; + MmcHost = MmcHostInstance->MmcHost; + + Status = MmcIdentificationMode (MmcHostInstance); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error in Identification Mode, Status=%r\n", Status)); + return Status; + } + + Status = MmcNotifyState (MmcHostInstance, MmcTransferState); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "InitializeMmcDevice(): Error MmcTransferState, Status=%r\n", Status)); + return Status; + } + + if (MmcHostInstance->CardInfo.CardType != EMMC_CARD) { + Status = InitializeSdMmcDevice (MmcHostInstance); + } else { + Status = InitializeEmmcDevice (MmcHostInstance); + } + if (EFI_ERROR (Status)) { + return Status; + } + + // Set Block Length + Status = MmcHost->SendCommand (MmcHost, MMC_CMD16, MmcHostInstance->BlockIo.Media->BlockSize); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD16): Error MmcHostInstance->BlockIo.Media->BlockSize: %d and Error = %r\n", + MmcHostInstance->BlockIo.Media->BlockSize, Status)); + return Status; + } + + // Block Count (not used). Could return an error for SD card + if (MmcHostInstance->CardInfo.CardType == MMC_CARD) { + Status = MmcHost->SendCommand (MmcHost, MMC_CMD23, BlockCount); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, "InitializeMmcDevice(MMC_CMD23): Error, Status=%r\n", Status)); + return Status; + } + } + + return EFI_SUCCESS; +} diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h new file mode 100644 index 000000000000..dfad80c719e1 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Include/Protocol/DwUsb.h @@ -0,0 +1,53 @@ +/** @file + * + * Copyright (c) 2015-2016, Linaro. All rights reserved. + * Copyright (c) 2015-2016, Hisilicon Limited. 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_USB_H__ +#define __DW_USB_H__ + +// +// Protocol GUID +// +#define DW_USB_PROTOCOL_GUID { 0x109fa264, 0x7811, 0x4862, { 0xa9, 0x73, 0x4a, 0xb2, 0xef, 0x2e, 0xe2, 0xff }} + +// +// Protocol interface structure +// +typedef struct _DW_USB_PROTOCOL DW_USB_PROTOCOL; + +#define USB_HOST_MODE 0 +#define USB_DEVICE_MODE 1 +#define USB_CABLE_NOT_ATTACHED 2 + +typedef +EFI_STATUS +(EFIAPI *DW_USB_GET_SERIAL_NO) ( + OUT CHAR16 *SerialNo, + OUT UINT8 *Length + ); + +typedef +EFI_STATUS +(EFIAPI *DW_USB_PHY_INIT) ( + IN UINT8 Mode + ); + +struct _DW_USB_PROTOCOL { + DW_USB_GET_SERIAL_NO Get; + DW_USB_PHY_INIT PhyInit; +}; + +extern EFI_GUID gDwUsbProtocolGuid; + +#endif diff --git a/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h new file mode 100644 index 000000000000..c0e65687e8d4 --- /dev/null +++ b/Platform/Broadcom/Bcm283x/Include/Protocol/PiMmcHost.h @@ -0,0 +1,187 @@ +/** @file + * + * Copyright (c) 2018, Andrei Warkentin + * Copyright (c) 2011-2014, ARM Limited. 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 __PI_MMC_HOST_H__ +#define __PI_MMC_HOST_H__ + +/// +/// Global ID for the MMC Host Protocol +/// +#define RASPBERRY_PI_MMC_HOST_PROTOCOL_GUID \ + { 0x3e591c00, 0x9e4a, 0x11df, {0x92, 0x44, 0x00, 0x02, 0xA5, 0xF5, 0xF5, 0x1B } } + +#define MMC_RESPONSE_TYPE_R1 0 +#define MMC_RESPONSE_TYPE_R1b 0 +#define MMC_RESPONSE_TYPE_R2 1 +#define MMC_RESPONSE_TYPE_R3 0 +#define MMC_RESPONSE_TYPE_R6 0 +#define MMC_RESPONSE_TYPE_R7 0 +#define MMC_RESPONSE_TYPE_OCR 0 +#define MMC_RESPONSE_TYPE_CID 1 +#define MMC_RESPONSE_TYPE_CSD 1 +#define MMC_RESPONSE_TYPE_RCA 0 + +typedef UINT32 MMC_RESPONSE_TYPE; + +typedef UINT32 MMC_CMD; + +#define MMC_CMD_WAIT_RESPONSE (1 << 16) +#define MMC_CMD_LONG_RESPONSE (1 << 17) +#define MMC_CMD_NO_CRC_RESPONSE (1 << 18) + +#define MMC_INDX(Index) ((Index) & 0xFFFF) +#define MMC_GET_INDX(MmcCmd) ((MmcCmd) & 0xFFFF) + +#define MMC_CMD0 (MMC_INDX(0) | MMC_CMD_NO_CRC_RESPONSE) +#define MMC_CMD1 (MMC_INDX(1) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE) +#define MMC_CMD2 (MMC_INDX(2) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE) +#define MMC_CMD3 (MMC_INDX(3) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD5 (MMC_INDX(5) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE) +#define MMC_CMD6 (MMC_INDX(6) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD7 (MMC_INDX(7) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD8 (MMC_INDX(8) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD9 (MMC_INDX(9) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_LONG_RESPONSE) +#define MMC_CMD11 (MMC_INDX(11) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD12 (MMC_INDX(12) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD13 (MMC_INDX(13) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD16 (MMC_INDX(16) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD17 (MMC_INDX(17) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD18 (MMC_INDX(18) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD20 (MMC_INDX(20) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD23 (MMC_INDX(23) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD24 (MMC_INDX(24) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD25 (MMC_INDX(25) | MMC_CMD_WAIT_RESPONSE) +#define MMC_CMD55 (MMC_INDX(55) | MMC_CMD_WAIT_RESPONSE) +#define MMC_ACMD22 (MMC_INDX(22) | MMC_CMD_WAIT_RESPONSE) +#define MMC_ACMD41 (MMC_INDX(41) | MMC_CMD_WAIT_RESPONSE | MMC_CMD_NO_CRC_RESPONSE) +#define MMC_ACMD51 (MMC_INDX(51) | MMC_CMD_WAIT_RESPONSE) + +// Valid responses for CMD1 in eMMC +#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 + +#define MMC_STATUS_APP_CMD (1 << 5) + +typedef enum _MMC_STATE { + MmcInvalidState = 0, + MmcHwInitializationState, + MmcIdleState, + MmcReadyState, + MmcIdentificationState, + MmcStandByState, + MmcTransferState, + MmcSendingDataState, + MmcReceiveDataState, + MmcProgrammingState, + MmcDisconnectState, +} MMC_STATE; + +#define EMMCBACKWARD (0) +#define EMMCHS26 (1 << 0) // High-Speed @26MHz at rated device voltages +#define EMMCHS52 (1 << 1) // High-Speed @52MHz at rated device voltages +#define EMMCHS52DDR1V8 (1 << 2) // High-Speed Dual Data Rate @52MHz 1.8V or 3V I/O +#define EMMCHS52DDR1V2 (1 << 3) // High-Speed Dual Data Rate @52MHz 1.2V I/O +#define EMMCHS200SDR1V8 (1 << 4) // HS200 Single Data Rate @200MHz 1.8V I/O +#define EMMCHS200SDR1V2 (1 << 5) // HS200 Single Data Rate @200MHz 1.2V I/O +#define EMMCHS400DDR1V8 (1 << 6) // HS400 Dual Data Rate @400MHz 1.8V I/O +#define EMMCHS400DDR1V2 (1 << 7) // HS400 Dual Data Rate @400MHz 1.2V I/O + +/// +/// Forward declaration for EFI_MMC_HOST_PROTOCOL +/// +typedef struct _EFI_MMC_HOST_PROTOCOL EFI_MMC_HOST_PROTOCOL; + +typedef BOOLEAN (EFIAPI *MMC_ISCARDPRESENT) ( + IN EFI_MMC_HOST_PROTOCOL *This + ); + +typedef BOOLEAN (EFIAPI *MMC_ISREADONLY) ( + IN EFI_MMC_HOST_PROTOCOL *This + ); + +typedef EFI_STATUS (EFIAPI *MMC_BUILDDEVICEPATH) ( + IN EFI_MMC_HOST_PROTOCOL *This, + OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath + ); + +typedef EFI_STATUS (EFIAPI *MMC_NOTIFYSTATE) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_STATE State + ); + +typedef EFI_STATUS (EFIAPI *MMC_SENDCOMMAND) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_CMD Cmd, + IN UINT32 Argument + ); + +typedef EFI_STATUS (EFIAPI *MMC_RECEIVERESPONSE) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_RESPONSE_TYPE Type, + IN UINT32 *Buffer + ); + +typedef EFI_STATUS (EFIAPI *MMC_READBLOCKDATA) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + OUT UINT32 *Buffer + ); + +typedef EFI_STATUS (EFIAPI *MMC_WRITEBLOCKDATA) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + IN UINT32 *Buffer + ); + +typedef EFI_STATUS (EFIAPI *MMC_SETIOS) ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN UINT32 BusClockFreq, + IN UINT32 BusWidth, + IN UINT32 TimingMode + ); + +typedef BOOLEAN (EFIAPI *MMC_ISMULTIBLOCK) ( + IN EFI_MMC_HOST_PROTOCOL *This + ); + +struct _EFI_MMC_HOST_PROTOCOL { + UINT32 Revision; + MMC_ISCARDPRESENT IsCardPresent; + MMC_ISREADONLY IsReadOnly; + MMC_BUILDDEVICEPATH BuildDevicePath; + + MMC_NOTIFYSTATE NotifyState; + + MMC_SENDCOMMAND SendCommand; + MMC_RECEIVERESPONSE ReceiveResponse; + + MMC_READBLOCKDATA ReadBlockData; + MMC_WRITEBLOCKDATA WriteBlockData; + + MMC_SETIOS SetIos; + MMC_ISMULTIBLOCK IsMultiBlock; +}; + +#define MMC_HOST_PROTOCOL_REVISION 0x00010002 // 1.2 + +#define MMC_HOST_HAS_SETIOS(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \ + Host->SetIos != NULL) +#define MMC_HOST_HAS_ISMULTIBLOCK(Host) (Host->Revision >= MMC_HOST_PROTOCOL_REVISION && \ + Host->IsMultiBlock != NULL) + +#endif + -- 2.17.0.windows.1