2 This driver is used for Opal Password Feature support at AHCI mode.
4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "OpalPasswordPei.h"
19 Start command for give slot on specific port.
21 @param AhciBar AHCI bar address.
22 @param Port The number of port.
23 @param CommandSlot The number of CommandSlot.
24 @param Timeout The timeout Value of start.
26 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
27 @retval EFI_TIMEOUT The operation is time out.
28 @retval EFI_SUCCESS The command start successfully.
41 Stop command running for giving port
43 @param AhciBar AHCI bar address.
44 @param Port The number of port.
45 @param Timeout The timeout Value of stop.
47 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
48 @retval EFI_TIMEOUT The operation is time out.
49 @retval EFI_SUCCESS The command stop successfully.
61 Read AHCI Operation register.
63 @param AhciBar AHCI bar address.
64 @param Offset The operation register offset.
66 @return The register content read.
80 Data
= MmioRead32 (AhciBar
+ Offset
);
86 Write AHCI Operation register.
88 @param AhciBar AHCI bar address.
89 @param Offset The operation register offset.
90 @param Data The Data used to write down.
101 MmioWrite32 (AhciBar
+ Offset
, Data
);
107 Do AND operation with the Value of AHCI Operation register.
109 @param AhciBar AHCI bar address.
110 @param Offset The operation register offset.
111 @param AndData The Data used to do AND operation.
124 Data
= AhciReadReg (AhciBar
, Offset
);
128 AhciWriteReg (AhciBar
, Offset
, Data
);
132 Do OR operation with the Value of AHCI Operation register.
134 @param AhciBar AHCI bar address.
135 @param Offset The operation register offset.
136 @param OrData The Data used to do OR operation.
149 Data
= AhciReadReg (AhciBar
, Offset
);
153 AhciWriteReg (AhciBar
, Offset
, Data
);
157 Wait for memory set to the test Value.
159 @param AhciBar AHCI bar address.
160 @param Offset The memory offset to test.
161 @param MaskValue The mask Value of memory.
162 @param TestValue The test Value of memory.
163 @param Timeout The time out Value for wait memory set.
165 @retval EFI_DEVICE_ERROR The memory is not set.
166 @retval EFI_TIMEOUT The memory setting is time out.
167 @retval EFI_SUCCESS The memory is correct set.
183 Delay
= (UINT32
) (DivU64x32(Timeout
, 1000) + 1);
186 Value
= AhciReadReg (AhciBar
, Offset
) & MaskValue
;
188 if (Value
== TestValue
) {
193 // Stall for 100 microseconds.
195 MicroSecondDelay (100);
204 Wait for the Value of the specified system memory set to the test Value.
206 @param Address The system memory address to test.
207 @param MaskValue The mask Value of memory.
208 @param TestValue The test Value of memory.
209 @param Timeout The time out Value for wait memory set, uses 100ns as a unit.
211 @retval EFI_TIMEOUT The system memory setting is time out.
212 @retval EFI_SUCCESS The system memory is correct set.
218 IN EFI_PHYSICAL_ADDRESS Address
,
227 Delay
= (UINT32
) (DivU64x32 (Timeout
, 1000) + 1);
231 // Access sytem memory to see if the Value is the tested one.
233 // The system memory pointed by Address will be updated by the
234 // SATA Host Controller, "volatile" is introduced to prevent
235 // compiler from optimizing the access to the memory address
236 // to only read once.
238 Value
= *(volatile UINT32
*) (UINTN
) Address
;
241 if (Value
== TestValue
) {
246 // Stall for 100 microseconds.
248 MicroSecondDelay (100);
258 Check the memory status to the test Value.
260 @param[in] Address The memory address to test.
261 @param[in] MaskValue The mask Value of memory.
262 @param[in] TestValue The test Value of memory.
263 @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once.
265 @retval EFI_NOTREADY The memory is not set.
266 @retval EFI_TIMEOUT The memory setting retry times out.
267 @retval EFI_SUCCESS The memory is correct set.
276 IN OUT UINTN
*RetryTimes OPTIONAL
281 if (RetryTimes
!= NULL
) {
285 Value
= *(volatile UINT32
*) Address
;
288 if (Value
== TestValue
) {
292 if ((RetryTimes
!= NULL
) && (*RetryTimes
== 0)) {
295 return EFI_NOT_READY
;
300 Clear the port interrupt and error status. It will also clear
301 HBA interrupt status.
303 @param AhciBar AHCI bar address.
304 @param Port The number of port.
309 AhciClearPortStatus (
317 // Clear any error status
319 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SERR
;
320 AhciWriteReg (AhciBar
, Offset
, AhciReadReg (AhciBar
, Offset
));
323 // Clear any port interrupt status
325 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_IS
;
326 AhciWriteReg (AhciBar
, Offset
, AhciReadReg (AhciBar
, Offset
));
329 // Clear any HBA interrupt status
331 AhciWriteReg (AhciBar
, EFI_AHCI_IS_OFFSET
, AhciReadReg (AhciBar
, EFI_AHCI_IS_OFFSET
));
335 Enable the FIS running for giving port.
337 @param AhciBar AHCI bar address.
338 @param Port The number of port.
339 @param Timeout The timeout Value of enabling FIS.
341 @retval EFI_DEVICE_ERROR The FIS enable setting fails.
342 @retval EFI_TIMEOUT The FIS enable setting is time out.
343 @retval EFI_SUCCESS The FIS enable successfully.
348 AhciEnableFisReceive (
356 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
357 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_FRE
);
359 return AhciWaitMmioSet (
362 EFI_AHCI_PORT_CMD_FR
,
363 EFI_AHCI_PORT_CMD_FR
,
369 Disable the FIS running for giving port.
371 @param AhciBar AHCI bar address.
372 @param Port The number of port.
373 @param Timeout The timeout Value of disabling FIS.
375 @retval EFI_DEVICE_ERROR The FIS disable setting fails.
376 @retval EFI_TIMEOUT The FIS disable setting is time out.
377 @retval EFI_UNSUPPORTED The port is in running state.
378 @retval EFI_SUCCESS The FIS disable successfully.
383 AhciDisableFisReceive (
392 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
393 Data
= AhciReadReg (AhciBar
, Offset
);
396 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
398 if ((Data
& (EFI_AHCI_PORT_CMD_ST
| EFI_AHCI_PORT_CMD_CR
)) != 0) {
399 return EFI_UNSUPPORTED
;
403 // Check if the Fis receive DMA engine for the port is running.
405 if ((Data
& EFI_AHCI_PORT_CMD_FR
) != EFI_AHCI_PORT_CMD_FR
) {
409 AhciAndReg (AhciBar
, Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_FRE
));
411 return AhciWaitMmioSet (
414 EFI_AHCI_PORT_CMD_FR
,
421 Build the command list, command table and prepare the fis receiver.
423 @param AhciContext The pointer to the AHCI_CONTEXT.
424 @param Port The number of port.
425 @param PortMultiplier The timeout Value of stop.
426 @param CommandFis The control fis will be used for the transfer.
427 @param CommandList The command list will be used for the transfer.
428 @param AtapiCommand The atapi command will be used for the transfer.
429 @param AtapiCommandLength The Length of the atapi command.
430 @param CommandSlotNumber The command slot will be used for the transfer.
431 @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address.
432 @param DataLength The Data count to be transferred.
438 IN AHCI_CONTEXT
*AhciContext
,
440 IN UINT8 PortMultiplier
,
441 IN EFI_AHCI_COMMAND_FIS
*CommandFis
,
442 IN EFI_AHCI_COMMAND_LIST
*CommandList
,
443 IN EFI_AHCI_ATAPI_COMMAND
*AtapiCommand OPTIONAL
,
444 IN UINT8 AtapiCommandLength
,
445 IN UINT8 CommandSlotNumber
,
446 IN OUT VOID
*DataPhysicalAddr
,
450 EFI_AHCI_REGISTERS
*AhciRegisters
;
459 AhciRegisters
= &AhciContext
->AhciRegisters
;
460 AhciBar
= AhciContext
->AhciBar
;
465 PrdtNumber
= DivU64x32 (DataLength
+ EFI_AHCI_MAX_DATA_PER_PRDT
- 1, EFI_AHCI_MAX_DATA_PER_PRDT
);
468 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.
469 // It also limits that the maximum amount of the PRDT entry in the command table
472 ASSERT (PrdtNumber
<= 1);
474 Data64
.Uint64
= (UINTN
) (AhciRegisters
->AhciRFis
);
476 BaseAddr
= Data64
.Uint64
;
478 ZeroMem ((VOID
*)((UINTN
) BaseAddr
), sizeof (EFI_AHCI_RECEIVED_FIS
));
480 ZeroMem (AhciRegisters
->AhciCommandTable
, sizeof (EFI_AHCI_COMMAND_TABLE
));
482 CommandFis
->AhciCFisPmNum
= PortMultiplier
;
484 CopyMem (&AhciRegisters
->AhciCommandTable
->CommandFis
, CommandFis
, sizeof (EFI_AHCI_COMMAND_FIS
));
486 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
487 if (AtapiCommand
!= NULL
) {
489 &AhciRegisters
->AhciCommandTable
->AtapiCmd
,
494 CommandList
->AhciCmdA
= 1;
495 CommandList
->AhciCmdP
= 1;
497 AhciOrReg (AhciBar
, Offset
, (EFI_AHCI_PORT_CMD_DLAE
| EFI_AHCI_PORT_CMD_ATAPI
));
499 AhciAndReg (AhciBar
, Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_DLAE
| EFI_AHCI_PORT_CMD_ATAPI
));
502 RemainedData
= (UINTN
) DataLength
;
503 MemAddr
= (UINTN
) DataPhysicalAddr
;
504 CommandList
->AhciCmdPrdtl
= (UINT32
)PrdtNumber
;
506 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDbc
= (UINT32
)RemainedData
- 1;
508 Data64
.Uint64
= (UINT64
)MemAddr
;
509 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDba
= Data64
.Uint32
.Lower32
;
510 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtDbau
= Data64
.Uint32
.Upper32
;
513 // Set the last PRDT to Interrupt On Complete
515 AhciRegisters
->AhciCommandTable
->PrdtTable
.AhciPrdtIoc
= 1;
518 (VOID
*) ((UINTN
) AhciRegisters
->AhciCmdList
+ (UINTN
) CommandSlotNumber
* sizeof (EFI_AHCI_COMMAND_LIST
)),
520 sizeof (EFI_AHCI_COMMAND_LIST
)
523 Data64
.Uint64
= (UINT64
)(UINTN
) AhciRegisters
->AhciCommandTable
;
524 AhciRegisters
->AhciCmdList
[CommandSlotNumber
].AhciCmdCtba
= Data64
.Uint32
.Lower32
;
525 AhciRegisters
->AhciCmdList
[CommandSlotNumber
].AhciCmdCtbau
= Data64
.Uint32
.Upper32
;
526 AhciRegisters
->AhciCmdList
[CommandSlotNumber
].AhciCmdPmp
= PortMultiplier
;
533 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
534 @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.
539 AhciBuildCommandFis (
540 IN OUT EFI_AHCI_COMMAND_FIS
*CmdFis
,
541 IN EFI_ATA_COMMAND_BLOCK
*AtaCommandBlock
544 ZeroMem (CmdFis
, sizeof (EFI_AHCI_COMMAND_FIS
));
546 CmdFis
->AhciCFisType
= EFI_AHCI_FIS_REGISTER_H2D
;
548 // Indicator it's a command
550 CmdFis
->AhciCFisCmdInd
= 0x1;
551 CmdFis
->AhciCFisCmd
= AtaCommandBlock
->AtaCommand
;
553 CmdFis
->AhciCFisFeature
= AtaCommandBlock
->AtaFeatures
;
554 CmdFis
->AhciCFisFeatureExp
= AtaCommandBlock
->AtaFeaturesExp
;
556 CmdFis
->AhciCFisSecNum
= AtaCommandBlock
->AtaSectorNumber
;
557 CmdFis
->AhciCFisSecNumExp
= AtaCommandBlock
->AtaSectorNumberExp
;
559 CmdFis
->AhciCFisClyLow
= AtaCommandBlock
->AtaCylinderLow
;
560 CmdFis
->AhciCFisClyLowExp
= AtaCommandBlock
->AtaCylinderLowExp
;
562 CmdFis
->AhciCFisClyHigh
= AtaCommandBlock
->AtaCylinderHigh
;
563 CmdFis
->AhciCFisClyHighExp
= AtaCommandBlock
->AtaCylinderHighExp
;
565 CmdFis
->AhciCFisSecCount
= AtaCommandBlock
->AtaSectorCount
;
566 CmdFis
->AhciCFisSecCountExp
= AtaCommandBlock
->AtaSectorCountExp
;
568 CmdFis
->AhciCFisDevHead
= (UINT8
) (AtaCommandBlock
->AtaDeviceHead
| 0xE0);
572 Start a PIO Data transfer on specific port.
574 @param AhciContext The pointer to the AHCI_CONTEXT.
575 @param Port The number of port.
576 @param PortMultiplier The timeout Value of stop.
577 @param AtapiCommand The atapi command will be used for the transfer.
578 @param AtapiCommandLength The Length of the atapi command.
579 @param Read The transfer direction.
580 @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.
581 @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.
582 @param MemoryAddr The pointer to the Data Buffer.
583 @param DataCount The Data count to be transferred.
584 @param Timeout The timeout Value of non Data transfer.
586 @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.
587 @retval EFI_TIMEOUT The operation is time out.
588 @retval EFI_UNSUPPORTED The device is not ready for transfer.
589 @retval EFI_SUCCESS The PIO Data transfer executes successfully.
595 IN AHCI_CONTEXT
*AhciContext
,
597 IN UINT8 PortMultiplier
,
598 IN EFI_AHCI_ATAPI_COMMAND
*AtapiCommand OPTIONAL
,
599 IN UINT8 AtapiCommandLength
,
601 IN EFI_ATA_COMMAND_BLOCK
*AtaCommandBlock
,
602 IN OUT EFI_ATA_STATUS_BLOCK
*AtaStatusBlock
,
603 IN OUT VOID
*MemoryAddr
,
609 EFI_AHCI_REGISTERS
*AhciRegisters
;
614 EFI_AHCI_COMMAND_FIS CFis
;
615 EFI_AHCI_COMMAND_LIST CmdList
;
623 AhciRegisters
= &AhciContext
->AhciRegisters
;
624 AhciBar
= AhciContext
->AhciBar
;
626 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
627 OldRfisLo
= AhciReadReg (AhciBar
, Offset
);
628 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FBU
;
629 OldRfisHi
= AhciReadReg (AhciBar
, Offset
);
630 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
631 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciRFis
);
632 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FBU
;
633 AhciWriteReg (AhciBar
, Offset
, 0);
636 // Single task envrionment, we only use one command table for all port
638 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
639 OldCmdListLo
= AhciReadReg (AhciBar
, Offset
);
640 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLBU
;
641 OldCmdListHi
= AhciReadReg (AhciBar
, Offset
);
642 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
643 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciCmdList
);
644 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLBU
;
645 AhciWriteReg (AhciBar
, Offset
, 0);
648 // Package read needed
650 AhciBuildCommandFis (&CFis
, AtaCommandBlock
);
652 ZeroMem (&CmdList
, sizeof (EFI_AHCI_COMMAND_LIST
));
654 CmdList
.AhciCmdCfl
= EFI_AHCI_FIS_REGISTER_H2D_LENGTH
/ 4;
655 CmdList
.AhciCmdW
= Read
? 0 : 1;
670 Status
= AhciStartCommand (
676 if (EFI_ERROR (Status
)) {
681 // Checking the status and wait the driver sending Data
683 FisBaseAddr
= (UINT32
)(UINTN
)AhciRegisters
->AhciRFis
;
684 if (Read
&& (AtapiCommand
== 0)) {
686 // Wait device sends the PIO setup fis before Data transfer
688 Status
= EFI_TIMEOUT
;
689 Delay
= (UINT32
) (DivU64x32 (Timeout
, 1000) + 1);
691 Offset
= FisBaseAddr
+ EFI_AHCI_PIO_FIS_OFFSET
;
693 Status
= AhciCheckMemSet (Offset
, EFI_AHCI_FIS_TYPE_MASK
, EFI_AHCI_FIS_PIO_SETUP
, 0);
694 if (!EFI_ERROR (Status
)) {
695 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
696 PortTfd
= AhciReadReg (AhciBar
, (UINT32
) Offset
);
698 // PxTFD will be updated if there is a D2H or SetupFIS received.
699 // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.
701 if ((PortTfd
& EFI_AHCI_PORT_TFD_ERR
) != 0) {
702 Status
= EFI_DEVICE_ERROR
;
706 PrdCount
= *(volatile UINT32
*) (&(AhciRegisters
->AhciCmdList
[0].AhciCmdPrdbc
));
707 if (PrdCount
== DataCount
) {
712 Offset
= FisBaseAddr
+ EFI_AHCI_D2H_FIS_OFFSET
;
713 Status
= AhciCheckMemSet (Offset
, EFI_AHCI_FIS_TYPE_MASK
, EFI_AHCI_FIS_REGISTER_D2H
, 0);
714 if (!EFI_ERROR (Status
)) {
715 Status
= EFI_DEVICE_ERROR
;
720 // Stall for 100 microseconds.
722 MicroSecondDelay(100);
728 // Wait for D2H Fis is received
730 Offset
= FisBaseAddr
+ EFI_AHCI_D2H_FIS_OFFSET
;
731 Status
= AhciWaitMemSet (
733 EFI_AHCI_FIS_TYPE_MASK
,
734 EFI_AHCI_FIS_REGISTER_D2H
,
738 if (EFI_ERROR (Status
)) {
742 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
743 PortTfd
= AhciReadReg (AhciBar
, (UINT32
) Offset
);
744 if ((PortTfd
& EFI_AHCI_PORT_TFD_ERR
) != 0) {
745 Status
= EFI_DEVICE_ERROR
;
756 AhciDisableFisReceive (
762 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
763 AhciWriteReg (AhciBar
, Offset
, OldRfisLo
);
764 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FBU
;
765 AhciWriteReg (AhciBar
, Offset
, OldRfisHi
);
767 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
768 AhciWriteReg (AhciBar
, Offset
, OldCmdListLo
);
769 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLBU
;
770 AhciWriteReg (AhciBar
, Offset
, OldCmdListHi
);
776 Stop command running for giving port
778 @param AhciBar AHCI bar address.
779 @param Port The number of port.
780 @param Timeout The timeout Value of stop.
782 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
783 @retval EFI_TIMEOUT The operation is time out.
784 @retval EFI_SUCCESS The command stop successfully.
798 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
799 Data
= AhciReadReg (AhciBar
, Offset
);
801 if ((Data
& (EFI_AHCI_PORT_CMD_ST
| EFI_AHCI_PORT_CMD_CR
)) == 0) {
805 if ((Data
& EFI_AHCI_PORT_CMD_ST
) != 0) {
806 AhciAndReg (AhciBar
, Offset
, (UINT32
)~(EFI_AHCI_PORT_CMD_ST
));
809 return AhciWaitMmioSet (
812 EFI_AHCI_PORT_CMD_CR
,
819 Start command for give slot on specific port.
821 @param AhciBar AHCI bar address.
822 @param Port The number of port.
823 @param CommandSlot The number of CommandSlot.
824 @param Timeout The timeout Value of start.
826 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
827 @retval EFI_TIMEOUT The operation is time out.
828 @retval EFI_SUCCESS The command start successfully.
836 IN UINT8 CommandSlot
,
849 // Collect AHCI controller information
851 Capability
= AhciReadReg(AhciBar
, EFI_AHCI_CAPABILITY_OFFSET
);
853 CmdSlotBit
= (UINT32
) (1 << CommandSlot
);
855 AhciClearPortStatus (
860 Status
= AhciEnableFisReceive (
866 if (EFI_ERROR (Status
)) {
870 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
871 PortStatus
= AhciReadReg (AhciBar
, Offset
);
874 if ((PortStatus
& EFI_AHCI_PORT_CMD_ALPE
) != 0) {
875 StartCmd
= AhciReadReg (AhciBar
, Offset
);
876 StartCmd
&= ~EFI_AHCI_PORT_CMD_ICC_MASK
;
877 StartCmd
|= EFI_AHCI_PORT_CMD_ACTIVE
;
880 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
881 PortTfd
= AhciReadReg (AhciBar
, Offset
);
883 if ((PortTfd
& (EFI_AHCI_PORT_TFD_BSY
| EFI_AHCI_PORT_TFD_DRQ
)) != 0) {
884 if ((Capability
& BIT24
) != 0) {
885 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
886 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_COL
);
891 EFI_AHCI_PORT_CMD_COL
,
898 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
899 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_ST
| StartCmd
);
902 // Setting the command
904 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SACT
;
905 AhciAndReg (AhciBar
, Offset
, 0);
906 AhciOrReg (AhciBar
, Offset
, CmdSlotBit
);
908 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CI
;
909 AhciAndReg (AhciBar
, Offset
, 0);
910 AhciOrReg (AhciBar
, Offset
, CmdSlotBit
);
918 @param[in] AhciBar AHCI bar address.
919 @param[in] Timeout The timeout Value of reset.
921 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.
922 @retval EFI_TIMEOUT The reset operation is time out.
923 @retval EFI_SUCCESS AHCI controller is reset successfully.
938 // Collect AHCI controller information
940 Capability
= AhciReadReg (AhciBar
, EFI_AHCI_CAPABILITY_OFFSET
);
943 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
945 if ((Capability
& EFI_AHCI_CAP_SAM
) == 0) {
946 AhciOrReg (AhciBar
, EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_ENABLE
);
949 AhciOrReg (AhciBar
, EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_RESET
);
951 Delay
= (UINT32
) (DivU64x32(Timeout
, 1000) + 1);
954 Value
= AhciReadReg(AhciBar
, EFI_AHCI_GHC_OFFSET
);
955 if ((Value
& EFI_AHCI_GHC_RESET
) == 0) {
960 // Stall for 100 microseconds.
962 MicroSecondDelay(100);
973 Send Buffer cmd to specific device.
975 @param[in] AhciContext The pointer to the AHCI_CONTEXT.
976 @param[in] Port The port number of attached ATA device.
977 @param[in] PortMultiplier The port number of port multiplier of attached ATA device.
978 @param[in, out] Buffer The Data Buffer to store IDENTIFY PACKET Data.
980 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.
981 @retval EFI_TIMEOUT The operation is time out.
982 @retval EFI_UNSUPPORTED The device is not ready for executing.
983 @retval EFI_SUCCESS The cmd executes successfully.
989 IN AHCI_CONTEXT
*AhciContext
,
991 IN UINT8 PortMultiplier
,
992 IN OUT ATA_IDENTIFY_DATA
*Buffer
996 EFI_ATA_COMMAND_BLOCK AtaCommandBlock
;
998 if (AhciContext
== NULL
|| Buffer
== NULL
) {
999 return EFI_INVALID_PARAMETER
;
1002 ZeroMem (&AtaCommandBlock
, sizeof (EFI_ATA_COMMAND_BLOCK
));
1004 AtaCommandBlock
.AtaCommand
= ATA_CMD_IDENTIFY_DRIVE
;
1005 AtaCommandBlock
.AtaSectorCount
= 1;
1007 Status
= AhciPioTransfer (
1017 sizeof (ATA_IDENTIFY_DATA
),
1025 Allocate transfer-related data struct which is used at AHCI mode.
1027 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.
1029 @retval EFI_OUT_OF_RESOURCE No enough resource.
1030 @retval EFI_SUCCESS Successful to allocate resource.
1035 AhciAllocateResource (
1036 IN OUT AHCI_CONTEXT
*AhciContext
1040 EFI_AHCI_REGISTERS
*AhciRegisters
;
1041 EFI_PHYSICAL_ADDRESS DeviceAddress
;
1045 AhciRegisters
= &AhciContext
->AhciRegisters
;
1048 // Allocate resources required by AHCI host controller.
1050 Status
= IoMmuAllocateBuffer (
1051 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1056 if (EFI_ERROR (Status
)) {
1057 return EFI_OUT_OF_RESOURCES
;
1059 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1060 AhciRegisters
->AhciRFisMapping
= Mapping
;
1061 AhciRegisters
->AhciRFis
= Base
;
1062 ZeroMem (AhciRegisters
->AhciRFis
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1064 Status
= IoMmuAllocateBuffer (
1065 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1070 if (EFI_ERROR (Status
)) {
1072 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1073 AhciRegisters
->AhciRFis
,
1074 AhciRegisters
->AhciRFisMapping
1076 AhciRegisters
->AhciRFis
= NULL
;
1077 return EFI_OUT_OF_RESOURCES
;
1079 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1080 AhciRegisters
->AhciCmdListMapping
= Mapping
;
1081 AhciRegisters
->AhciCmdList
= Base
;
1082 ZeroMem (AhciRegisters
->AhciCmdList
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)));
1084 Status
= IoMmuAllocateBuffer (
1085 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)),
1090 if (EFI_ERROR (Status
)) {
1092 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1093 AhciRegisters
->AhciCmdList
,
1094 AhciRegisters
->AhciCmdListMapping
1096 AhciRegisters
->AhciCmdList
= NULL
;
1098 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1099 AhciRegisters
->AhciRFis
,
1100 AhciRegisters
->AhciRFisMapping
1102 AhciRegisters
->AhciRFis
= NULL
;
1103 return EFI_OUT_OF_RESOURCES
;
1105 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1106 AhciRegisters
->AhciCommandTableMapping
= Mapping
;
1107 AhciRegisters
->AhciCommandTable
= Base
;
1108 ZeroMem (AhciRegisters
->AhciCommandTable
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)));
1111 // Allocate resources for data transfer.
1113 Status
= IoMmuAllocateBuffer (
1114 EFI_SIZE_TO_PAGES (HDD_PAYLOAD
),
1119 if (EFI_ERROR (Status
)) {
1121 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1122 AhciRegisters
->AhciCommandTable
,
1123 AhciRegisters
->AhciCommandTableMapping
1125 AhciRegisters
->AhciCommandTable
= NULL
;
1127 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1128 AhciRegisters
->AhciCmdList
,
1129 AhciRegisters
->AhciCmdListMapping
1131 AhciRegisters
->AhciCmdList
= NULL
;
1133 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1134 AhciRegisters
->AhciRFis
,
1135 AhciRegisters
->AhciRFisMapping
1137 AhciRegisters
->AhciRFis
= NULL
;
1138 return EFI_OUT_OF_RESOURCES
;
1140 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1141 AhciContext
->BufferMapping
= Mapping
;
1142 AhciContext
->Buffer
= Base
;
1143 ZeroMem (AhciContext
->Buffer
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (HDD_PAYLOAD
));
1147 "%a() AhciContext 0x%x 0x%x 0x%x 0x%x\n",
1149 AhciContext
->Buffer
,
1150 AhciRegisters
->AhciRFis
,
1151 AhciRegisters
->AhciCmdList
,
1152 AhciRegisters
->AhciCommandTable
1158 Free allocated transfer-related data struct which is used at AHCI mode.
1160 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.
1166 IN OUT AHCI_CONTEXT
*AhciContext
1169 EFI_AHCI_REGISTERS
*AhciRegisters
;
1171 AhciRegisters
= &AhciContext
->AhciRegisters
;
1173 if (AhciContext
->Buffer
!= NULL
) {
1175 EFI_SIZE_TO_PAGES (HDD_PAYLOAD
),
1176 AhciContext
->Buffer
,
1177 AhciContext
->BufferMapping
1179 AhciContext
->Buffer
= NULL
;
1182 if (AhciRegisters
->AhciCommandTable
!= NULL
) {
1184 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)),
1185 AhciRegisters
->AhciCommandTable
,
1186 AhciRegisters
->AhciCommandTableMapping
1188 AhciRegisters
->AhciCommandTable
= NULL
;
1191 if (AhciRegisters
->AhciCmdList
!= NULL
) {
1193 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1194 AhciRegisters
->AhciCmdList
,
1195 AhciRegisters
->AhciCmdListMapping
1197 AhciRegisters
->AhciCmdList
= NULL
;
1200 if (AhciRegisters
->AhciRFis
!= NULL
) {
1202 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1203 AhciRegisters
->AhciRFis
,
1204 AhciRegisters
->AhciRFisMapping
1206 AhciRegisters
->AhciRFis
= NULL
;
1211 Initialize ATA host controller at AHCI mode.
1213 The function is designed to initialize ATA host controller.
1215 @param[in] AhciContext The pointer to the AHCI_CONTEXT.
1216 @param[in] Port The port number to do initialization.
1221 AhciModeInitialize (
1222 IN AHCI_CONTEXT
*AhciContext
,
1227 EFI_AHCI_REGISTERS
*AhciRegisters
;
1232 UINT32 PhyDetectDelay
;
1234 AhciRegisters
= &AhciContext
->AhciRegisters
;
1235 AhciBar
= AhciContext
->AhciBar
;
1237 Status
= AhciReset (AhciBar
, ATA_TIMEOUT
);
1238 if (EFI_ERROR (Status
)) {
1243 // Collect AHCI controller information
1245 Capability
= AhciReadReg (AhciBar
, EFI_AHCI_CAPABILITY_OFFSET
);
1248 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1250 if ((Capability
& EFI_AHCI_CAP_SAM
) == 0) {
1251 AhciOrReg (AhciBar
, EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_ENABLE
);
1254 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
1255 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciRFis
);
1258 // Single task envrionment, we only use one command table for all port
1260 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
1261 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciCmdList
);
1263 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
1264 Data
= AhciReadReg (AhciBar
, Offset
);
1265 if ((Data
& EFI_AHCI_PORT_CMD_CPD
) != 0) {
1266 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_POD
);
1269 if ((Capability
& BIT27
) != 0) {
1270 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_SUD
);
1274 // Disable aggressive power management.
1276 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SCTL
;
1277 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_SCTL_IPM_INIT
);
1279 // Disable the reporting of the corresponding interrupt to system software.
1281 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_IE
;
1282 AhciAndReg (AhciBar
, Offset
, 0);
1284 Status
= AhciEnableFisReceive (
1289 ASSERT_EFI_ERROR (Status
);
1290 if (EFI_ERROR (Status
)) {
1295 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1296 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1298 PhyDetectDelay
= 16 * 1000;
1300 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SERR
;
1301 if (AhciReadReg(AhciBar
, Offset
) != 0) {
1302 AhciWriteReg (AhciBar
, Offset
, AhciReadReg(AhciBar
, Offset
));
1304 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
1306 Data
= AhciReadReg (AhciBar
, Offset
) & EFI_AHCI_PORT_TFD_MASK
;
1311 MicroSecondDelay (1000);
1313 } while (PhyDetectDelay
> 0);
1315 if (PhyDetectDelay
== 0) {
1316 return EFI_NOT_FOUND
;
1319 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SIG
;
1320 Status
= AhciWaitMmioSet (
1328 if (EFI_ERROR (Status
)) {