[edk2] [PATCH v2 edk2-platforms 11/20] Platform/Broadcom/RPi3: Add Arasan MMC driver

Pete Batard pete at akeo.ie
Mon Dec 10 04:38:44 PST 2018


Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Pete Batard <pete at akeo.ie>
---
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c   | 730 ++++++++++++++++++++
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h   |  50 ++
 Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf |  53 ++
 Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h       | 199 ++++++
 4 files changed, 1032 insertions(+)

diff --git a/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
new file mode 100644
index 000000000000..a070b6d5d8d9
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c
@@ -0,0 +1,730 @@
+/** @file
+ *
+ *  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+ *  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_FW_CLOCK_RATE_EMMC, &BaseFrequency);
+  if (EFI_ERROR(Status)) {
+    DEBUG((DEBUG_ERROR, "Couldn't get RPI_FW_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 out;
+  }
+
+  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 out;
+    }
+
+    // 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 out;
+  }
+
+ out:
+  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
+               )
+{
+  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: {
+    EFI_STATUS Status;
+    UINTN ClockFrequency = 25000000;
+    UINT32 Divisor;
+
+    // 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/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h
new file mode 100644
index 000000000000..6b4f0da4a425
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/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 <Uefi.h>
+
+#include <Library/BaseLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DmaLib.h>
+
+#include <Protocol/EmbeddedExternalDevice.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/PiMmcHost.h>
+#include <Protocol/RaspberryPiFirmware.h>
+
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/Bcm2836MmcHs.h>
+#include <IndustryStandard/RpiFirmware.h>
+
+#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/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
new file mode 100644
index 000000000000..4d70c1b72815
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
@@ -0,0 +1,53 @@
+#/** @file
+#
+#  Copyright (c) 2017, Andrei Warkentin <andrey.warkentin at gmail.com>
+#  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                    = 0x00010005
+  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/Broadcom/Bcm283x/RaspberryPiPkg.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/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.h
new file mode 100644
index 000000000000..2bd1742fcdff
--- /dev/null
+++ b/Platform/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836MmcHs.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 __BCM2836SDIO_H__
+#define __BCM2836SDIO_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 //__BCM2836SDIO_H__
-- 
2.17.0.windows.1



More information about the edk2-devel mailing list