/*******************************************************************************
|
Copyright (C) 2018 Marvell International Ltd.
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
*******************************************************************************/
|
|
#include "XenonSdMmcOverride.h"
|
|
STATIC EFI_HANDLE mXenonSdMmcOverrideHandle;
|
STATIC EDKII_SD_MMC_OVERRIDE *mSdMmcOverride;
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
XenonGetSdMmcDesc (
|
IN EFI_HANDLE ControllerHandle,
|
IN OUT MV_BOARD_SDMMC_DESC *SdMmcDesc
|
)
|
{
|
EFI_STATUS Status;
|
MV_BOARD_SDMMC_DESC *SdMmcDescs;
|
NON_DISCOVERABLE_DEVICE *Device;
|
MARVELL_BOARD_DESC_PROTOCOL *BoardDescProtocol;
|
UINTN Index;
|
|
Device = NULL;
|
Status = gBS->OpenProtocol (ControllerHandle,
|
&gEdkiiNonDiscoverableDeviceProtocolGuid,
|
(VOID **) &Device,
|
mXenonSdMmcOverrideHandle,
|
ControllerHandle,
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
BoardDescProtocol = NULL;
|
Status = gBS->LocateProtocol (&gMarvellBoardDescProtocolGuid,
|
NULL,
|
(VOID **) &BoardDescProtocol);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = BoardDescProtocol->BoardDescSdMmcGet (BoardDescProtocol, &SdMmcDescs);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
for (Index = 0; Index < SdMmcDescs->SdMmcDevCount; Index++) {
|
if (SdMmcDescs[Index].SoC->SdMmcBaseAddress ==
|
Device->Resources[0].AddrRangeMin) {
|
*SdMmcDesc = SdMmcDescs[Index];
|
break;
|
}
|
}
|
|
if (Index == SdMmcDescs->SdMmcDevCount) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
EFI_STATUS
|
XenonGetPciIo (
|
IN EFI_HANDLE ControllerHandle,
|
IN OUT EFI_PCI_IO_PROTOCOL **PciIo
|
)
|
{
|
EFI_STATUS Status;
|
|
*PciIo = NULL;
|
Status = gBS->OpenProtocol (ControllerHandle,
|
&gEfiPciIoProtocolGuid,
|
(VOID **) PciIo,
|
mXenonSdMmcOverrideHandle,
|
ControllerHandle,
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
return Status;
|
}
|
|
/**
|
Set SD Host Controller control 2 registry according to selected speed.
|
|
@param[in] ControllerHandle The EFI_HANDLE of the controller.
|
@param[in] Slot The slot number of the SD card to send the command to.
|
@param[in] Timing The timing to select.
|
|
@retval EFI_SUCCESS The override function completed successfully.
|
@retval EFI_NOT_FOUND The specified controller or slot does not exist.
|
**/
|
STATIC
|
EFI_STATUS
|
XenonSdMmcHcUhsSignaling (
|
IN EFI_HANDLE ControllerHandle,
|
IN UINT8 Slot,
|
IN SD_MMC_BUS_MODE Timing
|
)
|
{
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
EFI_STATUS Status;
|
UINT8 HostCtrl2;
|
UINT8 XenonUhsSelect;
|
|
if (Slot != 0) {
|
return EFI_NOT_FOUND;
|
}
|
|
//
|
// Update Host Control Register 2 only for HS200/HS400.
|
//
|
switch (Timing) {
|
case SdMmcMmcHs200:
|
XenonUhsSelect = XENON_SD_MMC_HC_CTRL_HS200;
|
break;
|
case SdMmcMmcHs400:
|
XenonUhsSelect = XENON_SD_MMC_HC_CTRL_HS400;
|
break;
|
default:
|
return EFI_SUCCESS;
|
}
|
|
Status = XenonGetPciIo (ControllerHandle, &PciIo);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
HostCtrl2 = (UINT8)~UHS_MODE_SELECT_MASK;
|
Status = XenonHcAndMmio (PciIo,
|
Slot,
|
SDHC_HOST_CTRL2,
|
sizeof (HostCtrl2),
|
&HostCtrl2);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = XenonHcOrMmio (PciIo,
|
Slot,
|
SDHC_HOST_CTRL2,
|
sizeof (XenonUhsSelect),
|
&XenonUhsSelect);
|
|
return Status;
|
}
|
|
/**
|
|
Additional operations specific for host controller
|
|
@param[in] ControllerHandle The EFI_HANDLE of the controller.
|
@param[in] Slot The 0 based slot index.
|
@param[in] Timing The timing which should be set by
|
host controller.
|
|
@retval EFI_SUCCESS The override function completed successfully.
|
@retval EFI_NOT_FOUND The specified controller or slot does not exist.
|
|
**/
|
STATIC
|
EFI_STATUS
|
XenonSwitchClockFreqPost (
|
IN EFI_HANDLE ControllerHandle,
|
IN UINT8 Slot,
|
IN SD_MMC_BUS_MODE Timing
|
)
|
{
|
EFI_STATUS Status;
|
MV_BOARD_SDMMC_DESC SdMmcDesc;
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
|
if (Slot != 0) {
|
return EFI_NOT_FOUND;
|
}
|
|
Status = XenonGetPciIo (ControllerHandle, &PciIo);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
Status = XenonGetSdMmcDesc (ControllerHandle, &SdMmcDesc);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = XenonSetPhy (PciIo,
|
SdMmcDesc.XenonSlowModeEnabled,
|
SdMmcDesc.XenonTuningStepDivisor,
|
Timing);
|
|
return Status;
|
}
|
|
/**
|
|
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] 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_UNSUPPORTED Nothing has been done in connection of PhaseType
|
@retval EFI_INVALID_PARAMETER PhaseType is invalid
|
|
**/
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
XenonSdMmcNotifyPhase (
|
IN EFI_HANDLE ControllerHandle,
|
IN UINT8 Slot,
|
IN EDKII_SD_MMC_PHASE_TYPE PhaseType,
|
IN OUT VOID *PhaseData
|
)
|
{
|
EFI_STATUS Status;
|
MV_BOARD_SDMMC_DESC SdMmcDesc;
|
EFI_PCI_IO_PROTOCOL *PciIo;
|
SD_MMC_BUS_MODE *Timing;
|
|
if (Slot != 0) {
|
return EFI_NOT_FOUND;
|
}
|
|
switch (PhaseType) {
|
case EdkiiSdMmcInitHostPre:
|
Status = XenonGetPciIo (ControllerHandle, &PciIo);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = XenonGetSdMmcDesc (ControllerHandle, &SdMmcDesc);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = XenonInit (PciIo,
|
SdMmcDesc.Xenon1v8Enabled,
|
SdMmcDesc.XenonSlowModeEnabled,
|
SdMmcDesc.XenonTuningStepDivisor);
|
return Status;
|
case EdkiiSdMmcUhsSignaling:
|
if (PhaseData == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Timing = (SD_MMC_BUS_MODE *)PhaseData;
|
|
Status = XenonSdMmcHcUhsSignaling (ControllerHandle,
|
Slot,
|
*Timing);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
break;
|
case EdkiiSdMmcSwitchClockFreqPost:
|
if (PhaseData == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Timing = (SD_MMC_BUS_MODE *)PhaseData;
|
|
Status = XenonSwitchClockFreqPost (ControllerHandle,
|
Slot,
|
*Timing);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
break;
|
default:
|
return EFI_SUCCESS;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
|
Override function for SDHCI capability bits
|
|
@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
|
XenonSdMmcCapability (
|
IN EFI_HANDLE ControllerHandle,
|
IN UINT8 Slot,
|
IN OUT VOID *SdMmcHcSlotCapability,
|
IN OUT UINT32 *BaseClkFreq
|
)
|
{
|
EFI_STATUS Status;
|
MV_BOARD_SDMMC_DESC SdMmcDesc;
|
UINT64 Capability;
|
|
if (SdMmcHcSlotCapability == NULL) {
|
return EFI_INVALID_PARAMETER;
|
}
|
if (Slot != 0) {
|
return EFI_NOT_FOUND;
|
}
|
Status = XenonGetSdMmcDesc (ControllerHandle, &SdMmcDesc);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Capability = ReadUnaligned64 (SdMmcHcSlotCapability);
|
|
//
|
// Override capabilities structure according to board configuration.
|
//
|
if (SdMmcDesc.Xenon1v8Enabled) {
|
Capability &= ~(UINT64)(SDHC_CAP_VOLTAGE_33 | SDHC_CAP_VOLTAGE_30);
|
} else {
|
Capability &= ~(UINT64)(SDHC_CAP_SDR104 | SDHC_CAP_DDR50 |
|
SDHC_CAP_SDR50 | SDHC_CAP_HS400 |
|
SDHC_CAP_VOLTAGE_18);
|
}
|
|
if (!SdMmcDesc.Xenon8BitBusEnabled) {
|
Capability &= ~(UINT64)(SDHC_CAP_BUS_WIDTH8);
|
}
|
|
if (SdMmcDesc.XenonSlowModeEnabled) {
|
Capability &= ~(UINT64)(SDHC_CAP_SDR104 | SDHC_CAP_DDR50 | SDHC_CAP_HS400);
|
}
|
|
Capability &= ~(UINT64)(SDHC_CAP_SLOT_TYPE_MASK);
|
Capability |= SdMmcDesc.SlotType << SDHC_CAP_SLOT_TYPE_OFFSET;
|
|
WriteUnaligned64 (SdMmcHcSlotCapability, Capability);
|
|
//
|
// Override inappropriate base clock frequency from Capabilities Register 1.
|
// Actual clock speed of Xenon controller is 400MHz.
|
//
|
*BaseClkFreq = XENON_MMC_MAX_CLK / 1000 / 1000;
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
The entry point for Xenon driver, used to install SdMMcOverrideProtocol
|
on the ImageHandle.
|
|
@param[in] ImageHandle The firmware allocated handle for this driver image.
|
@param[in] SystemTable Pointer to the EFI system table.
|
|
@retval EFI_SUCCESS Driver loaded.
|
@retval other Driver not loaded.
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
InitializeXenonDxe (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_STATUS Status;
|
|
mSdMmcOverride = AllocateZeroPool (sizeof (EDKII_SD_MMC_OVERRIDE));
|
if (mSdMmcOverride == NULL) {
|
DEBUG ((DEBUG_ERROR, "%a: Cannot allocate memory\n", __FUNCTION__));
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
mSdMmcOverride->Version = EDKII_SD_MMC_OVERRIDE_PROTOCOL_VERSION;
|
mSdMmcOverride->Capability = XenonSdMmcCapability;
|
mSdMmcOverride->NotifyPhase = XenonSdMmcNotifyPhase;
|
|
Status = gBS->InstallProtocolInterface (&ImageHandle,
|
&gEdkiiSdMmcOverrideProtocolGuid,
|
EFI_NATIVE_INTERFACE,
|
mSdMmcOverride);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR,
|
"%a: Filed to install SdMmcOverride protocol\n",
|
__FUNCTION__));
|
return Status;
|
}
|
|
mXenonSdMmcOverrideHandle = ImageHandle;
|
|
return Status;
|
}
|