This patch adds support for MMC library(MmcLib)to provide functions which will be used by MMC Host driver.
Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Meenakshi Aggarwal <meenakshi.aggar...@nxp.com> Signed-off-by: Vabhav <vabhav.sha...@nxp.com> --- Platform/NXP/Include/Library/MmcLib.h | 138 +++++++ Platform/NXP/Library/MmcLib/MmcInterface.c | 544 ++++++++++++++++++++++++++ Platform/NXP/Library/MmcLib/MmcInternal.h | 350 +++++++++++++++++ Platform/NXP/Library/MmcLib/MmcLib.c | 597 +++++++++++++++++++++++++++++ Platform/NXP/Library/MmcLib/MmcLib.inf | 39 ++ 5 files changed, 1668 insertions(+) create mode 100644 Platform/NXP/Include/Library/MmcLib.h create mode 100644 Platform/NXP/Library/MmcLib/MmcInterface.c create mode 100644 Platform/NXP/Library/MmcLib/MmcInternal.h create mode 100644 Platform/NXP/Library/MmcLib/MmcLib.c create mode 100644 Platform/NXP/Library/MmcLib/MmcLib.inf diff --git a/Platform/NXP/Include/Library/MmcLib.h b/Platform/NXP/Include/Library/MmcLib.h new file mode 100644 index 0000000..36941bc --- /dev/null +++ b/Platform/NXP/Include/Library/MmcLib.h @@ -0,0 +1,138 @@ +/** @file + Header Defining The MMC Memory Controller Constants, Function Prototype, Structures Etc + + Copyright 2017 NXP + + 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 __MMCLIB_H__ +#define __MMCLIB_H__ + +#include <Uefi.h> +#include <Library/DebugLib.h> + +//define MMC_DEBUG to enable debug feature +//#define MMC_DEBUG 1 +#ifdef MMC_DEBUG +#define DEBUG_MSG(_Fmt,...) DEBUG ((DEBUG_ERROR, "MMC: " _Fmt, ##__VA_ARGS__)); +#else +#define DEBUG_MSG(_Fmt,...) +#endif + +/** + MMC RESPONSE TYPE +**/ +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_136 (1 << 1) // 136 Bit Response +#define MMC_RSP_CRC (1 << 2) // Expect Valid Crc +#define MMC_RSP_BUSY (1 << 3) // Card May Send Busy +#define MMC_RSP_OPCODE (1 << 4) // Response Contains Opcode + +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R1b (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \ + MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R4 (MMC_RSP_PRESENT) +#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + +/** + Find most significant set + + @param X word to search + + @retval most significant set bit number + +**/ +static inline INT32 GenericFls (INT32 X) +{ + + INT32 I = 31; + + for (I=31; I >= 0; I--) { + if(X & (1<<I)) + return (I+1); + } + + return 0; +} + +struct SdCmd { + UINT16 CmdIdx; + UINT32 RespType; + UINT32 CmdArg; +}; + +EFI_STATUS +MmcInitialize ( + VOID + ); + +EFI_STATUS +RcvResp ( + IN UINT32 RespType, + OUT UINT32* Response, + IN UINT8 Data + ); + +BOOLEAN +DetectCardPresence ( + IN VOID + ); + +BOOLEAN +IsCardReadOnly ( + IN VOID + ); + +struct SdData { + UINT32 Flags; + UINT32 Blocks; + UINT32 Blocksize; + VOID *Addr; +}; + +EFI_STATUS +SendCmd ( + IN struct SdCmd *Cmd, + IN struct SdData *Data + ); + +VOID +SetIos ( + IN UINT32 BusClockFreq, + IN UINT32 BusWidth, + IN UINT32 TimingMode + ); + +EFI_STATUS +WriteBlock ( + IN UINTN Offset, + IN UINTN Length, + IN UINT32* Buffer, + IN struct SdCmd Cmd + ); + +EFI_STATUS +ReadBlock ( + IN UINTN Offset, + IN UINTN Length, + OUT UINT32* Buffer, + IN struct SdCmd Cmd + ); + +extern UINT64 GetSdxcFrequency(); + +#endif diff --git a/Platform/NXP/Library/MmcLib/MmcInterface.c b/Platform/NXP/Library/MmcLib/MmcInterface.c new file mode 100644 index 0000000..1b8ce25 --- /dev/null +++ b/Platform/NXP/Library/MmcLib/MmcInterface.c @@ -0,0 +1,544 @@ +/** @MmcInterface.c + + Functions for providing Library interface APIs. + + Copyright 2017 NXP + + 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 <Library/BaseMemoryLib/MemLibInternals.h> +#include <Library/BeIoLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/MmcLib.h> +#include <Library/TimerLib.h> + +#include "MmcInternal.h" + +struct Mmc *mMmc; + +/** + Function to detect card presence by checking host controller + present state register + + @retval Returns the card presence as TRUE/FALSE + +**/ +BOOLEAN +DetectCardPresence ( + IN VOID + ) +{ + struct SdxcRegs *Regs; + INT32 Timeout; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + Timeout = 1000; + + while (!(MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_CINS) && --Timeout) + MicroSecondDelay (10); + + if (Timeout > 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Function to check whether card is read only by verifying host controller + present state register + + @retval Returns the card read only or not as TRUE/FALSE + +**/ +BOOLEAN +IsCardReadOnly ( + IN VOID + ) +{ + struct SdxcRegs *Regs; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + + if (MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_WPSPL) { + return FALSE; + } else { + DEBUG ((DEBUG_ERROR, "SD/MMC : Write Protection PIN is high\n")); + return TRUE; + } +} + +/** + Function to prepare state(Wait for bus,Set up host controller + data,Transfer type) for command to be send + + @param Cmd Command to be used + @param Data Data with command + + @retval Returns the command status + +**/ +EFI_STATUS +SendCmd ( + IN struct SdCmd *Cmd, + IN struct SdData *Data + ) +{ + EFI_STATUS Status; + UINT32 Xfertype; + UINT32 Irqstat; + INT32 Timeout; + struct SdxcRegs *Regs; + + Status = 0; + Timeout = 100000; + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + + DEBUG_MSG ("0x%x : Cmd.Id %d, Arg 0x%x SdCmd.RespType 0x%x \n", + Regs, Cmd->CmdIdx, Cmd->CmdArg, Cmd->RespType); + + MmcWrite ((UINTN)&Regs->Irqstat, 0xFFFFFFFF); + + asm ("Dmb :"); + + // Wait for The Bus To Be Idle + while ((MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_CICHB) || + (MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_CIDHB)) + NanoSecondDelay (10); + + // Wait for data line to be active + while (MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_DLA) + NanoSecondDelay (10); + + // Wait Before The Next Command + MicroSecondDelay (1000); + + // Set Up for A Data Transfer if We Have One + if (Data) { + Status = SdxcSetupData (Data); + if (Status) { + return Status; + } + } + + // Figure Out The Transfer Arguments + Xfertype = SdxcXfertype (Cmd, Data); + + // Mask All Irqs + MmcWrite ((UINTN)&Regs->Irqsigen, 0); + + // Send The Command + MmcWrite ((UINTN)&Regs->CmdArg, Cmd->CmdArg); + MmcWrite ((UINTN)&Regs->Xfertype, Xfertype); + + // Wait for The Command To Complete + Timeout = 100000; + while ((!(MmcRead ((UINTN)&Regs->Irqstat) & (IRQSTATE_CC | IRQSTATE_CTOE))) + && Timeout--); + + if (Timeout <= 0) { + DEBUG ((DEBUG_ERROR, "Command not completed %d\n", Cmd->CmdIdx)); + return EFI_TIMEOUT; + } + + Irqstat = MmcRead ((UINTN)&Regs->Irqstat); + + if (Irqstat & CMD_ERR) { + Status = EFI_DEVICE_ERROR; + DEBUG ((DEBUG_ERROR, "SdxcSendCmd: Device Error(0x%x) for Cmd(%d)\n", + Irqstat, Cmd->CmdIdx)); + goto Out; + } + + if (Irqstat & IRQSTATE_CTOE) { + Status = EFI_TIMEOUT; + DEBUG ((DEBUG_ERROR, "SdxcSendCmd: Timeout for Cmd(%d)\n", Cmd->CmdIdx)); + goto Out; + } + +Out: + if (Status) { + ResetCmdFailedData (Regs, (Data != NULL)); + } + else { + MmcWrite ((UINTN)&Regs->Irqstat, 0xFFFFFFFF); + } + return Status; + +} + +/** + Function to receive command response + + @param RespType Type of response + @param Data Data of response + + @param Response Pointer to response buffer + + @retval Returns the command response status + +**/ +EFI_STATUS +RcvResp ( + IN UINT32 RespType, + OUT UINT32* Response, + IN UINT8 Data + ) +{ + if (RespType != 0xFF) { + INT32 Timeout; + struct SdxcRegs *Regs; + + Timeout = 25000; + Regs = (VOID *)PcdGet64(PcdSdxcBaseAddr); + + // Workaround for SDXC Errata ENGcm03648 + if (!Data && (RespType & MMC_RSP_BUSY)) { + Timeout = 25000; + + // Poll On DATA0 Line for Cmd With Busy Signal for 250 Ms + while (Timeout > 0 && !(MmcRead ((UINTN)&Regs->Prsstat) & + PRSSTATE_DAT0)) { + MicroSecondDelay (100); + Timeout--; + } + + if (Timeout <= 0) { + DEBUG ((DEBUG_ERROR, "Timeout Waiting for DAT0 To Go High!\n")); + ResetCmdFailedData (Regs, Data); + return EFI_TIMEOUT; + } + } + + // Copy The Response To The Response Buffer + if (RespType & MMC_RSP_136) { + UINT32 Rspns3, Rspns2, Rspns1, Rspns0; + + Rspns3 = MmcRead ((UINTN)&Regs->Rspns3); + Rspns2 = MmcRead ((UINTN)&Regs->Rspns2); + Rspns1 = MmcRead ((UINTN)&Regs->Rspns1); + Rspns0 = MmcRead ((UINTN)&Regs->Rspns0); + Response[3] = (Rspns3 << 8) | (Rspns2 >> 24); + Response[2] = (Rspns2 << 8) | (Rspns1 >> 24); + Response[1] = (Rspns1 << 8) | (Rspns0 >> 24); + Response[0] = (Rspns0 << 8); + DEBUG_MSG ("RESP : 0x%x : 0x%x : 0x%x : 0x%x \n", + Response[0], Response[1], Response[2], Response[3]); + } else { + Response[0] = MmcRead ((UINTN)&Regs->Rspns0); + } + } + + return EFI_SUCCESS; +} + +/** + Function to prepare command transfer + + @param Flags Flags for transferType of response + @param Length Length of block + @param Cmd Pointer to command structure + + @retval Returns the command status + +**/ +EFI_STATUS +PrepareTransfer ( + UINT32 Flags, + IN UINTN Length, + IN VOID* Buffer, + IN struct SdCmd *Cmd + ) +{ + EFI_STATUS Status; + struct SdData Data; + + Data.Flags = Flags; + + if (Length > MMC_MAX_BLOCK_LEN) { + Data.Blocks = (Length / MMC_MAX_BLOCK_LEN) + + ((Length % MMC_MAX_BLOCK_LEN) ? 1 : 0); + Data.Blocksize = MMC_MAX_BLOCK_LEN; + } else { + Data.Blocks = 1; + Data.Blocksize = Length; + } + + Data.Addr = Buffer; + + Status = SendCmd (Cmd, &Data); + + return Status; +} + +/** + Function to Read MMC Block + + @param Offset Offset to read from + @param Length Length of block + @param Cmd Pointer to command structure + + @param Buffer Pointer to buffer for data read + + @retval Returns the read block command status + +**/ +EFI_STATUS +ReadBlock ( + IN UINTN Offset, + IN UINTN Length, + OUT UINT32* Buffer, + IN struct SdCmd Cmd + ) +{ + EFI_STATUS Status; + struct DmaData DmaData; + VOID * Temp; + + Temp = NULL; + + DmaData.Bytes = Length; + DmaData.MapOperation = MapOperationBusMasterRead; + + Temp = GetDmaBuffer (&DmaData); + if (Temp == NULL) { + DEBUG ((DEBUG_ERROR,"Mmc Read : Failed to get DMA buffer \n")); + return EFI_OUT_OF_RESOURCES; + } + + Status = PrepareTransfer (MMC_DATA_READ, Length, Temp, &Cmd); + if (Status) { + DEBUG((DEBUG_ERROR,"Mmc Read: Fail to setup controller 0x%x \n", Status)); + FreeDmaBuffer (&DmaData); + return Status; + } + + Status = Transfer (); + if (Status) { + DEBUG ((DEBUG_ERROR,"Mmc Read Failed (0x%x) \n", Status)); + FreeDmaBuffer (&DmaData); + return Status; + } + + InternalMemCopyMem (Buffer, Temp , DmaData.Bytes); + + Status = FreeDmaBuffer (&DmaData); + if (Status) { + DEBUG ((DEBUG_ERROR,"Mmc Read : Failed to release DMA buffer \n")); + return Status; + } + + return Status; +} + +/** + Function to Write MMC Block + + @param Offset Offset to write to + @param Length Length of block + @param Buffer Pointer to buffer for data to be written + @param Cmd Pointer to command structure + + @retval Returns the write block command status + +**/ +EFI_STATUS +WriteBlock ( + IN UINTN Offset, + IN UINTN Length, + IN UINT32* Buffer, + IN struct SdCmd Cmd + ) +{ + EFI_STATUS Status; + struct DmaData DmaData; + VOID * Temp; + + Temp = NULL; + + DmaData.Bytes = Length; + DmaData.MapOperation = MapOperationBusMasterWrite; + + Temp = GetDmaBuffer (&DmaData); + if (Temp == NULL) { + DEBUG ((DEBUG_ERROR,"Mmc Write : Failed to get DMA buffer \n")); + return EFI_OUT_OF_RESOURCES; + } + + InternalMemCopyMem (Temp, Buffer, DmaData.Bytes); + + Status = PrepareTransfer (MMC_DATA_WRITE, Length, Temp, &Cmd); + if (Status) { + DEBUG ((DEBUG_ERROR,"Mmc Write: Fail to setup controller 0x%x \n", Status)); + FreeDmaBuffer (&DmaData); + return Status; + } + + Status = Transfer (); + if (Status) { + DEBUG((DEBUG_ERROR,"Mmc Write Failed (0x%x) \n", Status)); + FreeDmaBuffer (&DmaData); + return Status; + } + + Status = FreeDmaBuffer (&DmaData); + if (Status) { + DEBUG ((DEBUG_ERROR,"Mmc Write : Failed to release DMA buffer \n")); + return Status; + } + + return Status; +} + +/** + Function to Initialize MMC + + // Set Bus width + // Set protocol register + + @retval Returns the initialization status + +**/ +EFI_STATUS +InitMmc ( + IN VOID + ) +{ + EFI_STATUS Status; + struct SdxcRegs *Regs; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + + Status = SdxcInit (mMmc); + if (Status) { + return Status; + } + + mMmc->DdrMode = 0; + SdxcSetBusWidth (mMmc, 1); + + MmcAnd ((UINTN)&Regs->Proctl, ~PRCTL_BE); + + return Status; +} + +/** + Function to set MMC clock speed + + @param BusClockFreq Bus clock frequency to be set Offset to write to + @param BusWidth Bus width + @param TimingMode Timing mode to be set + +**/ +VOID +SetIos ( + IN UINT32 BusClockFreq, + IN UINT32 BusWidth, + IN UINT32 TimingMode + ) +{ + struct SdxcRegs *Regs; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + + DEBUG_MSG ("BusClockFreq %d, BusWidth %d\n", BusClockFreq, BusWidth); + + // Set The Clock Speed + if (BusClockFreq) { + SetSysctl (BusClockFreq); + } + + // Set The Bus Width + if (BusWidth) { + MmcAnd ((UINTN)&Regs->Proctl, ~(PRCTL_DTW_4 | PRCTL_DTW_8)); + } + + if (BusWidth == 4) { + MmcOr ((UINTN)&Regs->Proctl, PRCTL_DTW_4); + } + else if (BusWidth == 8) { + MmcOr ((UINTN)&Regs->Proctl, PRCTL_DTW_8); + } +} + +/** + Helper Function to initialize MMC + + // Reset MMC controller + // Set host voltage capabilities + // Set MMC clock + +**/ +EFI_STATUS +MmcInitialize ( + VOID + ) +{ + EFI_STATUS Status; + struct SdxcRegs *Regs; + + UINT32 Caps, VoltageCaps; + UINTN Voltages; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + + // First Reset The SDXC Controller + SdxcReset (Regs); + + VoltageCaps = 0; + Caps = MmcRead ((UINTN)&Regs->Hostcapblt); + + Caps = Caps | SDXC_HOSTCAPBLT_VS33; + + if (Caps & SDXC_HOSTCAPBLT_VS30) { + VoltageCaps |= MMC_VDD_29_30 | MMC_VDD_30_31; + } + if (Caps & SDXC_HOSTCAPBLT_VS33) { + VoltageCaps |= MMC_VDD_32_33 | MMC_VDD_33_34; + } + if (Caps & SDXC_HOSTCAPBLT_VS18) { + VoltageCaps |= MMC_VDD_165_195; + } + + Voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + if ((Voltages & VoltageCaps) == 0) { + DEBUG ((DEBUG_ERROR, "Voltage Not Supported By Controller\n")); + return EFI_DEVICE_ERROR; + } + + mMmc = (struct Mmc*)AllocatePool (sizeof (struct Mmc)); + if (mMmc == NULL) { + DEBUG ((DEBUG_ERROR, "Memory Allocation failed for gMMC\n")); + return EFI_OUT_OF_RESOURCES; + } + + InternalMemZeroMem (mMmc, sizeof (struct Mmc)); + + mMmc->SdhcClk = GetSdxcFrequency (); + + mMmc->HostCaps = MMC_MODE_4_BIT | MMC_MODE_8_BIT | MMC_MODE_HC; + + if (Caps & SDXC_HOSTCAPBLT_HSS) { + mMmc->HostCaps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + } + + mMmc->FMin = MIN_CLK_FREQUENCY; + mMmc->FMax = MIN ((UINT32)mMmc->SdhcClk, MAX_CLK_FREQUENCY); + + Status = InitMmc (); + if (Status != EFI_SUCCESS) { + DEBUG ((DEBUG_ERROR,"Failed to initialize MMC\n")); + return Status; + } + + return EFI_SUCCESS; +} diff --git a/Platform/NXP/Library/MmcLib/MmcInternal.h b/Platform/NXP/Library/MmcLib/MmcInternal.h new file mode 100644 index 0000000..0e56764 --- /dev/null +++ b/Platform/NXP/Library/MmcLib/MmcInternal.h @@ -0,0 +1,350 @@ +/** @file + Header Defining The MMC Memory Controller Constants, Function Prototype, Structures Etc + + Copyright 2017 NXP + + 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_INTERNAL_H__ +#define __MMC_INTERNAL_H__ + +#include <Library/DmaLib.h> + +#define MIN_CLK_FREQUENCY 400000 +#define MAX_CLK_FREQUENCY 52000000 + +#define ENABLE_CACHE_SNOOPING 0x00000040 +#define CMD_STOP_TRANSMISSION 12 +/** + Pre Div factor for DDR mode +**/ +#define DIV_2 2 +#define DIV_1 1 + +/** + SDXC-Specific Constants +**/ +#define SYSCTL 0x0002e02c +#define SYSCTL_INITA 0x08000000 +#define SYSCTL_TIMEOUT_MASK 0x000f0000 +#define SYSCTL_CLOCK_MASK 0x0000fff0 +#define SYSCTL_CKEN 0x00000008 +#define SYSCTL_PEREN 0x00000004 +#define SYSCTL_HCKEN 0x00000002 +#define SYSCTL_IPGEN 0x00000001 +#define SYSCTL_RSTA 0x01000000 +#define SYSCTL_RSTC 0x02000000 +#define SYSCTL_RSTD 0x04000000 + +/** + Host Capabilities +**/ +#define SDXC_HOSTCAPBLT_VS18 0x04000000 +#define SDXC_HOSTCAPBLT_VS30 0x02000000 +#define SDXC_HOSTCAPBLT_VS33 0x01000000 +#define SDXC_HOSTCAPBLT_SRS 0x00800000 +#define SDXC_HOSTCAPBLT_DMAS 0x00400000 +#define SDXC_HOSTCAPBLT_HSS 0x00200000 + +/** + VDD Voltage Range +**/ +#define MMC_VDD_165_195 0x00000080 // VDD Voltage 1.65 - 1.95 +#define MMC_VDD_20_21 0x00000100 // VDD Voltage 2.0 ~ 2.1 +#define MMC_VDD_21_22 0x00000200 // VDD Voltage 2.1 ~ 2.2 +#define MMC_VDD_22_23 0x00000400 // VDD Voltage 2.2 ~ 2.3 +#define MMC_VDD_23_24 0x00000800 // VDD Voltage 2.3 ~ 2.4 +#define MMC_VDD_24_25 0x00001000 // VDD Voltage 2.4 ~ 2.5 +#define MMC_VDD_25_26 0x00002000 // VDD Voltage 2.5 ~ 2.6 +#define MMC_VDD_26_27 0x00004000 // VDD Voltage 2.6 ~ 2.7 +#define MMC_VDD_27_28 0x00008000 // VDD Voltage 2.7 ~ 2.8 +#define MMC_VDD_28_29 0x00010000 // VDD Voltage 2.8 ~ 2.9 +#define MMC_VDD_29_30 0x00020000 // VDD Voltage 2.9 ~ 3.0 +#define MMC_VDD_30_31 0x00040000 // VDD Voltage 3.0 ~ 3.1 +#define MMC_VDD_31_32 0x00080000 // VDD Voltage 3.1 ~ 3.2 +#define MMC_VDD_32_33 0x00100000 // VDD Voltage 3.2 ~ 3.3 +#define MMC_VDD_33_34 0x00200000 // VDD Voltage 3.3 ~ 3.4 +#define MMC_VDD_34_35 0x00400000 // VDD Voltage 3.4 ~ 3.5 +#define MMC_VDD_35_36 0x00800000 // VDD Voltage 3.5 ~ 3.6 + +/** + MMC Operating Modes +**/ +#define MMC_MODE_HS (1 << 0) +#define MMC_MODE_HS_52MHz (1 << 1) +#define MMC_MODE_4_BIT (1 << 2) +#define MMC_MODE_8_BIT (1 << 3) +#define MMC_MODE_SPI (1 << 4) +#define MMC_MODE_HC (1 << 5) +#define MMC_MODE_DDR_52MHz (1 << 6) + +#define MMC_DATA_READ 1 +#define MMC_DATA_WRITE 2 + +/** + Maximum Block Size for MMC +**/ +#define MMC_MAX_BLOCK_LEN 512 + +#define WML_RD_MAX 0x10 +#define WML_WR_MAX 0x80 +#define WML_RD_MAX_VAL 0x00 +#define WML_WR_MAX_VAL 0x80 +#define WML_RD_MASK 0xff +#define WML_WR_MASK 0xff0000 + +#define XFERTYPE 0x0002e00c +#define XFERTYPE_CMD(X) ((X & 0x3f) << 24) +#define XFERTYPE_CMDTYP_NORMAL 0x0 +#define XFERTYPE_CMDTYP_SUSPEND 0x00400000 +#define XFERTYPE_CMDTYP_RESUME 0x00800000 +#define XFERTYPE_CMDTYP_ABORT 0x00c00000 +#define XFERTYPE_DPSEL 0x00200000 +#define XFERTYPE_CICEN 0x00100000 +#define XFERTYPE_CCCEN 0x00080000 +#define XFERTYPE_RSPTYP_NONE 0 +#define XFERTYPE_RSPTYP_136 0x00010000 +#define XFERTYPE_RSPTYP_48 0x00020000 +#define XFERTYPE_RSPTYP_48_BUSY 0x00030000 +#define XFERTYPE_MSBSEL 0x00000020 +#define XFERTYPE_DTDSEL 0x00000010 +#define XFERTYPE_AC12EN 0x00000004 +#define XFERTYPE_BCEN 0x00000002 +#define XFERTYPE_DMAEN 0x00000001 + +/** + IRQSTAT bits +**/ +#define IRQSTAT (0x0002e030) +#define IRQSTATE_DMAE (0x10000000) +#define IRQSTATE_AC12E (0x01000000) +#define IRQSTATE_DEBE (0x00400000) +#define IRQSTATE_DCE (0x00200000) +#define IRQSTATE_DTOE (0x00100000) +#define IRQSTATE_CIE (0x00080000) +#define IRQSTATE_CEBE (0x00040000) +#define IRQSTATE_CCE (0x00020000) +#define IRQSTATE_CTOE (0x00010000) +#define IRQSTATE_CINT (0x00000100) +#define IRQSTATE_CRM (0x00000080) +#define IRQSTATE_CINS (0x00000040) +#define IRQSTATE_BRR (0x00000020) +#define IRQSTATE_BWR (0x00000010) +#define IRQSTATE_DINT (0x00000008) +#define IRQSTATE_BGE (0x00000004) +#define IRQSTATE_TC (0x00000002) +#define IRQSTATE_CC (0x00000001) + +#define CMD_ERR (IRQSTATE_CIE | IRQSTATE_CEBE | IRQSTATE_CCE) +#define DATA_ERR (IRQSTATE_DEBE | IRQSTATE_DCE | IRQSTATE_DTOE | \ + IRQSTATE_DMAE) +#define DATA_COMPLETE (IRQSTATE_TC | IRQSTATE_DINT) + +#define IRQSTATE_EN (0x0002e034) +#define IRQSTATE_EN_DMAE (0x10000000) +#define IRQSTATE_EN_AC12E (0x01000000) +#define IRQSTATE_EN_DEBE (0x00400000) +#define IRQSTATE_EN_DCE (0x00200000) +#define IRQSTATE_EN_DTOE (0x00100000) +#define IRQSTATE_EN_CIE (0x00080000) +#define IRQSTATE_EN_CEBE (0x00040000) +#define IRQSTATE_EN_CCE (0x00020000) +#define IRQSTATE_EN_CTOE (0x00010000) +#define IRQSTATE_EN_CINT (0x00000100) +#define IRQSTATE_EN_CRM (0x00000080) +#define IRQSTATE_EN_CINS (0x00000040) +#define IRQSTATE_EN_BRR (0x00000020) +#define IRQSTATE_EN_BWR (0x00000010) +#define IRQSTATE_EN_DINT (0x00000008) +#define IRQSTATE_EN_BGE (0x00000004) +#define IRQSTATE_EN_TC (0x00000002) +#define IRQSTATE_EN_CC (0x00000001) + +#define PRSSTATE (0x0002e024) +#define PRSSTATE_DAT0 (0x01000000) +#define PRSSTATE_CLSL (0x00800000) +#define PRSSTATE_WPSPL (0x00080000) +#define PRSSTATE_CDPL (0x00040000) +#define PRSSTATE_CINS (0x00010000) +#define PRSSTATE_BREN (0x00000800) +#define PRSSTATE_BWEN (0x00000400) +#define PRSSTATE_DLA (0x00000004) +#define PRSSTATE_CICHB (0x00000002) +#define PRSSTATE_CIDHB (0x00000001) + +#define PRCTL 0x0002e028 +#define PRCTL_INIT 0x00000020 +#define PRCTL_DTW_4 0x00000002 +#define PRCTL_DTW_8 0x00000004 +#define PRCTL_BE 0x00000030 + +struct SdxcRegs { + UINT32 Dsaddr; // SDMA System Address Register + UINT32 Blkattr; // Block Attributes Register + UINT32 CmdArg; // Command Argument Register + UINT32 Xfertype; // Transfer Type Register + UINT32 Rspns0; // Command Response 0 Register + UINT32 Rspns1; // Command Response 1 Register + UINT32 Rspns2; // Command Response 2 Register + UINT32 Rspns3; // Command Response 3 Register + UINT32 Datport; // Buffer Data Port Register + UINT32 Prsstat; // Present State Register + UINT32 Proctl; // Protocol Control Register + UINT32 Sysctl; // System Control Register + UINT32 Irqstat; // Interrupt Status Register + UINT32 Irqstaten; // Interrupt Status Enable Register + UINT32 Irqsigen; // Interrupt Signal Enable Register + UINT32 Autoc12err; // Auto CMD Error Status Register + UINT32 Hostcapblt; // Host Controller Capabilities Register + UINT32 Wml; // Watermark Level Register + UINT32 Mixctrl; // for USDHC + CHAR8 Reserved1[4]; // Reserved + UINT32 Fevt; // Force Event Register + UINT32 Admaes; // ADMA Error Status Register + UINT32 Adsaddr; // ADMA System Address Register + CHAR8 Reserved2[100];// Reserved + UINT32 VendorSpec; //Vendor Specific Register + CHAR8 Reserved3[56];// Reserved + UINT32 Hostver; // Host Controller Version Register + CHAR8 Reserved4[4];// Reserved + UINT32 Dmaerraddr; // DMA Error Address Register + CHAR8 Reserved5[4];// Reserved + UINT32 Dmaerrattr; // DMA Error Attribute Register + CHAR8 Reserved6[4];// Reserved + UINT32 Hostcapblt2; // Host Controller Capabilities Register 2 + CHAR8 Reserved7[8];// Reserved + UINT32 Tcr; // Tuning Control Register + CHAR8 Reserved8[28];// Reserved + UINT32 Sddirctl; // SD Direction Control Register + CHAR8 Reserved9[712];// Reserved + UINT32 Scr; // SDXC Control Register +}; + +struct DmaData { + VOID *DmaAddr; + UINTN Bytes; + VOID *Mapping; + DMA_MAP_OPERATION MapOperation; +}; + +struct Mmc { + UINT32 SdhcClk; + UINT32 HostCaps; + UINT32 HasInit; + UINT32 FMin; + UINT32 FMax; + UINT32 BusWidth; + UINT32 Clock; + UINT32 CardCaps; + INT32 DdrMode; +}; + +VOID +SdxcSetClock ( + IN struct Mmc *Mmc, + IN UINT32 Clock + ); + +EFI_STATUS +SdxcInit ( + IN struct Mmc *Mmc + ); + +EFI_STATUS +SdxcGoIdle ( + IN struct Mmc *Mmc + ); + +VOID +SdxcReset ( + IN struct SdxcRegs *Regs + ); + +VOID * +GetDmaBuffer ( + IN struct DmaData *DmaData + ); + +EFI_STATUS +FreeDmaBuffer ( + IN struct DmaData *DmaData + ); + +EFI_STATUS +SdxcSetupData ( + IN struct SdData *Data + ); + +UINT32 +SdxcXfertype ( + IN struct SdCmd *Cmd, + IN struct SdData *Data + ); + +VOID +ResetCmdFailedData ( + IN struct SdxcRegs *Regs, + IN UINT8 Data + ); + +VOID +SdxcSetBusWidth ( + IN struct Mmc *Mmc, + IN UINT32 BWidth + ); + +EFI_STATUS +Transfer ( + IN VOID + ); + +VOID +SetSysctl ( + UINT32 Clock + ); + +UINT32 +EFIAPI +MmcRead ( + IN UINTN Address + ); + +UINT32 +EFIAPI +MmcWrite ( + IN UINTN Address, + IN UINT32 Value + ); + +UINT32 +EFIAPI +MmcAndThenOr ( + IN UINTN Address, + IN UINT32 AndData, + IN UINT32 OrData + ); + +UINT32 +EFIAPI +MmcOr ( + IN UINTN Address, + IN UINT32 OrData + ); + +UINT32 +EFIAPI +MmcAnd ( + IN UINTN Address, + IN UINT32 AndData + ); + +#endif diff --git a/Platform/NXP/Library/MmcLib/MmcLib.c b/Platform/NXP/Library/MmcLib/MmcLib.c new file mode 100644 index 0000000..83b12c9 --- /dev/null +++ b/Platform/NXP/Library/MmcLib/MmcLib.c @@ -0,0 +1,597 @@ +/**@file + + Copyright 2017 NXP + + 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 <Library/BeIoLib.h> +#include <Library/IoLib.h> +#include <Library/MmcLib.h> +#include <Library/TimerLib.h> +#include "MmcInternal.h" + +extern struct Mmc *mMmc; + +/** + Function to read from MMC depending upon pcd + + @param Address MMC register to read + + @retval Read Value from register + +**/ +UINT32 +EFIAPI +MmcRead ( + IN UINTN Address + ) +{ + if (FixedPcdGetBool (PcdMmcBigEndian)) { + return BeMmioRead32(Address); + } else { + return MmioRead32 (Address); + } +} + +/** + Function to write on MMC depeding upon pcd + + @param Address MMC register to write + +**/ +UINT32 +EFIAPI +MmcWrite ( + IN UINTN Address, + IN UINT32 Value + ) +{ + if (FixedPcdGetBool (PcdMmcBigEndian)) { + return BeMmioWrite32 (Address, Value); + } else { + return MmioWrite32 (Address, Value); + } +} + +/** + Function to call MmioAndThenOr32 depending upon pcd + + @param Address MMC register + @param AndData The value to AND with the read value from the MMC register + @param OrData The value to OR with the result of the AND operation. + + @retval Value written back to register + +**/ +UINT32 +EFIAPI +MmcAndThenOr ( + IN UINTN Address, + IN UINT32 AndData, + IN UINT32 OrData + ) +{ + if (FixedPcdGetBool (PcdMmcBigEndian)) { + return BeMmioAndThenOr32 (Address, AndData, OrData); + } else { + return MmioAndThenOr32 (Address, AndData, OrData); + } +} + +/** + Function to call MmioOr32 depending upon pcd + + @param Address MMC register + @param OrData The value to OR with the result of the AND operation. + + @retval Value written back to register + +**/ +UINT32 +EFIAPI +MmcOr ( + IN UINTN Address, + IN UINT32 OrData + ) +{ + if (FixedPcdGetBool (PcdMmcBigEndian)) { + return BeMmioOr32 (Address, OrData); + } else { + return MmioOr32 (Address, OrData); + } +} + +/** + Function to call MmioAnd32 depending upon pcd + + @param Address MMC register + @param AndData The value to AND with the read value from the MMC register + + @retval Value written back to register + +**/ +UINT32 +EFIAPI +MmcAnd ( + IN UINTN Address, + IN UINT32 AndData + ) +{ + if (FixedPcdGetBool (PcdMmcBigEndian)) { + return BeMmioAnd32 (Address, AndData); + } else { + return MmioAnd32 (Address, AndData); + } +} + +/** + Function to Dump MMC Controller register +**/ +VOID +DumpMmcRegs ( + IN VOID + ) +{ + struct SdxcRegs *Regs; + + Regs = (VOID *)PcdGet64(PcdSdxcBaseAddr); + + DEBUG ((DEBUG_ERROR, "Dsaddr : 0x%x \n", Regs->Dsaddr)); + DEBUG ((DEBUG_ERROR, "Blkattr : 0x%x \n", Regs->Blkattr)); + DEBUG ((DEBUG_ERROR, "CmdArg : 0x%x \n", Regs->CmdArg)); + DEBUG ((DEBUG_ERROR, "Xfertype : 0x%x \n", Regs->Xfertype)); + DEBUG ((DEBUG_ERROR, "Rspns0 : 0x%x \n", Regs->Rspns0)); + DEBUG ((DEBUG_ERROR, "Rspns1 : 0x%x \n", Regs->Rspns1)); + DEBUG ((DEBUG_ERROR, "Rspns1 : 0x%x \n", Regs->Rspns1)); + DEBUG ((DEBUG_ERROR, "Rspns3 : 0x%x \n", Regs->Rspns3)); + DEBUG ((DEBUG_ERROR, "Datport : 0x%x \n", Regs->Datport)); + DEBUG ((DEBUG_ERROR, "Prsstat : 0x%x \n", Regs->Prsstat)); + DEBUG ((DEBUG_ERROR, "Proctl : 0x%x \n", Regs->Proctl)); + DEBUG ((DEBUG_ERROR, "Sysctl : 0x%x \n", Regs->Sysctl)); + DEBUG ((DEBUG_ERROR, "Irqstat : 0x%x \n", Regs->Irqstat)); + DEBUG ((DEBUG_ERROR, "Irqstaten : 0x%x \n", Regs->Irqstaten)); + DEBUG ((DEBUG_ERROR, "Irqsigen : 0x%x \n", Regs->Irqsigen)); + DEBUG ((DEBUG_ERROR, "Autoc12err : 0x%x \n", Regs->Autoc12err)); + DEBUG ((DEBUG_ERROR, "Hostcapblt : 0x%x \n", Regs->Hostcapblt)); + DEBUG ((DEBUG_ERROR, "Wml : 0x%x \n", Regs->Wml)); + DEBUG ((DEBUG_ERROR, "Mixctrl : 0x%x \n", Regs->Mixctrl)); + DEBUG ((DEBUG_ERROR, "Fevt : 0x%x \n", Regs->Fevt)); + DEBUG ((DEBUG_ERROR, "Admaes : 0x%x \n", Regs->Admaes)); + DEBUG ((DEBUG_ERROR, "Adsaddr : 0x%x \n", Regs->Adsaddr)); + DEBUG ((DEBUG_ERROR, "Hostver : 0x%x \n", Regs->Hostver)); + DEBUG ((DEBUG_ERROR, "Dmaerraddr : 0x%x \n", Regs->Dmaerraddr)); + DEBUG ((DEBUG_ERROR, "Dmaerrattr : 0x%x \n", Regs->Dmaerrattr)); + DEBUG ((DEBUG_ERROR, "Hostcapblt2 : 0x%x \n", Regs->Hostcapblt2)); + DEBUG ((DEBUG_ERROR, "Tcr : 0x%x \n", Regs->Tcr)); + DEBUG ((DEBUG_ERROR, "Sddirctl : 0x%x \n", Regs->Sddirctl)); + DEBUG ((DEBUG_ERROR, "Scr : 0x%x \n", Regs->Scr)); +} + +/** + Function to create dma map for read/write operation + + @param DmaData Pointer to Dma data Structure + + @retval Address of dma map + +**/ +VOID * +GetDmaBuffer ( + IN struct DmaData *DmaData + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS PhyAddr; + + Status = DmaAllocateBuffer (EfiBootServicesData, + EFI_SIZE_TO_PAGES (DmaData->Bytes), + &(DmaData->DmaAddr)); + if (Status) { + DEBUG((DEBUG_ERROR,"DmaAllocateBuffer failed\n")); + return NULL; + } + + Status = DmaMap (DmaData->MapOperation, DmaData->DmaAddr, + &DmaData->Bytes, &PhyAddr, &DmaData->Mapping); + if (Status) { + DEBUG((DEBUG_ERROR,"DmaMap failed %d \n", Status)); + + DmaFreeBuffer(EFI_SIZE_TO_PAGES (DmaData->Bytes), DmaData->DmaAddr); + + return NULL; + } + return (VOID *)PhyAddr; +} + +/** + Function to free dma map + + @param DmaData Pointer to Dma data Structure + + @retval Address of dma map + +**/ +EFI_STATUS +FreeDmaBuffer ( + IN struct DmaData *DmaData + ) +{ + EFI_STATUS Status; + + Status = DmaUnmap (DmaData->Mapping); + if (Status) { + DEBUG((DEBUG_ERROR,"DmaUnmap failed 0x%x\n", Status)); + } + + Status = DmaFreeBuffer (EFI_SIZE_TO_PAGES (DmaData->Bytes), DmaData->DmaAddr); + if (Status) { + DEBUG((DEBUG_ERROR,"DmaFreeBuffer failed 0x%x\n", Status)); + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Function to select the transfer type flags depending upon given + command and data packet + + @param Cmd Pointer to MMC command + @param Data Pointer to MMC data + + @retval Returns the XFERTYPE flags + +**/ +UINT32 +SdxcXfertype ( + IN struct SdCmd *Cmd, + IN struct SdData *Data + ) +{ + UINT32 Xfertype; + + Xfertype = 0; + + if (Data) { + Xfertype |= XFERTYPE_DPSEL; + Xfertype |= XFERTYPE_DMAEN; //DMA Support + + if (Data->Blocks > 1) { + Xfertype |= XFERTYPE_MSBSEL; + Xfertype |= XFERTYPE_BCEN; + } + + if (Data->Flags & MMC_DATA_READ) { + Xfertype |= XFERTYPE_DTDSEL; + } + } + + if (Cmd->RespType & MMC_RSP_CRC) { + Xfertype |= XFERTYPE_CCCEN; + } + if (Cmd->RespType & MMC_RSP_OPCODE) { + Xfertype |= XFERTYPE_CICEN; + } + if (Cmd->RespType & MMC_RSP_136) { + Xfertype |= XFERTYPE_RSPTYP_136; + } + else if (Cmd->RespType & MMC_RSP_BUSY) { + Xfertype |= XFERTYPE_RSPTYP_48_BUSY; + } + else if (Cmd->RespType & MMC_RSP_PRESENT) { + Xfertype |= XFERTYPE_RSPTYP_48; + } + + if (Cmd->CmdIdx == CMD_STOP_TRANSMISSION) { + Xfertype |= XFERTYPE_CMDTYP_ABORT; + } + + return XFERTYPE_CMD (Cmd->CmdIdx) | Xfertype; +} + +/** + Function to set up MMC data(timeout value,watermark level, + system address,block attributes etc.) + + @param Data Pointer to MMC data + +**/ +EFI_STATUS +SdxcSetupData ( + IN struct SdData *Data + ) +{ + struct SdxcRegs *Regs; + INT32 Timeout; + UINT32 WmlVal; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + Timeout = 0; + WmlVal = 0; + + WmlVal = Data->Blocksize/4; + + if (Data->Flags & MMC_DATA_READ) { + + if (WmlVal > WML_RD_MAX) { + WmlVal = WML_RD_MAX_VAL; + } + + MmcAndThenOr ((UINTN)&Regs->Wml, ~WML_RD_MASK, WmlVal); + + } else { + if (WmlVal > WML_WR_MAX) { + WmlVal = WML_WR_MAX_VAL; + } + + if ((MmcRead ((UINTN)&Regs->Prsstat) & PRSSTATE_WPSPL) == 0) { + DEBUG ((DEBUG_ERROR, "The SD Card Is Locked. Can Not Write To A Locked Card.\n")); + return EFI_TIMEOUT; + } + + MmcAndThenOr ((UINTN)&Regs->Wml, ~WML_WR_MASK, WmlVal << 16); + } + + EFI_PHYSICAL_ADDRESS Addr = (EFI_PHYSICAL_ADDRESS)Data->Addr; + MmcWrite ((UINTN)&Regs->Dsaddr, Addr); + + MmcWrite ((UINTN)&Regs->Blkattr, Data->Blocks << 16 | Data->Blocksize); + + // Calculate The Timeout Period for Data Transactions + Timeout = GenericFls (mMmc->Clock/4); + Timeout -= 13; + + if (Timeout > 14) { + Timeout = 14; + } + + if (Timeout < 0) { + Timeout = 0; + } + + MmcAndThenOr ((UINTN)&Regs->Sysctl, ~SYSCTL_TIMEOUT_MASK, Timeout << 16); + + return EFI_SUCCESS; +} + +/** + Function to peform reset of MMC command and data + +**/ +VOID +ResetCmdFailedData ( + IN struct SdxcRegs *Regs, + IN UINT8 Data + ) +{ + INT32 Timeout; + + Timeout = 10000; + + // Reset CMD And DATA Portions On Error + MmcWrite ((UINTN)&Regs->Sysctl, MmcRead ((UINTN)&Regs->Sysctl) | + SYSCTL_RSTC); + + while ((MmcRead((UINTN)&Regs->Sysctl) & SYSCTL_RSTC) && Timeout--); + if (Timeout <= 0) { + DEBUG((DEBUG_ERROR, "Failed to reset CMD Portion On Error\n")); + return; + } + + Timeout = 10000; + if (Data) { + MmcWrite ((UINTN)&Regs->Sysctl, + MmcRead ((UINTN)&Regs->Sysctl) | SYSCTL_RSTD); + while ((MmcRead ((UINTN)&Regs->Sysctl) & SYSCTL_RSTD) && Timeout--); + if (Timeout <= 0) { + DEBUG ((DEBUG_ERROR, "Failed to reset DATA Portion On Error\n")); + } + } + + MmcWrite ((UINTN)&Regs->Irqstat, 0xFFFFFFFF); +} + +/** + Function to do MMC read/write transfer using DMA and checks + whether transfer is completed or not + +**/ +EFI_STATUS +Transfer ( + IN VOID + ) +{ + UINT32 Irqstat; + UINT32 Timeout; + struct SdxcRegs *Regs; + + Regs = (VOID *)PcdGet64 (PcdSdxcBaseAddr); + Timeout = 10000000; + do { + Irqstat = MmcRead ((UINTN)&Regs->Irqstat); + + if (Irqstat & IRQSTATE_DTOE) { + DumpMmcRegs (); + ResetCmdFailedData (Regs, 1); + return EFI_TIMEOUT; + } + + if (Irqstat & DATA_ERR) { + ResetCmdFailedData (Regs, 1); + return EFI_DEVICE_ERROR; + } + + MicroSecondDelay (10); + + } while ((!(Irqstat & DATA_COMPLETE)) && Timeout--); + + if (Timeout <= 0) { + DEBUG ((DEBUG_ERROR, "Timeout Waiting for DATA_COMPLETE to set\n")); + ResetCmdFailedData (Regs, 1); + return EFI_TIMEOUT; + } + + MmcWrite ((UINTN)&Regs->Irqstat, 0xFFFFFFFF); + return EFI_SUCCESS; +} + +/** + Function to set MMC host controller system control register + + @param Clock Clock value for setting the register + +**/ +VOID +SetSysctl ( + UINT32 Clock + ) +{ + INT32 Div, PreDiv; + struct SdxcRegs *Regs; + INT32 SdhcClk; + UINT32 Clk; + + Regs = (VOID *)PcdGet64(PcdSdxcBaseAddr); + SdhcClk = mMmc->SdhcClk; + + if (Clock < mMmc->FMin) { + Clock = mMmc->FMin; + } else if (Clock > mMmc->FMax) { + Clock = mMmc->FMax; + } + + mMmc->Clock = Clock; + + if (SdhcClk / 16 > Clock) { + for (PreDiv = 2; PreDiv < 256; PreDiv *= 2) + if ((SdhcClk / PreDiv) <= (Clock * 16)) { + break; + } + } else { + PreDiv = 2; + } + + for (Div = 1; Div <= 16; Div++) + if ((SdhcClk / (Div * PreDiv)) <= Clock) { + break; + } + + PreDiv >>= mMmc->DdrMode ? DIV_2 : DIV_1; + Div -= 1; + + Clk = (PreDiv << 8) | (Div << 4); + + MmcAnd ((UINTN)&Regs->Sysctl, ~SYSCTL_CKEN); + + MmcAndThenOr ((UINTN)&Regs->Sysctl, ~SYSCTL_CLOCK_MASK, Clk); + + MicroSecondDelay (100); + + Clk = SYSCTL_PEREN | SYSCTL_CKEN; + + MmcOr ((UINTN)&Regs->Sysctl, Clk); +} + +/** + Function to set MMC host controller bus width + + @param Mmc Pointer to MMC data structure + @param BWidth Bus width to be set + +**/ +VOID +SdxcSetBusWidth ( + IN struct Mmc *Mmc, + IN UINT32 BWidth + ) +{ + Mmc->BusWidth = BWidth; + + SetIos (Mmc->Clock, Mmc->BusWidth, 0); +} + +/** + Function to Initialize MMC host controller + + @param Mmc Pointer to MMC data structure + +**/ +EFI_STATUS +SdxcInit ( + IN struct Mmc *Mmc + ) +{ + struct SdxcRegs *Regs; + INT32 Timeout; + + Regs = (VOID *)PcdGet64(PcdSdxcBaseAddr); + Timeout = 1000; + + // Reset The Entire Host Controller + MmcOr ((UINTN)&Regs->Sysctl, SYSCTL_RSTA); + + // Wait Until The Controller Is Available + while ((MmcRead ((UINTN)&Regs->Sysctl) & SYSCTL_RSTA) && --Timeout) + MicroSecondDelay (1000); + + if (Timeout <= 0) { + DEBUG ((DEBUG_ERROR, "Host controller failed to reset \n")); + return EFI_DEVICE_ERROR; + } + + // Enable Cache Snooping + MmcWrite ((UINTN)&Regs->Scr, ENABLE_CACHE_SNOOPING); + + MmcOr ((UINTN)&Regs->Sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + + // Set The Initial Clock Speed + SetIos (MIN_CLK_FREQUENCY, Mmc->BusWidth, 0); + + // Disable The BRR And BWR Bits In IRQSTAT + MmcAnd ((UINTN)&Regs->Irqstaten, + ~(IRQSTATE_EN_BRR | IRQSTATE_EN_BWR)); + + // Set Little Endian mode for data Buffer + MmcWrite ((UINTN)&Regs->Proctl, PRCTL_INIT); + + // Set Timeout To The Maximum Value + MmcAndThenOr ((UINTN)&Regs->Sysctl, + ~SYSCTL_TIMEOUT_MASK, 14 << 16); + + return EFI_SUCCESS; +} + +/** + Function to reset MMC host controller + + @param Regs Pointer to MMC host Controller + +**/ +VOID +SdxcReset ( + IN struct SdxcRegs *Regs + ) +{ + UINT64 Timeout; + + Timeout = 100; + + // Reset The Controller + MmcWrite ((UINTN)&Regs->Sysctl, SYSCTL_RSTA); + + // Hardware Clears The Bit When It Is Done + while ((MmcRead ((UINTN)&Regs->Sysctl) & SYSCTL_RSTA) && --Timeout) + MicroSecondDelay (10); + + if (!Timeout) { + DEBUG((DEBUG_ERROR, "MMC/SD: Reset Never Completed.\n")); + } +} diff --git a/Platform/NXP/Library/MmcLib/MmcLib.inf b/Platform/NXP/Library/MmcLib/MmcLib.inf new file mode 100644 index 0000000..017c6ad --- /dev/null +++ b/Platform/NXP/Library/MmcLib/MmcLib.inf @@ -0,0 +1,39 @@ +##@file +# +# Copyright 2017 NXP +# +# 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 = MmcLib + FILE_GUID = c0f5dfa0-7599-11e0-9866-0002a5d5c61b + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = MmcLib + +[Sources.common] + MmcLib.c + MmcInterface.c + +[LibraryClasses] + BaseMemoryLib + BeIoLib + DmaLib + +[Packages] + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + Platform/NXP/NxpQoriqLs.dec + +[FixedPcd] + gNxpQoriqLsTokenSpaceGuid.PcdSdxcBaseAddr + gNxpQoriqLsTokenSpaceGuid.PcdMmcBigEndian -- 1.9.1 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel