/** @file SynQuacer DXE platform driver - eMMC support Copyright (c) 2017, Linaro, Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PlatformDxe.h" // F_SDH30 extended Controller registers #define F_SDH30_AHB_CONFIG 0x100 #define F_SDH30_AHB_BIGED BIT6 #define F_SDH30_BUSLOCK_DMA BIT5 #define F_SDH30_BUSLOCK_EN BIT4 #define F_SDH30_SIN BIT3 #define F_SDH30_AHB_INCR_16 BIT2 #define F_SDH30_AHB_INCR_8 BIT1 #define F_SDH30_AHB_INCR_4 BIT0 #define F_SDH30_TUNING_SETTING 0x108 #define F_SDH30_CMD_CHK_DIS BIT16 #define F_SDH30_IO_CONTROL2 0x114 #define F_SDH30_MSEL_O_1_8 BIT18 #define F_SDH30_CRES_O_DN BIT19 #define F_SDH30_ESD_CONTROL 0x124 #define F_SDH30_EMMC_RST BIT1 #define F_SDH30_EMMC_HS200 BIT24 #define F_SDH30_CMD_DAT_DELAY BIT9 #define F_SDH30_TUNING_SETTING 0x108 #define F_SDH30_CMD_CHK_DIS BIT16 #define F_SDH30_IO_CONTROL2 0x114 #define F_SDH30_MSEL_O_1_8 BIT18 #define F_SDH30_CRES_O_DN BIT19 #define F_SDH30_ESD_CONTROL 0x124 #define F_SDH30_EMMC_RST BIT1 #define F_SDH30_EMMC_HS200 BIT24 #define F_SDH30_CMD_DAT_DELAY BIT9 #define SD_HC_CLOCK_CTRL 0x2C #define SYNQUACER_CLOCK_CTRL_VAL 0xBC01 #define SD_HC_CAP_SDR104 BIT33 #define SD_HC_CAP_DDR50 BIT34 #define ESD_CONTROL_RESET_DELAY (20 * 1000) #define IO_CONTROL2_SETTLE_US 3000 STATIC EFI_HANDLE mSdMmcControllerHandle; /** Override function for SDHCI capability bits @param[in] PassThru A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance. @param[in] ControllerHandle The EFI_HANDLE of the controller. @param[in] Slot The 0 based slot index. @param[in,out] SdMmcHcSlotCapability The SDHCI capability structure. @param[in,out] BaseClkFreq The base clock frequency value that optionally can be updated. @retval EFI_SUCCESS The override function completed successfully. @retval EFI_NOT_FOUND The specified controller or slot does not exist. @retval EFI_INVALID_PARAMETER SdMmcHcSlotCapability is NULL **/ STATIC EFI_STATUS EFIAPI SynQuacerSdMmcCapability ( IN EFI_HANDLE ControllerHandle, IN UINT8 Slot, IN OUT VOID *SdMmcHcSlotCapability, IN OUT UINT32 *BaseClkFreq ) { UINT64 Capability; if (ControllerHandle != mSdMmcControllerHandle) { return EFI_SUCCESS; } ASSERT (Slot == 0); // // Clear the SDR104 capability bit. This avoids the need for a HS200 tuning // quirk that is difficult to support using the generic driver. // Clear the DDR50 bit as well to work around an issue with the Kingston // EMMC08G-M325-A52 part that was fitted on 96board DeveloperBox samples. // Capability = ReadUnaligned64 (SdMmcHcSlotCapability); Capability &= ~(UINT64)(SD_HC_CAP_SDR104 | SD_HC_CAP_DDR50); WriteUnaligned64 (SdMmcHcSlotCapability, Capability); return EFI_SUCCESS; } /** Override function for SDHCI controller operations @param[in] ControllerHandle The EFI_HANDLE of the controller. @param[in] Slot The 0 based slot index. @param[in] PhaseType The type of operation and whether the hook is invoked right before (pre) or right after (post) @param[in,out] PhaseData The pointer to a phase-specific data. @retval EFI_SUCCESS The override function completed successfully. @retval EFI_NOT_FOUND The specified controller or slot does not exist. @retval EFI_INVALID_PARAMETER PhaseType is invalid **/ STATIC EFI_STATUS EFIAPI SynQuacerSdMmcNotifyPhase ( IN EFI_HANDLE ControllerHandle, IN UINT8 Slot, IN EDKII_SD_MMC_PHASE_TYPE PhaseType, IN OUT VOID *PhaseData ) { if (ControllerHandle != mSdMmcControllerHandle) { return EFI_SUCCESS; } ASSERT (Slot == 0); switch (PhaseType) { case EdkiiSdMmcResetPre: // Soft reset does not complete unless the clock is already enabled. MmioWrite16 (SYNQUACER_EMMC_BASE + SD_HC_CLOCK_CTRL, SYNQUACER_CLOCK_CTRL_VAL); break; case EdkiiSdMmcInitHostPre: // init vendor specific regs MmioAnd16 (SYNQUACER_EMMC_BASE + F_SDH30_AHB_CONFIG, ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN)); MmioOr16 (SYNQUACER_EMMC_BASE + F_SDH30_AHB_CONFIG, F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 | F_SDH30_AHB_INCR_4); MmioAnd32 (SYNQUACER_EMMC_BASE + F_SDH30_ESD_CONTROL, ~F_SDH30_EMMC_RST); MemoryFence (); gBS->Stall (ESD_CONTROL_RESET_DELAY); MmioOr32 (SYNQUACER_EMMC_BASE + F_SDH30_ESD_CONTROL, F_SDH30_EMMC_RST | F_SDH30_CMD_DAT_DELAY | F_SDH30_EMMC_HS200); gBS->Stall (IO_CONTROL2_SETTLE_US); MmioOr32 (SYNQUACER_EMMC_BASE + F_SDH30_IO_CONTROL2, F_SDH30_CRES_O_DN); MemoryFence (); MmioOr32 (SYNQUACER_EMMC_BASE + F_SDH30_IO_CONTROL2, F_SDH30_MSEL_O_1_8); MemoryFence (); MmioAnd32 (SYNQUACER_EMMC_BASE + F_SDH30_IO_CONTROL2, ~F_SDH30_CRES_O_DN); MemoryFence (); gBS->Stall (IO_CONTROL2_SETTLE_US); MmioOr32 (SYNQUACER_EMMC_BASE + F_SDH30_TUNING_SETTING, F_SDH30_CMD_CHK_DIS); break; default: break; } return EFI_SUCCESS; } STATIC EDKII_SD_MMC_OVERRIDE mSdMmcOverride = { EDKII_SD_MMC_OVERRIDE_PROTOCOL_VERSION, SynQuacerSdMmcCapability, SynQuacerSdMmcNotifyPhase, }; EFI_STATUS EFIAPI RegisterEmmc ( VOID ) { EFI_STATUS Status; EFI_HANDLE Handle; Status = RegisterNonDiscoverableMmioDevice ( NonDiscoverableDeviceTypeSdhci, NonDiscoverableDeviceDmaTypeCoherent, NULL, &mSdMmcControllerHandle, 1, SYNQUACER_EMMC_BASE, SYNQUACER_EMMC_BASE_SZ); ASSERT_EFI_ERROR (Status); Handle = NULL; Status = gBS->InstallProtocolInterface (&Handle, &gEdkiiSdMmcOverrideProtocolGuid, EFI_NATIVE_INTERFACE, (VOID **)&mSdMmcOverride); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }