REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the MdeModulePkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
		
			
				
	
	
		
			2952 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2952 lines
		
	
	
		
			80 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
|   Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "EmmcBlockIoPei.h"
 | |
| 
 | |
| /**
 | |
|   Read/Write specified EMMC host controller mmio register.
 | |
| 
 | |
|   @param[in]      Address      The address of the mmio register to be read/written.
 | |
|   @param[in]      Read         A boolean to indicate it's read or write operation.
 | |
|   @param[in]      Count        The width of the mmio register in bytes.
 | |
|                                Must be 1, 2 , 4 or 8 bytes.
 | |
|   @param[in, out] Data         For read operations, the destination buffer to store
 | |
|                                the results. For write operations, the source buffer
 | |
|                                to write data from. The caller is responsible for
 | |
|                                having ownership of the data buffer and ensuring its
 | |
|                                size not less than Count bytes.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER The Address or the Data or the Count is not valid.
 | |
|   @retval EFI_SUCCESS           The read/write operation succeeds.
 | |
|   @retval Others                The read/write operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimHcRwMmio (
 | |
|   IN     UINTN    Address,
 | |
|   IN     BOOLEAN  Read,
 | |
|   IN     UINT8    Count,
 | |
|   IN OUT VOID     *Data
 | |
|   )
 | |
| {
 | |
|   if ((Address == 0) || (Data == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   switch (Count) {
 | |
|     case 1:
 | |
|       if (Read) {
 | |
|         *(UINT8 *)Data = MmioRead8 (Address);
 | |
|       } else {
 | |
|         MmioWrite8 (Address, *(UINT8 *)Data);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     case 2:
 | |
|       if (Read) {
 | |
|         *(UINT16 *)Data = MmioRead16 (Address);
 | |
|       } else {
 | |
|         MmioWrite16 (Address, *(UINT16 *)Data);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     case 4:
 | |
|       if (Read) {
 | |
|         *(UINT32 *)Data = MmioRead32 (Address);
 | |
|       } else {
 | |
|         MmioWrite32 (Address, *(UINT32 *)Data);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     case 8:
 | |
|       if (Read) {
 | |
|         *(UINT64 *)Data = MmioRead64 (Address);
 | |
|       } else {
 | |
|         MmioWrite64 (Address, *(UINT64 *)Data);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     default:
 | |
|       ASSERT (FALSE);
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Do OR operation with the value of the specified EMMC host controller mmio register.
 | |
| 
 | |
|   @param[in] Address           The address of the mmio register to be read/written.
 | |
|   @param[in] Count             The width of the mmio register in bytes.
 | |
|                                Must be 1, 2 , 4 or 8 bytes.
 | |
|   @param[in] OrData            The pointer to the data used to do OR operation.
 | |
|                                The caller is responsible for having ownership of
 | |
|                                the data buffer and ensuring its size not less than
 | |
|                                Count bytes.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER The Address or the OrData or the Count is not valid.
 | |
|   @retval EFI_SUCCESS           The OR operation succeeds.
 | |
|   @retval Others                The OR operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimHcOrMmio (
 | |
|   IN  UINTN  Address,
 | |
|   IN  UINT8  Count,
 | |
|   IN  VOID   *OrData
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT64      Data;
 | |
|   UINT64      Or;
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Data);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (Count == 1) {
 | |
|     Or = *(UINT8 *)OrData;
 | |
|   } else if (Count == 2) {
 | |
|     Or = *(UINT16 *)OrData;
 | |
|   } else if (Count == 4) {
 | |
|     Or = *(UINT32 *)OrData;
 | |
|   } else if (Count == 8) {
 | |
|     Or = *(UINT64 *)OrData;
 | |
|   } else {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Data  |= Or;
 | |
|   Status = EmmcPeimHcRwMmio (Address, FALSE, Count, &Data);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Do AND operation with the value of the specified EMMC host controller mmio register.
 | |
| 
 | |
|   @param[in] Address           The address of the mmio register to be read/written.
 | |
|   @param[in] Count             The width of the mmio register in bytes.
 | |
|                                Must be 1, 2 , 4 or 8 bytes.
 | |
|   @param[in] AndData           The pointer to the data used to do AND operation.
 | |
|                                The caller is responsible for having ownership of
 | |
|                                the data buffer and ensuring its size not less than
 | |
|                                Count bytes.
 | |
| 
 | |
|   @retval EFI_INVALID_PARAMETER The Address or the AndData or the Count is not valid.
 | |
|   @retval EFI_SUCCESS           The AND operation succeeds.
 | |
|   @retval Others                The AND operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimHcAndMmio (
 | |
|   IN  UINTN  Address,
 | |
|   IN  UINT8  Count,
 | |
|   IN  VOID   *AndData
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT64      Data;
 | |
|   UINT64      And;
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Data);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (Count == 1) {
 | |
|     And = *(UINT8 *)AndData;
 | |
|   } else if (Count == 2) {
 | |
|     And = *(UINT16 *)AndData;
 | |
|   } else if (Count == 4) {
 | |
|     And = *(UINT32 *)AndData;
 | |
|   } else if (Count == 8) {
 | |
|     And = *(UINT64 *)AndData;
 | |
|   } else {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Data  &= And;
 | |
|   Status = EmmcPeimHcRwMmio (Address, FALSE, Count, &Data);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait for the value of the specified MMIO register set to the test value.
 | |
| 
 | |
|   @param[in]  Address       The address of the mmio register to be checked.
 | |
|   @param[in]  Count         The width of the mmio register in bytes.
 | |
|                             Must be 1, 2, 4 or 8 bytes.
 | |
|   @param[in]  MaskValue     The mask value of memory.
 | |
|   @param[in]  TestValue     The test value of memory.
 | |
| 
 | |
|   @retval EFI_NOT_READY     The MMIO register hasn't set to the expected value.
 | |
|   @retval EFI_SUCCESS       The MMIO register has expected value.
 | |
|   @retval Others            The MMIO operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimHcCheckMmioSet (
 | |
|   IN  UINTN   Address,
 | |
|   IN  UINT8   Count,
 | |
|   IN  UINT64  MaskValue,
 | |
|   IN  UINT64  TestValue
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT64      Value;
 | |
| 
 | |
|   //
 | |
|   // Access PCI MMIO space to see if the value is the tested one.
 | |
|   //
 | |
|   Value  = 0;
 | |
|   Status = EmmcPeimHcRwMmio (Address, TRUE, Count, &Value);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Value &= MaskValue;
 | |
| 
 | |
|   if (Value == TestValue) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   return EFI_NOT_READY;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait for the value of the specified MMIO register set to the test value.
 | |
| 
 | |
|   @param[in]  Address       The address of the mmio register to wait.
 | |
|   @param[in]  Count         The width of the mmio register in bytes.
 | |
|                             Must be 1, 2, 4 or 8 bytes.
 | |
|   @param[in]  MaskValue     The mask value of memory.
 | |
|   @param[in]  TestValue     The test value of memory.
 | |
|   @param[in]  Timeout       The time out value for wait memory set, uses 1
 | |
|                             microsecond as a unit.
 | |
| 
 | |
|   @retval EFI_TIMEOUT       The MMIO register hasn't expected value in timeout
 | |
|                             range.
 | |
|   @retval EFI_SUCCESS       The MMIO register has expected value.
 | |
|   @retval Others            The MMIO operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimHcWaitMmioSet (
 | |
|   IN  UINTN   Address,
 | |
|   IN  UINT8   Count,
 | |
|   IN  UINT64  MaskValue,
 | |
|   IN  UINT64  TestValue,
 | |
|   IN  UINT64  Timeout
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   BOOLEAN     InfiniteWait;
 | |
| 
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   while (InfiniteWait || (Timeout > 0)) {
 | |
|     Status = EmmcPeimHcCheckMmioSet (
 | |
|                Address,
 | |
|                Count,
 | |
|                MaskValue,
 | |
|                TestValue
 | |
|                );
 | |
|     if (Status != EFI_NOT_READY) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 1 microsecond.
 | |
|     //
 | |
|     MicroSecondDelay (1);
 | |
| 
 | |
|     Timeout--;
 | |
|   }
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Software reset the specified EMMC host controller and enable all interrupts.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The software reset executes successfully.
 | |
|   @retval Others            The software reset fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcReset (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       SwReset;
 | |
| 
 | |
|   SwReset = 0xFF;
 | |
|   Status  = EmmcPeimHcRwMmio (Bar + EMMC_HC_SW_RST, FALSE, sizeof (SwReset), &SwReset);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimHcReset: write full 1 fails: %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcWaitMmioSet (
 | |
|              Bar + EMMC_HC_SW_RST,
 | |
|              sizeof (SwReset),
 | |
|              0xFF,
 | |
|              0x00,
 | |
|              EMMC_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_INFO, "EmmcPeimHcReset: reset done with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enable all interrupt after reset all.
 | |
|   //
 | |
|   Status = EmmcPeimHcEnableInterrupt (Bar);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Set all interrupt status bits in Normal and Error Interrupt Status Enable
 | |
|   register.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation executes successfully.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcEnableInterrupt (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT16      IntStatus;
 | |
| 
 | |
|   //
 | |
|   // Enable all bits in Error Interrupt Status Enable Register
 | |
|   //
 | |
|   IntStatus = 0xFFFF;
 | |
|   Status    = EmmcPeimHcRwMmio (Bar + EMMC_HC_ERR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enable all bits in Normal Interrupt Status Enable Register
 | |
|   //
 | |
|   IntStatus = 0xFFFF;
 | |
|   Status    = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS_EN, FALSE, sizeof (IntStatus), &IntStatus);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the capability data from the specified slot.
 | |
| 
 | |
|   @param[in]  Bar             The mmio base address of the slot to be accessed.
 | |
|   @param[out] Capability      The buffer to store the capability data.
 | |
| 
 | |
|   @retval EFI_SUCCESS         The operation executes successfully.
 | |
|   @retval Others              The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcGetCapability (
 | |
|   IN     UINTN          Bar,
 | |
|   OUT EMMC_HC_SLOT_CAP  *Capability
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT64      Cap;
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_CAP, TRUE, sizeof (Cap), &Cap);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   CopyMem (Capability, &Cap, sizeof (Cap));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Detect whether there is a EMMC card attached at the specified EMMC host controller
 | |
|   slot.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.1 for details.
 | |
| 
 | |
|   @param[in]  Bar           The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       There is a EMMC card attached.
 | |
|   @retval EFI_NO_MEDIA      There is not a EMMC card attached.
 | |
|   @retval Others            The detection fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcCardDetect (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT16      Data;
 | |
|   UINT32      PresentState;
 | |
| 
 | |
|   //
 | |
|   // Check Normal Interrupt Status Register
 | |
|   //
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, TRUE, sizeof (Data), &Data);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((Data & (BIT6 | BIT7)) != 0) {
 | |
|     //
 | |
|     // Clear BIT6 and BIT7 by writing 1 to these two bits if set.
 | |
|     //
 | |
|     Data  &= BIT6 | BIT7;
 | |
|     Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (Data), &Data);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check Present State Register to see if there is a card presented.
 | |
|   //
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((PresentState & BIT16) != 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   } else {
 | |
|     return EFI_NO_MEDIA;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Stop EMMC card clock.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.2.2 for details.
 | |
| 
 | |
|   @param[in]  Bar           The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       Succeed to stop EMMC clock.
 | |
|   @retval Others            Fail to stop EMMC clock.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcStopClock (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      PresentState;
 | |
|   UINT16      ClockCtrl;
 | |
| 
 | |
|   //
 | |
|   // Ensure no SD transactions are occurring on the SD Bus by
 | |
|   // waiting for Command Inhibit (DAT) and Command Inhibit (CMD)
 | |
|   // in the Present State register to be 0.
 | |
|   //
 | |
|   Status = EmmcPeimHcWaitMmioSet (
 | |
|              Bar + EMMC_HC_PRESENT_STATE,
 | |
|              sizeof (PresentState),
 | |
|              BIT0 | BIT1,
 | |
|              0,
 | |
|              EMMC_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SD Clock Enable in the Clock Control register to 0
 | |
|   //
 | |
|   ClockCtrl = (UINT16) ~BIT2;
 | |
|   Status    = EmmcPeimHcAndMmio (Bar + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   EMMC card clock supply.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] ClockFreq      The max clock frequency to be set. The unit is KHz.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The clock is supplied successfully.
 | |
|   @retval Others            The clock isn't supplied successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcClockSupply (
 | |
|   IN UINTN   Bar,
 | |
|   IN UINT64  ClockFreq
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS        Status;
 | |
|   EMMC_HC_SLOT_CAP  Capability;
 | |
|   UINT32            BaseClkFreq;
 | |
|   UINT32            SettingFreq;
 | |
|   UINT32            Divisor;
 | |
|   UINT32            Remainder;
 | |
|   UINT16            ControllerVer;
 | |
|   UINT16            ClockCtrl;
 | |
| 
 | |
|   //
 | |
|   // Calculate a divisor for SD clock frequency
 | |
|   //
 | |
|   Status = EmmcPeimHcGetCapability (Bar, &Capability);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ASSERT (Capability.BaseClkFreq != 0);
 | |
| 
 | |
|   BaseClkFreq = Capability.BaseClkFreq;
 | |
| 
 | |
|   if (ClockFreq == 0) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (ClockFreq > (BaseClkFreq * 1000)) {
 | |
|     ClockFreq = BaseClkFreq * 1000;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate the divisor of base frequency.
 | |
|   //
 | |
|   Divisor     = 0;
 | |
|   SettingFreq = BaseClkFreq * 1000;
 | |
|   while (ClockFreq < SettingFreq) {
 | |
|     Divisor++;
 | |
| 
 | |
|     SettingFreq = (BaseClkFreq * 1000) / (2 * Divisor);
 | |
|     Remainder   = (BaseClkFreq * 1000) % (2 * Divisor);
 | |
|     if ((ClockFreq == SettingFreq) && (Remainder == 0)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((ClockFreq == SettingFreq) && (Remainder != 0)) {
 | |
|       SettingFreq++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq));
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_CTRL_VER, TRUE, sizeof (ControllerVer), &ControllerVer);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SDCLK Frequency Select and Internal Clock Enable fields in Clock Control register.
 | |
|   //
 | |
|   if ((ControllerVer & 0xFF) == 2) {
 | |
|     ASSERT (Divisor <= 0x3FF);
 | |
|     ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2);
 | |
|   } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) {
 | |
|     //
 | |
|     // Only the most significant bit can be used as divisor.
 | |
|     //
 | |
|     if (((Divisor - 1) & Divisor) != 0) {
 | |
|       Divisor = 1 << (HighBitSet32 (Divisor) + 1);
 | |
|     }
 | |
| 
 | |
|     ASSERT (Divisor <= 0x80);
 | |
|     ClockCtrl = (Divisor & 0xFF) << 8;
 | |
|   } else {
 | |
|     DEBUG ((DEBUG_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer));
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Stop bus clock at first
 | |
|   //
 | |
|   Status = EmmcPeimHcStopClock (Bar);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Supply clock frequency with specified divisor
 | |
|   //
 | |
|   ClockCtrl |= BIT0;
 | |
|   Status     = EmmcPeimHcRwMmio (Bar + EMMC_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n"));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Wait Internal Clock Stable in the Clock Control register to be 1
 | |
|   //
 | |
|   Status = EmmcPeimHcWaitMmioSet (
 | |
|              Bar + EMMC_HC_CLOCK_CTRL,
 | |
|              sizeof (ClockCtrl),
 | |
|              BIT1,
 | |
|              BIT1,
 | |
|              EMMC_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SD Clock Enable in the Clock Control register to 1
 | |
|   //
 | |
|   ClockCtrl = BIT2;
 | |
|   Status    = EmmcPeimHcOrMmio (Bar + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   EMMC bus power control.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] PowerCtrl      The value setting to the power control register.
 | |
| 
 | |
|   @retval TRUE              There is a EMMC card attached.
 | |
|   @retval FALSE             There is no a EMMC card attached.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcPowerControl (
 | |
|   IN UINTN  Bar,
 | |
|   IN UINT8  PowerCtrl
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // Clr SD Bus Power
 | |
|   //
 | |
|   PowerCtrl &= (UINT8) ~BIT0;
 | |
|   Status     = EmmcPeimHcRwMmio (Bar + EMMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register
 | |
|   //
 | |
|   PowerCtrl |= BIT0;
 | |
|   Status     = EmmcPeimHcRwMmio (Bar + EMMC_HC_POWER_CTRL, FALSE, sizeof (PowerCtrl), &PowerCtrl);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Set the EMMC bus width.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.4 for details.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] BusWidth       The bus width used by the EMMC device, it must be 1, 4 or 8.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The bus width is set successfully.
 | |
|   @retval Others            The bus width isn't set successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcSetBusWidth (
 | |
|   IN UINTN   Bar,
 | |
|   IN UINT16  BusWidth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HostCtrl1;
 | |
| 
 | |
|   if (BusWidth == 1) {
 | |
|     HostCtrl1 = (UINT8) ~(BIT5 | BIT1);
 | |
|     Status    = EmmcPeimHcAndMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   } else if (BusWidth == 4) {
 | |
|     Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     HostCtrl1 |= BIT1;
 | |
|     HostCtrl1 &= (UINT8) ~BIT5;
 | |
|     Status     = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   } else if (BusWidth == 8) {
 | |
|     Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, TRUE, sizeof (HostCtrl1), &HostCtrl1);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     HostCtrl1 &= (UINT8) ~BIT1;
 | |
|     HostCtrl1 |= BIT5;
 | |
|     Status     = EmmcPeimHcRwMmio (Bar + EMMC_HC_HOST_CTRL1, FALSE, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   } else {
 | |
|     ASSERT (FALSE);
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Supply EMMC card with lowest clock frequency at initialization.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The clock is supplied successfully.
 | |
|   @retval Others            The clock isn't supplied successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcInitClockFreq (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS        Status;
 | |
|   EMMC_HC_SLOT_CAP  Capability;
 | |
|   UINT32            InitFreq;
 | |
| 
 | |
|   //
 | |
|   // Calculate a divisor for SD clock frequency
 | |
|   //
 | |
|   Status = EmmcPeimHcGetCapability (Bar, &Capability);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (Capability.BaseClkFreq == 0) {
 | |
|     //
 | |
|     // Don't support get Base Clock Frequency information via another method
 | |
|     //
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Supply 400KHz clock frequency at initialization phase.
 | |
|   //
 | |
|   InitFreq = 400;
 | |
|   Status   = EmmcPeimHcClockSupply (Bar, InitFreq);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Supply EMMC card with maximum voltage at initialization.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 3.3 for details.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The voltage is supplied successfully.
 | |
|   @retval Others            The voltage isn't supplied successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcInitPowerVoltage (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS        Status;
 | |
|   EMMC_HC_SLOT_CAP  Capability;
 | |
|   UINT8             MaxVoltage;
 | |
|   UINT8             HostCtrl2;
 | |
| 
 | |
|   //
 | |
|   // Get the support voltage of the Host Controller
 | |
|   //
 | |
|   Status = EmmcPeimHcGetCapability (Bar, &Capability);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate supported maximum voltage according to SD Bus Voltage Select
 | |
|   //
 | |
|   if (Capability.Voltage33 != 0) {
 | |
|     //
 | |
|     // Support 3.3V
 | |
|     //
 | |
|     MaxVoltage = 0x0E;
 | |
|   } else if (Capability.Voltage30 != 0) {
 | |
|     //
 | |
|     // Support 3.0V
 | |
|     //
 | |
|     MaxVoltage = 0x0C;
 | |
|   } else if (Capability.Voltage18 != 0) {
 | |
|     //
 | |
|     // Support 1.8V
 | |
|     //
 | |
|     MaxVoltage = 0x0A;
 | |
|     HostCtrl2  = BIT3;
 | |
|     Status     = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     MicroSecondDelay (5000);
 | |
|   } else {
 | |
|     ASSERT (FALSE);
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SD Bus Voltage Select and SD Bus Power fields in Power Control Register
 | |
|   //
 | |
|   Status = EmmcPeimHcPowerControl (Bar, MaxVoltage);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the Timeout Control register with most conservative value at initialization.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 2.2.15 for details.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The timeout control register is configured successfully.
 | |
|   @retval Others            The timeout control register isn't configured successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcInitTimeoutCtrl (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       Timeout;
 | |
| 
 | |
|   Timeout = 0x0E;
 | |
|   Status  = EmmcPeimHcRwMmio (Bar + EMMC_HC_TIMEOUT_CTRL, FALSE, sizeof (Timeout), &Timeout);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initial EMMC host controller with lowest clock frequency, max power and max timeout value
 | |
|   at initialization.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The host controller is initialized successfully.
 | |
|   @retval Others            The host controller isn't initialized successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcInitHost (
 | |
|   IN UINTN  Bar
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   Status = EmmcPeimHcInitClockFreq (Bar);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcInitPowerVoltage (Bar);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcInitTimeoutCtrl (Bar);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Turn on/off LED.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] On             The boolean to turn on/off LED.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The LED is turned on/off successfully.
 | |
|   @retval Others            The LED isn't turned on/off successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimHcLedOnOff (
 | |
|   IN UINTN    Bar,
 | |
|   IN BOOLEAN  On
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HostCtrl1;
 | |
| 
 | |
|   if (On) {
 | |
|     HostCtrl1 = BIT0;
 | |
|     Status    = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   } else {
 | |
|     HostCtrl1 = (UINT8) ~BIT0;
 | |
|     Status    = EmmcPeimHcAndMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Build ADMA descriptor table for transfer.
 | |
| 
 | |
|   Refer to SD Host Controller Simplified spec 3.0 Section 1.13 for details.
 | |
| 
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The ADMA descriptor table is created successfully.
 | |
|   @retval Others            The ADMA descriptor table isn't created successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| BuildAdmaDescTable (
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_PHYSICAL_ADDRESS  Data;
 | |
|   UINT64                DataLen;
 | |
|   UINT64                Entries;
 | |
|   UINT32                Index;
 | |
|   UINT64                Remaining;
 | |
|   UINT32                Address;
 | |
| 
 | |
|   Data    = Trb->DataPhy;
 | |
|   DataLen = Trb->DataLen;
 | |
|   //
 | |
|   // Only support 32bit ADMA Descriptor Table
 | |
|   //
 | |
|   if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0)
 | |
|   // for 32-bit address descriptor table.
 | |
|   //
 | |
|   if ((Data & (BIT0 | BIT1)) != 0) {
 | |
|     DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data));
 | |
|   }
 | |
| 
 | |
|   Entries = DivU64x32 ((DataLen + ADMA_MAX_DATA_PER_LINE - 1), ADMA_MAX_DATA_PER_LINE);
 | |
| 
 | |
|   Trb->AdmaDescSize = (UINTN)MultU64x32 (Entries, sizeof (EMMC_HC_ADMA_DESC_LINE));
 | |
|   Trb->AdmaDesc     = EmmcPeimAllocateMem (Trb->Slot->Private->Pool, Trb->AdmaDescSize);
 | |
|   if (Trb->AdmaDesc == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Remaining = DataLen;
 | |
|   Address   = (UINT32)Data;
 | |
|   for (Index = 0; Index < Entries; Index++) {
 | |
|     if (Remaining <= ADMA_MAX_DATA_PER_LINE) {
 | |
|       Trb->AdmaDesc[Index].Valid   = 1;
 | |
|       Trb->AdmaDesc[Index].Act     = 2;
 | |
|       Trb->AdmaDesc[Index].Length  = (UINT16)Remaining;
 | |
|       Trb->AdmaDesc[Index].Address = Address;
 | |
|       break;
 | |
|     } else {
 | |
|       Trb->AdmaDesc[Index].Valid   = 1;
 | |
|       Trb->AdmaDesc[Index].Act     = 2;
 | |
|       Trb->AdmaDesc[Index].Length  = 0;
 | |
|       Trb->AdmaDesc[Index].Address = Address;
 | |
|     }
 | |
| 
 | |
|     Remaining -= ADMA_MAX_DATA_PER_LINE;
 | |
|     Address   += ADMA_MAX_DATA_PER_LINE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set the last descriptor line as end of descriptor table
 | |
|   //
 | |
|   Trb->AdmaDesc[Index].End = 1;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Create a new TRB for the EMMC cmd request.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the EMMC card to send the command to.
 | |
|   @param[in] Packet         A pointer to the SD command data structure.
 | |
| 
 | |
|   @return Created Trb or NULL.
 | |
| 
 | |
| **/
 | |
| EMMC_TRB *
 | |
| EmmcPeimCreateTrb (
 | |
|   IN EMMC_PEIM_HC_SLOT    *Slot,
 | |
|   IN EMMC_COMMAND_PACKET  *Packet
 | |
|   )
 | |
| {
 | |
|   EMMC_TRB               *Trb;
 | |
|   EFI_STATUS             Status;
 | |
|   EMMC_HC_SLOT_CAP       Capability;
 | |
|   EDKII_IOMMU_OPERATION  MapOp;
 | |
|   UINTN                  MapLength;
 | |
| 
 | |
|   //
 | |
|   // Calculate a divisor for SD clock frequency
 | |
|   //
 | |
|   Status = EmmcPeimHcGetCapability (Slot->EmmcHcBase, &Capability);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Trb = AllocateZeroPool (sizeof (EMMC_TRB));
 | |
|   if (Trb == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   Trb->Slot      = Slot;
 | |
|   Trb->BlockSize = 0x200;
 | |
|   Trb->Packet    = Packet;
 | |
|   Trb->Timeout   = Packet->Timeout;
 | |
| 
 | |
|   if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) {
 | |
|     Trb->Data    = Packet->InDataBuffer;
 | |
|     Trb->DataLen = Packet->InTransferLength;
 | |
|     Trb->Read    = TRUE;
 | |
|   } else if ((Packet->OutTransferLength != 0) && (Packet->OutDataBuffer != NULL)) {
 | |
|     Trb->Data    = Packet->OutDataBuffer;
 | |
|     Trb->DataLen = Packet->OutTransferLength;
 | |
|     Trb->Read    = FALSE;
 | |
|   } else if ((Packet->InTransferLength == 0) && (Packet->OutTransferLength == 0)) {
 | |
|     Trb->Data    = NULL;
 | |
|     Trb->DataLen = 0;
 | |
|   } else {
 | |
|     goto Error;
 | |
|   }
 | |
| 
 | |
|   if ((Trb->DataLen != 0) && (Trb->DataLen < Trb->BlockSize)) {
 | |
|     Trb->BlockSize = (UINT16)Trb->DataLen;
 | |
|   }
 | |
| 
 | |
|   if (Packet->EmmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK) {
 | |
|     Trb->Mode = EmmcPioMode;
 | |
|   } else {
 | |
|     if (Trb->Read) {
 | |
|       MapOp = EdkiiIoMmuOperationBusMasterWrite;
 | |
|     } else {
 | |
|       MapOp = EdkiiIoMmuOperationBusMasterRead;
 | |
|     }
 | |
| 
 | |
|     if (Trb->DataLen != 0) {
 | |
|       MapLength = Trb->DataLen;
 | |
|       Status    = IoMmuMap (MapOp, Trb->Data, &MapLength, &Trb->DataPhy, &Trb->DataMap);
 | |
| 
 | |
|       if (EFI_ERROR (Status) || (MapLength != Trb->DataLen)) {
 | |
|         DEBUG ((DEBUG_ERROR, "EmmcPeimCreateTrb: Fail to map data buffer.\n"));
 | |
|         goto Error;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Trb->DataLen == 0) {
 | |
|       Trb->Mode = EmmcNoData;
 | |
|     } else if (Capability.Adma2 != 0) {
 | |
|       Trb->Mode = EmmcAdmaMode;
 | |
|       Status    = BuildAdmaDescTable (Trb);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto Error;
 | |
|       }
 | |
|     } else if (Capability.Sdma != 0) {
 | |
|       Trb->Mode = EmmcSdmaMode;
 | |
|     } else {
 | |
|       Trb->Mode = EmmcPioMode;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return Trb;
 | |
| 
 | |
| Error:
 | |
|   EmmcPeimFreeTrb (Trb);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Free the resource used by the TRB.
 | |
| 
 | |
|   @param[in] Trb        The pointer to the EMMC_TRB instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EmmcPeimFreeTrb (
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   if ((Trb != NULL) && (Trb->DataMap != NULL)) {
 | |
|     IoMmuUnmap (Trb->DataMap);
 | |
|   }
 | |
| 
 | |
|   if ((Trb != NULL) && (Trb->AdmaDesc != NULL)) {
 | |
|     EmmcPeimFreeMem (Trb->Slot->Private->Pool, Trb->AdmaDesc, Trb->AdmaDescSize);
 | |
|   }
 | |
| 
 | |
|   if (Trb != NULL) {
 | |
|     FreePool (Trb);
 | |
|   }
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if the env is ready for execute specified TRB.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The env is ready for TRB execution.
 | |
|   @retval EFI_NOT_READY     The env is not ready for TRB execution.
 | |
|   @retval Others            Some erros happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimCheckTrbEnv (
 | |
|   IN UINTN     Bar,
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS           Status;
 | |
|   EMMC_COMMAND_PACKET  *Packet;
 | |
|   UINT32               PresentState;
 | |
| 
 | |
|   Packet = Trb->Packet;
 | |
| 
 | |
|   if ((Packet->EmmcCmdBlk->CommandType == EmmcCommandTypeAdtc) ||
 | |
|       (Packet->EmmcCmdBlk->ResponseType == EmmcResponceTypeR1b) ||
 | |
|       (Packet->EmmcCmdBlk->ResponseType == EmmcResponceTypeR5b))
 | |
|   {
 | |
|     //
 | |
|     // Wait Command Inhibit (CMD) and Command Inhibit (DAT) in
 | |
|     // the Present State register to be 0
 | |
|     //
 | |
|     PresentState = BIT0 | BIT1;
 | |
|   } else {
 | |
|     //
 | |
|     // Wait Command Inhibit (CMD) in the Present State register
 | |
|     // to be 0
 | |
|     //
 | |
|     PresentState = BIT0;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcCheckMmioSet (
 | |
|              Bar + EMMC_HC_PRESENT_STATE,
 | |
|              sizeof (PresentState),
 | |
|              PresentState,
 | |
|              0
 | |
|              );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait for the env to be ready for execute specified TRB.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The env is ready for TRB execution.
 | |
|   @retval EFI_TIMEOUT       The env is not ready for TRB execution in time.
 | |
|   @retval Others            Some erros happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimWaitTrbEnv (
 | |
|   IN UINTN     Bar,
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS           Status;
 | |
|   EMMC_COMMAND_PACKET  *Packet;
 | |
|   UINT64               Timeout;
 | |
|   BOOLEAN              InfiniteWait;
 | |
| 
 | |
|   //
 | |
|   // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register
 | |
|   //
 | |
|   Packet  = Trb->Packet;
 | |
|   Timeout = Packet->Timeout;
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   while (InfiniteWait || (Timeout > 0)) {
 | |
|     //
 | |
|     // Check Trb execution result by reading Normal Interrupt Status register.
 | |
|     //
 | |
|     Status = EmmcPeimCheckTrbEnv (Bar, Trb);
 | |
|     if (Status != EFI_NOT_READY) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 1 microsecond.
 | |
|     //
 | |
|     MicroSecondDelay (1);
 | |
| 
 | |
|     Timeout--;
 | |
|   }
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute the specified TRB.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The TRB is sent to host controller successfully.
 | |
|   @retval Others            Some erros happen when sending this request to the host controller.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimExecTrb (
 | |
|   IN UINTN     Bar,
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS           Status;
 | |
|   EMMC_COMMAND_PACKET  *Packet;
 | |
|   UINT16               Cmd;
 | |
|   UINT16               IntStatus;
 | |
|   UINT32               Argument;
 | |
|   UINT16               BlkCount;
 | |
|   UINT16               BlkSize;
 | |
|   UINT16               TransMode;
 | |
|   UINT8                HostCtrl1;
 | |
|   UINT32               SdmaAddr;
 | |
|   UINT64               AdmaAddr;
 | |
| 
 | |
|   Packet = Trb->Packet;
 | |
|   //
 | |
|   // Clear all bits in Error Interrupt Status Register
 | |
|   //
 | |
|   IntStatus = 0xFFFF;
 | |
|   Status    = EmmcPeimHcRwMmio (Bar + EMMC_HC_ERR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Clear all bits in Normal Interrupt Status Register
 | |
|   //
 | |
|   IntStatus = 0xFFFF;
 | |
|   Status    = EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set Host Control 1 register DMA Select field
 | |
|   //
 | |
|   if (Trb->Mode == EmmcAdmaMode) {
 | |
|     HostCtrl1 = BIT4;
 | |
|     Status    = EmmcPeimHcOrMmio (Bar + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   EmmcPeimHcLedOnOff (Bar, TRUE);
 | |
| 
 | |
|   if (Trb->Mode == EmmcSdmaMode) {
 | |
|     if ((UINT64)(UINTN)Trb->DataPhy >= 0x100000000ul) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     SdmaAddr = (UINT32)(UINTN)Trb->DataPhy;
 | |
|     Status   = EmmcPeimHcRwMmio (Bar + EMMC_HC_SDMA_ADDR, FALSE, sizeof (SdmaAddr), &SdmaAddr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   } else if (Trb->Mode == EmmcAdmaMode) {
 | |
|     AdmaAddr = (UINT64)(UINTN)Trb->AdmaDesc;
 | |
|     Status   = EmmcPeimHcRwMmio (Bar + EMMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   BlkSize = Trb->BlockSize;
 | |
|   if (Trb->Mode == EmmcSdmaMode) {
 | |
|     //
 | |
|     // Set SDMA boundary to be 512K bytes.
 | |
|     //
 | |
|     BlkSize |= 0x7000;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_BLK_SIZE, FALSE, sizeof (BlkSize), &BlkSize);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   BlkCount = 0;
 | |
|   if (Trb->Mode != EmmcNoData) {
 | |
|     //
 | |
|     // Calculate Block Count.
 | |
|     //
 | |
|     BlkCount = (UINT16)(Trb->DataLen / Trb->BlockSize);
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_BLK_COUNT, FALSE, sizeof (BlkCount), &BlkCount);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Argument = Packet->EmmcCmdBlk->CommandArgument;
 | |
|   Status   = EmmcPeimHcRwMmio (Bar + EMMC_HC_ARG1, FALSE, sizeof (Argument), &Argument);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   TransMode = 0;
 | |
|   if (Trb->Mode != EmmcNoData) {
 | |
|     if (Trb->Mode != EmmcPioMode) {
 | |
|       TransMode |= BIT0;
 | |
|     }
 | |
| 
 | |
|     if (Trb->Read) {
 | |
|       TransMode |= BIT4;
 | |
|     }
 | |
| 
 | |
|     if (BlkCount > 1) {
 | |
|       TransMode |= BIT5 | BIT1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_TRANS_MOD, FALSE, sizeof (TransMode), &TransMode);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Cmd = (UINT16)LShiftU64 (Packet->EmmcCmdBlk->CommandIndex, 8);
 | |
|   if (Packet->EmmcCmdBlk->CommandType == EmmcCommandTypeAdtc) {
 | |
|     Cmd |= BIT5;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert ResponseType to value
 | |
|   //
 | |
|   if (Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeBc) {
 | |
|     switch (Packet->EmmcCmdBlk->ResponseType) {
 | |
|       case EmmcResponceTypeR1:
 | |
|       case EmmcResponceTypeR5:
 | |
|       case EmmcResponceTypeR6:
 | |
|       case EmmcResponceTypeR7:
 | |
|         Cmd |= (BIT1 | BIT3 | BIT4);
 | |
|         break;
 | |
|       case EmmcResponceTypeR2:
 | |
|         Cmd |= (BIT0 | BIT3);
 | |
|         break;
 | |
|       case EmmcResponceTypeR3:
 | |
|       case EmmcResponceTypeR4:
 | |
|         Cmd |= BIT1;
 | |
|         break;
 | |
|       case EmmcResponceTypeR1b:
 | |
|       case EmmcResponceTypeR5b:
 | |
|         Cmd |= (BIT0 | BIT1 | BIT3 | BIT4);
 | |
|         break;
 | |
|       default:
 | |
|         ASSERT (FALSE);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Execute cmd
 | |
|   //
 | |
|   Status = EmmcPeimHcRwMmio (Bar + EMMC_HC_COMMAND, FALSE, sizeof (Cmd), &Cmd);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check the TRB execution result.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The TRB is executed successfully.
 | |
|   @retval EFI_NOT_READY     The TRB is not completed for execution.
 | |
|   @retval Others            Some erros happen when executing this request.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimCheckTrbResult (
 | |
|   IN UINTN     Bar,
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS           Status;
 | |
|   EMMC_COMMAND_PACKET  *Packet;
 | |
|   UINT16               IntStatus;
 | |
|   UINT32               Response[4];
 | |
|   UINT32               SdmaAddr;
 | |
|   UINT8                Index;
 | |
|   UINT8                SwReset;
 | |
|   UINT32               PioLength;
 | |
| 
 | |
|   SwReset = 0;
 | |
|   Packet  = Trb->Packet;
 | |
|   //
 | |
|   // Check Trb execution result by reading Normal Interrupt Status register.
 | |
|   //
 | |
|   Status = EmmcPeimHcRwMmio (
 | |
|              Bar + EMMC_HC_NOR_INT_STS,
 | |
|              TRUE,
 | |
|              sizeof (IntStatus),
 | |
|              &IntStatus
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check Transfer Complete bit is set or not.
 | |
|   //
 | |
|   if ((IntStatus & BIT1) == BIT1) {
 | |
|     if ((IntStatus & BIT15) == BIT15) {
 | |
|       //
 | |
|       // Read Error Interrupt Status register to check if the error is
 | |
|       // Data Timeout Error.
 | |
|       // If yes, treat it as success as Transfer Complete has higher
 | |
|       // priority than Data Timeout Error.
 | |
|       //
 | |
|       Status = EmmcPeimHcRwMmio (
 | |
|                  Bar + EMMC_HC_ERR_INT_STS,
 | |
|                  TRUE,
 | |
|                  sizeof (IntStatus),
 | |
|                  &IntStatus
 | |
|                  );
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         if ((IntStatus & BIT4) == BIT4) {
 | |
|           Status = EFI_SUCCESS;
 | |
|         } else {
 | |
|           Status = EFI_DEVICE_ERROR;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check if there is a error happened during cmd execution.
 | |
|   // If yes, then do error recovery procedure to follow SD Host Controller
 | |
|   // Simplified Spec 3.0 section 3.10.1.
 | |
|   //
 | |
|   if ((IntStatus & BIT15) == BIT15) {
 | |
|     Status = EmmcPeimHcRwMmio (
 | |
|                Bar + EMMC_HC_ERR_INT_STS,
 | |
|                TRUE,
 | |
|                sizeof (IntStatus),
 | |
|                &IntStatus
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     if ((IntStatus & 0x0F) != 0) {
 | |
|       SwReset |= BIT1;
 | |
|     }
 | |
| 
 | |
|     if ((IntStatus & 0xF0) != 0) {
 | |
|       SwReset |= BIT2;
 | |
|     }
 | |
| 
 | |
|     Status = EmmcPeimHcRwMmio (
 | |
|                Bar + EMMC_HC_SW_RST,
 | |
|                FALSE,
 | |
|                sizeof (SwReset),
 | |
|                &SwReset
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     Status = EmmcPeimHcWaitMmioSet (
 | |
|                Bar + EMMC_HC_SW_RST,
 | |
|                sizeof (SwReset),
 | |
|                0xFF,
 | |
|                0,
 | |
|                EMMC_TIMEOUT
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check if DMA interrupt is signalled for the SDMA transfer.
 | |
|   //
 | |
|   if ((Trb->Mode == EmmcSdmaMode) && ((IntStatus & BIT3) == BIT3)) {
 | |
|     //
 | |
|     // Clear DMA interrupt bit.
 | |
|     //
 | |
|     IntStatus = BIT3;
 | |
|     Status    = EmmcPeimHcRwMmio (
 | |
|                   Bar + EMMC_HC_NOR_INT_STS,
 | |
|                   FALSE,
 | |
|                   sizeof (IntStatus),
 | |
|                   &IntStatus
 | |
|                   );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Update SDMA Address register.
 | |
|     //
 | |
|     SdmaAddr = EMMC_SDMA_ROUND_UP ((UINT32)(UINTN)Trb->DataPhy, EMMC_SDMA_BOUNDARY);
 | |
|     Status   = EmmcPeimHcRwMmio (
 | |
|                  Bar + EMMC_HC_SDMA_ADDR,
 | |
|                  FALSE,
 | |
|                  sizeof (UINT32),
 | |
|                  &SdmaAddr
 | |
|                  );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     Trb->DataPhy = (UINT32)(UINTN)SdmaAddr;
 | |
|   }
 | |
| 
 | |
|   if ((Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeAdtc) &&
 | |
|       (Packet->EmmcCmdBlk->ResponseType != EmmcResponceTypeR1b) &&
 | |
|       (Packet->EmmcCmdBlk->ResponseType != EmmcResponceTypeR5b))
 | |
|   {
 | |
|     if ((IntStatus & BIT0) == BIT0) {
 | |
|       Status = EFI_SUCCESS;
 | |
|       goto Done;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Packet->EmmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK) {
 | |
|     //
 | |
|     // When performing tuning procedure (Execute Tuning is set to 1) through PIO mode,
 | |
|     // wait Buffer Read Ready bit of Normal Interrupt Status Register to be 1.
 | |
|     // Refer to SD Host Controller Simplified Specification 3.0 figure 2-29 for details.
 | |
|     //
 | |
|     if ((IntStatus & BIT5) == BIT5) {
 | |
|       //
 | |
|       // Clear Buffer Read Ready interrupt at first.
 | |
|       //
 | |
|       IntStatus = BIT5;
 | |
|       EmmcPeimHcRwMmio (Bar + EMMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus);
 | |
|       //
 | |
|       // Read data out from Buffer Port register
 | |
|       //
 | |
|       for (PioLength = 0; PioLength < Trb->DataLen; PioLength += 4) {
 | |
|         EmmcPeimHcRwMmio (Bar + EMMC_HC_BUF_DAT_PORT, TRUE, 4, (UINT8 *)Trb->Data + PioLength);
 | |
|       }
 | |
| 
 | |
|       Status = EFI_SUCCESS;
 | |
|       goto Done;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = EFI_NOT_READY;
 | |
| Done:
 | |
|   //
 | |
|   // Get response data when the cmd is executed successfully.
 | |
|   //
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     if (Packet->EmmcCmdBlk->CommandType != EmmcCommandTypeBc) {
 | |
|       for (Index = 0; Index < 4; Index++) {
 | |
|         Status = EmmcPeimHcRwMmio (
 | |
|                    Bar + EMMC_HC_RESPONSE + Index * 4,
 | |
|                    TRUE,
 | |
|                    sizeof (UINT32),
 | |
|                    &Response[Index]
 | |
|                    );
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           EmmcPeimHcLedOnOff (Bar, FALSE);
 | |
|           return Status;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       CopyMem (Packet->EmmcStatusBlk, Response, sizeof (Response));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Status != EFI_NOT_READY) {
 | |
|     EmmcPeimHcLedOnOff (Bar, FALSE);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait for the TRB execution result.
 | |
| 
 | |
|   @param[in] Bar            The mmio base address of the slot to be accessed.
 | |
|   @param[in] Trb            The pointer to the EMMC_TRB instance.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The TRB is executed successfully.
 | |
|   @retval Others            Some erros happen when executing this request.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimWaitTrbResult (
 | |
|   IN UINTN     Bar,
 | |
|   IN EMMC_TRB  *Trb
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS           Status;
 | |
|   EMMC_COMMAND_PACKET  *Packet;
 | |
|   UINT64               Timeout;
 | |
|   BOOLEAN              InfiniteWait;
 | |
| 
 | |
|   Packet = Trb->Packet;
 | |
|   //
 | |
|   // Wait Command Complete Interrupt Status bit in Normal Interrupt Status Register
 | |
|   //
 | |
|   Timeout = Packet->Timeout;
 | |
|   if (Timeout == 0) {
 | |
|     InfiniteWait = TRUE;
 | |
|   } else {
 | |
|     InfiniteWait = FALSE;
 | |
|   }
 | |
| 
 | |
|   while (InfiniteWait || (Timeout > 0)) {
 | |
|     //
 | |
|     // Check Trb execution result by reading Normal Interrupt Status register.
 | |
|     //
 | |
|     Status = EmmcPeimCheckTrbResult (Bar, Trb);
 | |
|     if (Status != EFI_NOT_READY) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Stall for 1 microsecond.
 | |
|     //
 | |
|     MicroSecondDelay (1);
 | |
| 
 | |
|     Timeout--;
 | |
|   }
 | |
| 
 | |
|   return EFI_TIMEOUT;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Sends EMMC command to an EMMC card that is attached to the EMMC controller.
 | |
| 
 | |
|   If Packet is successfully sent to the EMMC card, then EFI_SUCCESS is returned.
 | |
| 
 | |
|   If a device error occurs while sending the Packet, then EFI_DEVICE_ERROR is returned.
 | |
| 
 | |
|   If Slot is not in a valid range for the EMMC controller, then EFI_INVALID_PARAMETER
 | |
|   is returned.
 | |
| 
 | |
|   If Packet defines a data command but both InDataBuffer and OutDataBuffer are NULL,
 | |
|   EFI_INVALID_PARAMETER is returned.
 | |
| 
 | |
|   @param[in]     Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in,out] Packet         A pointer to the EMMC command data structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The EMMC Command Packet was sent by the host.
 | |
|   @retval EFI_DEVICE_ERROR      A device error occurred while attempting to send the SD
 | |
|                                 command Packet.
 | |
|   @retval EFI_INVALID_PARAMETER Packet, Slot, or the contents of the Packet is invalid.
 | |
|   @retval EFI_INVALID_PARAMETER Packet defines a data command but both InDataBuffer and
 | |
|                                 OutDataBuffer are NULL.
 | |
|   @retval EFI_NO_MEDIA          SD Device not present in the Slot.
 | |
|   @retval EFI_UNSUPPORTED       The command described by the EMMC Command Packet is not
 | |
|                                 supported by the host controller.
 | |
|   @retval EFI_BAD_BUFFER_SIZE   The InTransferLength or OutTransferLength exceeds the
 | |
|                                 limit supported by EMMC card ( i.e. if the number of bytes
 | |
|                                 exceed the Last LBA).
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EmmcPeimExecCmd (
 | |
|   IN     EMMC_PEIM_HC_SLOT    *Slot,
 | |
|   IN OUT EMMC_COMMAND_PACKET  *Packet
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   EMMC_TRB    *Trb;
 | |
| 
 | |
|   if (Packet == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Packet->EmmcCmdBlk == NULL) || (Packet->EmmcStatusBlk == NULL)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Packet->OutDataBuffer == NULL) && (Packet->OutTransferLength != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Packet->InDataBuffer == NULL) && (Packet->InTransferLength != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Trb = EmmcPeimCreateTrb (Slot, Packet);
 | |
|   if (Trb == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimWaitTrbEnv (Slot->EmmcHcBase, Trb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimExecTrb (Slot->EmmcHcBase, Trb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimWaitTrbResult (Slot->EmmcHcBase, Trb);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
| Done:
 | |
|   EmmcPeimFreeTrb (Trb);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command GO_IDLE_STATE (CMD0 with argument of 0x00000000) to the device to
 | |
|   make it go to Idle State.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The EMMC device is reset correctly.
 | |
|   @retval Others            The device reset fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimReset (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_GO_IDLE_STATE;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeBc;
 | |
|   EmmcCmdBlk.ResponseType    = 0;
 | |
|   EmmcCmdBlk.CommandArgument = 0;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SEND_OP_COND to the EMMC device to get the data of the OCR register.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
 | |
| 
 | |
|   @param[in]      Slot      The slot number of the Emmc card to send the command to.
 | |
|   @param[in, out] Argument  On input, the argument of SEND_OP_COND is to send to the device.
 | |
|                             On output, the argument is the value of OCR register.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimGetOcr (
 | |
|   IN     EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN OUT UINT32             *Argument
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SEND_OP_COND;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeBcr;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR3;
 | |
|   EmmcCmdBlk.CommandArgument = *Argument;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
 | |
|     //
 | |
|     *Argument = EmmcStatusBlk.Resp0;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Broadcast command ALL_SEND_CID to the bus to ask all the EMMC devices to send the
 | |
|   data of their CID registers.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimGetAllCid (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_ALL_SEND_CID;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeBcr;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR2;
 | |
|   EmmcCmdBlk.CommandArgument = 0;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SET_RELATIVE_ADDR to the EMMC device to assign a Relative device
 | |
|   Address (RCA).
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSetRca (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SET_RELATIVE_ADDR;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = Rca << 16;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SEND_CSD to the EMMC device to get the data of the CSD register.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in]  Slot          The slot number of the Emmc card to send the command to.
 | |
|   @param[in]  Rca           The relative device address of selected device.
 | |
|   @param[out] Csd           The buffer to store the content of the CSD register.
 | |
|                             Note the caller should ignore the lowest byte of this
 | |
|                             buffer as the content of this byte is meaningless even
 | |
|                             if the operation succeeds.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimGetCsd (
 | |
|   IN     EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN     UINT32             Rca,
 | |
|   OUT EMMC_CSD              *Csd
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SEND_CSD;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR2;
 | |
|   EmmcCmdBlk.CommandArgument = Rca << 16;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // For details, refer to SD Host Controller Simplified Spec 3.0 Table 2-12.
 | |
|     //
 | |
|     CopyMem (((UINT8 *)Csd) + 1, &EmmcStatusBlk.Resp0, sizeof (EMMC_CSD) - 1);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SELECT_DESELECT_CARD to the EMMC device to select/deselect it.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address of selected device.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSelect (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SELECT_DESELECT_CARD;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = Rca << 16;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SEND_EXT_CSD to the EMMC device to get the data of the EXT_CSD register.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in]  Slot          The slot number of the Emmc card to send the command to.
 | |
|   @param[out] ExtCsd        The buffer to store the content of the EXT_CSD register.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimGetExtCsd (
 | |
|   IN     EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   OUT EMMC_EXT_CSD          *ExtCsd
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SEND_EXT_CSD;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAdtc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = 0x00000000;
 | |
| 
 | |
|   Packet.InDataBuffer     = ExtCsd;
 | |
|   Packet.InTransferLength = sizeof (EMMC_EXT_CSD);
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SWITCH to the EMMC device to switch the mode of operation of the
 | |
|   selected Device or modifies the EXT_CSD registers.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Access         The access mode of SWITCH command.
 | |
|   @param[in] Index          The offset of the field to be access.
 | |
|   @param[in] Value          The value to be set to the specified field of EXT_CSD register.
 | |
|   @param[in] CmdSet         The value of CmdSet field of EXT_CSD register.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitch (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT8              Access,
 | |
|   IN UINT8              Index,
 | |
|   IN UINT8              Value,
 | |
|   IN UINT8              CmdSet
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SWITCH;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1b;
 | |
|   EmmcCmdBlk.CommandArgument = (Access << 24) | (Index << 16) | (Value << 8) | CmdSet;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SEND_STATUS to the addressed EMMC device to get its status register.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in]  Slot          The slot number of the Emmc card to send the command to.
 | |
|   @param[in]  Rca           The relative device address of addressed device.
 | |
|   @param[out] DevStatus     The returned device status.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSendStatus (
 | |
|   IN     EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN     UINT32             Rca,
 | |
|   OUT UINT32                *DevStatus
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SEND_STATUS;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = Rca << 16;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     *DevStatus = EmmcStatusBlk.Resp0;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SET_BLOCK_COUNT to the addressed EMMC device to set the number of
 | |
|   blocks for the following block read/write cmd.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] BlockCount     The number of the logical block to access.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSetBlkCount (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT16             BlockCount
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SET_BLOCK_COUNT;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = BlockCount;
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command READ_MULTIPLE_BLOCK/WRITE_MULTIPLE_BLOCK to the addressed EMMC device
 | |
|   to read/write the specified number of blocks.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.10.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Lba            The logical block address of starting access.
 | |
|   @param[in] BlockSize      The block size of specified EMMC device partition.
 | |
|   @param[in] Buffer         The pointer to the transfer buffer.
 | |
|   @param[in] BufferSize     The size of transfer buffer.
 | |
|   @param[in] IsRead         Boolean to show the operation direction.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimRwMultiBlocks (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN EFI_LBA            Lba,
 | |
|   IN UINT32             BlockSize,
 | |
|   IN VOID               *Buffer,
 | |
|   IN UINTN              BufferSize,
 | |
|   IN BOOLEAN            IsRead
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   //
 | |
|   // Calculate timeout value through the below formula.
 | |
|   // Timeout = (transfer size) / (2MB/s).
 | |
|   // Taking 2MB/s as divisor is because it's nearest to the eMMC lowest
 | |
|   // transfer speed (2.4MB/s).
 | |
|   // Refer to eMMC 5.0 spec section 6.9.1 for details.
 | |
|   //
 | |
|   Packet.Timeout = (BufferSize / (2 * 1024 * 1024) + 1) * 1000 * 1000;
 | |
| 
 | |
|   if (IsRead) {
 | |
|     Packet.InDataBuffer     = Buffer;
 | |
|     Packet.InTransferLength = (UINT32)BufferSize;
 | |
| 
 | |
|     EmmcCmdBlk.CommandIndex = EMMC_READ_MULTIPLE_BLOCK;
 | |
|     EmmcCmdBlk.CommandType  = EmmcCommandTypeAdtc;
 | |
|     EmmcCmdBlk.ResponseType = EmmcResponceTypeR1;
 | |
|   } else {
 | |
|     Packet.OutDataBuffer     = Buffer;
 | |
|     Packet.OutTransferLength = (UINT32)BufferSize;
 | |
| 
 | |
|     EmmcCmdBlk.CommandIndex = EMMC_WRITE_MULTIPLE_BLOCK;
 | |
|     EmmcCmdBlk.CommandType  = EmmcCommandTypeAdtc;
 | |
|     EmmcCmdBlk.ResponseType = EmmcResponceTypeR1;
 | |
|   }
 | |
| 
 | |
|   if (Slot->SectorAddressing) {
 | |
|     EmmcCmdBlk.CommandArgument = (UINT32)Lba;
 | |
|   } else {
 | |
|     EmmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (Lba, BlockSize);
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send command SEND_TUNING_BLOCK to the EMMC device for HS200 optimal sampling point
 | |
|   detection.
 | |
| 
 | |
|   It may be sent up to 40 times until the host finishes the tuning procedure.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] BusWidth       The bus width to work.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSendTuningBlk (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT8              BusWidth
 | |
|   )
 | |
| {
 | |
|   EMMC_COMMAND_BLOCK   EmmcCmdBlk;
 | |
|   EMMC_STATUS_BLOCK    EmmcStatusBlk;
 | |
|   EMMC_COMMAND_PACKET  Packet;
 | |
|   EFI_STATUS           Status;
 | |
|   UINT8                TuningBlock[128];
 | |
| 
 | |
|   ZeroMem (&EmmcCmdBlk, sizeof (EmmcCmdBlk));
 | |
|   ZeroMem (&EmmcStatusBlk, sizeof (EmmcStatusBlk));
 | |
|   ZeroMem (&Packet, sizeof (Packet));
 | |
| 
 | |
|   Packet.EmmcCmdBlk    = &EmmcCmdBlk;
 | |
|   Packet.EmmcStatusBlk = &EmmcStatusBlk;
 | |
|   Packet.Timeout       = EMMC_TIMEOUT;
 | |
| 
 | |
|   EmmcCmdBlk.CommandIndex    = EMMC_SEND_TUNING_BLOCK;
 | |
|   EmmcCmdBlk.CommandType     = EmmcCommandTypeAdtc;
 | |
|   EmmcCmdBlk.ResponseType    = EmmcResponceTypeR1;
 | |
|   EmmcCmdBlk.CommandArgument = 0;
 | |
| 
 | |
|   Packet.InDataBuffer = TuningBlock;
 | |
|   if (BusWidth == 8) {
 | |
|     Packet.InTransferLength = sizeof (TuningBlock);
 | |
|   } else {
 | |
|     Packet.InTransferLength = 64;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimExecCmd (Slot, &Packet);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Tuning the clock to get HS200 optimal sampling point.
 | |
| 
 | |
|   Command SEND_TUNING_BLOCK may be sent up to 40 times until the host finishes the
 | |
|   tuning procedure.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 2-29 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] BusWidth       The bus width to work.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimTuningClkForHs200 (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT8              BusWidth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HostCtrl2;
 | |
|   UINT8       Retry;
 | |
| 
 | |
|   //
 | |
|   // Notify the host that the sampling clock tuning procedure starts.
 | |
|   //
 | |
|   HostCtrl2 = BIT6;
 | |
|   Status    = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Ask the device to send a sequence of tuning blocks till the tuning procedure is done.
 | |
|   //
 | |
|   Retry = 0;
 | |
|   do {
 | |
|     Status = EmmcPeimSendTuningBlk (Slot, BusWidth);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Status = EmmcPeimHcRwMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, TRUE, sizeof (HostCtrl2), &HostCtrl2);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if ((HostCtrl2 & (BIT6 | BIT7)) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((HostCtrl2 & (BIT6 | BIT7)) == BIT7) {
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|   } while (++Retry < 40);
 | |
| 
 | |
|   DEBUG ((DEBUG_ERROR, "EmmcPeimTuningClkForHs200: Send tuning block fails at %d times with HostCtrl2 %02x\n", Retry, HostCtrl2));
 | |
|   //
 | |
|   // Abort the tuning procedure and reset the tuning circuit.
 | |
|   //
 | |
|   HostCtrl2 = (UINT8) ~(BIT6 | BIT7);
 | |
|   Status    = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   return EFI_DEVICE_ERROR;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch the bus width to specified width.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 3-7 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
|   @param[in] IsDdr          If TRUE, use dual data rate data simpling method. Otherwise
 | |
|                             use single data rate data simpling method.
 | |
|   @param[in] BusWidth       The bus width to be set, it could be 4 or 8.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitchBusWidth (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca,
 | |
|   IN BOOLEAN            IsDdr,
 | |
|   IN UINT8              BusWidth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       Access;
 | |
|   UINT8       Index;
 | |
|   UINT8       Value;
 | |
|   UINT8       CmdSet;
 | |
|   UINT32      DevStatus;
 | |
| 
 | |
|   //
 | |
|   // Write Byte, the Value field is written into the byte pointed by Index.
 | |
|   //
 | |
|   Access = 0x03;
 | |
|   Index  = OFFSET_OF (EMMC_EXT_CSD, BusWidth);
 | |
|   if (BusWidth == 4) {
 | |
|     Value = 1;
 | |
|   } else if (BusWidth == 8) {
 | |
|     Value = 2;
 | |
|   } else {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (IsDdr) {
 | |
|     Value += 4;
 | |
|   }
 | |
| 
 | |
|   CmdSet = 0;
 | |
|   Status = EmmcPeimSwitch (Slot, Access, Index, Value, CmdSet);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimSendStatus (Slot, Rca, &DevStatus);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the switch operation is really successful or not.
 | |
|   //
 | |
|   if ((DevStatus & BIT7) != 0) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcSetBusWidth (Slot->EmmcHcBase, BusWidth);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch the clock frequency to the specified value.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 3-3 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
|   @param[in] HsTiming       The value to be written to HS_TIMING field of EXT_CSD register.
 | |
|   @param[in] ClockFreq      The max clock frequency to be set, the unit is MHz.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitchClockFreq (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca,
 | |
|   IN UINT8              HsTiming,
 | |
|   IN UINT32             ClockFreq
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       Access;
 | |
|   UINT8       Index;
 | |
|   UINT8       Value;
 | |
|   UINT8       CmdSet;
 | |
|   UINT32      DevStatus;
 | |
| 
 | |
|   //
 | |
|   // Write Byte, the Value field is written into the byte pointed by Index.
 | |
|   //
 | |
|   Access = 0x03;
 | |
|   Index  = OFFSET_OF (EMMC_EXT_CSD, HsTiming);
 | |
|   Value  = HsTiming;
 | |
|   CmdSet = 0;
 | |
| 
 | |
|   Status = EmmcPeimSwitch (Slot, Access, Index, Value, CmdSet);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimSendStatus (Slot, Rca, &DevStatus);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the switch operation is really successful or not.
 | |
|   //
 | |
|   if ((DevStatus & BIT7) != 0) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert the clock freq unit from MHz to KHz.
 | |
|   //
 | |
|   Status = EmmcPeimHcClockSupply (Slot->EmmcHcBase, ClockFreq * 1000);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch to the High Speed timing according to request.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 2-29 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
|   @param[in] ClockFreq      The max clock frequency to be set.
 | |
|   @param[in] IsDdr          If TRUE, use dual data rate data simpling method. Otherwise
 | |
|                             use single data rate data simpling method.
 | |
|   @param[in] BusWidth       The bus width to be set, it could be 4 or 8.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitchToHighSpeed (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca,
 | |
|   IN UINT32             ClockFreq,
 | |
|   IN BOOLEAN            IsDdr,
 | |
|   IN UINT8              BusWidth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HsTiming;
 | |
|   UINT8       HostCtrl1;
 | |
|   UINT8       HostCtrl2;
 | |
| 
 | |
|   Status = EmmcPeimSwitchBusWidth (Slot, Rca, IsDdr, BusWidth);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set to High Speed timing
 | |
|   //
 | |
|   HostCtrl1 = BIT2;
 | |
|   Status    = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HostCtrl2 = (UINT8) ~0x7;
 | |
|   Status    = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if (IsDdr) {
 | |
|     HostCtrl2 = BIT2;
 | |
|   } else if (ClockFreq == 52) {
 | |
|     HostCtrl2 = BIT0;
 | |
|   } else {
 | |
|     HostCtrl2 = 0;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HsTiming = 1;
 | |
|   Status   = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch to the HS200 timing according to request.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 2-29 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
|   @param[in] ClockFreq      The max clock frequency to be set.
 | |
|   @param[in] BusWidth       The bus width to be set, it could be 4 or 8.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitchToHS200 (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca,
 | |
|   IN UINT32             ClockFreq,
 | |
|   IN UINT8              BusWidth
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HsTiming;
 | |
|   UINT8       HostCtrl2;
 | |
|   UINT16      ClockCtrl;
 | |
| 
 | |
|   if ((BusWidth != 4) && (BusWidth != 8)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimSwitchBusWidth (Slot, Rca, FALSE, BusWidth);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set to HS200/SDR104 timing
 | |
|   //
 | |
|   //
 | |
|   // Stop bus clock at first
 | |
|   //
 | |
|   Status = EmmcPeimHcStopClock (Slot->EmmcHcBase);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HostCtrl2 = (UINT8) ~0x7;
 | |
|   Status    = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HostCtrl2 = BIT0 | BIT1;
 | |
|   Status    = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Wait Internal Clock Stable in the Clock Control register to be 1 before set SD Clock Enable bit
 | |
|   //
 | |
|   Status = EmmcPeimHcWaitMmioSet (
 | |
|              Slot->EmmcHcBase + EMMC_HC_CLOCK_CTRL,
 | |
|              sizeof (ClockCtrl),
 | |
|              BIT1,
 | |
|              BIT1,
 | |
|              EMMC_TIMEOUT
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set SD Clock Enable in the Clock Control register to 1
 | |
|   //
 | |
|   ClockCtrl = BIT2;
 | |
|   Status    = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl);
 | |
| 
 | |
|   HsTiming = 2;
 | |
|   Status   = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimTuningClkForHs200 (Slot, BusWidth);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch to the HS400 timing according to request.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 2-29 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
|   @param[in] ClockFreq      The max clock frequency to be set.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSwitchToHS400 (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca,
 | |
|   IN UINT32             ClockFreq
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HsTiming;
 | |
|   UINT8       HostCtrl2;
 | |
| 
 | |
|   Status = EmmcPeimSwitchToHS200 (Slot, Rca, ClockFreq, 8);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set to High Speed timing and set the clock frequency to a value less than 52MHz.
 | |
|   //
 | |
|   HsTiming = 1;
 | |
|   Status   = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, 52);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // HS400 mode must use 8 data lines.
 | |
|   //
 | |
|   Status = EmmcPeimSwitchBusWidth (Slot, Rca, TRUE, 8);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set to HS400 timing
 | |
|   //
 | |
|   HostCtrl2 = (UINT8) ~0x7;
 | |
|   Status    = EmmcPeimHcAndMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HostCtrl2 = BIT0 | BIT2;
 | |
|   Status    = EmmcPeimHcOrMmio (Slot->EmmcHcBase + EMMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   HsTiming = 3;
 | |
|   Status   = EmmcPeimSwitchClockFreq (Slot, Rca, HsTiming, ClockFreq);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Switch the high speed timing according to request.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
 | |
|   Simplified Spec 3.0 section Figure 2-29 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
|   @param[in] Rca            The relative device address to be assigned.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation is done correctly.
 | |
|   @retval Others            The operation fails.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimSetBusMode (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot,
 | |
|   IN UINT32             Rca
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS        Status;
 | |
|   EMMC_HC_SLOT_CAP  Capability;
 | |
|   UINT8             HsTiming;
 | |
|   BOOLEAN           IsDdr;
 | |
|   UINT32            ClockFreq;
 | |
|   UINT8             BusWidth;
 | |
| 
 | |
|   Status = EmmcPeimGetCsd (Slot, Rca, &Slot->Csd);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimSetBusMode: EmmcPeimGetCsd fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   if ((Slot->Csd.CSizeLow | Slot->Csd.CSizeHigh << 2) == 0xFFF) {
 | |
|     Slot->SectorAddressing = TRUE;
 | |
|   } else {
 | |
|     Slot->SectorAddressing = FALSE;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimSelect (Slot, Rca);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimSetBusMode: EmmcPeimSelect fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = EmmcPeimHcGetCapability (Slot->EmmcHcBase, &Capability);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimSetBusMode: EmmcPeimHcGetCapability fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ASSERT (Capability.BaseClkFreq != 0);
 | |
|   //
 | |
|   // Check if the Host Controller support 8bits bus width.
 | |
|   //
 | |
|   if (Capability.BusWidth8 != 0) {
 | |
|     BusWidth = 8;
 | |
|   } else {
 | |
|     BusWidth = 4;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get Device_Type from EXT_CSD register.
 | |
|   //
 | |
|   Status = EmmcPeimGetExtCsd (Slot, &Slot->ExtCsd);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimSetBusMode: EmmcPeimGetExtCsd fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate supported bus speed/bus width/clock frequency.
 | |
|   //
 | |
|   HsTiming  = 0;
 | |
|   IsDdr     = FALSE;
 | |
|   ClockFreq = 0;
 | |
|   if (((Slot->ExtCsd.DeviceType & (BIT4 | BIT5))  != 0) && (Capability.Sdr104 != 0)) {
 | |
|     HsTiming  = 2;
 | |
|     IsDdr     = FALSE;
 | |
|     ClockFreq = 200;
 | |
|   } else if (((Slot->ExtCsd.DeviceType & (BIT2 | BIT3))  != 0) && (Capability.Ddr50 != 0)) {
 | |
|     HsTiming  = 1;
 | |
|     IsDdr     = TRUE;
 | |
|     ClockFreq = 52;
 | |
|   } else if (((Slot->ExtCsd.DeviceType & BIT1)  != 0) && (Capability.HighSpeed != 0)) {
 | |
|     HsTiming  = 1;
 | |
|     IsDdr     = FALSE;
 | |
|     ClockFreq = 52;
 | |
|   } else if (((Slot->ExtCsd.DeviceType & BIT0)  != 0) && (Capability.HighSpeed != 0)) {
 | |
|     HsTiming  = 1;
 | |
|     IsDdr     = FALSE;
 | |
|     ClockFreq = 26;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check if both of the device and the host controller support HS400 DDR mode.
 | |
|   //
 | |
|   if (((Slot->ExtCsd.DeviceType & (BIT6 | BIT7))  != 0) && (Capability.Hs400 != 0)) {
 | |
|     //
 | |
|     // The host controller supports 8bits bus.
 | |
|     //
 | |
|     ASSERT (BusWidth == 8);
 | |
|     HsTiming  = 3;
 | |
|     IsDdr     = TRUE;
 | |
|     ClockFreq = 200;
 | |
|   }
 | |
| 
 | |
|   if ((ClockFreq == 0) || (HsTiming == 0)) {
 | |
|     //
 | |
|     // Continue using default setting.
 | |
|     //
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((DEBUG_INFO, "HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n", HsTiming, ClockFreq, BusWidth, IsDdr ? "TRUE" : "FALSE"));
 | |
| 
 | |
|   if (HsTiming == 3) {
 | |
|     //
 | |
|     // Execute HS400 timing switch procedure
 | |
|     //
 | |
|     Status = EmmcPeimSwitchToHS400 (Slot, Rca, ClockFreq);
 | |
|   } else if (HsTiming == 2) {
 | |
|     //
 | |
|     // Execute HS200 timing switch procedure
 | |
|     //
 | |
|     Status = EmmcPeimSwitchToHS200 (Slot, Rca, ClockFreq, BusWidth);
 | |
|   } else {
 | |
|     //
 | |
|     // Execute High Speed timing switch procedure
 | |
|     //
 | |
|     Status = EmmcPeimSwitchToHighSpeed (Slot, Rca, ClockFreq, IsDdr, BusWidth);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute EMMC device identification procedure.
 | |
| 
 | |
|   Refer to EMMC Electrical Standard Spec 5.1 Section 6.4 for details.
 | |
| 
 | |
|   @param[in] Slot           The slot number of the Emmc card to send the command to.
 | |
| 
 | |
|   @retval EFI_SUCCESS       There is a EMMC card.
 | |
|   @retval Others            There is not a EMMC card.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EmmcPeimIdentification (
 | |
|   IN EMMC_PEIM_HC_SLOT  *Slot
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      Ocr;
 | |
|   UINT32      Rca;
 | |
|   UINTN       Retry;
 | |
| 
 | |
|   Status = EmmcPeimReset (Slot);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimIdentification: EmmcPeimReset fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Ocr   = 0;
 | |
|   Retry = 0;
 | |
|   do {
 | |
|     Status = EmmcPeimGetOcr (Slot, &Ocr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       DEBUG ((DEBUG_ERROR, "EmmcPeimIdentification: EmmcPeimGetOcr fails with %r\n", Status));
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (Retry++ == 100) {
 | |
|       DEBUG ((DEBUG_ERROR, "EmmcPeimIdentification: EmmcPeimGetOcr fails too many times\n"));
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     MicroSecondDelay (10 * 1000);
 | |
|   } while ((Ocr & BIT31) == 0);
 | |
| 
 | |
|   Status = EmmcPeimGetAllCid (Slot);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimIdentification: EmmcPeimGetAllCid fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Don't support multiple devices on the slot, that is
 | |
|   // shared bus slot feature.
 | |
|   //
 | |
|   Rca    = 1;
 | |
|   Status = EmmcPeimSetRca (Slot, Rca);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     DEBUG ((DEBUG_ERROR, "EmmcPeimIdentification: EmmcPeimSetRca fails with %r\n", Status));
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enter Data Tranfer Mode.
 | |
|   //
 | |
|   DEBUG ((DEBUG_INFO, "Found a EMMC device at slot [%d], RCA [%d]\n", Slot, Rca));
 | |
| 
 | |
|   Status = EmmcPeimSetBusMode (Slot, Rca);
 | |
| 
 | |
|   return Status;
 | |
| }
 |