X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FBus%2FPci%2FSdMmcPciHcDxe%2FSdMmcPciHci.c;h=6548ef71dee1d7797bde8bcd06744e92f77d9918;hp=5d1f977e55d296e82aae112090a8953ff114162a;hb=9bfaa3da1ee553ee40f584e47aa6418d7d83460d;hpb=9d510e61fceee7b92955ef9a3c20343752d8ce3f diff --git a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c index 5d1f977e55..6548ef71de 100644 --- a/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c +++ b/MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHci.c @@ -7,7 +7,7 @@ It would expose EFI_SD_MMC_PASS_THRU_PROTOCOL for upper layer use. Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. - Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.
+ Copyright (c) 2015 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -758,16 +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] BaseClkFreq The base clock frequency of host controller in MHz. - @param[in] ControllerVer The version of host controller. + @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. @@ -775,11 +799,11 @@ SdMmcHcStopClock ( **/ EFI_STATUS SdMmcHcClockSupply ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT8 Slot, - IN UINT64 ClockFreq, - IN UINT32 BaseClkFreq, - IN UINT16 ControllerVer + 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; @@ -787,13 +811,15 @@ SdMmcHcClockSupply ( UINT32 Divisor; UINT32 Remainder; UINT16 ClockCtrl; + UINT32 BaseClkFreq; + UINT16 ControllerVer; + EFI_PCI_IO_PROTOCOL *PciIo; - // - // Calculate a divisor for SD clock frequency - // - ASSERT (BaseClkFreq != 0); + PciIo = Private->PciIo; + BaseClkFreq = Private->BaseClkFreq[Slot]; + ControllerVer = Private->ControllerVersion[Slot]; - if (ClockFreq == 0) { + if (BaseClkFreq == 0 || ClockFreq == 0) { return EFI_INVALID_PARAMETER; } @@ -877,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; } @@ -1038,49 +1088,6 @@ SdMmcHcInitV4Enhancements ( return EFI_SUCCESS; } -/** - Supply SD/MMC card with lowest clock frequency 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] BaseClkFreq The base clock frequency of host controller in MHz. - @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 ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT8 Slot, - IN UINT32 BaseClkFreq, - IN UINT16 ControllerVer - ) -{ - EFI_STATUS Status; - UINT32 InitFreq; - - // - // According to SDHCI specification ver. 4.2, BaseClkFreq field value of - // the Capability Register 1 can be zero, which means a need for obtaining - // the clock frequency via another method. Fail in case it is not updated - // by SW at this point. - // - if (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 = SdMmcHcClockSupply (PciIo, Slot, InitFreq, BaseClkFreq, ControllerVer); - return Status; -} - /** Supply SD/MMC card with maximum voltage at initialization. @@ -1216,7 +1223,14 @@ SdMmcHcInitHost ( return Status; } - Status = SdMmcHcInitClockFreq (PciIo, Slot, Private->BaseClkFreq[Slot], Private->ControllerVersion[Slot]); + // + // 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; } @@ -1339,6 +1353,40 @@ SdMmcHcUhsSignaling ( 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. @@ -1498,6 +1546,8 @@ BuildAdmaDescTable ( PciIo, Trb->AdmaMap ); + Trb->AdmaMap = NULL; + PciIo->FreeBuffer ( PciIo, EFI_SIZE_TO_PAGES (TableSize), @@ -1597,6 +1647,141 @@ BuildAdmaDescTable ( return EFI_SUCCESS; } +/** + Prints the contents of the command packet to the debug port. + + @param[in] DebugLevel Debug level at which the packet should be printed. + @param[in] Packet Pointer to packet to print. +**/ +VOID +SdMmcPrintPacket ( + IN UINT32 DebugLevel, + IN EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet + ) +{ + if (Packet == NULL) { + return; + } + + DEBUG ((DebugLevel, "Printing EFI_SD_MMC_PASS_THRU_COMMAND_PACKET\n")); + if (Packet->SdMmcCmdBlk != NULL) { + DEBUG ((DebugLevel, "Command index: %d, argument: %X\n", Packet->SdMmcCmdBlk->CommandIndex, Packet->SdMmcCmdBlk->CommandArgument)); + DEBUG ((DebugLevel, "Command type: %d, response type: %d\n", Packet->SdMmcCmdBlk->CommandType, Packet->SdMmcCmdBlk->ResponseType)); + } + if (Packet->SdMmcStatusBlk != NULL) { + DEBUG ((DebugLevel, "Response 0: %X, 1: %X, 2: %X, 3: %X\n", + Packet->SdMmcStatusBlk->Resp0, + Packet->SdMmcStatusBlk->Resp1, + Packet->SdMmcStatusBlk->Resp2, + Packet->SdMmcStatusBlk->Resp3 + )); + } + DEBUG ((DebugLevel, "Timeout: %ld\n", Packet->Timeout)); + DEBUG ((DebugLevel, "InDataBuffer: %p\n", Packet->InDataBuffer)); + DEBUG ((DebugLevel, "OutDataBuffer: %p\n", Packet->OutDataBuffer)); + DEBUG ((DebugLevel, "InTransferLength: %d\n", Packet->InTransferLength)); + DEBUG ((DebugLevel, "OutTransferLength: %d\n", Packet->OutTransferLength)); + DEBUG ((DebugLevel, "TransactionStatus: %r\n", Packet->TransactionStatus)); +} + +/** + Prints the contents of the TRB to the debug port. + + @param[in] DebugLevel Debug level at which the TRB should be printed. + @param[in] Trb Pointer to the TRB structure. +**/ +VOID +SdMmcPrintTrb ( + IN UINT32 DebugLevel, + IN SD_MMC_HC_TRB *Trb + ) +{ + if (Trb == NULL) { + return; + } + + DEBUG ((DebugLevel, "Printing SD_MMC_HC_TRB\n")); + DEBUG ((DebugLevel, "Slot: %d\n", Trb->Slot)); + DEBUG ((DebugLevel, "BlockSize: %d\n", Trb->BlockSize)); + DEBUG ((DebugLevel, "Data: %p\n", Trb->Data)); + DEBUG ((DebugLevel, "DataLen: %d\n", Trb->DataLen)); + DEBUG ((DebugLevel, "Read: %d\n", Trb->Read)); + DEBUG ((DebugLevel, "DataPhy: %lX\n", Trb->DataPhy)); + DEBUG ((DebugLevel, "DataMap: %p\n", Trb->DataMap)); + DEBUG ((DebugLevel, "Mode: %d\n", Trb->Mode)); + DEBUG ((DebugLevel, "AdmaLengthMode: %d\n", Trb->AdmaLengthMode)); + DEBUG ((DebugLevel, "Event: %p\n", Trb->Event)); + DEBUG ((DebugLevel, "Started: %d\n", Trb->Started)); + DEBUG ((DebugLevel, "CommandComplete: %d\n", Trb->CommandComplete)); + DEBUG ((DebugLevel, "Timeout: %ld\n", Trb->Timeout)); + DEBUG ((DebugLevel, "Retries: %d\n", Trb->Retries)); + DEBUG ((DebugLevel, "PioModeTransferCompleted: %d\n", Trb->PioModeTransferCompleted)); + DEBUG ((DebugLevel, "PioBlockIndex: %d\n", Trb->PioBlockIndex)); + DEBUG ((DebugLevel, "Adma32Desc: %p\n", Trb->Adma32Desc)); + DEBUG ((DebugLevel, "Adma64V3Desc: %p\n", Trb->Adma64V3Desc)); + DEBUG ((DebugLevel, "Adma64V4Desc: %p\n", Trb->Adma64V4Desc)); + DEBUG ((DebugLevel, "AdmaMap: %p\n", Trb->AdmaMap)); + DEBUG ((DebugLevel, "AdmaPages: %X\n", Trb->AdmaPages)); + + SdMmcPrintPacket (DebugLevel, Trb->Packet); +} + +/** + Sets up host memory to allow DMA transfer. + + @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] Packet A pointer to the SD command data structure. + + @retval EFI_SUCCESS Memory has been mapped for DMA transfer. + @retval Others Memory has not been mapped. +**/ +EFI_STATUS +SdMmcSetupMemoryForDmaTransfer ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_PCI_IO_PROTOCOL_OPERATION Flag; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN MapLength; + EFI_STATUS Status; + + if (Trb->Read) { + Flag = EfiPciIoOperationBusMasterWrite; + } else { + Flag = EfiPciIoOperationBusMasterRead; + } + + PciIo = Private->PciIo; + if (Trb->Data != NULL && 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)) { + return EFI_BAD_BUFFER_SIZE; + } + } + + if (Trb->Mode == SdMmcAdma32bMode || + Trb->Mode == SdMmcAdma64bV3Mode || + Trb->Mode == SdMmcAdma64bV4Mode) { + Status = BuildAdmaDescTable (Trb, Private->ControllerVersion[Slot]); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + /** Create a new TRB for the SD/MMC cmd request. @@ -1621,9 +1806,6 @@ SdMmcCreateTrb ( SD_MMC_HC_TRB *Trb; EFI_STATUS Status; EFI_TPL OldTpl; - EFI_PCI_IO_PROTOCOL_OPERATION Flag; - EFI_PCI_IO_PROTOCOL *PciIo; - UINTN MapLength; Trb = AllocateZeroPool (sizeof (SD_MMC_HC_TRB)); if (Trb == NULL) { @@ -1636,7 +1818,11 @@ SdMmcCreateTrb ( Trb->Packet = Packet; Trb->Event = Event; Trb->Started = FALSE; + Trb->CommandComplete = FALSE; Trb->Timeout = Packet->Timeout; + Trb->Retries = SD_MMC_TRB_RETRIES; + Trb->PioModeTransferCompleted = FALSE; + Trb->PioBlockIndex = 0; Trb->Private = Private; if ((Packet->InTransferLength != 0) && (Packet->InDataBuffer != NULL)) { @@ -1664,29 +1850,6 @@ SdMmcCreateTrb ( (Packet->SdMmcCmdBlk->CommandIndex == SD_SEND_TUNING_BLOCK))) { Trb->Mode = SdMmcPioMode; } else { - if (Trb->Read) { - Flag = EfiPciIoOperationBusMasterWrite; - } else { - Flag = EfiPciIoOperationBusMasterRead; - } - - 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) { @@ -1704,13 +1867,16 @@ SdMmcCreateTrb ( if (Private->ControllerVersion[Slot] >= SD_MMC_HC_CTRL_VER_410) { Trb->AdmaLengthMode = SdMmcAdmaLen26b; } - Status = BuildAdmaDescTable (Trb, Private->ControllerVersion[Slot]); + Status = SdMmcSetupMemoryForDmaTransfer (Private, Slot, Trb); if (EFI_ERROR (Status)) { - PciIo->Unmap (PciIo, Trb->DataMap); goto Error; } } else if (Private->Capability[Slot].Sdma != 0) { Trb->Mode = SdMmcSdmaMode; + Status = SdMmcSetupMemoryForDmaTransfer (Private, Slot, Trb); + if (EFI_ERROR (Status)) { + goto Error; + } } else { Trb->Mode = SdMmcPioMode; } @@ -2092,240 +2258,528 @@ SdMmcExecTrb ( } /** - Check the TRB execution result. - - @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. - @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + Performs SW reset based on passed error status mask. - @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. + @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 -SdMmcCheckTrbResult ( - IN SD_MMC_HC_PRIVATE_DATA *Private, - IN SD_MMC_HC_TRB *Trb +SdMmcSoftwareReset ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN UINT8 Slot, + IN UINT16 ErrIntStatus ) { - EFI_STATUS Status; - EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; - UINT16 IntStatus; - UINT32 Response[4]; - UINT64 SdmaAddr; - UINT8 Index; - UINT8 SwReset; - UINT32 PioLength; + UINT8 SwReset; + EFI_STATUS Status; SwReset = 0; - Packet = Trb->Packet; - // - // Check Trb execution result by reading Normal Interrupt Status register. - // + 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, - Trb->Slot, - SD_MMC_HC_NOR_INT_STS, + Slot, + SD_MMC_HC_ERR_INT_STS, TRUE, - sizeof (IntStatus), - &IntStatus + sizeof (ErrIntStatus), + &ErrIntStatus ); if (EFI_ERROR (Status)) { - goto Done; + return Status; } + + DEBUG ((DEBUG_ERROR, "Error reported by SDHCI\n")); + DEBUG ((DEBUG_ERROR, "Interrupt status = %X\n", IntStatus)); + DEBUG ((DEBUG_ERROR, "Error interrupt status = %X\n", ErrIntStatus)); + // - // Check Transfer Complete bit is set or not. + // 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 ((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; - } - } - } - - goto Done; + if (((ErrIntStatus & BIT4) != 0) && ((IntStatus & BIT1) != 0)) { + return EFI_SUCCESS; } + // - // 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. + // 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 ((IntStatus & BIT15) == BIT15) { + 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; +} + +/** + Reads the response data into the TRB buffer. + This function assumes that caller made sure that + command has completed. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS Response read successfully. + @retval Others Failed to get response. +**/ +EFI_STATUS +SdMmcGetResponse ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT8 Index; + UINT32 Response[4]; + EFI_STATUS Status; + + Packet = Trb->Packet; + + if (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeBc) { + return EFI_SUCCESS; + } + + for (Index = 0; Index < 4; Index++) { Status = SdMmcHcRwMmio ( Private->PciIo, Trb->Slot, - SD_MMC_HC_ERR_INT_STS, + SD_MMC_HC_RESPONSE + Index * 4, TRUE, - sizeof (IntStatus), - &IntStatus + sizeof (UINT32), + &Response[Index] + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + CopyMem (Packet->SdMmcStatusBlk, Response, sizeof (Response)); + + return EFI_SUCCESS; +} + +/** + Checks if the command completed. If the command + completed it gets the response and records the + command completion in the TRB. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + @param[in] IntStatus Snapshot of the normal interrupt status register. + + @retval EFI_SUCCESS Command completed successfully. + @retval EFI_NOT_READY Command completion still pending. + @retval Others Command failed to complete. +**/ +EFI_STATUS +SdMmcCheckCommandComplete ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb, + IN UINT16 IntStatus + ) +{ + UINT16 Data16; + EFI_STATUS Status; + + if ((IntStatus & BIT0) != 0) { + Data16 = BIT0; + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + FALSE, + sizeof (Data16), + &Data16 ); if (EFI_ERROR (Status)) { - goto Done; + return Status; } - if ((IntStatus & 0x0F) != 0) { - SwReset |= BIT1; + Status = SdMmcGetResponse (Private, Trb); + if (EFI_ERROR (Status)) { + return Status; + } + Trb->CommandComplete = TRUE; + return EFI_SUCCESS; + } + + return EFI_NOT_READY; +} + +/** + Transfers data from card using PIO method. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + @param[in] IntStatus Snapshot of the normal interrupt status register. + + @retval EFI_SUCCESS PIO transfer completed successfully. + @retval EFI_NOT_READY PIO transfer completion still pending. + @retval Others PIO transfer failed to complete. +**/ +EFI_STATUS +SdMmcTransferDataWithPio ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb, + IN UINT16 IntStatus + ) +{ + EFI_STATUS Status; + UINT16 Data16; + UINT32 BlockCount; + EFI_PCI_IO_PROTOCOL_WIDTH Width; + UINTN Count; + + BlockCount = (Trb->DataLen / Trb->BlockSize); + if (Trb->DataLen % Trb->BlockSize != 0) { + BlockCount += 1; + } + + if (Trb->PioBlockIndex >= BlockCount) { + return EFI_SUCCESS; + } + + switch (Trb->BlockSize % sizeof (UINT32)) { + case 0: + Width = EfiPciIoWidthFifoUint32; + Count = Trb->BlockSize / sizeof (UINT32); + break; + case 2: + Width = EfiPciIoWidthFifoUint16; + Count = Trb->BlockSize / sizeof (UINT16); + break; + case 1: + case 3: + default: + Width = EfiPciIoWidthFifoUint8; + Count = Trb->BlockSize; + break; } - if ((IntStatus & 0xF0) != 0) { - SwReset |= BIT2; + + if (Trb->Read) { + if ((IntStatus & BIT5) == 0) { + return EFI_NOT_READY; } + Data16 = BIT5; + SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data16), &Data16); - Status = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_SW_RST, - FALSE, - sizeof (SwReset), - &SwReset - ); + Status = Private->PciIo->Mem.Read ( + Private->PciIo, + Width, + Trb->Slot, + SD_MMC_HC_BUF_DAT_PORT, + Count, + (VOID*)((UINT8*)Trb->Data + (Trb->BlockSize * Trb->PioBlockIndex)) + ); if (EFI_ERROR (Status)) { - goto Done; + return Status; + } + Trb->PioBlockIndex++; + } else { + if ((IntStatus & BIT4) == 0) { + return EFI_NOT_READY; } - Status = SdMmcHcWaitMmioSet ( + Data16 = BIT4; + SdMmcHcRwMmio (Private->PciIo, Trb->Slot, SD_MMC_HC_NOR_INT_STS, FALSE, sizeof (Data16), &Data16); + + Status = Private->PciIo->Mem.Write ( Private->PciIo, + Width, Trb->Slot, - SD_MMC_HC_SW_RST, - sizeof (SwReset), - 0xFF, - 0, - SD_MMC_HC_GENERIC_TIMEOUT + SD_MMC_HC_BUF_DAT_PORT, + Count, + (VOID*)((UINT8*)Trb->Data + (Trb->BlockSize * Trb->PioBlockIndex)) ); if (EFI_ERROR (Status)) { - goto Done; + return Status; } + Trb->PioBlockIndex++; + } - Status = EFI_DEVICE_ERROR; - goto Done; + if (Trb->PioBlockIndex >= BlockCount) { + Trb->PioModeTransferCompleted = TRUE; + return EFI_SUCCESS; + } else { + return EFI_NOT_READY; } - // - // Check if DMA interrupt is signalled for the SDMA transfer. - // - if ((Trb->Mode == SdMmcSdmaMode) && ((IntStatus & BIT3) == BIT3)) { - // - // Clear DMA interrupt bit. - // - IntStatus = BIT3; - Status = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_NOR_INT_STS, - FALSE, - sizeof (IntStatus), - &IntStatus - ); +} + +/** + Update the SDMA address on the SDMA buffer boundary interrupt. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + + @retval EFI_SUCCESS Updated SDMA buffer address. + @retval Others Failed to update SDMA buffer address. +**/ +EFI_STATUS +SdMmcUpdateSdmaAddress ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + UINT64 SdmaAddr; + EFI_STATUS Status; + + 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, + FALSE, + sizeof (UINT32), + &SdmaAddr + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + Trb->DataPhy = (UINT64)(UINTN)SdmaAddr; + return EFI_SUCCESS; +} + +/** + Checks if the data transfer completed and performs any actions + neccessary to continue the data transfer such as SDMA system + address fixup or PIO data transfer. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_TRB instance. + @param[in] IntStatus Snapshot of the normal interrupt status register. + + @retval EFI_SUCCESS Data transfer completed successfully. + @retval EFI_NOT_READY Data transfer completion still pending. + @retval Others Data transfer failed to complete. +**/ +EFI_STATUS +SdMmcCheckDataTransfer ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb, + IN UINT16 IntStatus + ) +{ + UINT16 Data16; + EFI_STATUS Status; + + if ((IntStatus & BIT1) != 0) { + Data16 = BIT1; + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + FALSE, + sizeof (Data16), + &Data16 + ); + return Status; + } + + if (Trb->Mode == SdMmcPioMode && !Trb->PioModeTransferCompleted) { + Status = SdMmcTransferDataWithPio (Private, Trb, IntStatus); if (EFI_ERROR (Status)) { - goto Done; + return Status; } - // - // Update SDMA Address register. - // - 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, - FALSE, - sizeof (UINT32), - &SdmaAddr - ); + if ((Trb->Mode == SdMmcSdmaMode) && ((IntStatus & BIT3) != 0)) { + Data16 = BIT3; + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + FALSE, + sizeof (Data16), + &Data16 + ); + if (EFI_ERROR (Status)) { + return Status; } - + Status = SdMmcUpdateSdmaAddress (Private, Trb); if (EFI_ERROR (Status)) { - goto Done; + return Status; } - Trb->DataPhy = (UINT64)(UINTN)SdmaAddr; } - if ((Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeAdtc) && - (Packet->SdMmcCmdBlk->ResponseType != SdMmcResponseTypeR1b) && - (Packet->SdMmcCmdBlk->ResponseType != SdMmcResponseTypeR5b)) { - if ((IntStatus & BIT0) == BIT0) { - Status = EFI_SUCCESS; - goto Done; - } + return EFI_NOT_READY; +} + +/** + Check the TRB execution result. + + @param[in] Private A pointer to the SD_MMC_HC_PRIVATE_DATA instance. + @param[in] Trb The pointer to the SD_MMC_HC_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 +SdMmcCheckTrbResult ( + IN SD_MMC_HC_PRIVATE_DATA *Private, + IN SD_MMC_HC_TRB *Trb + ) +{ + EFI_STATUS Status; + EFI_SD_MMC_PASS_THRU_COMMAND_PACKET *Packet; + UINT16 IntStatus; + + Packet = Trb->Packet; + // + // Check Trb execution result by reading Normal Interrupt Status register. + // + Status = SdMmcHcRwMmio ( + Private->PciIo, + Trb->Slot, + SD_MMC_HC_NOR_INT_STS, + TRUE, + sizeof (IntStatus), + &IntStatus + ); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Check if there are any errors reported by host controller + // and if neccessary recover the controller before next command is executed. + // + Status = SdMmcCheckAndRecoverErrors (Private, Trb->Slot, IntStatus); + if (EFI_ERROR (Status)) { + goto Done; } + // + // Tuning commands are the only ones that do not generate command + // complete interrupt. Process them here before entering the code + // that waits for command completion. + // 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))) { - // - // 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; - 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; + Status = SdMmcTransferDataWithPio (Private, Trb, IntStatus); + goto Done; + } + + if (!Trb->CommandComplete) { + Status = SdMmcCheckCommandComplete (Private, Trb, IntStatus); + if (EFI_ERROR (Status)) { goto Done; } } - Status = EFI_NOT_READY; -Done: - // - // Get response data when the cmd is executed successfully. - // - if (!EFI_ERROR (Status)) { - if (Packet->SdMmcCmdBlk->CommandType != SdMmcCommandTypeBc) { - for (Index = 0; Index < 4; Index++) { - Status = SdMmcHcRwMmio ( - Private->PciIo, - Trb->Slot, - SD_MMC_HC_RESPONSE + Index * 4, - TRUE, - sizeof (UINT32), - &Response[Index] - ); - if (EFI_ERROR (Status)) { - SdMmcHcLedOnOff (Private->PciIo, Trb->Slot, FALSE); - return Status; - } - } - CopyMem (Packet->SdMmcStatusBlk, Response, sizeof (Response)); - } + if (Packet->SdMmcCmdBlk->CommandType == SdMmcCommandTypeAdtc || + Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR1b || + Packet->SdMmcCmdBlk->ResponseType == SdMmcResponseTypeR5b) { + Status = SdMmcCheckDataTransfer (Private, Trb, IntStatus); + } else { + Status = EFI_SUCCESS; } +Done: if (Status != EFI_NOT_READY) { SdMmcHcLedOnOff (Private->PciIo, Trb->Slot, FALSE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "TRB failed with %r\n", Status)); + SdMmcPrintTrb (DEBUG_ERROR, Trb); + } else { + DEBUG ((DEBUG_VERBOSE, "TRB success\n")); + SdMmcPrintTrb (DEBUG_VERBOSE, Trb); + } } return Status;