X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FBus%2FAta%2FAtaAtapiPassThru%2FAhciMode.c;h=a9b126acfa7fa92221667800d5b2d9fbd8ef1345;hb=5d9f5dc1a064fb308ddb802d8150dc0fbcf4e22b;hp=a36d03e827a9512e6c658214cdd8950505b5c65a;hpb=3d0a23854e80f952458bd74c4666f2887e79cac9;p=mirror_edk2.git diff --git a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c index a36d03e827..a9b126acfa 100644 --- a/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c +++ b/MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c @@ -133,24 +133,23 @@ AhciOrReg ( } /** - Wait for memory set to the test value. + Wait for the value of the specified MMIO register set to the test value. @param PciIo The PCI IO protocol instance. - @param Offset The memory address to test. + @param Offset The MMIO address to test. @param MaskValue The mask value of memory. @param TestValue The test value of memory. - @param Timeout The time out value for wait memory set. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. - @retval EFI_DEVICE_ERROR The memory is not set. - @retval EFI_TIMEOUT The memory setting is time out. - @retval EFI_SUCCESS The memory is correct set. + @retval EFI_TIMEOUT The MMIO setting is time out. + @retval EFI_SUCCESS The MMIO is correct set. **/ EFI_STATUS EFIAPI -AhciWaitMemSet ( +AhciWaitMmioSet ( IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT32 Offset, + IN UINTN Offset, IN UINT32 MaskValue, IN UINT32 TestValue, IN UINT64 Timeout @@ -159,10 +158,13 @@ AhciWaitMemSet ( UINT32 Value; UINT32 Delay; - Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); + Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1); do { - Value = AhciReadReg (PciIo, Offset) & MaskValue; + // + // Access PCI MMIO space to see if the value is the tested one. + // + Value = AhciReadReg (PciIo, (UINT32) Offset) & MaskValue; if (Value == TestValue) { return EFI_SUCCESS; @@ -177,21 +179,70 @@ AhciWaitMemSet ( } while (Delay > 0); - if (Delay == 0) { - return EFI_TIMEOUT; - } + return EFI_TIMEOUT; +} + +/** + Wait for the value of the specified system memory set to the test value. + + @param Address The system memory address to test. + @param MaskValue The mask value of memory. + @param TestValue The test value of memory. + @param Timeout The time out value for wait memory set, uses 100ns as a unit. + + @retval EFI_TIMEOUT The system memory setting is time out. + @retval EFI_SUCCESS The system memory is correct set. + +**/ +EFI_STATUS +EFIAPI +AhciWaitMemSet ( + IN EFI_PHYSICAL_ADDRESS Address, + IN UINT32 MaskValue, + IN UINT32 TestValue, + IN UINT64 Timeout + ) +{ + UINT32 Value; + UINT32 Delay; + + Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1); + + do { + // + // Access sytem memory to see if the value is the tested one. + // + // The system memory pointed by Address will be updated by the + // SATA Host Controller, "volatile" is introduced to prevent + // compiler from optimizing the access to the memory address + // to only read once. + // + Value = *(volatile UINT32 *) (UINTN) Address; + Value &= MaskValue; + + if (Value == TestValue) { + return EFI_SUCCESS; + } + + // + // Stall for 100 microseconds. + // + MicroSecondDelay (100); + + Delay--; - return EFI_DEVICE_ERROR; + } while (Delay > 0); + + return EFI_TIMEOUT; } /** Check the memory status to the test value. - @param[in] PciIo The PCI IO protocol instance. - @param[in] Offset The memory address to test. + @param[in] Address The memory address to test. @param[in] MaskValue The mask value of memory. @param[in] TestValue The test value of memory. - @param[in, out] RetryTimes The retry times value for waitting memory set. + @param[in, out] RetryTimes The retry times value for waitting memory set. If 0, then just try once. @retval EFI_NOTREADY The memory is not set. @retval EFI_TIMEOUT The memory setting retry times out. @@ -201,24 +252,26 @@ AhciWaitMemSet ( EFI_STATUS EFIAPI AhciCheckMemSet ( - IN EFI_PCI_IO_PROTOCOL *PciIo, - IN UINT32 Offset, + IN UINTN Address, IN UINT32 MaskValue, IN UINT32 TestValue, - IN OUT UINTN *RetryTimes + IN OUT UINTN *RetryTimes OPTIONAL ) { UINT32 Value; - (*RetryTimes) --; - - Value = AhciReadReg (PciIo, Offset) & MaskValue; + if (RetryTimes != NULL) { + (*RetryTimes)--; + } + + Value = *(volatile UINT32 *) Address; + Value &= MaskValue; if (Value == TestValue) { return EFI_SUCCESS; } - if ((*RetryTimes) == 0) { + if ((RetryTimes != NULL) && (*RetryTimes == 0)) { return EFI_TIMEOUT; } else { return EFI_NOT_READY; @@ -338,7 +391,7 @@ AhciDumpPortStatus ( @param PciIo The PCI IO protocol instance. @param Port The number of port. - @param Timeout The timeout value of enabling FIS. + @param Timeout The timeout value of enabling FIS, uses 100ns as a unit. @retval EFI_DEVICE_ERROR The FIS enable setting fails. @retval EFI_TIMEOUT The FIS enable setting is time out. @@ -358,7 +411,7 @@ AhciEnableFisReceive ( Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE); - return AhciWaitMemSet ( + return AhciWaitMmioSet ( PciIo, Offset, EFI_AHCI_PORT_CMD_FR, @@ -372,7 +425,7 @@ AhciEnableFisReceive ( @param PciIo The PCI IO protocol instance. @param Port The number of port. - @param Timeout The timeout value of disabling FIS. + @param Timeout The timeout value of disabling FIS, uses 100ns as a unit. @retval EFI_DEVICE_ERROR The FIS disable setting fails. @retval EFI_TIMEOUT The FIS disable setting is time out. @@ -410,7 +463,7 @@ AhciDisableFisReceive ( AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE)); - return AhciWaitMemSet ( + return AhciWaitMmioSet ( PciIo, Offset, EFI_AHCI_PORT_CMD_FR, @@ -596,7 +649,7 @@ AhciBuildCommandFis ( @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data. @param[in, out] MemoryAddr The pointer to the data buffer. @param[in] DataCount The data count to be transferred. - @param[in] Timeout The timeout value of non data transfer. + @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit. @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK used by non-blocking mode. @@ -626,15 +679,16 @@ AhciPioTransfer ( { EFI_STATUS Status; UINTN FisBaseAddr; - UINT32 Offset; - UINT32 Value; + UINTN Offset; EFI_PHYSICAL_ADDRESS PhyAddr; VOID *Map; UINTN MapLength; EFI_PCI_IO_PROTOCOL_OPERATION Flag; UINT32 Delay; EFI_AHCI_COMMAND_FIS CFis; - EFI_AHCI_COMMAND_LIST CmdList; + EFI_AHCI_COMMAND_LIST CmdList; + UINT32 PortTfd; + UINT32 PrdCount; if (Read) { Flag = EfiPciIoOperationBusMasterWrite; @@ -656,7 +710,7 @@ AhciPioTransfer ( ); if (EFI_ERROR (Status) || (DataCount != MapLength)) { - return EFI_OUT_OF_RESOURCES; + return EFI_BAD_BUFFER_SIZE; } // @@ -697,56 +751,66 @@ AhciPioTransfer ( // Check the status and wait the driver sending data // FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS); - // - // Wait device sends the PIO setup fis before data transfer - // - Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); - do { - Value = *(volatile UINT32 *) (FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET); - - if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_PIO_SETUP) { - break; - } + if (Read && (AtapiCommand == 0)) { // - // Stall for 100 microseconds. + // Wait device sends the PIO setup fis before data transfer // - MicroSecondDelay(100); + Status = EFI_TIMEOUT; + Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1); + do { + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (PciIo, (UINT32) Offset); - Delay--; - } while (Delay > 0); + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; + break; + } + Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET; - if (Delay == 0) { - Status = EFI_TIMEOUT; - goto Exit; - } + Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0); + if (!EFI_ERROR (Status)) { + PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc)); + if (PrdCount == DataCount) { + break; + } + } - // - // Wait for command compelte - // - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI; - Status = AhciWaitMemSet ( - PciIo, - Offset, - 0xFFFFFFFF, - 0, - Timeout - ); + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0); + if (!EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + break; + } - if (EFI_ERROR (Status)) { - goto Exit; - } + // + // Stall for 100 microseconds. + // + MicroSecondDelay(100); - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS; - Status = AhciWaitMemSet ( - PciIo, - Offset, - EFI_AHCI_PORT_IS_PSS, - EFI_AHCI_PORT_IS_PSS, - Timeout - ); - if (EFI_ERROR (Status)) { - goto Exit; + Delay--; + } while (Delay > 0); + } else { + // + // Wait for D2H Fis is received + // + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciWaitMemSet ( + Offset, + EFI_AHCI_FIS_TYPE_MASK, + EFI_AHCI_FIS_REGISTER_D2H, + Timeout + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (PciIo, (UINT32) Offset); + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; + } } Exit: @@ -787,7 +851,7 @@ Exit: @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data. @param[in, out] MemoryAddr The pointer to the data buffer. @param[in] DataCount The data count to be transferred. - @param[in] Timeout The timeout value of non data transfer. + @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit. @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK used by non-blocking mode. @@ -810,22 +874,24 @@ AhciDmaTransfer ( IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock, IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock, IN OUT VOID *MemoryAddr, - IN UINTN DataCount, + IN UINT32 DataCount, IN UINT64 Timeout, IN ATA_NONBLOCK_TASK *Task ) { EFI_STATUS Status; - UINT32 Offset; + UINTN Offset; EFI_PHYSICAL_ADDRESS PhyAddr; VOID *Map; UINTN MapLength; EFI_PCI_IO_PROTOCOL_OPERATION Flag; EFI_AHCI_COMMAND_FIS CFis; EFI_AHCI_COMMAND_LIST CmdList; + UINTN FisBaseAddr; + UINT32 PortTfd; - EFI_PCI_IO_PROTOCOL *PciIo; - EFI_TPL OldTpl; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_TPL OldTpl; Map = NULL; PciIo = Instance->PciIo; @@ -877,7 +943,7 @@ AhciDmaTransfer ( ); if (EFI_ERROR (Status) || (DataCount != MapLength)) { - return EFI_OUT_OF_RESOURCES; + return EFI_BAD_BUFFER_SIZE; } if (Task != NULL) { @@ -916,45 +982,28 @@ AhciDmaTransfer ( if (EFI_ERROR (Status)) { goto Exit; } - - // - // Wait device PRD processed - // - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS; - Status = AhciWaitMemSet ( - PciIo, - Offset, - EFI_AHCI_PORT_IS_DPS, - EFI_AHCI_PORT_IS_DPS, - Timeout - ); - - if (EFI_ERROR (Status)) { - goto Exit; - } } // // Wait for command compelte // - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI; + FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS); + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; if (Task != NULL) { // // For Non-blocking // Status = AhciCheckMemSet ( - PciIo, Offset, - 0xFFFFFFFF, - 0, + EFI_AHCI_FIS_TYPE_MASK, + EFI_AHCI_FIS_REGISTER_D2H, (UINTN *) (&Task->RetryTimes) ); } else { Status = AhciWaitMemSet ( - PciIo, Offset, - 0xFFFFFFFF, - 0, + EFI_AHCI_FIS_TYPE_MASK, + EFI_AHCI_FIS_REGISTER_D2H, Timeout ); } @@ -963,31 +1012,10 @@ AhciDmaTransfer ( goto Exit; } - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS; - - if (Task != NULL) { - // - // For Non-blocking - // - Status = AhciCheckMemSet ( - PciIo, - Offset, - EFI_AHCI_PORT_IS_DHRS, - EFI_AHCI_PORT_IS_DHRS, - (UINTN *) (&Task->RetryTimes) - ); - } else { - Status = AhciWaitMemSet ( - PciIo, - Offset, - EFI_AHCI_PORT_IS_DHRS, - EFI_AHCI_PORT_IS_DHRS, - Timeout - ); - } - - if (EFI_ERROR (Status)) { - goto Exit; + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (PciIo, (UINT32) Offset); + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; } Exit: @@ -1039,7 +1067,7 @@ Exit: @param[in] AtapiCommandLength The length of the atapi command. @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data. @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data. - @param[in] Timeout The timeout value of non data transfer. + @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit. @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK used by non-blocking mode. @@ -1066,10 +1094,8 @@ AhciNonDataTransfer ( { EFI_STATUS Status; UINTN FisBaseAddr; - UINT32 Offset; - UINT32 Value; - UINT32 Delay; - + UINTN Offset; + UINT32 PortTfd; EFI_AHCI_COMMAND_FIS CFis; EFI_AHCI_COMMAND_LIST CmdList; @@ -1110,39 +1136,23 @@ AhciNonDataTransfer ( // Wait device sends the Response Fis // FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS); - // - // Wait device sends the PIO setup fis before data transfer - // - Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); - do { - Value = *(volatile UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET); + Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET; + Status = AhciWaitMemSet ( + Offset, + EFI_AHCI_FIS_TYPE_MASK, + EFI_AHCI_FIS_REGISTER_D2H, + Timeout + ); - if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) { - break; - } - - // - // Stall for 100 microseconds. - // - MicroSecondDelay(100); - - Delay --; - } while (Delay > 0); - - if (Delay == 0) { - Status = EFI_TIMEOUT; + if (EFI_ERROR (Status)) { goto Exit; } - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI; - - Status = AhciWaitMemSet ( - PciIo, - Offset, - 0xFFFFFFFF, - 0, - Timeout - ); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; + PortTfd = AhciReadReg (PciIo, (UINT32) Offset); + if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) { + Status = EFI_DEVICE_ERROR; + } Exit: AhciStopCommand ( @@ -1167,7 +1177,7 @@ Exit: @param PciIo The PCI IO protocol instance. @param Port The number of port. - @param Timeout The timeout value of stop. + @param Timeout The timeout value of stop, uses 100ns as a unit. @retval EFI_DEVICE_ERROR The command stop unsuccessfully. @retval EFI_TIMEOUT The operation is time out. @@ -1196,7 +1206,7 @@ AhciStopCommand ( AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST)); } - return AhciWaitMemSet ( + return AhciWaitMmioSet ( PciIo, Offset, EFI_AHCI_PORT_CMD_CR, @@ -1211,7 +1221,7 @@ AhciStopCommand ( @param PciIo The PCI IO protocol instance. @param Port The number of port. @param CommandSlot The number of Command Slot. - @param Timeout The timeout value of start. + @param Timeout The timeout value of start, uses 100ns as a unit. @retval EFI_DEVICE_ERROR The command start unsuccessfully. @retval EFI_TIMEOUT The operation is time out. @@ -1273,12 +1283,12 @@ AhciStartCommand ( if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) { if ((Capability & BIT24) != 0) { Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; - AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_COL); + AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO); - AhciWaitMemSet ( + AhciWaitMmioSet ( PciIo, Offset, - EFI_AHCI_PORT_CMD_COL, + EFI_AHCI_PORT_CMD_CLO, 0, Timeout ); @@ -1307,7 +1317,7 @@ AhciStartCommand ( @param PciIo The PCI IO protocol instance. @param Port The number of port. - @param Timeout The timeout value of reset. + @param Timeout The timeout value of reset, uses 100ns as a unit. @retval EFI_DEVICE_ERROR The port reset unsuccessfully @retval EFI_TIMEOUT The reset operation is time out. @@ -1353,7 +1363,7 @@ AhciPortReset ( // Wait for communication to be re-established // Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS; - Status = AhciWaitMemSet ( + Status = AhciWaitMmioSet ( PciIo, Offset, EFI_AHCI_PORT_SSTS_DET_MASK, @@ -1375,7 +1385,7 @@ AhciPortReset ( Do AHCI HBA reset. @param PciIo The PCI IO protocol instance. - @param Timeout The timeout value of reset. + @param Timeout The timeout value of reset, uses 100ns as a unit. @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset. @retval EFI_TIMEOUT The reset operation is time out. @@ -1389,7 +1399,6 @@ AhciReset ( IN UINT64 Timeout ) { - EFI_STATUS Status; UINT32 Delay; UINT32 Value; @@ -1397,8 +1406,6 @@ AhciReset ( AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET); - Status = EFI_TIMEOUT; - Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1); do { @@ -1816,7 +1823,6 @@ AhciPacketCommandExecute ( EFI_ATA_COMMAND_BLOCK AtaCommandBlock; EFI_ATA_STATUS_BLOCK AtaStatusBlock; BOOLEAN Read; - UINT8 Retry; if (Packet == NULL || Packet->Cdb == NULL) { return EFI_INVALID_PARAMETER; @@ -1860,35 +1866,21 @@ AhciPacketCommandExecute ( NULL ); } else { - // - // READ_CAPACITY cmd may execute failure. Retry 5 times - // - if (((UINT8 *)Packet->Cdb)[0] == ATA_CMD_READ_CAPACITY) { - Retry = 5; - } else { - Retry = 1; - } - do { - Status = AhciPioTransfer ( - PciIo, - AhciRegisters, - Port, - PortMultiplier, - Packet->Cdb, - Packet->CdbLength, - Read, - &AtaCommandBlock, - &AtaStatusBlock, - Buffer, - Length, - Packet->Timeout, - NULL - ); - if (!EFI_ERROR (Status)) { - break; - } - Retry--; - } while (Retry != 0); + Status = AhciPioTransfer ( + PciIo, + AhciRegisters, + Port, + PortMultiplier, + Packet->Cdb, + Packet->CdbLength, + Read, + &AtaCommandBlock, + &AtaStatusBlock, + Buffer, + Length, + Packet->Timeout, + NULL + ); } return Status; } @@ -2152,8 +2144,6 @@ AhciModeInitialization ( UINT32 Capability; UINT8 MaxPortNumber; UINT32 PortImplementBitMap; - UINT8 MaxCommandSlotNumber; - BOOLEAN Support64Bit; EFI_AHCI_REGISTERS *AhciRegisters; @@ -2165,7 +2155,8 @@ AhciModeInitialization ( EFI_ATA_DEVICE_TYPE DeviceType; EFI_ATA_COLLECTIVE_MODE *SupportedModes; EFI_ATA_TRANSFER_MODE TransferMode; - + UINT32 PhyDetectDelay; + if (Instance == NULL) { return EFI_INVALID_PARAMETER; } @@ -2173,7 +2164,7 @@ AhciModeInitialization ( PciIo = Instance->PciIo; IdeInit = Instance->IdeControllerInit; - Status = AhciReset (PciIo, ATA_ATAPI_TIMEOUT); + Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; @@ -2192,15 +2183,13 @@ AhciModeInitialization ( // // Get the number of command slots per port supported by this HBA. // - MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1); - Support64Bit = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE); + MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1); // // Get the bit map of those ports exposed by this HBA. // It indicates which ports that the HBA supports are available for software to use. // PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET); - MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1); AhciRegisters = &Instance->AhciRegisters; Status = AhciCreateTransferDescriptor (PciIo, AhciRegisters); @@ -2210,191 +2199,227 @@ AhciModeInitialization ( } for (Port = 0; Port < MaxPortNumber; Port ++) { - Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFisPciAddr) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port; - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; - AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32); - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU; - AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32); - - // - // Single task envrionment, we only use one command table for all port - // - Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdListPciAddr); - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; - AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32); - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU; - AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32); - if ((PortImplementBitMap & (BIT0 << Port)) != 0) { + IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port); + + // + // Initialize FIS Base Address Register and Command List Base Address Register for use. + // + Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFisPciAddr) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port; + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB; + AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU; + AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32); + + Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdListPciAddr); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB; + AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU; + AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32); + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; - - if ((Capability & EFI_AHCI_PORT_CMD_ASP) != 0) { - AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_SUD); - } Data = AhciReadReg (PciIo, Offset); if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) { AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_POD); } - - AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE|EFI_AHCI_PORT_CMD_COL|EFI_AHCI_PORT_CMD_ST)); - } - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL; - AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_SCTL_IPM_MASK)); - - AhciAndReg (PciIo, Offset,(UINT32) ~(EFI_AHCI_PORT_SCTL_IPM_PSD)); - AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_PSD); - - AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_SCTL_IPM_SSD)); - AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_SSD); - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE; - AhciAndReg (PciIo, Offset, 0); - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR; - AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset)); - } - // - // Stall for 100 milliseconds. - // - MicroSecondDelay(100000); - - IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port); - - for (Port = 0; Port < MaxPortNumber; Port ++) { - if ((PortImplementBitMap & (BIT0 << Port)) != 0) { - - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS; - Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK; + if ((Capability & EFI_AHCI_CAP_SSS) != 0) { + AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_SUD); + } + + // + // Disable aggressive power management. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL; + AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT); + // + // Disable the reporting of the corresponding interrupt to system software. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE; + AhciAndReg (PciIo, Offset, 0); - if (Data == 0) { + // + // Now inform the IDE Controller Init Module. + // + IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, Port); + + // + // Enable FIS Receive DMA engine for the first D2H FIS. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD; + AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE); + Status = AhciWaitMmioSet ( + PciIo, + Offset, + EFI_AHCI_PORT_CMD_FR, + EFI_AHCI_PORT_CMD_FR, + EFI_AHCI_PORT_CMD_FR_CLEAR_TIMEOUT + ); + if (EFI_ERROR (Status)) { continue; } + // - // Found device in the port + // Wait no longer than 10 ms to wait the Phy to detect the presence of a device. + // It's the requirment from SATA1.0a spec section 5.2. // - if (Data == EFI_AHCI_PORT_SSTS_DET_PCE) { - Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG; - - Status = AhciWaitMemSet ( - PciIo, - Offset, - 0x0000FFFF, - 0x00000101, - ATA_ATAPI_TIMEOUT - ); - if (EFI_ERROR (Status)) { - continue; + PhyDetectDelay = EFI_AHCI_BUS_PHY_DETECT_TIMEOUT; + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS; + do { + Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK; + if ((Data == EFI_AHCI_PORT_SSTS_DET_PCE) || (Data == EFI_AHCI_PORT_SSTS_DET)) { + break; } + MicroSecondDelay (1000); + PhyDetectDelay--; + } while (PhyDetectDelay > 0); + + if (PhyDetectDelay == 0) { // - // Now inform the IDE Controller Init Module. + // No device detected at this port. // - IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, Port); + continue; + } - Data = AhciReadReg (PciIo, Offset); + // + // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ + // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec. + // + PhyDetectDelay = 16 * 1000; + do { + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR; + if (AhciReadReg(PciIo, Offset) != 0) { + AhciWriteReg (PciIo, Offset, AhciReadReg(PciIo, Offset)); + } + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD; - if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) { - Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer); + Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_TFD_MASK; + if (Data == 0) { + break; + } - if (EFI_ERROR (Status)) { - continue; - } + MicroSecondDelay (1000); + PhyDetectDelay--; + } while (PhyDetectDelay > 0); + + if (PhyDetectDelay == 0) { + continue; + } - DeviceType = EfiIdeCdrom; - } else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) { - Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer); + // + // When the first D2H register FIS is received, the content of PxSIG register is updated. + // + Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG; + Status = AhciWaitMmioSet ( + PciIo, + Offset, + 0x0000FFFF, + 0x00000101, + EFI_TIMER_PERIOD_SECONDS(16) + ); + if (EFI_ERROR (Status)) { + continue; + } - if (EFI_ERROR (Status)) { - REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED)); - continue; - } + Data = AhciReadReg (PciIo, Offset); + if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) { + Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer); - DeviceType = EfiIdeHarddisk; - } else { + if (EFI_ERROR (Status)) { continue; } - DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n", - Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk")); - - // - // If the device is a hard disk, then try to enable S.M.A.R.T feature - // - if (DeviceType == EfiIdeHarddisk) { - AhciAtaSmartSupport ( - PciIo, - AhciRegisters, - Port, - 0, - &Buffer, - NULL - ); - } - - // - // Submit identify data to IDE controller init driver - // - IdeInit->SubmitData (IdeInit, Port, 0, &Buffer); + DeviceType = EfiIdeCdrom; + } else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) { + Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer); - // - // Now start to config ide device parameter and transfer mode. - // - Status = IdeInit->CalculateMode ( - IdeInit, - Port, - 0, - &SupportedModes - ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Calculate Mode Fail, Status = %r\n", Status)); + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED)); continue; } - // - // Set best supported PIO mode on this IDE device - // - if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) { - TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO; - } else { - TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO; - } + DeviceType = EfiIdeHarddisk; + } else { + continue; + } + DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n", + Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk")); - TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode); - - // - // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't - // be set together. Only one DMA mode can be set to a device. If setting - // DMA mode operation fails, we can continue moving on because we only use - // PIO mode at boot time. DMA modes are used by certain kind of OS booting - // - if (SupportedModes->UdmaMode.Valid) { - TransferMode.ModeCategory = EFI_ATA_MODE_UDMA; - TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode); - } else if (SupportedModes->MultiWordDmaMode.Valid) { - TransferMode.ModeCategory = EFI_ATA_MODE_MDMA; - TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode; - } + // + // If the device is a hard disk, then try to enable S.M.A.R.T feature + // + if (DeviceType == EfiIdeHarddisk) { + AhciAtaSmartSupport ( + PciIo, + AhciRegisters, + Port, + 0, + &Buffer, + NULL + ); + } - Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode)); + // + // Submit identify data to IDE controller init driver + // + IdeInit->SubmitData (IdeInit, Port, 0, &Buffer); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); - continue; - } - // - // Found a ATA or ATAPI device, add it into the device list. - // - CreateNewDeviceInfo (Instance, Port, 0, DeviceType, &Buffer); - if (DeviceType == EfiIdeHarddisk) { - REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE)); - } + // + // Now start to config ide device parameter and transfer mode. + // + Status = IdeInit->CalculateMode ( + IdeInit, + Port, + 0, + &SupportedModes + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Calculate Mode Fail, Status = %r\n", Status)); + continue; + } + + // + // Set best supported PIO mode on this IDE device + // + if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) { + TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO; + } else { + TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO; + } + + TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode); + + // + // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't + // be set together. Only one DMA mode can be set to a device. If setting + // DMA mode operation fails, we can continue moving on because we only use + // PIO mode at boot time. DMA modes are used by certain kind of OS booting + // + if (SupportedModes->UdmaMode.Valid) { + TransferMode.ModeCategory = EFI_ATA_MODE_UDMA; + TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode); + } else if (SupportedModes->MultiWordDmaMode.Valid) { + TransferMode.ModeCategory = EFI_ATA_MODE_MDMA; + TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode; + } + + Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode)); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status)); + continue; + } + + // + // Found a ATA or ATAPI device, add it into the device list. + // + CreateNewDeviceInfo (Instance, Port, 0, DeviceType, &Buffer); + if (DeviceType == EfiIdeHarddisk) { + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE)); } } } + return EFI_SUCCESS; }