X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FBus%2FPci%2FSdMmcPciHcDxe%2FSdMmcPciHci.c;h=7971196a253a84339798ec9480166edd8dcd884f;hb=643623147a1feaddd734ddd84604e1d8e9dcebee;hp=d2bd112c14273aafa5966bb72f6c5b132d85bd0c;hpb=48555339beb65f90bd42995a15bd38cd45e378f5;p=mirror_edk2.git diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c index d2bd112c14..7971196a25 100644 --- a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c @@ -1,17 +1,14 @@ /** @file This driver is used to manage SD/MMC PCI host controllers which are compliance - with SD Host Controller Simplified Specification version 3.00. + with SD Host Controller Simplified Specification version 3.00 plus the 64-bit + System Addressing support in SD Host Controller Simplified Specification version + 4.20. It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. - Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. + Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -33,46 +30,47 @@ DumpCapabilityReg ( // // Dump Capability Data // - DEBUG ((EFI_D_INFO, " == Slot [%d] Capability is 0x%x ==\n", Slot, Capability)); - DEBUG ((EFI_D_INFO, " Timeout Clk Freq %d%a\n", Capability->TimeoutFreq, (Capability->TimeoutUnit) ? "MHz" : "KHz")); - DEBUG ((EFI_D_INFO, " Base Clk Freq %dMHz\n", Capability->BaseClkFreq)); - DEBUG ((EFI_D_INFO, " Max Blk Len %dbytes\n", 512 * (1 << Capability->MaxBlkLen))); - DEBUG ((EFI_D_INFO, " 8-bit Support %a\n", Capability->BusWidth8 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " ADMA2 Support %a\n", Capability->Adma2 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " SDMA Support %a\n", Capability->Sdma ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Suspend/Resume %a\n", Capability->SuspRes ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Voltage 3.3 %a\n", Capability->Voltage33 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Voltage 3.0 %a\n", Capability->Voltage30 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Voltage 1.8 %a\n", Capability->Voltage18 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " 64-bit Sys Bus %a\n", Capability->SysBus64 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Async Interrupt %a\n", Capability->AsyncInt ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " SlotType ")); + DEBUG ((DEBUG_INFO, " == Slot [%d] Capability is 0x%x ==\n", Slot, Capability)); + DEBUG ((DEBUG_INFO, " Timeout Clk Freq %d%a\n", Capability->TimeoutFreq, (Capability->TimeoutUnit) ? "MHz" : "KHz")); + DEBUG ((DEBUG_INFO, " Base Clk Freq %dMHz\n", Capability->BaseClkFreq)); + DEBUG ((DEBUG_INFO, " Max Blk Len %dbytes\n", 512 * (1 << Capability->MaxBlkLen))); + DEBUG ((DEBUG_INFO, " 8-bit Support %a\n", Capability->BusWidth8 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " ADMA2 Support %a\n", Capability->Adma2 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " HighSpeed Support %a\n", Capability->HighSpeed ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " SDMA Support %a\n", Capability->Sdma ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Suspend/Resume %a\n", Capability->SuspRes ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Voltage 3.3 %a\n", Capability->Voltage33 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Voltage 3.0 %a\n", Capability->Voltage30 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Voltage 1.8 %a\n", Capability->Voltage18 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " V4 64-bit Sys Bus %a\n", Capability->SysBus64V4 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " V3 64-bit Sys Bus %a\n", Capability->SysBus64V3 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Async Interrupt %a\n", Capability->AsyncInt ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " SlotType ")); if (Capability->SlotType == 0x00) { - DEBUG ((EFI_D_INFO, "%a\n", "Removable Slot")); + DEBUG ((DEBUG_INFO, "%a\n", "Removable Slot")); } else if (Capability->SlotType == 0x01) { - DEBUG ((EFI_D_INFO, "%a\n", "Embedded Slot")); + DEBUG ((DEBUG_INFO, "%a\n", "Embedded Slot")); } else if (Capability->SlotType == 0x02) { - DEBUG ((EFI_D_INFO, "%a\n", "Shared Bus Slot")); + DEBUG ((DEBUG_INFO, "%a\n", "Shared Bus Slot")); } else { - DEBUG ((EFI_D_INFO, "%a\n", "Reserved")); - } - DEBUG ((EFI_D_INFO, " SDR50 Support %a\n", Capability->Sdr50 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " SDR104 Support %a\n", Capability->Sdr104 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " DDR50 Support %a\n", Capability->Ddr50 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Driver Type A %a\n", Capability->DriverTypeA ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Driver Type C %a\n", Capability->DriverTypeC ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Driver Type D %a\n", Capability->DriverTypeD ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Driver Type 4 %a\n", Capability->DriverType4 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, "%a\n", "Reserved")); + } + DEBUG ((DEBUG_INFO, " SDR50 Support %a\n", Capability->Sdr50 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " SDR104 Support %a\n", Capability->Sdr104 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " DDR50 Support %a\n", Capability->Ddr50 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Driver Type A %a\n", Capability->DriverTypeA ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Driver Type C %a\n", Capability->DriverTypeC ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Driver Type D %a\n", Capability->DriverTypeD ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Driver Type 4 %a\n", Capability->DriverType4 ? "TRUE" : "FALSE")); if (Capability->TimerCount == 0) { - DEBUG ((EFI_D_INFO, " Retuning TimerCnt Disabled\n", 2 * (Capability->TimerCount - 1))); + DEBUG ((DEBUG_INFO, " Retuning TimerCnt Disabled\n", 2 * (Capability->TimerCount - 1))); } else { - DEBUG ((EFI_D_INFO, " Retuning TimerCnt %dseconds\n", 2 * (Capability->TimerCount - 1))); + DEBUG ((DEBUG_INFO, " Retuning TimerCnt %dseconds\n", 2 * (Capability->TimerCount - 1))); } - DEBUG ((EFI_D_INFO, " SDR50 Tuning %a\n", Capability->TuningSDR50 ? "TRUE" : "FALSE")); - DEBUG ((EFI_D_INFO, " Retuning Mode Mode %d\n", Capability->RetuningMod + 1)); - DEBUG ((EFI_D_INFO, " Clock Multiplier M = %d\n", Capability->ClkMultiplier + 1)); - DEBUG ((EFI_D_INFO, " HS 400 %a\n", Capability->Hs400 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " SDR50 Tuning %a\n", Capability->TuningSDR50 ? "TRUE" : "FALSE")); + DEBUG ((DEBUG_INFO, " Retuning Mode Mode %d\n", Capability->RetuningMod + 1)); + DEBUG ((DEBUG_INFO, " Clock Multiplier M = %d\n", Capability->ClkMultiplier + 1)); + DEBUG ((DEBUG_INFO, " HS 400 %a\n", Capability->Hs400 ? "TRUE" : "FALSE")); return; } @@ -150,19 +148,36 @@ SdMmcHcRwMmio ( ) { EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL_WIDTH Width; if ((PciIo == NULL) || (Data == NULL)) { return EFI_INVALID_PARAMETER; } - if ((Count != 1) && (Count != 2) && (Count != 4) && (Count != 8)) { - return EFI_INVALID_PARAMETER; + switch (Count) { + case 1: + Width = EfiPciIoWidthUint8; + break; + case 2: + Width = EfiPciIoWidthUint16; + Count = 1; + break; + case 4: + Width = EfiPciIoWidthUint32; + Count = 1; + break; + case 8: + Width = EfiPciIoWidthUint32; + Count = 2; + break; + default: + return EFI_INVALID_PARAMETER; } if (Read) { Status = PciIo->Mem.Read ( PciIo, - EfiPciIoWidthUint8, + Width, BarIndex, (UINT64) Offset, Count, @@ -171,7 +186,7 @@ SdMmcHcRwMmio ( } else { Status = PciIo->Mem.Write ( PciIo, - EfiPciIoWidthUint8, + Width, BarIndex, (UINT64) Offset, Count, @@ -416,10 +431,40 @@ SdMmcHcWaitMmioSet ( return EFI_TIMEOUT; } +/** + Get the controller version information from the specified slot. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[out] Version The buffer to store the version information. + + @retval EFI_SUCCESS The operation executes successfully. + @retval Others The operation fails. + +**/ +EFI_STATUS +SdMmcHcGetControllerVersion ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + OUT UINT16 *Version + ) +{ + EFI_STATUS Status; + + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CTRL_VER, TRUE, sizeof (UINT16), Version); + if (EFI_ERROR (Status)) { + return Status; + } + + *Version &= 0xFF; + + return EFI_SUCCESS; +} + /** Software reset the specified SD/MMC host controller and enable all interrupts. - @param[in] PciIo The PCI IO protocol instance. + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. @param[in] Slot The slot number of the SD card to send the command to. @retval EFI_SUCCESS The software reset executes successfully. @@ -428,18 +473,38 @@ SdMmcHcWaitMmioSet ( **/ EFI_STATUS SdMmcHcReset ( - IN EFI_PCI_IO_PROTOCOL *PciIo, + IN SD_MMC_HC_PRIVATE_DATA *Private, IN UINT8 Slot ) { EFI_STATUS Status; UINT8 SwReset; + EFI_PCI_IO_PROTOCOL *PciIo; + + // + // Notify the SD/MMC override protocol that we are about to reset + // the SD/MMC host controller. + // + if (mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + Private->ControllerHandle, + Slot, + EdkiiSdMmcResetPre, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, + "%a: SD/MMC pre reset notifier callback failed - %r\n", + __FUNCTION__, Status)); + return Status; + } + } - SwReset = 0xFF; - Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_SW_RST, FALSE, sizeof (SwReset), &SwReset); + PciIo = Private->PciIo; + SwReset = BIT0; + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_SW_RST, sizeof (SwReset), &SwReset); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "SdMmcHcReset: write full 1 fails: %r\n", Status)); + DEBUG ((DEBUG_ERROR, "SdMmcHcReset: write SW Reset for All fails: %r\n", Status)); return Status; } @@ -448,18 +513,41 @@ SdMmcHcReset ( Slot, SD_MMC_HC_SW_RST, sizeof (SwReset), - 0xFF, + BIT0, 0x00, SD_MMC_HC_GENERIC_TIMEOUT ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "SdMmcHcReset: reset done with %r\n", Status)); + DEBUG ((DEBUG_INFO, "SdMmcHcReset: reset done with %r\n", Status)); return Status; } + // // Enable all interrupt after reset all. // Status = SdMmcHcEnableInterrupt (PciIo, Slot); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "SdMmcHcReset: SdMmcHcEnableInterrupt done with %r\n", + Status)); + return Status; + } + + // + // Notify the SD/MMC override protocol that we have just reset + // the SD/MMC host controller. + // + if (mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + Private->ControllerHandle, + Slot, + EdkiiSdMmcResetPost, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, + "%a: SD/MMC post reset notifier callback failed - %r\n", + __FUNCTION__, Status)); + } + } return Status; } @@ -583,6 +671,20 @@ SdMmcHcCardDetect ( UINT16 Data; UINT32 PresentState; + // + // Check Present State Register to see if there is a card presented. + // + Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((PresentState & BIT16) != 0) { + *MediaPresent = TRUE; + } else { + *MediaPresent = FALSE; + } + // // Check Normal Interrupt Status Register // @@ -601,19 +703,6 @@ SdMmcHcCardDetect ( return Status; } - // - // Check Present State Register to see if there is a card presented. - // - Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_PRESENT_STATE, TRUE, sizeof (PresentState), &PresentState); - if (EFI_ERROR (Status)) { - return Status; - } - - if ((PresentState & BIT16) != 0) { - *MediaPresent = TRUE; - } else { - *MediaPresent = FALSE; - } return EFI_MEDIA_CHANGED; } @@ -669,15 +758,40 @@ SdMmcHcStopClock ( return Status; } +/** + Start the SD clock. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] Slot The slot number. + + @retval EFI_SUCCESS Succeeded to start the SD clock. + @retval Others Failed to start the SD clock. +**/ +EFI_STATUS +SdMmcHcStartSdClock ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot + ) +{ + UINT16 ClockCtrl; + + // + // Set SD Clock Enable in the Clock Control register to 1 + // + ClockCtrl = BIT2; + return SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); +} + /** SD/MMC card clock supply. Refer to SD Host Controller Simplified spec 3.0 Section 3.2.1 for details. - @param[in] PciIo The PCI IO protocol instance. - @param[in] Slot The slot number of the SD card to send the command to. - @param[in] ClockFreq The max clock frequency to be set. The unit is KHz. - @param[in] Capability The capability of the slot. + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Slot The slot number of the SD card to send the command to. + @param[in] BusTiming BusTiming at which the frequency change is done. + @param[in] FirstTimeSetup Flag to indicate whether the clock is being setup for the first time. + @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. @@ -685,29 +799,34 @@ SdMmcHcStopClock ( **/ EFI_STATUS SdMmcHcClockSupply ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT8 Slot, - IN UINT64 ClockFreq, - IN SD_MMC_HC_SLOT_CAP Capability + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN SD_MMC_BUS_MODE BusTiming, + IN BOOLEAN FirstTimeSetup, + IN UINT64 ClockFreq ) { EFI_STATUS Status; - UINT32 BaseClkFreq; UINT32 SettingFreq; UINT32 Divisor; UINT32 Remainder; - UINT16 ControllerVer; UINT16 ClockCtrl; + UINT32 BaseClkFreq; + UINT16 ControllerVer; + EFI_PCI_IO_PROTOCOL *PciIo; - // - // Calculate a divisor for SD clock frequency - // - ASSERT (Capability.BaseClkFreq != 0); + PciIo = Private->PciIo; + BaseClkFreq = Private->BaseClkFreq[Slot]; + ControllerVer = Private->ControllerVersion[Slot]; - BaseClkFreq = Capability.BaseClkFreq; - if ((ClockFreq > (BaseClkFreq * 1000)) || (ClockFreq == 0)) { + if (BaseClkFreq == 0 || ClockFreq == 0) { return EFI_INVALID_PARAMETER; } + + if (ClockFreq > (BaseClkFreq * 1000)) { + ClockFreq = BaseClkFreq * 1000; + } + // // Calculate the divisor of base frequency. // @@ -726,19 +845,17 @@ SdMmcHcClockSupply ( } } - DEBUG ((EFI_D_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); + DEBUG ((DEBUG_INFO, "BaseClkFreq %dMHz Divisor %d ClockFreq %dKhz\n", BaseClkFreq, Divisor, ClockFreq)); - Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_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) { + if ((ControllerVer >= SD_MMC_HC_CTRL_VER_300) && + (ControllerVer <= SD_MMC_HC_CTRL_VER_420)) { ASSERT (Divisor <= 0x3FF); ClockCtrl = ((Divisor & 0xFF) << 8) | ((Divisor & 0x300) >> 2); - } else if (((ControllerVer & 0xFF) == 0) || ((ControllerVer & 0xFF) == 1)) { + } else if ((ControllerVer == SD_MMC_HC_CTRL_VER_100) || + (ControllerVer == SD_MMC_HC_CTRL_VER_200)) { // // Only the most significant bit can be used as divisor. // @@ -748,7 +865,7 @@ SdMmcHcClockSupply ( ASSERT (Divisor <= 0x80); ClockCtrl = (Divisor & 0xFF) << 8; } else { - DEBUG ((EFI_D_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); + DEBUG ((DEBUG_ERROR, "Unknown SD Host Controller Spec version [0x%x]!!!\n", ControllerVer)); return EFI_UNSUPPORTED; } @@ -766,7 +883,7 @@ SdMmcHcClockSupply ( ClockCtrl |= BIT0; Status = SdMmcHcRwMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, FALSE, sizeof (ClockCtrl), &ClockCtrl); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); + DEBUG ((DEBUG_ERROR, "Set SDCLK Frequency Select and Internal Clock Enable fields fails\n")); return Status; } @@ -786,11 +903,35 @@ SdMmcHcClockSupply ( return Status; } + Status = SdMmcHcStartSdClock (PciIo, Slot); + if (EFI_ERROR (Status)) { + return Status; + } + // - // Set SD Clock Enable in the Clock Control register to 1 + // We don't notify the platform on first time setup to avoid changing + // legacy behavior. During first time setup we also don't know what type + // of the card slot it is and which enum value of BusTiming applies. // - ClockCtrl = BIT2; - Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_CLOCK_CTRL, sizeof (ClockCtrl), &ClockCtrl); + if (!FirstTimeSetup && mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + Private->ControllerHandle, + Slot, + EdkiiSdMmcSwitchClockFreqPost, + &BusTiming + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: SD/MMC switch clock freq post notifier callback failed - %r\n", + __FUNCTION__, + Status + )); + return Status; + } + } + + Private->Slot[Slot].CurrentFreq = ClockFreq; return Status; } @@ -886,41 +1027,65 @@ SdMmcHcSetBusWidth ( } /** - Supply SD/MMC card with lowest clock frequency at initialization. + Configure V4 controller enhancements at initialization. @param[in] PciIo The PCI IO protocol instance. @param[in] Slot The slot number of the SD card to send the command to. @param[in] Capability The capability of the slot. + @param[in] ControllerVer The version of host controller. @retval EFI_SUCCESS The clock is supplied successfully. - @retval Others The clock isn't supplied successfully. **/ EFI_STATUS -SdMmcHcInitClockFreq ( +SdMmcHcInitV4Enhancements ( IN EFI_PCI_IO_PROTOCOL *PciIo, IN UINT8 Slot, - IN SD_MMC_HC_SLOT_CAP Capability + IN SD_MMC_HC_SLOT_CAP Capability, + IN UINT16 ControllerVer ) { EFI_STATUS Status; - UINT32 InitFreq; + UINT16 HostCtrl2; // - // Calculate a divisor for SD clock frequency + // Check if controller version V4 or higher // - if (Capability.BaseClkFreq == 0) { + if (ControllerVer >= SD_MMC_HC_CTRL_VER_400) { + HostCtrl2 = SD_MMC_HC_V4_EN; // - // Don't support get Base Clock Frequency information via another method + // Check if controller version V4.0 // - return EFI_UNSUPPORTED; + if (ControllerVer == SD_MMC_HC_CTRL_VER_400) { + // + // Check if 64bit support is available + // + if (Capability.SysBus64V3 != 0) { + HostCtrl2 |= SD_MMC_HC_64_ADDR_EN; + DEBUG ((DEBUG_INFO, "Enabled V4 64 bit system bus support\n")); + } + } + // + // Check if controller version V4.10 or higher + // + else if (ControllerVer >= SD_MMC_HC_CTRL_VER_410) { + // + // Check if 64bit support is available + // + if (Capability.SysBus64V4 != 0) { + HostCtrl2 |= SD_MMC_HC_64_ADDR_EN; + DEBUG ((DEBUG_INFO, "Enabled V4 64 bit system bus support\n")); + } + HostCtrl2 |= SD_MMC_HC_26_DATA_LEN_ADMA_EN; + DEBUG ((DEBUG_INFO, "Enabled V4 26 bit data length ADMA support\n")); + } + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } } - // - // Supply 400KHz clock frequency at initialization phase. - // - InitFreq = 400; - Status = SdMmcHcClockSupply (PciIo, Slot, InitFreq, Capability); - return Status; + + return EFI_SUCCESS; } /** @@ -1015,9 +1180,8 @@ SdMmcHcInitTimeoutCtrl ( Initial SD/MMC host controller with lowest clock frequency, max power and max timeout value at initialization. - @param[in] PciIo The PCI IO protocol instance. + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. @param[in] Slot The slot number of the SD card to send the command to. - @param[in] Capability The capability of the slot. @retval EFI_SUCCESS The host controller is initialized successfully. @retval Others The host controller isn't initialized successfully. @@ -1025,14 +1189,48 @@ SdMmcHcInitTimeoutCtrl ( **/ EFI_STATUS SdMmcHcInitHost ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT8 Slot, - IN SD_MMC_HC_SLOT_CAP Capability + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot ) { - EFI_STATUS Status; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + SD_MMC_HC_SLOT_CAP Capability; - Status = SdMmcHcInitClockFreq (PciIo, Slot, Capability); + // + // Notify the SD/MMC override protocol that we are about to initialize + // the SD/MMC host controller. + // + if (mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + Private->ControllerHandle, + Slot, + EdkiiSdMmcInitHostPre, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, + "%a: SD/MMC pre init notifier callback failed - %r\n", + __FUNCTION__, Status)); + return Status; + } + } + + PciIo = Private->PciIo; + Capability = Private->Capability[Slot]; + + Status = SdMmcHcInitV4Enhancements (PciIo, Slot, Capability, Private->ControllerVersion[Slot]); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Perform first time clock setup with 400 KHz frequency. + // We send the 0 as the BusTiming value because at this time + // we still do not know the slot type and which enum value will apply. + // Since it is a first time setup SdMmcHcClockSupply won't notify + // the platofrm driver anyway so it doesn't matter. + // + Status = SdMmcHcClockSupply (Private, Slot, 0, TRUE, 400); if (EFI_ERROR (Status)) { return Status; } @@ -1043,9 +1241,152 @@ SdMmcHcInitHost ( } Status = SdMmcHcInitTimeoutCtrl (PciIo, Slot); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Notify the SD/MMC override protocol that we are have just initialized + // the SD/MMC host controller. + // + if (mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + Private->ControllerHandle, + Slot, + EdkiiSdMmcInitHostPost, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, + "%a: SD/MMC post init notifier callback failed - %r\n", + __FUNCTION__, Status)); + } + } return Status; } +/** + Set SD Host Controler control 2 registry according to selected speed. + + @param[in] ControllerHandle The handle of the controller. + @param[in] PciIo The PCI IO protocol instance. + @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 timing is set successfully. + @retval Others The timing isn't set successfully. +**/ +EFI_STATUS +SdMmcHcUhsSignaling ( + IN EFI_HANDLE ControllerHandle, + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 Slot, + IN SD_MMC_BUS_MODE Timing + ) +{ + EFI_STATUS Status; + UINT8 HostCtrl2; + + HostCtrl2 = (UINT8)~SD_MMC_HC_CTRL_UHS_MASK; + Status = SdMmcHcAndMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + switch (Timing) { + case SdMmcUhsSdr12: + HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR12; + break; + case SdMmcUhsSdr25: + HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR25; + break; + case SdMmcUhsSdr50: + HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR50; + break; + case SdMmcUhsSdr104: + HostCtrl2 = SD_MMC_HC_CTRL_UHS_SDR104; + break; + case SdMmcUhsDdr50: + HostCtrl2 = SD_MMC_HC_CTRL_UHS_DDR50; + break; + case SdMmcMmcLegacy: + HostCtrl2 = SD_MMC_HC_CTRL_MMC_LEGACY; + break; + case SdMmcMmcHsSdr: + HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS_SDR; + break; + case SdMmcMmcHsDdr: + HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS_DDR; + break; + case SdMmcMmcHs200: + HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS200; + break; + case SdMmcMmcHs400: + HostCtrl2 = SD_MMC_HC_CTRL_MMC_HS400; + break; + default: + HostCtrl2 = 0; + break; + } + Status = SdMmcHcOrMmio (PciIo, Slot, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + if (mOverride != NULL && mOverride->NotifyPhase != NULL) { + Status = mOverride->NotifyPhase ( + ControllerHandle, + Slot, + EdkiiSdMmcUhsSignaling, + &Timing + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: SD/MMC uhs signaling notifier callback failed - %r\n", + __FUNCTION__, + Status + )); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Set driver strength in host controller. + + @param[in] PciIo The PCI IO protocol instance. + @param[in] SlotIndex The slot index of the card. + @param[in] DriverStrength DriverStrength to set in the controller. + + @retval EFI_SUCCESS Driver strength programmed successfully. + @retval Others Failed to set driver strength. +**/ +EFI_STATUS +SdMmcSetDriverStrength ( + IN EFI_PCI_IO_PROTOCOL *PciIo, + IN UINT8 SlotIndex, + IN SD_DRIVER_STRENGTH_TYPE DriverStrength + ) +{ + EFI_STATUS Status; + UINT16 HostCtrl2; + + if (DriverStrength == SdDriverStrengthIgnore) { + return EFI_SUCCESS; + } + + HostCtrl2 = (UINT16)~SD_MMC_HC_CTRL_DRIVER_STRENGTH_MASK; + Status = SdMmcHcAndMmio (PciIo, SlotIndex, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); + if (EFI_ERROR (Status)) { + return Status; + } + + HostCtrl2 = (DriverStrength << 4) & SD_MMC_HC_CTRL_DRIVER_STRENGTH_MASK; + return SdMmcHcOrMmio (PciIo, SlotIndex, SD_MMC_HC_HOST_CTRL2, sizeof (HostCtrl2), &HostCtrl2); +} + /** Turn on/off LED. @@ -1081,9 +1422,10 @@ SdMmcHcLedOnOff ( /** Build ADMA descriptor table for transfer. - Refer to SD Host Controller Simplified spec 3.0 Section 1.13 for details. + Refer to SD Host Controller Simplified spec 4.2 Section 1.13 for details. @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + @param[in] ControllerVer The version of host controller. @retval EFI_SUCCESS The ADMA descriptor table is created successfully. @retval Others The ADMA descriptor table isn't created successfully. @@ -1091,7 +1433,8 @@ SdMmcHcLedOnOff ( **/ EFI_STATUS BuildAdmaDescTable ( - IN SD_MMC_HC_TRB *Trb + IN SD_MMC_HC_TRB *Trb, + IN UINT16 ControllerVer ) { EFI_PHYSICAL_ADDRESS Data; @@ -1099,49 +1442,84 @@ BuildAdmaDescTable ( UINT64 Entries; UINT32 Index; UINT64 Remaining; - UINT32 Address; + UINT64 Address; UINTN TableSize; EFI_PCI_IO_PROTOCOL *PciIo; EFI_STATUS Status; UINTN Bytes; + UINT32 AdmaMaxDataPerLine; + UINT32 DescSize; + VOID *AdmaDesc; + + AdmaMaxDataPerLine = ADMA_MAX_DATA_PER_LINE_16B; + DescSize = sizeof (SD_MMC_HC_ADMA_32_DESC_LINE); + AdmaDesc = NULL; Data = Trb->DataPhy; DataLen = Trb->DataLen; PciIo = Trb->Private->PciIo; + // - // Only support 32bit ADMA Descriptor Table + // Check for valid ranges in 32bit ADMA Descriptor Table // - if ((Data >= 0x100000000ul) || ((Data + DataLen) > 0x100000000ul)) { + if ((Trb->Mode == SdMmcAdma32bMode) && + ((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. + // Check address field alignment + // + if (Trb->Mode != SdMmcAdma32bMode) { + // + // Address field shall be set on 64-bit boundary (Lower 3-bit is always set to 0) + // + if ((Data & (BIT0 | BIT1 | BIT2)) != 0) { + DEBUG ((DEBUG_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 8 bytes boundary!\n", Data)); + } + } else { + // + // Address field shall be set on 32-bit boundary (Lower 2-bit is always set to 0) + // + 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)); + } + } + + // + // Configure 64b ADMA. + // + if (Trb->Mode == SdMmcAdma64bV3Mode) { + DescSize = sizeof (SD_MMC_HC_ADMA_64_V3_DESC_LINE); + }else if (Trb->Mode == SdMmcAdma64bV4Mode) { + DescSize = sizeof (SD_MMC_HC_ADMA_64_V4_DESC_LINE); + } + // + // Configure 26b data length. // - if ((Data & (BIT0 | BIT1)) != 0) { - DEBUG ((EFI_D_INFO, "The buffer [0x%x] to construct ADMA desc is not aligned to 4 bytes boundary!\n", Data)); + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + AdmaMaxDataPerLine = ADMA_MAX_DATA_PER_LINE_26B; } - Entries = DivU64x32 ((DataLen + ADMA_MAX_DATA_PER_LINE - 1), ADMA_MAX_DATA_PER_LINE); - TableSize = (UINTN)MultU64x32 (Entries, sizeof (SD_MMC_HC_ADMA_DESC_LINE)); + Entries = DivU64x32 ((DataLen + AdmaMaxDataPerLine - 1), AdmaMaxDataPerLine); + TableSize = (UINTN)MultU64x32 (Entries, DescSize); Trb->AdmaPages = (UINT32)EFI_SIZE_TO_PAGES (TableSize); Status = PciIo->AllocateBuffer ( PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (TableSize), - (VOID **)&Trb->AdmaDesc, + (VOID **)&AdmaDesc, 0 ); if (EFI_ERROR (Status)) { return EFI_OUT_OF_RESOURCES; } - ZeroMem (Trb->AdmaDesc, TableSize); + ZeroMem (AdmaDesc, TableSize); Bytes = TableSize; Status = PciIo->Map ( PciIo, EfiPciIoOperationBusMasterCommonBuffer, - Trb->AdmaDesc, + AdmaDesc, &Bytes, &Trb->AdmaDescPhy, &Trb->AdmaMap @@ -1154,12 +1532,13 @@ BuildAdmaDescTable ( PciIo->FreeBuffer ( PciIo, EFI_SIZE_TO_PAGES (TableSize), - Trb->AdmaDesc + AdmaDesc ); return EFI_OUT_OF_RESOURCES; } - if ((UINT64)(UINTN)Trb->AdmaDescPhy > 0x100000000ul) { + if ((Trb->Mode == SdMmcAdma32bMode) && + (UINT64)(UINTN)Trb->AdmaDescPhy > 0x100000000ul) { // // The ADMA doesn't support 64bit addressing. // @@ -1167,38 +1546,104 @@ BuildAdmaDescTable ( PciIo, Trb->AdmaMap ); + Trb->AdmaMap = NULL; + PciIo->FreeBuffer ( PciIo, EFI_SIZE_TO_PAGES (TableSize), - Trb->AdmaDesc + AdmaDesc ); return EFI_DEVICE_ERROR; } Remaining = DataLen; - Address = (UINT32)Data; + Address = Data; + if (Trb->Mode == SdMmcAdma32bMode) { + Trb->Adma32Desc = AdmaDesc; + } else if (Trb->Mode == SdMmcAdma64bV3Mode) { + Trb->Adma64V3Desc = AdmaDesc; + } else { + Trb->Adma64V4Desc = AdmaDesc; + } + 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; + if (Trb->Mode == SdMmcAdma32bMode) { + if (Remaining <= AdmaMaxDataPerLine) { + Trb->Adma32Desc[Index].Valid = 1; + Trb->Adma32Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma32Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); + } + Trb->Adma32Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); + Trb->Adma32Desc[Index].Address = (UINT32)Address; + break; + } else { + Trb->Adma32Desc[Index].Valid = 1; + Trb->Adma32Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma32Desc[Index].UpperLength = 0; + } + Trb->Adma32Desc[Index].LowerLength = 0; + Trb->Adma32Desc[Index].Address = (UINT32)Address; + } + } else if (Trb->Mode == SdMmcAdma64bV3Mode) { + if (Remaining <= AdmaMaxDataPerLine) { + Trb->Adma64V3Desc[Index].Valid = 1; + Trb->Adma64V3Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma64V3Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); + } + Trb->Adma64V3Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); + Trb->Adma64V3Desc[Index].LowerAddress = (UINT32)Address; + Trb->Adma64V3Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); + break; + } else { + Trb->Adma64V3Desc[Index].Valid = 1; + Trb->Adma64V3Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma64V3Desc[Index].UpperLength = 0; + } + Trb->Adma64V3Desc[Index].LowerLength = 0; + Trb->Adma64V3Desc[Index].LowerAddress = (UINT32)Address; + Trb->Adma64V3Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); + } } else { - Trb->AdmaDesc[Index].Valid = 1; - Trb->AdmaDesc[Index].Act = 2; - Trb->AdmaDesc[Index].Length = 0; - Trb->AdmaDesc[Index].Address = Address; + if (Remaining <= AdmaMaxDataPerLine) { + Trb->Adma64V4Desc[Index].Valid = 1; + Trb->Adma64V4Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma64V4Desc[Index].UpperLength = (UINT16)RShiftU64 (Remaining, 16); + } + Trb->Adma64V4Desc[Index].LowerLength = (UINT16)(Remaining & MAX_UINT16); + Trb->Adma64V4Desc[Index].LowerAddress = (UINT32)Address; + Trb->Adma64V4Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); + break; + } else { + Trb->Adma64V4Desc[Index].Valid = 1; + Trb->Adma64V4Desc[Index].Act = 2; + if (Trb->AdmaLengthMode == SdMmcAdmaLen26b) { + Trb->Adma64V4Desc[Index].UpperLength = 0; + } + Trb->Adma64V4Desc[Index].LowerLength = 0; + Trb->Adma64V4Desc[Index].LowerAddress = (UINT32)Address; + Trb->Adma64V4Desc[Index].UpperAddress = (UINT32)RShiftU64 (Address, 32); + } } - Remaining -= ADMA_MAX_DATA_PER_LINE; - Address += ADMA_MAX_DATA_PER_LINE; + Remaining -= AdmaMaxDataPerLine; + Address += AdmaMaxDataPerLine; } // // Set the last descriptor line as end of descriptor table // - Trb->AdmaDesc[Index].End = 1; + if (Trb->Mode == SdMmcAdma32bMode) { + Trb->Adma32Desc[Index].End = 1; + } else if (Trb->Mode == SdMmcAdma64bV3Mode) { + Trb->Adma64V3Desc[Index].End = 1; + } else { + Trb->Adma64V4Desc[Index].End = 1; + } return EFI_SUCCESS; } @@ -1242,6 +1687,7 @@ SdMmcCreateTrb ( Trb->Event = Event; Trb->Started = FALSE; Trb->Timeout = Packet->Timeout; + Trb->Retries = SD_MMC_TRB_RETRIES; Trb->Private = Private; if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { @@ -1259,52 +1705,69 @@ SdMmcCreateTrb ( goto Error; } - if (Trb->Read) { - Flag = EfiPciIoOperationBusMasterWrite; - } else { - Flag = EfiPciIoOperationBusMasterRead; + if ((Trb->DataLen != 0) && (Trb->DataLen < Trb->BlockSize)) { + Trb->BlockSize = (UINT16)Trb->DataLen; } - PciIo = Private->PciIo; - if (Trb->DataLen != 0) { - MapLength = Trb->DataLen; - Status = PciIo->Map ( - PciIo, - Flag, - Trb->Data, - &MapLength, - &Trb->DataPhy, - &Trb->DataMap - ); - if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { - Status = EFI_BAD_BUFFER_SIZE; - goto Error; + if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || + ((Private->Slot[Trb->Slot].CardType == SdCardType) && + (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { + Trb->Mode = SdMmcPioMode; + } else { + if (Trb->Read) { + Flag = EfiPciIoOperationBusMasterWrite; + } else { + Flag = EfiPciIoOperationBusMasterRead; } - } - if ((Trb->DataLen % Trb->BlockSize) != 0) { - if (Trb->DataLen < Trb->BlockSize) { - Trb->BlockSize = (UINT16)Trb->DataLen; + PciIo = Private->PciIo; + if (Trb->DataLen != 0) { + MapLength = Trb->DataLen; + Status = PciIo->Map ( + PciIo, + Flag, + Trb->Data, + &MapLength, + &Trb->DataPhy, + &Trb->DataMap + ); + if (EFI_ERROR (Status) || (Trb->DataLen != MapLength)) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } } - } - if (Trb->DataLen == 0) { - Trb->Mode = SdMmcNoData; - } else if (Private->Capability[Slot].Adma2 != 0) { - Trb->Mode = SdMmcAdmaMode; - Status = BuildAdmaDescTable (Trb); - if (EFI_ERROR (Status)) { - PciIo->Unmap (PciIo, Trb->DataMap); - goto Error; + if (Trb->DataLen == 0) { + Trb->Mode = SdMmcNoData; + } else if (Private->Capability[Slot].Adma2 != 0) { + Trb->Mode = SdMmcAdma32bMode; + Trb->AdmaLengthMode = SdMmcAdmaLen16b; + if ((Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_300) && + (Private->Capability[Slot].SysBus64V3 == 1)) { + Trb->Mode = SdMmcAdma64bV3Mode; + } else if (((Private->ControllerVersion[Slot] == SD_MMC_HC_CTRL_VER_400) && + (Private->Capability[Slot].SysBus64V3 == 1)) || + ((Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410) && + (Private->Capability[Slot].SysBus64V4 == 1))) { + Trb->Mode = SdMmcAdma64bV4Mode; + } + if (Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410) { + Trb->AdmaLengthMode = SdMmcAdmaLen26b; + } + Status = BuildAdmaDescTable (Trb, Private->ControllerVersion[Slot]); + if (EFI_ERROR (Status)) { + goto Error; + } + } else if (Private->Capability[Slot].Sdma != 0) { + Trb->Mode = SdMmcSdmaMode; + } else { + Trb->Mode = SdMmcPioMode; } - } else if (Private->Capability[Slot].Sdma != 0) { - Trb->Mode = SdMmcSdmaMode; - } else { - Trb->Mode = SdMmcPioMode; } if (Event != NULL) { - OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); InsertTailList (&Private->Queue, &Trb->TrbList); gBS->RestoreTPL (OldTpl); } @@ -1337,11 +1800,25 @@ SdMmcFreeTrb ( Trb->AdmaMap ); } - if (Trb->AdmaDesc != NULL) { + if (Trb->Adma32Desc != NULL) { + PciIo->FreeBuffer ( + PciIo, + Trb->AdmaPages, + Trb->Adma32Desc + ); + } + if (Trb->Adma64V3Desc != NULL) { PciIo->FreeBuffer ( PciIo, Trb->AdmaPages, - Trb->AdmaDesc + Trb->Adma64V3Desc + ); + } + if (Trb->Adma64V4Desc != NULL) { + PciIo->FreeBuffer ( + PciIo, + Trb->AdmaPages, + Trb->Adma64V4Desc ); } if (Trb->DataMap != NULL) { @@ -1386,15 +1863,6 @@ SdMmcCheckTrbEnv ( // the Present State register to be 0 // PresentState = BIT0 | BIT1; - // - // For Send Tuning Block cmd, just wait for Command Inhibit (CMD) to be 0 - // - if (((Private->Slot[Trb->Slot].CardType == EmmcCardType) && - (Packet->SdMmcCmdBlk->CommandIndex == EMMC_SEND_TUNING_BLOCK)) || - ((Private->Slot[Trb->Slot].CardType == SdCardType) && - (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { - PresentState = BIT0; - } } else { // // Wait Command Inhibit (CMD) in the Present State register @@ -1490,12 +1958,15 @@ SdMmcExecTrb ( UINT16 Cmd; UINT16 IntStatus; UINT32 Argument; - UINT16 BlkCount; + UINT32 BlkCount; UINT16 BlkSize; UINT16 TransMode; UINT8 HostCtrl1; - UINT32 SdmaAddr; + UINT64 SdmaAddr; UINT64 AdmaAddr; + BOOLEAN AddressingMode64; + + AddressingMode64 = FALSE; Packet = Trb->Packet; PciIo = Trb->Private->PciIo; @@ -1515,30 +1986,55 @@ SdMmcExecTrb ( if (EFI_ERROR (Status)) { return Status; } + + if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { + Status = SdMmcHcCheckMmioSet(PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL2, sizeof(UINT16), + SD_MMC_HC_64_ADDR_EN, SD_MMC_HC_64_ADDR_EN); + if (!EFI_ERROR (Status)) { + AddressingMode64 = TRUE; + } + } + // // Set Host Control 1 register DMA Select field // - if (Trb->Mode == SdMmcAdmaMode) { + if ((Trb->Mode == SdMmcAdma32bMode) || + (Trb->Mode == SdMmcAdma64bV4Mode)) { HostCtrl1 = BIT4; Status = SdMmcHcOrMmio (PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); if (EFI_ERROR (Status)) { return Status; } + } else if (Trb->Mode == SdMmcAdma64bV3Mode) { + HostCtrl1 = BIT4|BIT3; + Status = SdMmcHcOrMmio (PciIo, Trb->Slot, SD_MMC_HC_HOST_CTRL1, sizeof (HostCtrl1), &HostCtrl1); + if (EFI_ERROR (Status)) { + return Status; + } } SdMmcHcLedOnOff (PciIo, Trb->Slot, TRUE); if (Trb->Mode == SdMmcSdmaMode) { - if ((UINT64)(UINTN)Trb->DataPhy >= 0x100000000ul) { + if ((!AddressingMode64) && + ((UINT64)(UINTN)Trb->DataPhy >= 0x100000000ul)) { return EFI_INVALID_PARAMETER; } - SdmaAddr = (UINT32)(UINTN)Trb->DataPhy; - Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (SdmaAddr), &SdmaAddr); + SdmaAddr = (UINT64)(UINTN)Trb->DataPhy; + + if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (UINT64), &SdmaAddr); + } else { + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (UINT32), &SdmaAddr); + } + if (EFI_ERROR (Status)) { return Status; } - } else if (Trb->Mode == SdMmcAdmaMode) { + } else if ((Trb->Mode == SdMmcAdma32bMode) || + (Trb->Mode == SdMmcAdma64bV3Mode) || + (Trb->Mode == SdMmcAdma64bV4Mode)) { AdmaAddr = (UINT64)(UINTN)Trb->AdmaDescPhy; Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_ADMA_SYS_ADDR, FALSE, sizeof (AdmaAddr), &AdmaAddr); if (EFI_ERROR (Status)) { @@ -1559,8 +2055,18 @@ SdMmcExecTrb ( return Status; } - BlkCount = (UINT16)(Trb->DataLen / Trb->BlockSize); - Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_COUNT, FALSE, sizeof (BlkCount), &BlkCount); + BlkCount = 0; + if (Trb->Mode != SdMmcNoData) { + // + // Calcuate Block Count. + // + BlkCount = (Trb->DataLen / Trb->BlockSize); + } + if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_410) { + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, FALSE, sizeof (UINT32), &BlkCount); + } else { + Status = SdMmcHcRwMmio (PciIo, Trb->Slot, SD_MMC_HC_BLK_COUNT, FALSE, sizeof (UINT16), &BlkCount); + } if (EFI_ERROR (Status)) { return Status; } @@ -1579,7 +2085,7 @@ SdMmcExecTrb ( if (Trb->Read) { TransMode |= BIT4; } - if (BlkCount != 0) { + if (BlkCount > 1) { TransMode |= BIT5 | BIT1; } // @@ -1635,6 +2141,137 @@ SdMmcExecTrb ( return Status; } +/** + Performs SW reset based on passed error status mask. + + @param[in] Private Pointer to driver private data. + @param[in] Slot Index of the slot to reset. + @param[in] ErrIntStatus Error interrupt status mask. + + @retval EFI_SUCCESS Software reset performed successfully. + @retval Other Software reset failed. +**/ +EFI_STATUS +SdMmcSoftwareReset ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN UINT16 ErrIntStatus + ) +{ + UINT8 SwReset; + EFI_STATUS Status; + + SwReset = 0; + if ((ErrIntStatus & 0x0F) != 0) { + SwReset |= BIT1; + } + if ((ErrIntStatus & 0x70) != 0) { + SwReset |= BIT2; + } + + Status = SdMmcHcRwMmio ( + Private->PciIo, + Slot, + SD_MMC_HC_SW_RST, + FALSE, + sizeof (SwReset), + &SwReset + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SdMmcHcWaitMmioSet ( + Private->PciIo, + Slot, + SD_MMC_HC_SW_RST, + sizeof (SwReset), + 0xFF, + 0, + SD_MMC_HC_GENERIC_TIMEOUT + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Checks the error status in error status register + and issues appropriate software reset as described in + SD specification section 3.10. + + @param[in] Private Pointer to driver private data. + @param[in] Slot Index of the slot for device. + @param[in] IntStatus Normal interrupt status mask. + + @retval EFI_CRC_ERROR CRC error happened during CMD execution. + @retval EFI_SUCCESS No error reported. + @retval Others Some other error happened. + +**/ +EFI_STATUS +SdMmcCheckAndRecoverErrors ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN UINT16 IntStatus + ) +{ + UINT16 ErrIntStatus; + EFI_STATUS Status; + EFI_STATUS ErrorStatus; + + if ((IntStatus & BIT15) == 0) { + return EFI_SUCCESS; + } + + Status = SdMmcHcRwMmio ( + Private->PciIo, + Slot, + SD_MMC_HC_ERR_INT_STS, + TRUE, + sizeof (ErrIntStatus), + &ErrIntStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If the data timeout error is reported + // but data transfer is signaled as completed we + // have to ignore data timeout. We also assume that no + // other error is present on the link since data transfer + // completed successfully. Error interrupt status + // register is going to be reset when the next command + // is started. + // + if (((ErrIntStatus & BIT4) != 0) && ((IntStatus & BIT1) != 0)) { + return EFI_SUCCESS; + } + + // + // We treat both CMD and DAT CRC errors and + // end bits errors as EFI_CRC_ERROR. This will + // let higher layer know that the error possibly + // happened due to random bus condition and the + // command can be retried. + // + if ((ErrIntStatus & (BIT1 | BIT2 | BIT5 | BIT6)) != 0) { + ErrorStatus = EFI_CRC_ERROR; + } else { + ErrorStatus = EFI_DEVICE_ERROR; + } + + Status = SdMmcSoftwareReset (Private, Slot, ErrIntStatus); + if (EFI_ERROR (Status)) { + return Status; + } + + return ErrorStatus; +} + /** Check the TRB execution result. @@ -1656,11 +2293,10 @@ SdMmcCheckTrbResult ( EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; UINT16 IntStatus; UINT32 Response[4]; - UINT32 SdmaAddr; + UINT64 SdmaAddr; UINT8 Index; - UINT8 SwReset; + UINT32 PioLength; - SwReset = 0; Packet = Trb->Packet; // // Check Trb execution result by reading Normal Interrupt Status register. @@ -1676,87 +2312,23 @@ SdMmcCheckTrbResult ( if (EFI_ERROR (Status)) { goto Done; } + // - // Check Transfer Complete bit is set or not. + // Check if there are any errors reported by host controller + // and if neccessary recover the controller before next command is executed. // - 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 = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_ERR_INT_STS, - TRUE, - sizeof (IntStatus), - &IntStatus - ); - if (!EFI_ERROR (Status)) { - if ((IntStatus & BIT4) == BIT4) { - Status = EFI_SUCCESS; - } else { - Status = EFI_DEVICE_ERROR; - } - } - } - + Status = SdMmcCheckAndRecoverErrors (Private, Trb->Slot, IntStatus); + if (EFI_ERROR (Status)) { 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. + // Check Transfer Complete bit is set or not. // - if ((IntStatus & BIT15) == BIT15) { - Status = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_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 = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_SW_RST, - FALSE, - sizeof (SwReset), - &SwReset - ); - if (EFI_ERROR (Status)) { - goto Done; - } - Status = SdMmcHcWaitMmioSet ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_SW_RST, - sizeof (SwReset), - 0xFF, - 0, - SD_MMC_HC_GENERIC_TIMEOUT - ); - if (EFI_ERROR (Status)) { - goto Done; - } - - Status = EFI_DEVICE_ERROR; + if ((IntStatus & BIT1) == BIT1) { goto Done; } + // // Check if DMA interrupt is signalled for the SDMA transfer. // @@ -1779,8 +2351,19 @@ SdMmcCheckTrbResult ( // // Update SDMA Address register. // - SdmaAddr = SD_MMC_SDMA_ROUND_UP ((UINT32)(UINTN)Trb->DataPhy, SD_MMC_SDMA_BOUNDARY); - Status = SdMmcHcRwMmio ( + SdmaAddr = SD_MMC_SDMA_ROUND_UP ((UINTN)Trb->DataPhy, SD_MMC_SDMA_BOUNDARY); + + if (Private->ControllerVersion[Trb->Slot] >= SD_MMC_HC_CTRL_VER_400) { + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_ADMA_SYS_ADDR, + FALSE, + sizeof (UINT64), + &SdmaAddr + ); + } else { + Status = SdMmcHcRwMmio ( Private->PciIo, Trb->Slot, SD_MMC_HC_SDMA_ADDR, @@ -1788,10 +2371,12 @@ SdMmcCheckTrbResult ( sizeof (UINT32), &SdmaAddr ); + } + if (EFI_ERROR (Status)) { goto Done; } - Trb->DataPhy = (UINT32)(UINTN)SdmaAddr; + Trb->DataPhy = (UINT64)(UINTN)SdmaAddr; } if ((Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeAdtc) && @@ -1808,12 +2393,25 @@ SdMmcCheckTrbResult ( ((Private->Slot[Trb->Slot].CardType == SdCardType) && (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { // - // While performing tuning procedure (Execute Tuning is set to 1), - // Transfer Completeis not set to 1 - // Refer to SD Host Controller Simplified Specification 3.0 table 2-23 for details. + // 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. // - Status = EFI_SUCCESS; - goto Done; + if ((IntStatus & BIT5) == BIT5) { + // + // Clear Buffer Read Ready interrupt at first. + // + IntStatus = BIT5; + SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (IntStatus), &IntStatus); + // + // Read data out from Buffer Port register + // + for (PioLength = 0; PioLength < Trb->DataLen; PioLength += 4) { + SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_BUF_DAT_PORT, TRUE, 4, (UINT8*)Trb->Data + PioLength); + } + Status = EFI_SUCCESS; + goto Done; + } } Status = EFI_NOT_READY;