From: Pete Batard <pete@akeo.ie>
To: edk2-devel@lists.01.org
Subject: [PATCH v4 edk2-platforms 15/23] Platform/Raspberry/Pi3: Add Arasan MMC driver
Date: Tue, 29 Jan 2019 16:26:47 +0000 [thread overview]
Message-ID: <20190129162655.3800-16-pete@akeo.ie> (raw)
In-Reply-To: <20190129162655.3800-1-pete@akeo.ie>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Pete Batard <pete@akeo.ie>
---
Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.c | 723 ++++++++++++++++++++
Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.h | 50 ++
Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf | 52 ++
Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836Sdio.h | 199 ++++++
4 files changed, 1024 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 <andrey.warkentin@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_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 <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/RpiMmcHost.h>
+#include <Protocol/RpiFirmware.h>
+
+#include <IndustryStandard/Bcm2836.h>
+#include <IndustryStandard/Bcm2836Sdio.h>
+#include <IndustryStandard/RpiMbox.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/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
new file mode 100644
index 000000000000..14a4038a5e6b
--- /dev/null
+++ b/Platform/Raspberry/Pi3/Drivers/ArasanMmcHostDxe/ArasanMmcHostDxe.inf
@@ -0,0 +1,52 @@
+#/** @file
+#
+# Copyright (c) 2017, Andrei Warkentin <andrey.warkentin@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 = 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
+ Silicon/Broadcom/Bcm283x/Bcm283x.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/Bcm283x/Include/IndustryStandard/Bcm2836Sdio.h b/Silicon/Broadcom/Bcm283x/Include/IndustryStandard/Bcm2836Sdio.h
new file mode 100644
index 000000000000..502ccb498247
--- /dev/null
+++ b/Silicon/Broadcom/Bcm283x/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
next prev parent reply other threads:[~2019-01-29 16:27 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-29 16:26 [PATCH v4 edk2-platforms 00/23] Platform/Raspberry: Add Raspberry Pi 3 support Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 01/23] Silicon/Broadcom/Bcm282x: Add interrupt driver Pete Batard
2019-01-31 15:24 ` Leif Lindholm
2019-01-31 17:19 ` Ard Biesheuvel
2019-01-31 19:57 ` Leif Lindholm
2019-01-31 21:01 ` Andrew Fish
2019-02-01 8:43 ` Laszlo Ersek
2019-02-01 10:28 ` Pete Batard
2019-02-01 15:18 ` Leif Lindholm
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 02/23] Silicon/Broadcom/Bcm283x: Add GpioLib Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 03/23] Platform/Raspberry/Pi3: Add ACPI tables Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 04/23] Platform/Raspberry/Pi3: Add reset and memory init libraries Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 05/23] Platform/Raspberry/Pi3: Add platform library Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 06/23] Platform/Raspberry/Pi3: Add RTC library Pete Batard
2019-01-30 22:22 ` Leif Lindholm
2019-01-31 12:31 ` Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 07/23] Platform/Raspberry/Pi3: Add firmware driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 08/23] Platform/Raspberry/Pi3: Add platform config driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 09/23] Platform/Raspberry/Pi3: Add SMBIOS driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 10/23] Platform/Raspberry/Pi3: Add display driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 11/23] Platform/Raspberry/Pi3: Add console driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 12/23] Platform/Raspberry/Pi3: Add NV storage driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 13/23] Platform/Raspberry/Pi3: Add Device Tree driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 14/23] Platform/Raspberry/Pi3: Add base MMC driver Pete Batard
2019-01-29 16:26 ` Pete Batard [this message]
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 16/23] Platform/Raspberry/Pi3: Platform/Raspberry/Pi3: Add SD Host driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 17/23] Platform/Raspberry/Pi3: Add platform boot manager and helper libraries Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 18/23] Platform/Raspberry/Pi3: Add USB host driver Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 19/23] Platform/Raspberry/Pi3: Add platform Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 20/23] Platform/Raspberry/Pi3: Add platform readme Pete Batard
2019-01-30 21:50 ` Leif Lindholm
2019-01-31 12:30 ` Pete Batard
2019-01-31 14:13 ` Leif Lindholm
2019-01-31 14:36 ` Ard Biesheuvel
2019-01-31 14:44 ` Ard Biesheuvel
2019-01-31 17:19 ` Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 21/23] Platform/Raspberry/Pi3 *NON-OSI*: Add ATF binaries Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 22/23] Platform/Raspberry/Pi3 *NON-OSI*: Add Device Tree binaries Pete Batard
2019-01-29 16:26 ` [PATCH v4 edk2-platforms 23/23] Platform/Raspberry/Pi3 *NON-OSI*: Add logo driver Pete Batard
2019-01-29 17:40 ` [PATCH v4 edk2-platforms 00/23] Platform/Raspberry: Add Raspberry Pi 3 support Ard Biesheuvel
2019-01-29 21:09 ` Pete Batard
2019-01-30 19:38 ` Ard Biesheuvel
2019-01-30 19:42 ` Leif Lindholm
2019-01-30 19:45 ` Ard Biesheuvel
2019-01-30 21:59 ` Leif Lindholm
2019-01-30 22:28 ` Leif Lindholm
2019-01-31 12:31 ` Pete Batard
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190129162655.3800-16-pete@akeo.ie \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox