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::544; helo=mail-ed1-x544.google.com; envelope-from=pete@akeo.ie; receiver=edk2-devel@lists.01.org Received: from mail-ed1-x544.google.com (mail-ed1-x544.google.com [IPv6:2a00:1450:4864:20::544]) (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 5CE432194D387 for ; Tue, 5 Feb 2019 08:26:29 -0800 (PST) Received: by mail-ed1-x544.google.com with SMTP id r15so1904890eds.9 for ; Tue, 05 Feb 2019 08:26:29 -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=kD+bw0oPRGpDClQBC56RK4a0IHId6XttGS11mcyn4w0=; b=H/ZfkIgVBotNl4p9bliywrjwY/BJA2s2nUcz6twPhfCFxBGiPIzxz7RAUDyXBC/AXT 5bCb0pyveUgEuZgJpWRagm6x9lSPHkozVT/icO1GHFxpccGhR4NIJww9VmL+j/8oC9wj LK80DQZe3hwQhIqtXZY6bVxys8eRtVyp610z4XxJV/7iJ/HJxJkIvo5LZ8YvFLzsDkAj pdtjI9/ljwUYTzmjqlmLTH4U4tPjB6EeTU5g5hljUUbpA5VqC7ExJwxIHsWawvJDTcmU FWWZKcGhgXh0b8/oJD9oiKkM52yDZAv3LlSAfCMjmdEFXmrWdbANBmNNOpKWxdA2Fi82 6rUQ== 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=kD+bw0oPRGpDClQBC56RK4a0IHId6XttGS11mcyn4w0=; b=VtuyKLmMBdhetFDZexjXdO/z2RNPNEcjyGx8FoG6a55SLXhg6mxsCSg2nTMS0gmI81 7lum3jI3i4lPqaljHi/KjkOi1LXsGddsenEM7SwBtARuy8YvaEvJVtcnzMKGDNDU3DVd aFvsJc37S/VAW6JeonJa0pgr44C44jXOR9eoZ7VLTf6Mg+aI96kzXzKwXnd3xCDztSFP U212y/5FtuaC/dWh9MyB3iRJ8en+LcpwJBXmLY5dmJK9Cogd2e6aFkuvtjZViaIxOHGe 2jZiKbF4ufczPH9v7qWmcpGqBkjkcxG9nGUHxD+SdTZQk508X8xUltUywgJVkfUwR6ZE MLUA== X-Gm-Message-State: AHQUAuY+M5PA6mENFwVQ4Cn+HVirE1B+vc4v9D3IiFEmEFopBoX+IyEi ZwuLucJTUzvjbEwf/H9dCntgaVmZzCU= X-Google-Smtp-Source: AHgI3Ia8ZKnVe7FKAnxPdvk94E/nM4lRD/ugn7ireh/dhjA3W9nVkCAfms04PVRPfkC3uPT/KHKCUw== X-Received: by 2002:a50:f5b0:: with SMTP id u45mr4715166edm.45.1549383987055; Tue, 05 Feb 2019 08:26:27 -0800 (PST) Received: from localhost.localdomain ([84.203.58.139]) by smtp.gmail.com with ESMTPSA id j16sm3191430ejq.59.2019.02.05.08.26.25 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 05 Feb 2019 08:26:26 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Tue, 5 Feb 2019 16:25:30 +0000 Message-Id: <20190205162537.6472-16-pete@akeo.ie> X-Mailer: git-send-email 2.17.0.windows.1 In-Reply-To: <20190205162537.6472-1-pete@akeo.ie> References: <20190205162537.6472-1-pete@akeo.ie> Subject: [PATCH v5 edk2-platforms 15/22] Platform/RaspberryPi/RPi3: Add SD Host 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: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 X-List-Received-Date: Tue, 05 Feb 2019 16:26:30 -0000 The BCM283x family includes two SD card controllers, the second one being the "internal" SD HOST controller. This driver implements support for this one. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Pete Batard --- Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.c | 787 ++++++++++++++++++++ Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.inf | 55 ++ Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h | 92 +++ 3 files changed, 934 insertions(+) diff --git a/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.c b/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.c new file mode 100644 index 000000000000..3bf789f96b27 --- /dev/null +++ b/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.c @@ -0,0 +1,787 @@ +/** @file + * + * Copyright (c) 2017, Andrei Warkentin + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SDHOST_BLOCK_BYTE_LENGTH 512 + +// Driver Timing Parameters +#define CMD_STALL_AFTER_POLL_US 1 +#define CMD_MIN_POLL_TOTAL_TIME_US 100000 // 100ms +#define CMD_MAX_POLL_COUNT (CMD_MIN_POLL_TOTAL_TIME_US / CMD_STALL_AFTER_POLL_US) +#define CMD_MAX_RETRY_COUNT 3 +#define CMD_STALL_AFTER_RETRY_US 20 // 20us +#define FIFO_MAX_POLL_COUNT 1000000 +#define STALL_TO_STABILIZE_US 10000 // 10ms + +#define IDENT_MODE_SD_CLOCK_FREQ_HZ 400000 // 400KHz + +// Macros adopted from MmcDxe internal header +#define SDHOST_R0_READY_FOR_DATA BIT8 +#define SDHOST_R0_CURRENTSTATE(Response) ((Response >> 9) & 0xF) + +#define DEBUG_MMCHOST_SD DEBUG_VERBOSE +#define DEBUG_MMCHOST_SD_INFO DEBUG_INFO +#define DEBUG_MMCHOST_SD_ERROR DEBUG_ERROR + +STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol; + +// Per Physical Layer Simplified Specs +#ifndef NDEBUG +STATIC CONST CHAR8* mStrSdState[] = { "idle", "ready", "ident", "stby", + "tran", "data", "rcv", "prg", "dis", + "ina" }; +STATIC CONST CHAR8 *mFsmState[] = { "identmode", "datamode", "readdata", + "writedata", "readwait", "readcrc", + "writecrc", "writewait1", "powerdown", + "powerup", "writestart1", "writestart2", + "genpulses", "writewait2", "?", + "startpowdown" }; +#endif /* NDEBUG */ +STATIC UINT32 mLastGoodCmd = MMC_GET_INDX (MMC_CMD0); + +STATIC inline BOOLEAN +IsAppCmd ( + VOID + ) +{ + return mLastGoodCmd == MMC_CMD55; +} + +STATIC BOOLEAN +IsBusyCmd ( + IN UINT32 MmcCmd + ) +{ + if (IsAppCmd ()) { + return FALSE; + } + + return MmcCmd == MMC_CMD7 || MmcCmd == MMC_CMD12; +} + +STATIC BOOLEAN +IsWriteCmd ( + IN UINT32 MmcCmd + ) +{ + if (IsAppCmd ()) { + return FALSE; + } + + return MmcCmd == MMC_CMD24 || MmcCmd == MMC_CMD25; +} + +STATIC BOOLEAN +IsReadCmd ( + IN UINT32 MmcCmd, + IN UINT32 Argument + ) +{ + if (MmcCmd == MMC_CMD8 && !IsAppCmd ()) { + if (Argument == CMD8_MMC_ARG) { + DEBUG ((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n")); + return TRUE; + } else { + ASSERT (Argument == CMD8_SD_ARG); + DEBUG ((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n")); + return FALSE; + } + } + + return + (MmcCmd == MMC_CMD6 && !IsAppCmd ()) || + (MmcCmd == MMC_CMD17 && !IsAppCmd ()) || + (MmcCmd == MMC_CMD18 && !IsAppCmd ()) || + (MmcCmd == MMC_CMD13 && IsAppCmd ()) || + (MmcCmd == MMC_ACMD22 && IsAppCmd ()) || + (MmcCmd == MMC_ACMD51 && IsAppCmd ()); +} + +STATIC VOID +SdHostDumpRegisters ( + VOID + ) +{ + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: Registers Dump:\n")); + DEBUG ((DEBUG_MMCHOST_SD, " CMD: 0x%8.8X\n", MmioRead32 (SDHOST_CMD))); + DEBUG ((DEBUG_MMCHOST_SD, " ARG: 0x%8.8X\n", MmioRead32 (SDHOST_ARG))); + DEBUG ((DEBUG_MMCHOST_SD, " TOUT: 0x%8.8X\n", MmioRead32 (SDHOST_TOUT))); + DEBUG ((DEBUG_MMCHOST_SD, " CDIV: 0x%8.8X\n", MmioRead32 (SDHOST_CDIV))); + DEBUG ((DEBUG_MMCHOST_SD, " RSP0: 0x%8.8X\n", MmioRead32 (SDHOST_RSP0))); + DEBUG ((DEBUG_MMCHOST_SD, " RSP1: 0x%8.8X\n", MmioRead32 (SDHOST_RSP1))); + DEBUG ((DEBUG_MMCHOST_SD, " RSP2: 0x%8.8X\n", MmioRead32 (SDHOST_RSP2))); + DEBUG ((DEBUG_MMCHOST_SD, " RSP3: 0x%8.8X\n", MmioRead32 (SDHOST_RSP3))); + DEBUG ((DEBUG_MMCHOST_SD, " HSTS: 0x%8.8X\n", MmioRead32 (SDHOST_HSTS))); + DEBUG ((DEBUG_MMCHOST_SD, " VDD: 0x%8.8X\n", MmioRead32 (SDHOST_VDD))); + DEBUG ((DEBUG_MMCHOST_SD, " EDM: 0x%8.8X\n", MmioRead32 (SDHOST_EDM))); + DEBUG ((DEBUG_MMCHOST_SD, " HCFG: 0x%8.8X\n", MmioRead32 (SDHOST_HCFG))); + DEBUG ((DEBUG_MMCHOST_SD, " HBCT: 0x%8.8X\n", MmioRead32 (SDHOST_HBCT))); + DEBUG ((DEBUG_MMCHOST_SD, " HBLC: 0x%8.8X\n\n", MmioRead32 (SDHOST_HBLC))); +} + +#ifndef NDEBUG +STATIC EFI_STATUS +SdHostGetSdStatus ( + UINT32* SdStatus + ) +{ + ASSERT (SdStatus != NULL); + + // On command completion with R1 or R1b response type + // the SDCard status will be in RSP0 + UINT32 Rsp0 = MmioRead32 (SDHOST_RSP0); + if (Rsp0 != 0xFFFFFFFF) { + *SdStatus = Rsp0; + return EFI_SUCCESS; + } + + return EFI_NO_RESPONSE; +} +#endif /* NDEBUG */ + +STATIC VOID +SdHostDumpSdCardStatus ( + VOID + ) +{ +#ifndef NDEBUG + UINT32 SdCardStatus; + EFI_STATUS Status = SdHostGetSdStatus (&SdCardStatus); + if (!EFI_ERROR (Status)) { + UINT32 CurrState = SDHOST_R0_CURRENTSTATE (SdCardStatus); + DEBUG ((DEBUG_MMCHOST_SD, + "SdHost: SdCardStatus 0x%8.8X: ReadyForData?%d, State[%d]: %a\n", + SdCardStatus, + ((SdCardStatus & SDHOST_R0_READY_FOR_DATA) ? 1 : 0), + CurrState, + ((CurrState < (sizeof (mStrSdState) / sizeof (*mStrSdState))) ? + mStrSdState[CurrState] : "UNDEF"))); + } +#endif /* NDEBUG */ +} + +STATIC VOID +SdHostDumpStatus ( + VOID + ) +{ + SdHostDumpRegisters (); + +#ifndef NDEBUG + UINT32 Hsts = MmioRead32 (SDHOST_HSTS); + + if (Hsts & SDHOST_HSTS_ERROR) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: Diagnose HSTS: 0x%8.8X\n", Hsts)); + + DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: Last Good CMD = %u\n", MMC_GET_INDX (mLastGoodCmd))); + if (Hsts & SDHOST_HSTS_FIFO_ERROR) + DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - Fifo Error\n")); + if (Hsts & SDHOST_HSTS_CRC7_ERROR) + DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CRC7 Error\n")); + if (Hsts & SDHOST_HSTS_CRC16_ERROR) + DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CRC16 Error\n")); + if (Hsts & SDHOST_HSTS_CMD_TIME_OUT) + DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - CMD Timeout (TOUT %x)\n", MmioRead32 (SDHOST_TOUT))); + if (Hsts & SDHOST_HSTS_REW_TIME_OUT) + DEBUG ((DEBUG_MMCHOST_SD_ERROR, " - Read/Erase/Write Transfer Timeout\n")); + } + + UINT32 Edm = MmioRead32 (SDHOST_EDM); + DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, + "SdHost: Diagnose EDM: 0x%8.8X\n", Edm)); + DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, + " - FSM: 0x%x (%a)\n", (Edm & 0xF), mFsmState[Edm & 0xF])); + DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, + " - Fifo Count: %d\n", ((Edm >> 4) & 0x1F))); + DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, + " - Fifo Write Threshold: %d\n", + ((Edm >> SDHOST_EDM_WRITE_THRESHOLD_SHIFT) & SDHOST_EDM_THRESHOLD_MASK))); + DEBUG (((Hsts & SDHOST_HSTS_ERROR) ? DEBUG_MMCHOST_SD_ERROR : DEBUG_MMCHOST_SD, + " - Fifo Read Threshold: %d\n", + ((Edm >> SDHOST_EDM_READ_THRESHOLD_SHIFT) & SDHOST_EDM_THRESHOLD_MASK))); +#endif + + SdHostDumpSdCardStatus (); +} + +STATIC EFI_STATUS +SdHostSetClockFrequency ( + IN UINTN TargetSdFreqHz + ) +{ + EFI_STATUS Status; + UINT32 CoreClockFreqHz = 0; + + // First figure out the core clock + Status = mFwProtocol->GetClockRate (RPI_MBOX_CLOCK_RATE_CORE, &CoreClockFreqHz); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (CoreClockFreqHz != 0); + + // fSDCLK = fcore_pclk/(ClockDiv+2) + UINT32 ClockDiv = (CoreClockFreqHz - (2 * TargetSdFreqHz)) / TargetSdFreqHz; + UINT32 ActualSdFreqHz = CoreClockFreqHz / (ClockDiv + 2); + + DEBUG ((DEBUG_MMCHOST_SD_INFO, + "SdHost: CoreClock=%dHz, CDIV=%d, Requested SdClock=%dHz, Actual SdClock=%dHz\n", + CoreClockFreqHz, ClockDiv, TargetSdFreqHz, ActualSdFreqHz)); + + MmioWrite32 (SDHOST_CDIV, ClockDiv); + // Set timeout after 1 second, i.e ActualSdFreqHz SD clock cycles + MmioWrite32 (SDHOST_TOUT, ActualSdFreqHz); + + gBS->Stall (STALL_TO_STABILIZE_US); + + return Status; +} + +STATIC BOOLEAN +SdIsCardPresent ( + IN EFI_MMC_HOST_PROTOCOL *This + ) +{ + return TRUE; +} + +STATIC BOOLEAN +SdIsReadOnly ( + IN EFI_MMC_HOST_PROTOCOL *This + ) +{ + return FALSE; +} + +STATIC EFI_STATUS +SdBuildDevicePath ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL **DevicePath + ) +{ + EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode; + EFI_GUID DevicePathGuid = EFI_CALLER_ID_GUID; + + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdBuildDevicePath()\n")); + + NewDevicePathNode = CreateDeviceNode (HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (VENDOR_DEVICE_PATH)); + CopyGuid (&((VENDOR_DEVICE_PATH*)NewDevicePathNode)->Guid, &DevicePathGuid); + *DevicePath = NewDevicePathNode; + + return EFI_SUCCESS; +} + +STATIC EFI_STATUS +SdSendCommand ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_CMD MmcCmd, + IN UINT32 Argument + ) +{ + UINT32 Hsts; + + // + // Fail fast, CMD5 (CMD_IO_SEND_OP_COND) + // is only valid for SDIO cards and thus + // expected to always fail. + // + if (MmcCmd == MMC_CMD5) { + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdSendCommand(CMD%d, Argument: %08x) ignored\n", + MMC_GET_INDX (MmcCmd), Argument)); + return EFI_UNSUPPORTED; + } + + if (MmioRead32 (SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, + "SdHost: SdSendCommand(): Failed to execute CMD%d, a CMD is already being executed.\n", + MMC_GET_INDX (MmcCmd))); + SdHostDumpStatus (); + return EFI_DEVICE_ERROR; + } + + // Write command argument + MmioWrite32 (SDHOST_ARG, Argument); + + UINT32 SdCmd = 0; + { + // Set response type + if (MmcCmd & MMC_CMD_WAIT_RESPONSE) { + if (MmcCmd & MMC_CMD_LONG_RESPONSE) { + SdCmd |= SDHOST_CMD_RESPONSE_CMD_LONG_RESP; + } + } else { + SdCmd |= SDHOST_CMD_RESPONSE_CMD_NO_RESP; + } + + if (IsBusyCmd (MmcCmd)) { + SdCmd |= SDHOST_CMD_BUSY_CMD; + } + + if (IsReadCmd (MmcCmd, Argument)) { + SdCmd |= SDHOST_CMD_READ_CMD; + } + + if (IsWriteCmd (MmcCmd)) { + SdCmd |= SDHOST_CMD_WRITE_CMD; + } + + SdCmd |= MMC_GET_INDX (MmcCmd); + } + + if (IsReadCmd (MmcCmd, Argument) || IsWriteCmd (MmcCmd)) { + if (IsAppCmd () && MmcCmd == MMC_ACMD22) { + MmioWrite32 (SDHOST_HBCT, 0x4); + } else if (IsAppCmd () && MmcCmd == MMC_ACMD51) { + MmioWrite32 (SDHOST_HBCT, 0x8); + } else if (!IsAppCmd () && MmcCmd == MMC_CMD6) { + MmioWrite32 (SDHOST_HBCT, 0x40); + } else { + MmioWrite32 (SDHOST_HBCT, SDHOST_BLOCK_BYTE_LENGTH); + } + } + + DEBUG ((DEBUG_MMCHOST_SD, + "SdHost: SdSendCommand(CMD%d, Argument: %08x): BUSY=%d, RESP=%d, WRITE=%d, READ=%d\n", + MMC_GET_INDX (MmcCmd), Argument, ((SdCmd & SDHOST_CMD_BUSY_CMD) ? 1 : 0), + ((SdCmd & (SDHOST_CMD_RESPONSE_CMD_LONG_RESP | SDHOST_CMD_RESPONSE_CMD_NO_RESP)) >> 9), + ((SdCmd & SDHOST_CMD_WRITE_CMD) ? 1 : 0), ((SdCmd & SDHOST_CMD_READ_CMD) ? 1 : 0))); + + UINT32 PollCount = 0; + UINT32 RetryCount = 0; + BOOLEAN IsCmdExecuted = FALSE; + EFI_STATUS Status = EFI_SUCCESS; + + // Keep retrying the command until it succeeds. + while ((RetryCount < CMD_MAX_RETRY_COUNT) && !IsCmdExecuted) { + Status = EFI_SUCCESS; + + // Clear prev cmd status + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); + + if (IsReadCmd (MmcCmd, Argument) || IsWriteCmd (MmcCmd)) { + // Flush Fifo if this cmd will start a new transfer in case + // there is stale bytes in the Fifo + MmioOr32 (SDHOST_EDM, SDHOST_EDM_FIFO_CLEAR); + } + + if (MmioRead32 (SDHOST_CMD) & SDHOST_CMD_NEW_FLAG) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, + "%a(%u): CMD%d is still being executed after %d trial(s)\n", + __FUNCTION__, __LINE__, MMC_GET_INDX (MmcCmd), RetryCount)); + } + + // Write command and set it to start execution + MmioWrite32 (SDHOST_CMD, SDHOST_CMD_NEW_FLAG | SdCmd); + + // Poll for the command status until it finishes execution + while (PollCount < CMD_MAX_POLL_COUNT) { + UINT32 CmdReg = MmioRead32 (SDHOST_CMD); + + // Read status of command response + if (CmdReg & SDHOST_CMD_FAIL_FLAG) { + Status = EFI_DEVICE_ERROR; + /* + * Must fall-through and wait for the command completion! + */ + } + + // Check if command is completed. + if (!(CmdReg & SDHOST_CMD_NEW_FLAG)) { + IsCmdExecuted = TRUE; + break; + } + + ++PollCount; + gBS->Stall (CMD_STALL_AFTER_POLL_US); + } + + if (!IsCmdExecuted) { + ++RetryCount; + gBS->Stall (CMD_STALL_AFTER_RETRY_US); + } + } + + if (RetryCount == CMD_MAX_RETRY_COUNT) { + Status = EFI_TIMEOUT; + } + + Hsts = MmioRead32 (SDHOST_HSTS); + if (EFI_ERROR (Status) || + (Hsts & SDHOST_HSTS_ERROR) != 0) { + if (MmcCmd == MMC_CMD1 && (Hsts & SDHOST_HSTS_CRC7_ERROR) != 0) { + /* + * SdHost seems to have no way to specify + * R3 as a transfer type. + */ + IsCmdExecuted = TRUE; + Status = EFI_SUCCESS; + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); + } else if (MmcCmd == MMC_CMD7 && Argument == 0) { + /* + * Deselecting the SDCard with CMD7 and RCA=0x0 + * always timeout on SDHost. + */ + Status = EFI_SUCCESS; + } else { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, "%a(%u): CMD%d execution failed after %d trial(s)\n", + __FUNCTION__, __LINE__, MMC_GET_INDX (MmcCmd), RetryCount)); + SdHostDumpStatus (); + } + + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); + } + + if (IsCmdExecuted && !EFI_ERROR (Status)) { + ASSERT (!(MmioRead32 (SDHOST_HSTS) & SDHOST_HSTS_ERROR)); + mLastGoodCmd = MmcCmd; + } + + return Status; +} + +STATIC EFI_STATUS +SdReceiveResponse ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_RESPONSE_TYPE Type, + IN UINT32* Buffer + ) +{ + if (Buffer == NULL) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdReceiveResponse(): Input Buffer is NULL\n")); + return EFI_INVALID_PARAMETER; + } + + if ((Type == MMC_RESPONSE_TYPE_R1) || + (Type == MMC_RESPONSE_TYPE_R1b) || + (Type == MMC_RESPONSE_TYPE_R3) || + (Type == MMC_RESPONSE_TYPE_R6) || + (Type == MMC_RESPONSE_TYPE_R7)) { + Buffer[0] = MmioRead32 (SDHOST_RSP0); + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdReceiveResponse(Type: %x), Buffer[0]: %08x\n", + Type, Buffer[0])); + + } else if (Type == MMC_RESPONSE_TYPE_R2) { + Buffer[0] = MmioRead32 (SDHOST_RSP0); + Buffer[1] = MmioRead32 (SDHOST_RSP1); + Buffer[2] = MmioRead32 (SDHOST_RSP2); + Buffer[3] = MmioRead32 (SDHOST_RSP3); + + DEBUG ((DEBUG_MMCHOST_SD, + "SdHost: SdReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n", + Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3])); + } + + return EFI_SUCCESS; +} + +STATIC EFI_STATUS +SdReadBlockData ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + IN UINT32* Buffer + ) +{ + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdReadBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", + (UINT32)Lba, Length, Buffer)); + + ASSERT (Buffer != NULL); + ASSERT (Length % 4 == 0); + + EFI_STATUS Status = EFI_SUCCESS; + + mFwProtocol->SetLed (TRUE); + { + UINT32 NumWords = Length / 4; + UINT32 WordIdx; + + for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) { + UINT32 PollCount = 0; + while (PollCount < FIFO_MAX_POLL_COUNT) { + UINT32 Hsts = MmioRead32 (SDHOST_HSTS); + if ((Hsts & SDHOST_HSTS_DATA_FLAG) != 0) { + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG); + Buffer[WordIdx] = MmioRead32 (SDHOST_DATA); + break; + } + + ++PollCount; + gBS->Stall (CMD_STALL_AFTER_RETRY_US); + } + + if (PollCount == FIFO_MAX_POLL_COUNT) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, + "SdHost: SdReadBlockData(): Block Word%d read poll timed-out\n", WordIdx)); + SdHostDumpStatus (); + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); + Status = EFI_TIMEOUT; + break; + } + } + } + mFwProtocol->SetLed (FALSE); + + return Status; +} + +STATIC EFI_STATUS +SdWriteBlockData ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + IN UINT32* Buffer + ) +{ + DEBUG ((DEBUG_MMCHOST_SD, + "SdHost: SdWriteBlockData(LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", + (UINT32)Lba, Length, Buffer)); + + ASSERT (Buffer != NULL); + ASSERT (Length % SDHOST_BLOCK_BYTE_LENGTH == 0); + + EFI_STATUS Status = EFI_SUCCESS; + + mFwProtocol->SetLed (TRUE); + { + UINT32 NumWords = Length / 4; + UINT32 WordIdx; + + for (WordIdx = 0; WordIdx < NumWords; ++WordIdx) { + UINT32 PollCount = 0; + while (PollCount < FIFO_MAX_POLL_COUNT) { + if (MmioRead32 (SDHOST_HSTS) & SDHOST_HSTS_DATA_FLAG) { + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_DATA_FLAG); + MmioWrite32 (SDHOST_DATA, Buffer[WordIdx]); + break; + } + + ++PollCount; + gBS->Stall (CMD_STALL_AFTER_RETRY_US); + } + + if (PollCount == FIFO_MAX_POLL_COUNT) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, + "SdHost: SdWriteBlockData(): Block Word%d write poll timed-out\n", WordIdx)); + SdHostDumpStatus (); + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR); + Status = EFI_TIMEOUT; + break; + } + } + } + mFwProtocol->SetLed (FALSE); + + return Status; +} + +STATIC EFI_STATUS +SdSetIos ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN UINT32 BusClockFreq, + IN UINT32 BusWidth, + IN UINT32 TimingMode + ) +{ + if (BusWidth != 0) { + UINT32 Hcfg = MmioRead32 (SDHOST_HCFG); + + DEBUG ((DEBUG_MMCHOST_SD_INFO, "Setting BusWidth %u\n", BusWidth)); + if (BusWidth == 4) { + Hcfg |= SDHOST_HCFG_WIDE_EXT_BUS; + } else { + Hcfg &= ~SDHOST_HCFG_WIDE_EXT_BUS; + } + + Hcfg |= SDHOST_HCFG_WIDE_INT_BUS | SDHOST_HCFG_SLOW_CARD; + MmioWrite32 (SDHOST_HCFG, Hcfg); + } + + if (BusClockFreq != 0) { + DEBUG ((DEBUG_MMCHOST_SD_INFO, "Setting Freq %u Hz\n", BusClockFreq)); + SdHostSetClockFrequency (BusClockFreq); + } + + return EFI_SUCCESS; +} + +STATIC EFI_STATUS +SdNotifyState ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_STATE State + ) +{ + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: SdNotifyState(State: %d) ", State)); + + switch (State) { + case MmcHwInitializationState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcHwInitializationState\n", State)); + + // Turn-off SD Card power + MmioWrite32 (SDHOST_VDD, 0); + + // Reset command and arg + MmioWrite32 (SDHOST_CMD, 0); + MmioWrite32 (SDHOST_ARG, 0); + // Reset clock divider + MmioWrite32 (SDHOST_CDIV, 0); + // Default timeout + MmioWrite32 (SDHOST_TOUT, 0xffffffff); + // Clear status flags + MmioWrite32 (SDHOST_HSTS, SDHOST_HSTS_CLEAR);; + // Reset controller configs + MmioWrite32 (SDHOST_HCFG, 0); + MmioWrite32 (SDHOST_HBCT, 0); + MmioWrite32 (SDHOST_HBLC, 0); + + gBS->Stall (STALL_TO_STABILIZE_US); + + // Turn-on SD Card power + MmioWrite32 (SDHOST_VDD, 1); + + gBS->Stall (STALL_TO_STABILIZE_US); + + // Write controller configs + UINT32 Hcfg = 0; + Hcfg |= SDHOST_HCFG_WIDE_INT_BUS; + Hcfg |= SDHOST_HCFG_SLOW_CARD; // Use all bits of CDIV in DataMode + MmioWrite32 (SDHOST_HCFG, Hcfg); + + // Set default clock frequency + EFI_STATUS Status = SdHostSetClockFrequency (IDENT_MODE_SD_CLOCK_FREQ_HZ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_MMCHOST_SD_ERROR, + "SdHost: SdNotifyState(): Fail to initialize SD clock to %dHz\n", + IDENT_MODE_SD_CLOCK_FREQ_HZ)); + SdHostDumpStatus (); + return Status; + } + break; + case MmcIdleState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcIdleState\n", State)); + break; + case MmcReadyState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcReadyState\n", State)); + break; + case MmcIdentificationState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcIdentificationState\n", State)); + break; + case MmcStandByState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcStandByState\n", State)); + break; + case MmcTransferState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcTransferState\n", State)); + break; + break; + case MmcSendingDataState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcSendingDataState\n", State)); + break; + case MmcReceiveDataState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcReceiveDataState\n", State)); + break; + case MmcProgrammingState: + DEBUG ((DEBUG_MMCHOST_SD, "MmcProgrammingState\n", State)); + break; + case MmcDisconnectState: + case MmcInvalidState: + default: + DEBUG ((DEBUG_MMCHOST_SD_ERROR, "SdHost: SdNotifyState(): Invalid State: %d\n", State)); + ASSERT (0); + } + + return EFI_SUCCESS; +} + +BOOLEAN +SdIsMultiBlock ( + IN EFI_MMC_HOST_PROTOCOL *This + ) +{ + return TRUE; +} + +EFI_MMC_HOST_PROTOCOL gMmcHost = + { + MMC_HOST_PROTOCOL_REVISION, + SdIsCardPresent, + SdIsReadOnly, + SdBuildDevicePath, + SdNotifyState, + SdSendCommand, + SdReceiveResponse, + SdReadBlockData, + SdWriteBlockData, + SdSetIos, + SdIsMultiBlock + }; + +EFI_STATUS +SdHostInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle = NULL; + + if (PcdGet32 (PcdSdIsArasan)) { + DEBUG ((DEBUG_INFO, "SD is not routed to SdHost\n")); + return EFI_REQUEST_UNLOAD_IMAGE; + } + + Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL, (VOID**)&mFwProtocol); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_MMCHOST_SD, "SdHost: Initialize\n")); + DEBUG ((DEBUG_MMCHOST_SD, "Config:\n")); + DEBUG ((DEBUG_MMCHOST_SD, " - FIFO_MAX_POLL_COUNT=%d\n", FIFO_MAX_POLL_COUNT)); + DEBUG ((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_POLL_US=%dus\n", CMD_STALL_AFTER_POLL_US)); + DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MIN_POLL_TOTAL_TIME_US=%dms\n", CMD_MIN_POLL_TOTAL_TIME_US / 1000)); + DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MAX_POLL_COUNT=%d\n", CMD_MAX_POLL_COUNT)); + DEBUG ((DEBUG_MMCHOST_SD, " - CMD_MAX_RETRY_COUNT=%d\n", CMD_MAX_RETRY_COUNT)); + DEBUG ((DEBUG_MMCHOST_SD, " - CMD_STALL_AFTER_RETRY_US=%dus\n", CMD_STALL_AFTER_RETRY_US)); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gRaspberryPiMmcHostProtocolGuid, + &gMmcHost, + NULL + ); + ASSERT_EFI_ERROR (Status); + return Status; +} diff --git a/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.inf b/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.inf new file mode 100644 index 000000000000..49d25a84154a --- /dev/null +++ b/Platform/RaspberryPi/RPi3/Drivers/SdHostDxe/SdHostDxe.inf @@ -0,0 +1,55 @@ +#/** @file +# +# Copyright (c) 2017, Andrei Warkentin +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +#**/ + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = SdHost + FILE_GUID = 58ABD787-F64D-4CA2-A034-B9AC2D5AD0CF + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = SdHostInitialize + + +[Sources.common] + SdHostDxe.c + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + Silicon/Broadcom/Bcm283x/Bcm283x.dec + Platform/RaspberryPi/RPi3/RPi3.dec + +[LibraryClasses] + PcdLib + UefiLib + UefiDriverEntryPoint + MemoryAllocationLib + IoLib + DmaLib + CacheMaintenanceLib + +[Guids] + +[Protocols] + gRaspberryPiMmcHostProtocolGuid ## PRODUCES + gRaspberryPiFirmwareProtocolGuid ## CONSUMES + +[Pcd] + gRaspberryPiTokenSpaceGuid.PcdSdIsArasan + +[Depex] + gRaspberryPiFirmwareProtocolGuid AND gRaspberryPiConfigAppliedProtocolGuid diff --git a/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h b/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h new file mode 100644 index 000000000000..1841cc285751 --- /dev/null +++ b/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836SdHost.h @@ -0,0 +1,92 @@ +/** @file + * + * Copyright (c) 2017, Andrei Warkentin + * Copyright (c) Microsoft Corporation. All rights reserved. + * + * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + **/ + +#ifndef __BCM2836_SDHOST_H__ +#define __BCM2836_SDHOST_H__ + +#define SDHOST_BASE_ADDRESS (BCM2836_SOC_REGISTERS + 0x00202000) +#define SDHOST_REG(X) (SDHOST_BASE_ADDRESS + (X)) +#define SDHOST_CMD SDHOST_REG(0x0) +#define SDHOST_ARG SDHOST_REG(0x4) +#define SDHOST_TOUT SDHOST_REG(0x8) +#define SDHOST_CDIV SDHOST_REG(0xC) +#define SDHOST_RSP0 SDHOST_REG(0x10) // [31:0] +#define SDHOST_RSP1 SDHOST_REG(0x14) // [63:32] +#define SDHOST_RSP2 SDHOST_REG(0x18) // [95:64] +#define SDHOST_RSP3 SDHOST_REG(0x1C) // [127:96] +#define SDHOST_HSTS SDHOST_REG(0x20) +#define SDHOST_VDD SDHOST_REG(0x30) +#define SDHOST_EDM SDHOST_REG(0x34) +#define SDHOST_HCFG SDHOST_REG(0x38) +#define SDHOST_HBCT SDHOST_REG(0x3C) +#define SDHOST_DATA SDHOST_REG(0x40) +#define SDHOST_HBLC SDHOST_REG(0x50) + +// +// CMD +// +#define SDHOST_CMD_READ_CMD BIT6 +#define SDHOST_CMD_WRITE_CMD BIT7 +#define SDHOST_CMD_RESPONSE_CMD_LONG_RESP BIT9 +#define SDHOST_CMD_RESPONSE_CMD_NO_RESP BIT10 +#define SDHOST_CMD_BUSY_CMD BIT11 +#define SDHOST_CMD_FAIL_FLAG BIT14 +#define SDHOST_CMD_NEW_FLAG BIT15 + +// +// VDD +// +#define SDHOST_VDD_POWER_ON BIT0 + +// +// HSTS +// +#define SDHOST_HSTS_CLEAR 0x7F8 +#define SDHOST_HSTS_BLOCK_IRPT BIT9 +#define SDHOST_HSTS_REW_TIME_OUT BIT7 +#define SDHOST_HSTS_CMD_TIME_OUT BIT6 +#define SDHOST_HSTS_CRC16_ERROR BIT5 +#define SDHOST_HSTS_CRC7_ERROR BIT4 +#define SDHOST_HSTS_FIFO_ERROR BIT3 +#define SDHOST_HSTS_DATA_FLAG BIT0 + +#define SDHOST_HSTS_TIMOUT_ERROR (SDHOST_HSTS_CMD_TIME_OUT | SDHOST_HSTS_REW_TIME_OUT) +#define SDHOST_HSTS_TRANSFER_ERROR (SDHOST_HSTS_FIFO_ERROR | SDHOST_HSTS_CRC7_ERROR | SDHOST_HSTS_CRC16_ERROR) +#define SDHOST_HSTS_ERROR (SDHOST_HSTS_TIMOUT_ERROR | SDHOST_HSTS_TRANSFER_ERROR) + +// +// HCFG +// +#define SDHOST_HCFG_SLOW_CARD BIT3 +#define SDHOST_HCFG_WIDE_EXT_BUS BIT2 +#define SDHOST_HCFG_WIDE_INT_BUS BIT1 +#define SDHOST_HCFG_DATA_IRPT_EN BIT4 +#define SDHOST_HCFG_BLOCK_IRPT_EN BIT8 +#define SDHOST_HCFG_BUSY_IRPT_EN BIT10 + +// +// EDM +// +#define SDHOST_EDM_FIFO_CLEAR BIT21 +#define SDHOST_EDM_WRITE_THRESHOLD_SHIFT 9 +#define SDHOST_EDM_READ_THRESHOLD_SHIFT 14 +#define SDHOST_EDM_THRESHOLD_MASK 0x1F +#define SDHOST_EDM_READ_THRESHOLD(X) ((X) << SDHOST_EDM_READ_THRESHOLD_SHIFT) +#define SDHOST_EDM_WRITE_THRESHOLD(X) ((X) << SDHOST_EDM_WRITE_THRESHOLD_SHIFT) + +#define CMD8_SD_ARG (0x0UL << 12 | BIT8 | 0xCEUL << 0) +#define CMD8_MMC_ARG (0) + +#endif /*__BCM2836_SDHOST_H__ */ -- 2.17.0.windows.1