1 /** @file
2 This driver is used for Opal Password Feature support at AHCI mode.
3
4 Copyright (c) 2016, 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
9
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.
12
13 **/
14
15
16 #include "OpalPasswordSmm.h"
17
18 /**
19 Start command for give slot on specific port.
20
21 @param Port The number of port.
22 @param CommandSlot The number of CommandSlot.
23 @param Timeout The timeout Value of start.
24
25 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
26 @retval EFI_TIMEOUT The operation is time out.
27 @retval EFI_SUCCESS The command start successfully.
28
29 **/
30 EFI_STATUS
31 EFIAPI
32 AhciStartCommand (
33 IN UINT8 Port,
34 IN UINT8 CommandSlot,
35 IN UINT64 Timeout
36 );
37
38 /**
39 Stop command running for giving port
40
41 @param Port The number of port.
42 @param Timeout The timeout Value of stop.
43
44 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
45 @retval EFI_TIMEOUT The operation is time out.
46 @retval EFI_SUCCESS The command stop successfully.
47
48 **/
49 EFI_STATUS
50 EFIAPI
51 AhciStopCommand (
52 IN UINT8 Port,
53 IN UINT64 Timeout
54 );
55
56 /**
57 Read AHCI Operation register.
58
59 @param Offset The operation register offset.
60
61 @return The register content read.
62
63 **/
64 UINT32
65 EFIAPI
AhciReadReg(IN UINT32 Offset)66 AhciReadReg (
67 IN UINT32 Offset
68 )
69 {
70 UINT32 Data;
71
72 Data = 0;
73
74 Data = MmioRead32 (mAhciBar + Offset);
75
76 return Data;
77 }
78
79 /**
80 Write AHCI Operation register.
81
82 @param Offset The operation register offset.
83 @param Data The Data used to write down.
84
85 **/
86 VOID
87 EFIAPI
AhciWriteReg(IN UINT32 Offset,IN UINT32 Data)88 AhciWriteReg (
89 IN UINT32 Offset,
90 IN UINT32 Data
91 )
92 {
93 MmioWrite32 (mAhciBar + Offset, Data);
94
95 return ;
96 }
97
98 /**
99 Do AND operation with the Value of AHCI Operation register.
100
101 @param Offset The operation register offset.
102 @param AndData The Data used to do AND operation.
103
104 **/
105 VOID
106 EFIAPI
AhciAndReg(IN UINT32 Offset,IN UINT32 AndData)107 AhciAndReg (
108 IN UINT32 Offset,
109 IN UINT32 AndData
110 )
111 {
112 UINT32 Data;
113
114 Data = AhciReadReg (Offset);
115
116 Data &= AndData;
117
118 AhciWriteReg (Offset, Data);
119 }
120
121 /**
122 Do OR operation with the Value of AHCI Operation register.
123
124 @param Offset The operation register offset.
125 @param OrData The Data used to do OR operation.
126
127 **/
128 VOID
129 EFIAPI
AhciOrReg(IN UINT32 Offset,IN UINT32 OrData)130 AhciOrReg (
131 IN UINT32 Offset,
132 IN UINT32 OrData
133 )
134 {
135 UINT32 Data;
136
137 Data = AhciReadReg (Offset);
138
139 Data |= OrData;
140
141 AhciWriteReg (Offset, Data);
142 }
143
144 /**
145 Wait for memory set to the test Value.
146
147 @param Offset The memory address to test.
148 @param MaskValue The mask Value of memory.
149 @param TestValue The test Value of memory.
150 @param Timeout The time out Value for wait memory set.
151
152 @retval EFI_DEVICE_ERROR The memory is not set.
153 @retval EFI_TIMEOUT The memory setting is time out.
154 @retval EFI_SUCCESS The memory is correct set.
155
156 **/
157 EFI_STATUS
158 EFIAPI
AhciWaitMmioSet(IN UINT32 Offset,IN UINT32 MaskValue,IN UINT32 TestValue,IN UINT64 Timeout)159 AhciWaitMmioSet (
160 IN UINT32 Offset,
161 IN UINT32 MaskValue,
162 IN UINT32 TestValue,
163 IN UINT64 Timeout
164 )
165 {
166 UINT32 Value;
167 UINT32 Delay;
168
169 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
170
171 do {
172 Value = AhciReadReg (Offset) & MaskValue;
173
174 if (Value == TestValue) {
175 return EFI_SUCCESS;
176 }
177
178 //
179 // Stall for 100 microseconds.
180 //
181 MicroSecondDelay (100);
182
183 Delay--;
184
185 } while (Delay > 0);
186
187 return EFI_TIMEOUT;
188 }
189 /**
190 Wait for the Value of the specified system memory set to the test Value.
191
192 @param Address The system memory address to test.
193 @param MaskValue The mask Value of memory.
194 @param TestValue The test Value of memory.
195 @param Timeout The time out Value for wait memory set, uses 100ns as a unit.
196
197 @retval EFI_TIMEOUT The system memory setting is time out.
198 @retval EFI_SUCCESS The system memory is correct set.
199
200 **/
201 EFI_STATUS
202 EFIAPI
AhciWaitMemSet(IN EFI_PHYSICAL_ADDRESS Address,IN UINT32 MaskValue,IN UINT32 TestValue,IN UINT64 Timeout)203 AhciWaitMemSet (
204 IN EFI_PHYSICAL_ADDRESS Address,
205 IN UINT32 MaskValue,
206 IN UINT32 TestValue,
207 IN UINT64 Timeout
208 )
209 {
210 UINT32 Value;
211 UINT32 Delay;
212
213 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
214
215 do {
216 //
217 // Access sytem memory to see if the Value is the tested one.
218 //
219 // The system memory pointed by Address will be updated by the
220 // SATA Host Controller, "volatile" is introduced to prevent
221 // compiler from optimizing the access to the memory address
222 // to only read once.
223 //
224 Value = *(volatile UINT32 *) (UINTN) Address;
225 Value &= MaskValue;
226
227 if (Value == TestValue) {
228 return EFI_SUCCESS;
229 }
230
231 //
232 // Stall for 100 microseconds.
233 //
234 MicroSecondDelay (100);
235
236 Delay--;
237
238 } while (Delay > 0);
239
240 return EFI_TIMEOUT;
241 }
242
243 /**
244 Check the memory status to the test Value.
245
246 @param[in] Address The memory address to test.
247 @param[in] MaskValue The mask Value of memory.
248 @param[in] TestValue The test Value of memory.
249 @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once.
250
251 @retval EFI_NOTREADY The memory is not set.
252 @retval EFI_TIMEOUT The memory setting retry times out.
253 @retval EFI_SUCCESS The memory is correct set.
254
255 **/
256 EFI_STATUS
257 EFIAPI
AhciCheckMemSet(IN UINTN Address,IN UINT32 MaskValue,IN UINT32 TestValue,IN OUT UINTN * RetryTimes OPTIONAL)258 AhciCheckMemSet (
259 IN UINTN Address,
260 IN UINT32 MaskValue,
261 IN UINT32 TestValue,
262 IN OUT UINTN *RetryTimes OPTIONAL
263 )
264 {
265 UINT32 Value;
266
267 if (RetryTimes != NULL) {
268 (*RetryTimes)--;
269 }
270
271 Value = *(volatile UINT32 *) Address;
272 Value &= MaskValue;
273
274 if (Value == TestValue) {
275 return EFI_SUCCESS;
276 }
277
278 if ((RetryTimes != NULL) && (*RetryTimes == 0)) {
279 return EFI_TIMEOUT;
280 } else {
281 return EFI_NOT_READY;
282 }
283 }
284
285 /**
286 Clear the port interrupt and error status. It will also clear
287 HBA interrupt status.
288
289 @param Port The number of port.
290
291 **/
292 VOID
293 EFIAPI
AhciClearPortStatus(IN UINT8 Port)294 AhciClearPortStatus (
295 IN UINT8 Port
296 )
297 {
298 UINT32 Offset;
299
300 //
301 // Clear any error status
302 //
303 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
304 AhciWriteReg (Offset, AhciReadReg (Offset));
305
306 //
307 // Clear any port interrupt status
308 //
309 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;
310 AhciWriteReg (Offset, AhciReadReg (Offset));
311
312 //
313 // Clear any HBA interrupt status
314 //
315 AhciWriteReg (EFI_AHCI_IS_OFFSET, AhciReadReg (EFI_AHCI_IS_OFFSET));
316 }
317
318 /**
319 Enable the FIS running for giving port.
320
321 @param Port The number of port.
322 @param Timeout The timeout Value of enabling FIS.
323
324 @retval EFI_DEVICE_ERROR The FIS enable setting fails.
325 @retval EFI_TIMEOUT The FIS enable setting is time out.
326 @retval EFI_SUCCESS The FIS enable successfully.
327
328 **/
329 EFI_STATUS
330 EFIAPI
AhciEnableFisReceive(IN UINT8 Port,IN UINT64 Timeout)331 AhciEnableFisReceive (
332 IN UINT8 Port,
333 IN UINT64 Timeout
334 )
335 {
336 UINT32 Offset;
337
338 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
339 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_FRE);
340
341 return AhciWaitMmioSet (
342 Offset,
343 EFI_AHCI_PORT_CMD_FR,
344 EFI_AHCI_PORT_CMD_FR,
345 Timeout
346 );
347 }
348
349 /**
350 Disable the FIS running for giving port.
351
352 @param Port The number of port.
353 @param Timeout The timeout Value of disabling FIS.
354
355 @retval EFI_DEVICE_ERROR The FIS disable setting fails.
356 @retval EFI_TIMEOUT The FIS disable setting is time out.
357 @retval EFI_UNSUPPORTED The port is in running state.
358 @retval EFI_SUCCESS The FIS disable successfully.
359
360 **/
361 EFI_STATUS
362 EFIAPI
AhciDisableFisReceive(IN UINT8 Port,IN UINT64 Timeout)363 AhciDisableFisReceive (
364 IN UINT8 Port,
365 IN UINT64 Timeout
366 )
367 {
368 UINT32 Offset;
369 UINT32 Data;
370
371 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
372 Data = AhciReadReg (Offset);
373
374 //
375 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.
376 //
377 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {
378 return EFI_UNSUPPORTED;
379 }
380
381 //
382 // Check if the Fis receive DMA engine for the port is running.
383 //
384 if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {
385 return EFI_SUCCESS;
386 }
387
388 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));
389
390 return AhciWaitMmioSet (
391 Offset,
392 EFI_AHCI_PORT_CMD_FR,
393 0,
394 Timeout
395 );
396 }
397
398 /**
399 Build the command list, command table and prepare the fis receiver.
400
401 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
402 @param Port The number of port.
403 @param PortMultiplier The timeout Value of stop.
404 @param CommandFis The control fis will be used for the transfer.
405 @param CommandList The command list will be used for the transfer.
406 @param AtapiCommand The atapi command will be used for the transfer.
407 @param AtapiCommandLength The Length of the atapi command.
408 @param CommandSlotNumber The command slot will be used for the transfer.
409 @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address.
410 @param DataLength The Data count to be transferred.
411
412 **/
413 VOID
414 EFIAPI
AhciBuildCommand(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN EFI_AHCI_COMMAND_FIS * CommandFis,IN EFI_AHCI_COMMAND_LIST * CommandList,IN EFI_AHCI_ATAPI_COMMAND * AtapiCommand OPTIONAL,IN UINT8 AtapiCommandLength,IN UINT8 CommandSlotNumber,IN OUT VOID * DataPhysicalAddr,IN UINT64 DataLength)415 AhciBuildCommand (
416 IN EFI_AHCI_REGISTERS *AhciRegisters,
417 IN UINT8 Port,
418 IN UINT8 PortMultiplier,
419 IN EFI_AHCI_COMMAND_FIS *CommandFis,
420 IN EFI_AHCI_COMMAND_LIST *CommandList,
421 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
422 IN UINT8 AtapiCommandLength,
423 IN UINT8 CommandSlotNumber,
424 IN OUT VOID *DataPhysicalAddr,
425 IN UINT64 DataLength
426 )
427 {
428 UINT64 BaseAddr;
429 UINT64 PrdtNumber;
430 UINTN RemainedData;
431 UINTN MemAddr;
432 DATA_64 Data64;
433 UINT32 Offset;
434
435 //
436 // Filling the PRDT
437 //
438 PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);
439
440 //
441 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.
442 // It also limits that the maximum amount of the PRDT entry in the command table
443 // is 65535.
444 //
445 ASSERT (PrdtNumber <= 1);
446
447 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);
448
449 BaseAddr = Data64.Uint64;
450
451 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));
452
453 ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));
454
455 CommandFis->AhciCFisPmNum = PortMultiplier;
456
457 CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));
458
459 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
460 if (AtapiCommand != NULL) {
461 CopyMem (
462 &AhciRegisters->AhciCommandTable->AtapiCmd,
463 AtapiCommand,
464 AtapiCommandLength
465 );
466
467 CommandList->AhciCmdA = 1;
468 CommandList->AhciCmdP = 1;
469
470 AhciOrReg (Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
471 } else {
472 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));
473 }
474
475 RemainedData = (UINTN) DataLength;
476 MemAddr = (UINTN) DataPhysicalAddr;
477 CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;
478
479 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;
480
481 Data64.Uint64 = (UINT64)MemAddr;
482 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba = Data64.Uint32.Lower32;
483 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;
484
485 //
486 // Set the last PRDT to Interrupt On Complete
487 //
488 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;
489
490 CopyMem (
491 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),
492 CommandList,
493 sizeof (EFI_AHCI_COMMAND_LIST)
494 );
495
496 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;
497 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;
498 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;
499 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;
500
501 }
502
503 /**
504 Buid a command FIS.
505
506 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.
507 @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.
508
509 **/
510 VOID
511 EFIAPI
AhciBuildCommandFis(IN OUT EFI_AHCI_COMMAND_FIS * CmdFis,IN EFI_ATA_COMMAND_BLOCK * AtaCommandBlock)512 AhciBuildCommandFis (
513 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,
514 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock
515 )
516 {
517 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));
518
519 CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;
520 //
521 // Indicator it's a command
522 //
523 CmdFis->AhciCFisCmdInd = 0x1;
524 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;
525
526 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;
527 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;
528
529 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;
530 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;
531
532 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;
533 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;
534
535 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;
536 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;
537
538 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;
539 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;
540
541 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);
542 }
543
544 /**
545 Start a PIO Data transfer on specific port.
546
547 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
548 @param Port The number of port.
549 @param PortMultiplier The timeout Value of stop.
550 @param AtapiCommand The atapi command will be used for the transfer.
551 @param AtapiCommandLength The Length of the atapi command.
552 @param Read The transfer direction.
553 @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.
554 @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.
555 @param MemoryAddr The pointer to the Data Buffer.
556 @param DataCount The Data count to be transferred.
557 @param Timeout The timeout Value of non Data transfer.
558
559 @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.
560 @retval EFI_TIMEOUT The operation is time out.
561 @retval EFI_UNSUPPORTED The device is not ready for transfer.
562 @retval EFI_SUCCESS The PIO Data transfer executes successfully.
563
564 **/
565 EFI_STATUS
566 EFIAPI
AhciPioTransfer(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN EFI_AHCI_ATAPI_COMMAND * AtapiCommand OPTIONAL,IN UINT8 AtapiCommandLength,IN BOOLEAN Read,IN EFI_ATA_COMMAND_BLOCK * AtaCommandBlock,IN OUT EFI_ATA_STATUS_BLOCK * AtaStatusBlock,IN OUT VOID * MemoryAddr,IN UINT32 DataCount,IN UINT64 Timeout)567 AhciPioTransfer (
568 IN EFI_AHCI_REGISTERS *AhciRegisters,
569 IN UINT8 Port,
570 IN UINT8 PortMultiplier,
571 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,
572 IN UINT8 AtapiCommandLength,
573 IN BOOLEAN Read,
574 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,
575 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,
576 IN OUT VOID *MemoryAddr,
577 IN UINT32 DataCount,
578 IN UINT64 Timeout
579 )
580 {
581 EFI_STATUS Status;
582 UINT32 FisBaseAddr;
583 UINT32 Offset;
584 UINT32 Delay;
585 EFI_AHCI_COMMAND_FIS CFis;
586 EFI_AHCI_COMMAND_LIST CmdList;
587 UINT32 PortTfd;
588 UINT32 PrdCount;
589 UINT32 OldRfisLo;
590 UINT32 OldRfisHi;
591 UINT32 OldCmdListLo;
592 UINT32 OldCmdListHi;
593
594 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
595 OldRfisLo = AhciReadReg (Offset);
596 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
597 OldRfisHi = AhciReadReg (Offset);
598 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
599 AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);
600 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
601 AhciWriteReg (Offset, 0);
602
603 //
604 // Single task envrionment, we only use one command table for all port
605 //
606 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
607 OldCmdListLo = AhciReadReg (Offset);
608 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
609 OldCmdListHi = AhciReadReg (Offset);
610 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
611 AhciWriteReg (Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);
612 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
613 AhciWriteReg (Offset, 0);
614
615 //
616 // Package read needed
617 //
618 AhciBuildCommandFis (&CFis, AtaCommandBlock);
619
620 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));
621
622 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;
623 CmdList.AhciCmdW = Read ? 0 : 1;
624
625 AhciBuildCommand (
626 AhciRegisters,
627 Port,
628 PortMultiplier,
629 &CFis,
630 &CmdList,
631 AtapiCommand,
632 AtapiCommandLength,
633 0,
634 (VOID *)(UINTN)MemoryAddr,
635 DataCount
636 );
637
638 Status = AhciStartCommand (
639 Port,
640 0,
641 Timeout
642 );
643 if (EFI_ERROR (Status)) {
644 goto Exit;
645 }
646
647 //
648 // Checking the status and wait the driver sending Data
649 //
650 FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;
651 if (Read && (AtapiCommand == 0)) {
652 //
653 // Wait device sends the PIO setup fis before Data transfer
654 //
655 Status = EFI_TIMEOUT;
656 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);
657 do {
658 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;
659
660 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);
661 if (!EFI_ERROR (Status)) {
662 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
663 PortTfd = AhciReadReg ((UINT32) Offset);
664 //
665 // PxTFD will be updated if there is a D2H or SetupFIS received.
666 // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.
667 //
668 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
669 Status = EFI_DEVICE_ERROR;
670 break;
671 }
672
673 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));
674 if (PrdCount == DataCount) {
675 break;
676 }
677 }
678
679 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
680 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);
681 if (!EFI_ERROR (Status)) {
682 Status = EFI_DEVICE_ERROR;
683 break;
684 }
685
686 //
687 // Stall for 100 microseconds.
688 //
689 MicroSecondDelay(100);
690
691 Delay--;
692 } while (Delay > 0);
693 } else {
694 //
695 // Wait for D2H Fis is received
696 //
697 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;
698 Status = AhciWaitMemSet (
699 Offset,
700 EFI_AHCI_FIS_TYPE_MASK,
701 EFI_AHCI_FIS_REGISTER_D2H,
702 Timeout
703 );
704
705 if (EFI_ERROR (Status)) {
706 goto Exit;
707 }
708
709 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
710 PortTfd = AhciReadReg ((UINT32) Offset);
711 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {
712 Status = EFI_DEVICE_ERROR;
713 }
714 }
715
716 Exit:
717 AhciStopCommand (
718 Port,
719 Timeout
720 );
721
722 AhciDisableFisReceive (
723 Port,
724 Timeout
725 );
726
727 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
728 AhciWriteReg (Offset, OldRfisLo);
729 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;
730 AhciWriteReg (Offset, OldRfisHi);
731
732 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
733 AhciWriteReg (Offset, OldCmdListLo);
734 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;
735 AhciWriteReg (Offset, OldCmdListHi);
736
737 return Status;
738 }
739
740 /**
741 Stop command running for giving port
742
743 @param Port The number of port.
744 @param Timeout The timeout Value of stop.
745
746 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.
747 @retval EFI_TIMEOUT The operation is time out.
748 @retval EFI_SUCCESS The command stop successfully.
749
750 **/
751 EFI_STATUS
752 EFIAPI
AhciStopCommand(IN UINT8 Port,IN UINT64 Timeout)753 AhciStopCommand (
754 IN UINT8 Port,
755 IN UINT64 Timeout
756 )
757 {
758 UINT32 Offset;
759 UINT32 Data;
760
761 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
762 Data = AhciReadReg (Offset);
763
764 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {
765 return EFI_SUCCESS;
766 }
767
768 if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {
769 AhciAndReg (Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));
770 }
771
772 return AhciWaitMmioSet (
773 Offset,
774 EFI_AHCI_PORT_CMD_CR,
775 0,
776 Timeout
777 );
778 }
779
780 /**
781 Start command for give slot on specific port.
782
783 @param Port The number of port.
784 @param CommandSlot The number of CommandSlot.
785 @param Timeout The timeout Value of start.
786
787 @retval EFI_DEVICE_ERROR The command start unsuccessfully.
788 @retval EFI_TIMEOUT The operation is time out.
789 @retval EFI_SUCCESS The command start successfully.
790
791 **/
792 EFI_STATUS
793 EFIAPI
AhciStartCommand(IN UINT8 Port,IN UINT8 CommandSlot,IN UINT64 Timeout)794 AhciStartCommand (
795 IN UINT8 Port,
796 IN UINT8 CommandSlot,
797 IN UINT64 Timeout
798 )
799 {
800 UINT32 CmdSlotBit;
801 EFI_STATUS Status;
802 UINT32 PortStatus;
803 UINT32 StartCmd;
804 UINT32 PortTfd;
805 UINT32 Offset;
806 UINT32 Capability;
807
808 //
809 // Collect AHCI controller information
810 //
811 Capability = AhciReadReg(EFI_AHCI_CAPABILITY_OFFSET);
812
813 CmdSlotBit = (UINT32) (1 << CommandSlot);
814
815 AhciClearPortStatus (
816 Port
817 );
818
819 Status = AhciEnableFisReceive (
820 Port,
821 Timeout
822 );
823
824 if (EFI_ERROR (Status)) {
825 return Status;
826 }
827
828 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
829 PortStatus = AhciReadReg (Offset);
830
831 StartCmd = 0;
832 if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {
833 StartCmd = AhciReadReg (Offset);
834 StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;
835 StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;
836 }
837
838 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
839 PortTfd = AhciReadReg (Offset);
840
841 if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {
842 if ((Capability & BIT24) != 0) {
843 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
844 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_COL);
845
846 AhciWaitMmioSet (
847 Offset,
848 EFI_AHCI_PORT_CMD_COL,
849 0,
850 Timeout
851 );
852 }
853 }
854
855 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
856 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);
857
858 //
859 // Setting the command
860 //
861 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;
862 AhciAndReg (Offset, 0);
863 AhciOrReg (Offset, CmdSlotBit);
864
865 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;
866 AhciAndReg (Offset, 0);
867 AhciOrReg (Offset, CmdSlotBit);
868 return EFI_SUCCESS;
869 }
870
871
872 /**
873 Do AHCI HBA reset.
874
875 @param[in] Timeout The timeout Value of reset.
876
877 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.
878 @retval EFI_TIMEOUT The reset operation is time out.
879 @retval EFI_SUCCESS AHCI controller is reset successfully.
880
881 **/
882 EFI_STATUS
883 EFIAPI
AhciReset(IN UINT64 Timeout)884 AhciReset (
885 IN UINT64 Timeout
886 )
887 {
888 UINT32 Delay;
889 UINT32 Value;
890 UINT32 Capability;
891
892 //
893 // Collect AHCI controller information
894 //
895 Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
896
897 //
898 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
899 //
900 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
901 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
902 }
903
904 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);
905
906 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);
907
908 do {
909 Value = AhciReadReg(EFI_AHCI_GHC_OFFSET);
910 if ((Value & EFI_AHCI_GHC_RESET) == 0) {
911 return EFI_SUCCESS;
912 }
913
914 //
915 // Stall for 100 microseconds.
916 //
917 MicroSecondDelay(100);
918
919 Delay--;
920 } while (Delay > 0);
921
922 return EFI_TIMEOUT;
923
924
925 }
926
927 /**
928 Send Buffer cmd to specific device.
929
930 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.
931 @param[in] Port The port number of attached ATA device.
932 @param[in] PortMultiplier The port number of port multiplier of attached ATA device.
933 @param[in, out] Buffer The Data Buffer to store IDENTIFY PACKET Data.
934
935 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.
936 @retval EFI_TIMEOUT The operation is time out.
937 @retval EFI_UNSUPPORTED The device is not ready for executing.
938 @retval EFI_SUCCESS The cmd executes successfully.
939
940 **/
941 EFI_STATUS
942 EFIAPI
AhciIdentify(IN EFI_AHCI_REGISTERS * AhciRegisters,IN UINT8 Port,IN UINT8 PortMultiplier,IN OUT ATA_IDENTIFY_DATA * Buffer)943 AhciIdentify (
944 IN EFI_AHCI_REGISTERS *AhciRegisters,
945 IN UINT8 Port,
946 IN UINT8 PortMultiplier,
947 IN OUT ATA_IDENTIFY_DATA *Buffer
948 )
949 {
950 EFI_STATUS Status;
951 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;
952
953 if (AhciRegisters == NULL || Buffer == NULL) {
954 return EFI_INVALID_PARAMETER;
955 }
956
957 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));
958
959 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;
960 AtaCommandBlock.AtaSectorCount = 1;
961
962 Status = AhciPioTransfer (
963 AhciRegisters,
964 Port,
965 PortMultiplier,
966 NULL,
967 0,
968 TRUE,
969 &AtaCommandBlock,
970 NULL,
971 Buffer,
972 sizeof (ATA_IDENTIFY_DATA),
973 ATA_TIMEOUT
974 );
975
976 return Status;
977 }
978
979 /**
980 Get AHCI mode MMIO Bar Size.
981
982 @param[in] Bus The bus number of ata host controller.
983 @param[in] Device The device number of ata host controller.
984 @param[in] Function The function number of ata host controller.
985
986 @retval The Size of AHCI MMIO BAR.
987
988 **/
989 UINT32
990 EFIAPI
GetAhciBarSize(IN UINTN Bus,IN UINTN Device,IN UINTN Function)991 GetAhciBarSize (
992 IN UINTN Bus,
993 IN UINTN Device,
994 IN UINTN Function
995 )
996 {
997 UINT32 Size;
998 UINT32 OldBar;
999
1000 OldBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1001 //
1002 // Disable PCI CMD.MSE bit before calculating MMIO Bar Size as it needs write all 1 to BAR register.
1003 //
1004 PciAnd32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), (UINT32)~BIT1);
1005
1006 //
1007 // Get AHCI MMIO Bar Size.
1008 //
1009 PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), 0xFFFFFFFF);
1010 Size = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1011 Size = (~(Size & 0xFFFFFFF0)) + 1;
1012
1013 //
1014 // Restore old MMIO Bar.
1015 //
1016 PciWrite32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24), OldBar);
1017 //
1018 // Enable PCI CMD.MSE bit after restoring MMIO Bar.
1019 //
1020 PciOr32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x04), BIT1);
1021
1022 return Size;
1023 }
1024
1025 /**
1026 This function check if the memory region is in GCD MMIO region.
1027
1028 @param Addr The memory region start address to be checked.
1029 @param Size The memory region length to be checked.
1030
1031 @retval TRUE This memory region is in GCD MMIO region.
1032 @retval FALSE This memory region is not in GCD MMIO region.
1033 **/
1034 BOOLEAN
1035 EFIAPI
OpalIsValidMmioSpace(IN EFI_PHYSICAL_ADDRESS Addr,IN UINTN Size)1036 OpalIsValidMmioSpace (
1037 IN EFI_PHYSICAL_ADDRESS Addr,
1038 IN UINTN Size
1039 )
1040 {
1041 UINTN Index;
1042 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
1043
1044 for (Index = 0; Index < mNumberOfDescriptors; Index ++) {
1045 Desc = &mGcdMemSpace[Index];
1046 if ((Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && (Addr >= Desc->BaseAddress) && ((Addr + Size) <= (Desc->BaseAddress + Desc->Length))) {
1047 return TRUE;
1048 }
1049 }
1050
1051 return FALSE;
1052 }
1053 /**
1054 Get AHCI mode base address registers' Value.
1055
1056 @param[in] Bus The bus number of ata host controller.
1057 @param[in] Device The device number of ata host controller.
1058 @param[in] Function The function number of ata host controller.
1059
1060 @retval EFI_UNSUPPORTED Return this Value when the BARs is not IO type
1061 @retval EFI_SUCCESS Get the Base address successfully
1062 @retval Other Read the pci configureation Data error
1063
1064 **/
1065 EFI_STATUS
1066 EFIAPI
GetAhciBaseAddress(IN UINTN Bus,IN UINTN Device,IN UINTN Function)1067 GetAhciBaseAddress (
1068 IN UINTN Bus,
1069 IN UINTN Device,
1070 IN UINTN Function
1071 )
1072 {
1073 UINT32 Size;
1074
1075 //
1076 // Get AHCI MMIO Bar
1077 //
1078 mAhciBar = PciRead32 (PCI_LIB_ADDRESS (Bus, Device, Function, 0x24));
1079 //
1080 // Get AHCI MMIO Bar Size
1081 //
1082 Size = GetAhciBarSize (Bus, Device, Function);
1083 //
1084 // Check if the AHCI Bar region is in SMRAM to avoid malicious attack by modifying MMIO Bar to point to SMRAM.
1085 //
1086 if (!OpalIsValidMmioSpace ((EFI_PHYSICAL_ADDRESS)mAhciBar, Size)) {
1087 return EFI_UNSUPPORTED;
1088 }
1089
1090 return EFI_SUCCESS;
1091 }
1092
1093 /**
1094 Allocate transfer-related Data struct which is used at AHCI mode.
1095
1096 @retval EFI_OUT_OF_RESOURCE The allocation is failure.
1097 @retval EFI_SUCCESS Successful to allocate memory.
1098
1099 **/
1100 EFI_STATUS
1101 EFIAPI
AhciAllocateResource(VOID)1102 AhciAllocateResource (
1103 VOID
1104 )
1105 {
1106 EFI_STATUS Status;
1107 EFI_PHYSICAL_ADDRESS Base;
1108
1109 //
1110 // Allocate resources required by AHCI host controller.
1111 //
1112 Base = 0xFFFFFFFF;
1113 Status = gBS->AllocatePages (
1114 AllocateMaxAddress,
1115 EfiACPIMemoryNVS,
1116 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),
1117 &Base
1118 );
1119 if (EFI_ERROR (Status)) {
1120 return EFI_OUT_OF_RESOURCES;
1121 }
1122
1123 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1124 mAhciRegisters.AhciRFis = (VOID *)(UINTN)Base;
1125
1126 Base = 0xFFFFFFFF;
1127 Status = gBS->AllocatePages (
1128 AllocateMaxAddress,
1129 EfiACPIMemoryNVS,
1130 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),
1131 &Base
1132 );
1133 if (EFI_ERROR (Status)) {
1134 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1135 return EFI_OUT_OF_RESOURCES;
1136 }
1137 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1138 mAhciRegisters.AhciCmdList = (VOID *)(UINTN)Base;
1139
1140 Base = 0xFFFFFFFF;
1141 Status = gBS->AllocatePages (
1142 AllocateMaxAddress,
1143 EfiACPIMemoryNVS,
1144 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),
1145 &Base
1146 );
1147 if (EFI_ERROR (Status)) {
1148 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1149 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1150 return EFI_OUT_OF_RESOURCES;
1151 }
1152 ZeroMem ((VOID *)(UINTN)Base, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1153 mAhciRegisters.AhciCommandTable = (VOID *)(UINTN)Base;
1154 return EFI_SUCCESS;
1155 }
1156
1157 /**
1158 Free allocated transfer-related Data struct which is used at AHCI mode.
1159
1160 **/
1161 VOID
1162 EFIAPI
AhciFreeResource(VOID)1163 AhciFreeResource (
1164 VOID
1165 )
1166 {
1167 if (mAhciRegisters.AhciRFis != NULL) {
1168 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciRFis, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));
1169 }
1170
1171 if (mAhciRegisters.AhciCmdList != NULL) {
1172 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCmdList, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));
1173 }
1174
1175 if (mAhciRegisters.AhciCommandTable != NULL) {
1176 gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)mAhciRegisters.AhciCommandTable, EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));
1177 }
1178 }
1179
1180 /**
1181 Initialize ATA host controller at AHCI mode.
1182
1183 The function is designed to initialize ATA host controller.
1184
1185 @param[in] Port The port number to do initialization.
1186
1187 **/
1188 EFI_STATUS
1189 EFIAPI
AhciModeInitialize(UINT8 Port)1190 AhciModeInitialize (
1191 UINT8 Port
1192 )
1193 {
1194 EFI_STATUS Status;
1195 UINT32 Capability;
1196 UINT32 Offset;
1197 UINT32 Data;
1198 UINT32 PhyDetectDelay;
1199
1200 Status = AhciReset (ATA_TIMEOUT);
1201 if (EFI_ERROR (Status)) {
1202 return Status;
1203 }
1204
1205 //
1206 // Collect AHCI controller information
1207 //
1208 Capability = AhciReadReg (EFI_AHCI_CAPABILITY_OFFSET);
1209
1210 //
1211 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set
1212 //
1213 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {
1214 AhciOrReg (EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);
1215 }
1216
1217 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;
1218 AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciRFis);
1219
1220 //
1221 // Single task envrionment, we only use one command table for all port
1222 //
1223 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;
1224 AhciWriteReg (Offset, (UINT32)(UINTN)mAhciRegisters.AhciCmdList);
1225
1226 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;
1227 Data = AhciReadReg (Offset);
1228 if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {
1229 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_POD);
1230 }
1231
1232 if ((Capability & BIT27) != 0) {
1233 AhciOrReg (Offset, EFI_AHCI_PORT_CMD_SUD);
1234 }
1235
1236 //
1237 // Disable aggressive power management.
1238 //
1239 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;
1240 AhciOrReg (Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);
1241 //
1242 // Disable the reporting of the corresponding interrupt to system software.
1243 //
1244 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;
1245 AhciAndReg (Offset, 0);
1246
1247 Status = AhciEnableFisReceive (
1248 Port,
1249 EFI_TIMER_PERIOD_MILLISECONDS(500)
1250 );
1251 ASSERT_EFI_ERROR (Status);
1252 if (EFI_ERROR (Status)) {
1253 return Status;
1254 }
1255
1256 //
1257 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ
1258 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.
1259 //
1260 PhyDetectDelay = 16 * 1000;
1261 do {
1262 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;
1263 if (AhciReadReg(Offset) != 0) {
1264 AhciWriteReg (Offset, AhciReadReg(Offset));
1265 }
1266 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;
1267
1268 Data = AhciReadReg (Offset) & EFI_AHCI_PORT_TFD_MASK;
1269 if (Data == 0) {
1270 break;
1271 }
1272
1273 MicroSecondDelay (1000);
1274 PhyDetectDelay--;
1275 } while (PhyDetectDelay > 0);
1276
1277 if (PhyDetectDelay == 0) {
1278 return EFI_NOT_FOUND;
1279 }
1280
1281 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;
1282 Status = AhciWaitMmioSet (
1283 Offset,
1284 0x0000FFFF,
1285 0x00000101,
1286 EFI_TIMER_PERIOD_SECONDS(16)
1287 );
1288
1289 if (EFI_ERROR (Status)) {
1290 return Status;
1291 }
1292
1293 return Status;
1294 }
1295
1296