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 Allocate transfer-related data struct which is used at AHCI mode.
975 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.
977 @retval EFI_OUT_OF_RESOURCE No enough resource.
978 @retval EFI_SUCCESS Successful to allocate resource.
983 AhciAllocateResource (
984 IN OUT AHCI_CONTEXT
*AhciContext
988 EFI_AHCI_REGISTERS
*AhciRegisters
;
989 EFI_PHYSICAL_ADDRESS DeviceAddress
;
993 AhciRegisters
= &AhciContext
->AhciRegisters
;
996 // Allocate resources required by AHCI host controller.
998 Status
= IoMmuAllocateBuffer (
999 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1004 if (EFI_ERROR (Status
)) {
1005 return EFI_OUT_OF_RESOURCES
;
1007 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1008 AhciRegisters
->AhciRFisMapping
= Mapping
;
1009 AhciRegisters
->AhciRFis
= Base
;
1010 ZeroMem (AhciRegisters
->AhciRFis
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)));
1012 Status
= IoMmuAllocateBuffer (
1013 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1018 if (EFI_ERROR (Status
)) {
1020 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1021 AhciRegisters
->AhciRFis
,
1022 AhciRegisters
->AhciRFisMapping
1024 AhciRegisters
->AhciRFis
= NULL
;
1025 return EFI_OUT_OF_RESOURCES
;
1027 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1028 AhciRegisters
->AhciCmdListMapping
= Mapping
;
1029 AhciRegisters
->AhciCmdList
= Base
;
1030 ZeroMem (AhciRegisters
->AhciCmdList
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)));
1032 Status
= IoMmuAllocateBuffer (
1033 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)),
1038 if (EFI_ERROR (Status
)) {
1040 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1041 AhciRegisters
->AhciCmdList
,
1042 AhciRegisters
->AhciCmdListMapping
1044 AhciRegisters
->AhciCmdList
= NULL
;
1046 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1047 AhciRegisters
->AhciRFis
,
1048 AhciRegisters
->AhciRFisMapping
1050 AhciRegisters
->AhciRFis
= NULL
;
1051 return EFI_OUT_OF_RESOURCES
;
1053 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1054 AhciRegisters
->AhciCommandTableMapping
= Mapping
;
1055 AhciRegisters
->AhciCommandTable
= Base
;
1056 ZeroMem (AhciRegisters
->AhciCommandTable
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)));
1059 // Allocate resources for data transfer.
1061 Status
= IoMmuAllocateBuffer (
1062 EFI_SIZE_TO_PAGES (HDD_PAYLOAD
),
1067 if (EFI_ERROR (Status
)) {
1069 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1070 AhciRegisters
->AhciCommandTable
,
1071 AhciRegisters
->AhciCommandTableMapping
1073 AhciRegisters
->AhciCommandTable
= NULL
;
1075 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1076 AhciRegisters
->AhciCmdList
,
1077 AhciRegisters
->AhciCmdListMapping
1079 AhciRegisters
->AhciCmdList
= NULL
;
1081 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1082 AhciRegisters
->AhciRFis
,
1083 AhciRegisters
->AhciRFisMapping
1085 AhciRegisters
->AhciRFis
= NULL
;
1086 return EFI_OUT_OF_RESOURCES
;
1088 ASSERT (DeviceAddress
== ((EFI_PHYSICAL_ADDRESS
) (UINTN
) Base
));
1089 AhciContext
->BufferMapping
= Mapping
;
1090 AhciContext
->Buffer
= Base
;
1091 ZeroMem (AhciContext
->Buffer
, EFI_PAGE_SIZE
* EFI_SIZE_TO_PAGES (HDD_PAYLOAD
));
1095 "%a() AhciContext 0x%x 0x%x 0x%x 0x%x\n",
1097 AhciContext
->Buffer
,
1098 AhciRegisters
->AhciRFis
,
1099 AhciRegisters
->AhciCmdList
,
1100 AhciRegisters
->AhciCommandTable
1106 Free allocated transfer-related data struct which is used at AHCI mode.
1108 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.
1114 IN OUT AHCI_CONTEXT
*AhciContext
1117 EFI_AHCI_REGISTERS
*AhciRegisters
;
1119 AhciRegisters
= &AhciContext
->AhciRegisters
;
1121 if (AhciContext
->Buffer
!= NULL
) {
1123 EFI_SIZE_TO_PAGES (HDD_PAYLOAD
),
1124 AhciContext
->Buffer
,
1125 AhciContext
->BufferMapping
1127 AhciContext
->Buffer
= NULL
;
1130 if (AhciRegisters
->AhciCommandTable
!= NULL
) {
1132 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE
)),
1133 AhciRegisters
->AhciCommandTable
,
1134 AhciRegisters
->AhciCommandTableMapping
1136 AhciRegisters
->AhciCommandTable
= NULL
;
1139 if (AhciRegisters
->AhciCmdList
!= NULL
) {
1141 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST
)),
1142 AhciRegisters
->AhciCmdList
,
1143 AhciRegisters
->AhciCmdListMapping
1145 AhciRegisters
->AhciCmdList
= NULL
;
1148 if (AhciRegisters
->AhciRFis
!= NULL
) {
1150 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS
)),
1151 AhciRegisters
->AhciRFis
,
1152 AhciRegisters
->AhciRFisMapping
1154 AhciRegisters
->AhciRFis
= NULL
;
1159 Initialize ATA host controller at AHCI mode.
1161 The function is designed to initialize ATA host controller.
1163 @param[in] AhciContext The pointer to the AHCI_CONTEXT.
1164 @param[in] Port The port number to do initialization.
1169 AhciModeInitialize (
1170 IN AHCI_CONTEXT
*AhciContext
,
1175 EFI_AHCI_REGISTERS
*AhciRegisters
;
1180 UINT32 PhyDetectDelay
;
1182 AhciRegisters
= &AhciContext
->AhciRegisters
;
1183 AhciBar
= AhciContext
->AhciBar
;
1185 Status
= AhciReset (AhciBar
, ATA_TIMEOUT
);
1186 if (EFI_ERROR (Status
)) {
1191 // Collect AHCI controller information
1193 Capability
= AhciReadReg (AhciBar
, EFI_AHCI_CAPABILITY_OFFSET
);
1196 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1198 if ((Capability
& EFI_AHCI_CAP_SAM
) == 0) {
1199 AhciOrReg (AhciBar
, EFI_AHCI_GHC_OFFSET
, EFI_AHCI_GHC_ENABLE
);
1202 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_FB
;
1203 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciRFis
);
1206 // Single task envrionment, we only use one command table for all port
1208 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CLB
;
1209 AhciWriteReg (AhciBar
, Offset
, (UINT32
)(UINTN
)AhciRegisters
->AhciCmdList
);
1211 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_CMD
;
1212 Data
= AhciReadReg (AhciBar
, Offset
);
1213 if ((Data
& EFI_AHCI_PORT_CMD_CPD
) != 0) {
1214 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_POD
);
1217 if ((Capability
& BIT27
) != 0) {
1218 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_CMD_SUD
);
1222 // Disable aggressive power management.
1224 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SCTL
;
1225 AhciOrReg (AhciBar
, Offset
, EFI_AHCI_PORT_SCTL_IPM_INIT
);
1227 // Disable the reporting of the corresponding interrupt to system software.
1229 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_IE
;
1230 AhciAndReg (AhciBar
, Offset
, 0);
1232 Status
= AhciEnableFisReceive (
1237 ASSERT_EFI_ERROR (Status
);
1238 if (EFI_ERROR (Status
)) {
1243 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1244 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1246 PhyDetectDelay
= 16 * 1000;
1248 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SERR
;
1249 if (AhciReadReg(AhciBar
, Offset
) != 0) {
1250 AhciWriteReg (AhciBar
, Offset
, AhciReadReg(AhciBar
, Offset
));
1252 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_TFD
;
1254 Data
= AhciReadReg (AhciBar
, Offset
) & EFI_AHCI_PORT_TFD_MASK
;
1259 MicroSecondDelay (1000);
1261 } while (PhyDetectDelay
> 0);
1263 if (PhyDetectDelay
== 0) {
1264 return EFI_NOT_FOUND
;
1267 Offset
= EFI_AHCI_PORT_START
+ Port
* EFI_AHCI_PORT_REG_WIDTH
+ EFI_AHCI_PORT_SIG
;
1268 Status
= AhciWaitMmioSet (
1276 if (EFI_ERROR (Status
)) {