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 D47EF2194D387 for ; Mon, 28 Jan 2019 04:45:36 -0800 (PST) Received: by mail-ed1-x544.google.com with SMTP id x30so12922921edx.2 for ; Mon, 28 Jan 2019 04:45:36 -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=p0+bbcNuKHpOxOI/DiL9zK/4PgPLnz2mnZinZlUtCiU=; b=lO0YU3aOQjB6NGbhCd8/WKCyWjpm2Z5jJhMa+IixYy2mkDvXOqco5jBVjBmhAY8Iwj g/WFklev1cDG8h+HgxypecS1TZKzOBixBIHGZwCoRWImjRH2OqE+W57pNIllrqXS0zeC KeVd2bOIWb8KMQOglDLntEZ/qynbLv4r3cg/LSmlH6c6AugIORbeYg27dq7fO8XpBspZ Ji6o7MHMmX344CSgtPYWca+lgEtf8G1jhDQRNHbqe+DQGhLu9TKN2IRc1KUXAeIWDNce g/FMBiFrJNZAXxnRZwQfsOUoZDz3yZOJM0EK4THgPUEwerf+9LRhwJM0dp8j7mk9jY1U e5+g== 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=p0+bbcNuKHpOxOI/DiL9zK/4PgPLnz2mnZinZlUtCiU=; b=KIhMVmhStjxCvKDNZC2gDRmgEwOuYJUvn4T83HPN7mEDlYaCFT4knxyoDXEoyEuGAR Xzug4VdbyhVAgFbMjrDJkZE5RMI9O89o+dsJ9iSRCJVSukZA1Y+j+Klyn9oHjjVviCKp Y/mq6bewste/0nZYZJUUXHbzGeCoIKSkQtHhmx24lY5/5Z6PiO7Xii8fwQ1Rpe17Arwr scUS1Pw6UTGDQQ5wyVHK5ArbPPsx3VNe+6Pcl0Qe3IDH0xm3LSkZR0alLM8QaOv/R+T6 uK5a95D1+AJgEWt3wt4Ah6rQXOdl4pZGtzPH755aP78WIhkrKtfYo8xQaqnK5x3p05SU sWmA== X-Gm-Message-State: AJcUukdzB4/2s/xpoWrNOLM5IBASbPo2VDwRyzVaRecArzCskIacacO7 uPx0uEMoXicLKhVUDpSnrhweW0aWOJg= X-Google-Smtp-Source: ALg8bN6J3JVL36wTvOORm8YiUM9wHfDo31sUkKIh7EoKCLvcPV2lbNtUbwsV2MsZls3mzXOsh2VkCQ== X-Received: by 2002:a50:9ac4:: with SMTP id p62mr21744043edb.179.1548679534631; Mon, 28 Jan 2019 04:45:34 -0800 (PST) Received: from localhost.localdomain ([84.203.95.186]) by smtp.gmail.com with ESMTPSA id y16sm13757367edb.41.2019.01.28.04.45.32 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 28 Jan 2019 04:45:33 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Mon, 28 Jan 2019 12:44:37 +0000 Message-Id: <20190128124445.9868-16-pete@akeo.ie> X-Mailer: git-send-email 2.17.0.windows.1 In-Reply-To: <20190128124445.9868-1-pete@akeo.ie> References: <20190128124445.9868-1-pete@akeo.ie> Subject: [PATCH v3 edk2-platforms 15/23] Platform/Raspberry/Pi3: Add Arasan 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, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 X-List-Received-Date: Mon, 28 Jan 2019 12:45:37 -0000 Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Pete Batard --- Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 723 ++++++++++++++++++++ Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h | 50 ++ Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf | 51 ++ Silicon/Broadcom/Include/IndustryStandard/Bcm2836Sdio.h | 199 ++++++ 4 files changed, 1023 insertions(+) diff --git a/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c new file mode 100644 index 000000000000..828b40f82a5f --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c @@ -0,0 +1,723 @@ +/** @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 "ArasanMmcHostDxe.h" + +#define DEBUG_MMCHOST_SD DEBUG_VERBOSE + +BOOLEAN PreviousIsCardPresent = FALSE; +UINT32 LastExecutedCommand = (UINT32) -1; + +STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol; + +/** + These SD commands are optional, according to the SD Spec +**/ +BOOLEAN +IgnoreCommand ( + UINT32 Command + ) +{ + switch (Command) { + case MMC_CMD20: + return TRUE; + default: + return FALSE; + } +} + +/** + Translates a generic SD command into the format used by the Arasan SD Host Controller +**/ +UINT32 +TranslateCommand ( + UINT32 Command, + UINT32 Argument + ) +{ + UINT32 Translation = 0xffffffff; + + if (LastExecutedCommand == CMD55) { + switch (Command) { + case MMC_CMD6: + Translation = ACMD6; + DEBUG ((DEBUG_MMCHOST_SD, "ACMD6\n")); + break; + case MMC_ACMD22: + Translation = ACMD22; + DEBUG ((DEBUG_MMCHOST_SD, "ACMD22\n")); + break; + case MMC_ACMD41: + Translation = ACMD41; + DEBUG ((DEBUG_MMCHOST_SD, "ACMD41\n")); + break; + case MMC_ACMD51: + Translation = ACMD51; + DEBUG ((DEBUG_MMCHOST_SD, "ACMD51\n")); + break; + default: + DEBUG ((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized App command: %d\n", Command)); + } + } else { + switch (Command) { + case MMC_CMD0: + Translation = CMD0; + break; + case MMC_CMD1: + Translation = CMD1; + break; + case MMC_CMD2: + Translation = CMD2; + break; + case MMC_CMD3: + Translation = CMD3; + break; + case MMC_CMD5: + Translation = CMD5; + break; + case MMC_CMD6: + Translation = CMD6; + break; + case MMC_CMD7: + Translation = CMD7; + break; + case MMC_CMD8: { + if (Argument == CMD8_SD_ARG) { + Translation = CMD8_SD; + DEBUG ((DEBUG_MMCHOST_SD, "Sending SD CMD8 variant\n")); + } else { + ASSERT (Argument == CMD8_MMC_ARG); + Translation = CMD8_MMC; + DEBUG ((DEBUG_MMCHOST_SD, "Sending MMC CMD8 variant\n")); + } + break; + } + case MMC_CMD9: + Translation = CMD9; + break; + case MMC_CMD11: + Translation = CMD11; + break; + case MMC_CMD12: + Translation = CMD12; + break; + case MMC_CMD13: + Translation = CMD13; + break; + case MMC_CMD16: + Translation = CMD16; + break; + case MMC_CMD17: + Translation = CMD17; + break; + case MMC_CMD18: + Translation = CMD18; + break; + case MMC_CMD23: + Translation = CMD23; + break; + case MMC_CMD24: + Translation = CMD24; + break; + case MMC_CMD25: + Translation = CMD25; + break; + case MMC_CMD55: + Translation = CMD55; + break; + default: + DEBUG ((DEBUG_ERROR, "ArasanMMCHost: TranslateCommand(): Unrecognized Command: %d\n", Command)); + } + } + + return Translation; +} + +/** + Repeatedly polls a register until its value becomes correct, or until MAX_RETRY_COUNT polls is reached +**/ +EFI_STATUS +PollRegisterWithMask ( + IN UINTN Register, + IN UINTN Mask, + IN UINTN ExpectedValue + ) +{ + UINTN RetryCount = 0; + + while (RetryCount < MAX_RETRY_COUNT) { + if ((MmioRead32 (Register) & Mask) != ExpectedValue) { + RetryCount++; + gBS->Stall (STALL_AFTER_RETRY_US); + } else { + break; + } + } + + if (RetryCount == MAX_RETRY_COUNT) { + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +SoftReset ( + IN UINT32 Mask + ) +{ + MmioOr32 (MMCHS_SYSCTL, Mask); + if (PollRegisterWithMask (MMCHS_SYSCTL, Mask, 0) == EFI_TIMEOUT) { + DEBUG ((DEBUG_ERROR, "Failed to SoftReset with mask 0x%x\n", Mask)); + return EFI_TIMEOUT; + } + + return EFI_SUCCESS; +} + +/** + Calculate the clock divisor +**/ +EFI_STATUS +CalculateClockFrequencyDivisor ( + IN UINTN TargetFrequency, + OUT UINT32 *DivisorValue, + OUT UINTN *ActualFrequency + ) +{ + EFI_STATUS Status; + UINT32 Divisor; + UINT32 BaseFrequency = 0; + + Status = mFwProtocol->GetClockRate (RPI_MBOX_CLOCK_RATE_EMMC, &BaseFrequency); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Couldn't get RPI_MBOX_CLOCK_RATE_EMMC\n")); + return Status; + } + + ASSERT (BaseFrequency != 0); + Divisor = BaseFrequency / TargetFrequency; + + // Arasan controller is based on 3.0 spec so the div is multiple of 2 + // Actual Frequency = BaseFequency/(Div*2) + Divisor /= 2; + + if ((TargetFrequency < BaseFrequency) && + (TargetFrequency * 2 * Divisor != BaseFrequency)) { + Divisor += 1; + } + + if (Divisor > MAX_DIVISOR_VALUE) { + Divisor = MAX_DIVISOR_VALUE; + } + + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: BaseFrequency 0x%x Divisor 0x%x\n", BaseFrequency, Divisor)); + + *DivisorValue = (Divisor & 0xFF) << 8; + Divisor >>= 8; + *DivisorValue |= (Divisor & 0x03) << 6; + + if (ActualFrequency) { + if (Divisor == 0) { + *ActualFrequency = BaseFrequency; + } else { + *ActualFrequency = BaseFrequency / Divisor; + *ActualFrequency >>= 1; + } + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: *ActualFrequency 0x%x\n", *ActualFrequency)); + } + + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: *DivisorValue 0x%x\n", *DivisorValue)); + + return EFI_SUCCESS; +} + +BOOLEAN +MMCIsCardPresent ( + IN EFI_MMC_HOST_PROTOCOL *This +) +{ + return TRUE; +} + +BOOLEAN +MMCIsReadOnly ( + IN EFI_MMC_HOST_PROTOCOL *This + ) +{ + BOOLEAN IsReadOnly = !((MmioRead32 (MMCHS_PRES_STATE) & WRITE_PROTECT_OFF) == WRITE_PROTECT_OFF); + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCIsReadOnly(): %d\n", IsReadOnly)); + return IsReadOnly; +} + +EFI_STATUS +MMCBuildDevicePath ( + 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, "ArasanMMCHost: MMCBuildDevicePath()\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; +} + +EFI_STATUS +MMCSendCommand ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_CMD MmcCmd, + IN UINT32 Argument + ) +{ + UINTN MmcStatus; + UINTN RetryCount = 0; + UINTN CmdSendOKMask; + EFI_STATUS Status = EFI_SUCCESS; + BOOLEAN IsAppCmd = (LastExecutedCommand == CMD55); + BOOLEAN IsDATCmd = FALSE; + BOOLEAN IsADTCCmd = FALSE; + + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCSendCommand(MmcCmd: %08x, Argument: %08x)\n", MmcCmd, Argument)); + + if (IgnoreCommand (MmcCmd)) { + return EFI_SUCCESS; + } + + MmcCmd = TranslateCommand (MmcCmd, Argument); + if (MmcCmd == 0xffffffff) { + return EFI_UNSUPPORTED; + } + + if ((MmcCmd & CMD_R1_ADTC) == CMD_R1_ADTC) { + IsADTCCmd = TRUE; + } + if (((MmcCmd & CMD_R1B) == CMD_R1B && + /* + * Abort commands don't get inhibited by DAT. + */ + (MmcCmd & TYPE (CMD_TYPE_ABORT)) != TYPE (CMD_TYPE_ABORT)) || + IsADTCCmd || + /* + * We want to detect BRR/BWR change. + */ + MmcCmd == CMD_SEND_STATUS) { + IsDATCmd = TRUE; + } + + CmdSendOKMask = CMDI_MASK; + if (IsDATCmd) { + CmdSendOKMask |= DATI_MASK; + } + + if (PollRegisterWithMask (MMCHS_PRES_STATE, + CmdSendOKMask, 0) == EFI_TIMEOUT) { + DEBUG ((DEBUG_ERROR, "%a(%u): not ready for MMC_CMD%u PresState 0x%x MmcStatus 0x%x\n", + __FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd), + MmioRead32 (MMCHS_PRES_STATE), MmioRead32 (MMCHS_INT_STAT))); + Status = EFI_TIMEOUT; + goto Exit; + } + + if (IsAppCmd && MmcCmd == ACMD22) { + MmioWrite32 (MMCHS_BLK, 4); + } else if (IsAppCmd && MmcCmd == ACMD51) { + MmioWrite32 (MMCHS_BLK, 8); + } else if (!IsAppCmd && MmcCmd == CMD6) { + MmioWrite32 (MMCHS_BLK, 64); + } else if (IsADTCCmd) { + MmioWrite32 (MMCHS_BLK, BLEN_512BYTES); + } + + // Set Data timeout counter value to max value. + MmioAndThenOr32 (MMCHS_SYSCTL, (UINT32) ~DTO_MASK, DTO_VAL); + + // + // Clear Interrupt Status Register, but not the Card Inserted bit + // to avoid messing with card detection logic. + // + MmioWrite32 (MMCHS_INT_STAT, ALL_EN & ~(CARD_INS)); + + // Set command argument register + MmioWrite32 (MMCHS_ARG, Argument); + + // Send the command + MmioWrite32 (MMCHS_CMD, MmcCmd); + + // Check for the command status. + while (RetryCount < MAX_RETRY_COUNT) { + MmcStatus = MmioRead32 (MMCHS_INT_STAT); + + // Read status of command response + if ((MmcStatus & ERRI) != 0) { + // + // CMD5 (CMD_IO_SEND_OP_COND) is only valid for SDIO + // cards and thus expected to fail. + // + if (MmcCmd != CMD_IO_SEND_OP_COND) { + DEBUG ((DEBUG_ERROR, "%a(%u): MMC_CMD%u ERRI MmcStatus 0x%x\n", + __FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd), MmcStatus)); + } + + SoftReset (SRC); + + Status = EFI_DEVICE_ERROR; + goto Exit; + } + + // Check if command is completed. + if ((MmcStatus & CC) == CC) { + MmioWrite32 (MMCHS_INT_STAT, CC); + break; + } + + RetryCount++; + gBS->Stall (STALL_AFTER_RETRY_US); + } + + gBS->Stall (STALL_AFTER_SEND_CMD_US); + + if (RetryCount == MAX_RETRY_COUNT) { + DEBUG ((DEBUG_ERROR, "%a(%u): MMC_CMD%u completion TIMEOUT PresState 0x%x MmcStatus 0x%x\n", + __FUNCTION__, __LINE__, MMC_CMD_NUM (MmcCmd), + MmioRead32 (MMCHS_PRES_STATE), MmcStatus)); + Status = EFI_TIMEOUT; + goto Exit; + } + +Exit: + if (EFI_ERROR (Status)) { + LastExecutedCommand = (UINT32) -1; + } else { + LastExecutedCommand = MmcCmd; + } + return Status; +} + +EFI_STATUS +MMCNotifyState ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_STATE State + ) +{ + EFI_STATUS Status; + UINTN ClockFrequency; + UINT32 Divisor; + + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCNotifyState(State: %d)\n", State)); + + switch (State) { + case MmcHwInitializationState: + { + EFI_STATUS Status; + UINT32 Divisor; + + Status = SoftReset (SRA); + if (EFI_ERROR (Status)) { + return Status; + } + + // Attempt to set the clock to 400Khz which is the expected initialization speed + Status = CalculateClockFrequencyDivisor (400000, &Divisor, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Fail to initialize SD clock\n")); + return Status; + } + + // Set Data Timeout Counter value, set clock frequency, enable internal clock + MmioOr32 (MMCHS_SYSCTL, DTO_VAL | Divisor | CEN | ICS | ICE); + + // Enable interrupts + MmioWrite32 (MMCHS_IE, ALL_EN); + } + break; + case MmcIdleState: + break; + case MmcReadyState: + break; + case MmcIdentificationState: + break; + case MmcStandByState: + ClockFrequency = 25000000; + + // First turn off the clock + MmioAnd32 (MMCHS_SYSCTL, ~CEN); + + Status = CalculateClockFrequencyDivisor (ClockFrequency, &Divisor, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MmcStandByState(): Fail to initialize SD clock to %u Hz\n", + ClockFrequency)); + return Status; + } + + // Setup new divisor + MmioAndThenOr32 (MMCHS_SYSCTL, (UINT32) ~CLKD_MASK, Divisor); + + // Wait for the clock to stabilise + while ((MmioRead32 (MMCHS_SYSCTL) & ICS_MASK) != ICS); + + // Set Data Timeout Counter value, set clock frequency, enable internal clock + MmioOr32 (MMCHS_SYSCTL, CEN); + break; + case MmcTransferState: + break; + case MmcSendingDataState: + break; + case MmcReceiveDataState: + break; + case MmcProgrammingState: + break; + case MmcDisconnectState: + case MmcInvalidState: + default: + DEBUG ((DEBUG_ERROR, "ArasanMMCHost: MMCNotifyState(): Invalid State: %d\n", State)); + ASSERT (0); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +MMCReceiveResponse ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN MMC_RESPONSE_TYPE Type, + IN UINT32* Buffer + ) +{ + ASSERT (Buffer != NULL); + + if (Type == MMC_RESPONSE_TYPE_R2) { + + // 16-byte response + Buffer[0] = MmioRead32 (MMCHS_RSP10); + Buffer[1] = MmioRead32 (MMCHS_RSP32); + Buffer[2] = MmioRead32 (MMCHS_RSP54); + Buffer[3] = MmioRead32 (MMCHS_RSP76); + + Buffer[3] <<= 8; + Buffer[3] |= (Buffer[2] >> 24) & 0xFF; + Buffer[2] <<= 8; + Buffer[2] |= (Buffer[1] >> 24) & 0xFF; + Buffer[1] <<= 8; + Buffer[1] |= (Buffer[0] >> 24) & 0xFF; + Buffer[0] <<= 8; + + DEBUG ((DEBUG_MMCHOST_SD, + "ArasanMMCHost: MMCReceiveResponse(Type: %x), Buffer[0-3]: %08x, %08x, %08x, %08x\n", + Type, Buffer[0], Buffer[1], Buffer[2], Buffer[3])); + } else { + // 4-byte response + Buffer[0] = MmioRead32 (MMCHS_RSP10); + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCReceiveResponse(Type: %08x), Buffer[0]: %08x\n", Type, Buffer[0])); + } + + gBS->Stall (STALL_AFTER_REC_RESP_US); + if (LastExecutedCommand == CMD_STOP_TRANSMISSION) { + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: soft-resetting after CMD12\n")); + return SoftReset (SRC | SRD); + } + return EFI_SUCCESS; +} + +EFI_STATUS +MMCReadBlockData ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + IN UINT32* Buffer + ) +{ + UINTN MmcStatus; + UINTN RemLength; + UINTN Count; + + DEBUG ((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", + __FUNCTION__, __LINE__, Lba, Length, Buffer)); + + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "%a(%u): NULL Buffer\n", __FUNCTION__, __LINE__)); + return EFI_INVALID_PARAMETER; + } + + if (Length % sizeof (UINT32) != 0) { + DEBUG ((DEBUG_ERROR, "%a(%u): bad Length %u\n", __FUNCTION__, __LINE__, Length)); + return EFI_INVALID_PARAMETER; + } + + RemLength = Length; + while (RemLength != 0) { + UINTN RetryCount = 0; + UINT32 BlockLen = MIN (RemLength, BLEN_512BYTES); + + while (RetryCount < MAX_RETRY_COUNT) { + MmcStatus = MmioRead32 (MMCHS_INT_STAT); + if ((MmcStatus & BRR) != 0) { + MmioWrite32 (MMCHS_INT_STAT, BRR); + /* + * Data is ready. + */ + mFwProtocol->SetLed (TRUE); + for (Count = 0; Count < BlockLen; Count += 4, Buffer++) { + *Buffer = MmioRead32 (MMCHS_DATA); + } + + mFwProtocol->SetLed (FALSE); + break; + } + + gBS->Stall (STALL_AFTER_RETRY_US); + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + DEBUG ((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n", + __FUNCTION__, __LINE__, Length - RemLength, Length, MmcStatus)); + return EFI_TIMEOUT; + } + + RemLength -= BlockLen; + gBS->Stall (STALL_AFTER_READ_US); + } + + MmioWrite32 (MMCHS_INT_STAT, BRR); + return EFI_SUCCESS; +} + +EFI_STATUS +MMCWriteBlockData ( + IN EFI_MMC_HOST_PROTOCOL *This, + IN EFI_LBA Lba, + IN UINTN Length, + IN UINT32* Buffer + ) +{ + UINTN MmcStatus; + UINTN RemLength; + UINTN Count; + + DEBUG ((DEBUG_VERBOSE, "%a(%u): LBA: 0x%x, Length: 0x%x, Buffer: 0x%x)\n", + __FUNCTION__, __LINE__, Lba, Length, Buffer)); + + if (Buffer == NULL) { + DEBUG ((DEBUG_ERROR, "%a(%u): NULL Buffer\n", __FUNCTION__, __LINE__)); + return EFI_INVALID_PARAMETER; + } + + if (Length % sizeof (UINT32) != 0) { + DEBUG ((DEBUG_ERROR, "%a(%u): bad Length %u\n", __FUNCTION__, __LINE__, Length)); + return EFI_INVALID_PARAMETER; + } + + RemLength = Length; + while (RemLength != 0) { + UINTN RetryCount = 0; + UINT32 BlockLen = MIN (RemLength, BLEN_512BYTES); + + while (RetryCount < MAX_RETRY_COUNT) { + MmcStatus = MmioRead32 (MMCHS_INT_STAT); + if ((MmcStatus & BWR) != 0) { + MmioWrite32 (MMCHS_INT_STAT, BWR); + /* + * Can write data. + */ + mFwProtocol->SetLed (TRUE); + for (Count = 0; Count < BlockLen; Count += 4, Buffer++) { + MmioWrite32 (MMCHS_DATA, *Buffer); + } + + mFwProtocol->SetLed (FALSE); + break; + } + + gBS->Stall (STALL_AFTER_RETRY_US); + RetryCount++; + } + + if (RetryCount == MAX_RETRY_COUNT) { + DEBUG ((DEBUG_ERROR, "%a(%u): %lu/%lu MMCHS_INT_STAT: %08x\n", + __FUNCTION__, __LINE__, Length - RemLength, Length, MmcStatus)); + return EFI_TIMEOUT; + } + + RemLength -= BlockLen; + gBS->Stall (STALL_AFTER_WRITE_US); + } + + MmioWrite32 (MMCHS_INT_STAT, BWR); + return EFI_SUCCESS; +} + +BOOLEAN +MMCIsMultiBlock ( + IN EFI_MMC_HOST_PROTOCOL *This + ) +{ + return TRUE; +} + +EFI_MMC_HOST_PROTOCOL gMMCHost = +{ + MMC_HOST_PROTOCOL_REVISION, + MMCIsCardPresent, + MMCIsReadOnly, + MMCBuildDevicePath, + MMCNotifyState, + MMCSendCommand, + MMCReceiveResponse, + MMCReadBlockData, + MMCWriteBlockData, + NULL, + MMCIsMultiBlock +}; + +EFI_STATUS +MMCInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle = NULL; + + DEBUG ((DEBUG_MMCHOST_SD, "ArasanMMCHost: MMCInitialize()\n")); + + if (!PcdGet32 (PcdSdIsArasan)) { + DEBUG ((DEBUG_INFO, "SD is not routed to Arasan\n")); + return EFI_REQUEST_UNLOAD_IMAGE; + } + + Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL, + (VOID**)&mFwProtocol); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gRaspberryPiMmcHostProtocolGuid, + &gMMCHost, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h new file mode 100644 index 000000000000..d1a3f014b45e --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h @@ -0,0 +1,50 @@ +/** @file + * + * 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 _MMC_HOST_DXE_H_ +#define _MMC_HOST_DXE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX_RETRY_COUNT (1000 * 20) + +#define STALL_AFTER_SEND_CMD_US (200) // in microseconds +#define STALL_AFTER_REC_RESP_US (50) +#define STALL_AFTER_WRITE_US (200) +#define STALL_AFTER_READ_US (20) +#define STALL_AFTER_RETRY_US (20) + +#define MAX_DIVISOR_VALUE 1023 + +#endif diff --git a/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf new file mode 100644 index 000000000000..8e857471ba62 --- /dev/null +++ b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf @@ -0,0 +1,51 @@ +#/** @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 = ArasanMMCHost + FILE_GUID = 100c2cfa-b586-4198-9b4c-1683d195b1da + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = MMCInitialize + +[Sources.common] + ArasanMmcHostDxe.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + Platform/Raspberry/Pi3/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/Include/IndustryStandard/Bcm2836Sdio.h b/Silicon/Broadcom/Include/IndustryStandard/Bcm2836Sdio.h new file mode 100644 index 000000000000..502ccb498247 --- /dev/null +++ b/Silicon/Broadcom/Include/IndustryStandard/Bcm2836Sdio.h @@ -0,0 +1,199 @@ +/** @file + * + * 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_SDIO_H__ +#define __BCM2836_SDIO_H__ + +//MMC/SD/SDIO1 register definitions. +#define MMCHS1BASE 0x3F300000 + +#define MMCHS_BLK (MMCHS1BASE + 0x4) +#define BLEN_512BYTES (0x200UL << 0) + +#define MMCHS_ARG (MMCHS1BASE + 0x8) + +#define MMCHS_CMD (MMCHS1BASE + 0xC) +#define BCE_ENABLE BIT1 +#define DDIR_READ BIT4 +#define DDIR_WRITE (0x0UL << 4) +#define MSBS_SGLEBLK (0x0UL << 5) +#define MSBS_MULTBLK BIT5 +#define RSP_TYPE_MASK (0x3UL << 16) +#define RSP_TYPE_136BITS BIT16 +#define RSP_TYPE_48BITS (0x2UL << 16) +#define RSP_TYPE_48BUSY (0x3UL << 16) +#define CCCE_ENABLE BIT19 +#define CICE_ENABLE BIT20 +#define DP_ENABLE BIT21 + +#define CMD_TYPE_NORMAL 0 +#define CMD_TYPE_ABORT 3 +#define TYPE(CMD_TYPE) (((CMD_TYPE) & 0x3) << 22) +#define _INDX(CMD_INDX) ((CMD_INDX & 0x3F) << 24) +#define MMC_CMD_NUM(CMD) (((CMD) >> 24) & 0x3F) +#define INDX(CMD_INDX) (TYPE(CMD_TYPE_NORMAL) | _INDX(CMD_INDX)) +#define INDX_ABORT(CMD_INDX) (TYPE(CMD_TYPE_ABORT) | _INDX(CMD_INDX)) + +#define MMCHS_RSP10 (MMCHS1BASE + 0x10) +#define MMCHS_RSP32 (MMCHS1BASE + 0x14) +#define MMCHS_RSP54 (MMCHS1BASE + 0x18) +#define MMCHS_RSP76 (MMCHS1BASE + 0x1C) +#define MMCHS_DATA (MMCHS1BASE + 0x20) + +#define MMCHS_PRES_STATE (MMCHS1BASE + 0x24) +#define CMDI_MASK BIT0 +#define CMDI_ALLOWED (0x0UL << 0) +#define CMDI_NOT_ALLOWED BIT0 +#define DATI_MASK BIT1 +#define DATI_ALLOWED (0x0UL << 1) +#define DATI_NOT_ALLOWED BIT1 +#define WRITE_PROTECT_OFF BIT19 + +#define MMCHS_HCTL (MMCHS1BASE + 0x28) +#define DTW_1_BIT (0x0UL << 1) +#define DTW_4_BIT BIT1 +#define SDBP_MASK BIT8 +#define SDBP_OFF (0x0UL << 8) +#define SDBP_ON BIT8 +#define SDVS_1_8_V (0x5UL << 9) +#define SDVS_3_0_V (0x6UL << 9) +#define IWE BIT24 + +#define MMCHS_SYSCTL (MMCHS1BASE + 0x2C) +#define ICE BIT0 +#define ICS_MASK BIT1 +#define ICS BIT1 +#define CEN BIT2 +#define CLKD_MASK (0x3FFUL << 6) +#define CLKD_80KHZ (0x258UL) //(96*1000/80)/2 +#define CLKD_400KHZ (0xF0UL) +#define CLKD_12500KHZ (0x200UL) +#define DTO_MASK (0xFUL << 16) +#define DTO_VAL (0xEUL << 16) +#define SRA BIT24 +#define SRC_MASK BIT25 +#define SRC BIT25 +#define SRD BIT26 + +#define MMCHS_INT_STAT (MMCHS1BASE + 0x30) +#define CC BIT0 +#define TC BIT1 +#define BWR BIT4 +#define BRR BIT5 +#define CARD_INS BIT6 +#define ERRI BIT15 +#define CTO BIT16 +#define DTO BIT20 +#define DCRC BIT21 +#define DEB BIT22 + +#define MMCHS_IE (MMCHS1BASE + 0x34) +#define CC_EN BIT0 +#define TC_EN BIT1 +#define BWR_EN BIT4 +#define BRR_EN BIT5 +#define CTO_EN BIT16 +#define CCRC_EN BIT17 +#define CEB_EN BIT18 +#define CIE_EN BIT19 +#define DTO_EN BIT20 +#define DCRC_EN BIT21 +#define DEB_EN BIT22 +#define CERR_EN BIT28 +#define BADA_EN BIT29 +#define ALL_EN 0xFFFFFFFF + +#define MMCHS_ISE (MMCHS1BASE + 0x38) +#define CC_SIGEN BIT0 +#define TC_SIGEN BIT1 +#define BWR_SIGEN BIT4 +#define BRR_SIGEN BIT5 +#define CTO_SIGEN BIT16 +#define CCRC_SIGEN BIT17 +#define CEB_SIGEN BIT18 +#define CIE_SIGEN BIT19 +#define DTO_SIGEN BIT20 +#define DCRC_SIGEN BIT21 +#define DEB_SIGEN BIT22 +#define CERR_SIGEN BIT28 +#define BADA_SIGEN BIT29 + +#define MMCHS_AC12 (MMCHS1BASE + 0x3C) + +#define MMCHS_CAPA (MMCHS1BASE + 0x40) +#define VS30 BIT25 +#define VS18 BIT26 + +#define MMCHS_CUR_CAPA (MMCHS1BASE + 0x48) +#define MMCHS_REV (MMCHS1BASE + 0xFC) + +#define BLOCK_COUNT_SHIFT 16 +#define RCA_SHIFT 16 + +#define CMD_R1 (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE) +#define CMD_R1B (RSP_TYPE_48BUSY | CCCE_ENABLE | CICE_ENABLE) +#define CMD_R2 (RSP_TYPE_136BITS | CCCE_ENABLE) +#define CMD_R3 (RSP_TYPE_48BITS) +#define CMD_R6 (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE) +#define CMD_R7 (RSP_TYPE_48BITS | CCCE_ENABLE | CICE_ENABLE) + +#define CMD_R1_ADTC (CMD_R1 | DP_ENABLE) +#define CMD_R1_ADTC_READ (CMD_R1_ADTC | DDIR_READ) +#define CMD_R1_ADTC_WRITE (CMD_R1_ADTC | DDIR_WRITE) + +#define CMD0 (INDX(0)) // Go idle +#define CMD1 (INDX(1) | CMD_R3) // MMC: Send Op Cond +#define CMD2 (INDX(2) | CMD_R2) // Send CID +#define CMD3 (INDX(3) | CMD_R6) // Set Relative Addr +#define CMD4 (INDX(4)) // Set DSR +#define CMD5 (INDX(5) | CMD_R1B) // SDIO: Sleep/Awake +#define CMD6 (INDX(6) | CMD_R1_ADTC_READ) // Switch +#define CMD7 (INDX(7) | CMD_R1B) // Select/Deselect +#define CMD8_SD (INDX(8) | CMD_R7) // Send If Cond +#define CMD8_SD_ARG (0x0UL << 12 | BIT8 | 0xCEUL << 0) +#define CMD8_MMC (INDX(8) | CMD_R1_ADTC_READ) // Send Ext Csd +#define CMD8_MMC_ARG (0) +#define CMD9 (INDX(9) | CMD_R2) // Send CSD +#define CMD10 (INDX(10) | CMD_R2) // Send CID +#define CMD11 (INDX(11) | CMD_R1) // Voltage Switch +#define CMD12 (INDX_ABORT(12) | CMD_R1B) // Stop Transmission +#define CMD13 (INDX(13) | CMD_R1) // Send Status +#define CMD15 (INDX(15)) // Go inactive state +#define CMD16 (INDX(16) | CMD_R1) // Set Blocklen +#define CMD17 (INDX(17) | CMD_R1_ADTC_READ) // Read Single Block +#define CMD18 (INDX(18) | CMD_R1_ADTC_READ | MSBS_MULTBLK) // Read Multiple Blocks +#define CMD19 (INDX(19) | CMD_R1_ADTC_READ) // SD: Send Tuning Block (64 bytes) +#define CMD20 (INDX(20) | CMD_R1B) // SD: Speed Class Control +#define CMD23 (INDX(23) | CMD_R1) // Set Block Count for CMD18 and CMD25 +#define CMD24 (INDX(24) | CMD_R1_ADTC_WRITE) // Write Block +#define CMD25 (INDX(25) | CMD_R1_ADTC_WRITE | MSBS_MULTBLK) // Write Multiple Blocks +#define CMD55 (INDX(55) | CMD_R1) // App Cmd + +#define ACMD6 (INDX(6) | CMD_R1) // Set Bus Width +#define ACMD22 (INDX(22) | CMD_R1_ADTC_READ) // SEND_NUM_WR_BLOCKS +#define ACMD41 (INDX(41) | CMD_R3) // Send Op Cond +#define ACMD51 (INDX(51) | CMD_R1_ADTC_READ) // Send SCR + +// User-friendly command names +#define CMD_IO_SEND_OP_COND CMD5 +#define CMD_SEND_CSD CMD9 // CSD: Card-Specific Data +#define CMD_STOP_TRANSMISSION CMD12 +#define CMD_SEND_STATUS CMD13 +#define CMD_READ_SINGLE_BLOCK CMD17 +#define CMD_READ_MULTIPLE_BLOCK CMD18 +#define CMD_SET_BLOCK_COUNT CMD23 +#define CMD_WRITE_SINGLE_BLOCK CMD24 +#define CMD_WRITE_MULTIPLE_BLOCK CMD25 + +#endif /* __BCM2836_SDIO_H__ */ -- 2.17.0.windows.1