]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
IntelSiliconPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AtaAtapiPassThru / AhciMode.c
CommitLineData
a41b5272 1/** @file\r
2 The file for AHCI mode of ATA host controller.\r
1aff716a 3\r
8d3c4b55 4 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
9c32277a 5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
1aff716a 6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
a41b5272 10\r
1aff716a 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
a41b5272 13\r
14**/\r
15\r
16#include "AtaAtapiPassThru.h"\r
17\r
18/**\r
19 Read AHCI Operation register.\r
20\r
21 @param PciIo The PCI IO protocol instance.\r
22 @param Offset The operation register offset.\r
23\r
24 @return The register content read.\r
25\r
26**/\r
27UINT32\r
28EFIAPI\r
29AhciReadReg (\r
30 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
31 IN UINT32 Offset\r
32 )\r
33{\r
34 UINT32 Data;\r
35\r
36 ASSERT (PciIo != NULL);\r
1aff716a 37\r
a41b5272 38 Data = 0;\r
39\r
40 PciIo->Mem.Read (\r
41 PciIo,\r
42 EfiPciIoWidthUint32,\r
43 EFI_AHCI_BAR_INDEX,\r
44 (UINT64) Offset,\r
45 1,\r
46 &Data\r
47 );\r
48\r
49 return Data;\r
50}\r
51\r
52/**\r
53 Write AHCI Operation register.\r
54\r
55 @param PciIo The PCI IO protocol instance.\r
56 @param Offset The operation register offset.\r
57 @param Data The data used to write down.\r
58\r
59**/\r
60VOID\r
61EFIAPI\r
62AhciWriteReg (\r
63 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
64 IN UINT32 Offset,\r
65 IN UINT32 Data\r
66 )\r
67{\r
68 ASSERT (PciIo != NULL);\r
69\r
70 PciIo->Mem.Write (\r
71 PciIo,\r
72 EfiPciIoWidthUint32,\r
73 EFI_AHCI_BAR_INDEX,\r
74 (UINT64) Offset,\r
75 1,\r
76 &Data\r
77 );\r
78\r
79 return ;\r
80}\r
81\r
82/**\r
83 Do AND operation with the value of AHCI Operation register.\r
84\r
85 @param PciIo The PCI IO protocol instance.\r
86 @param Offset The operation register offset.\r
87 @param AndData The data used to do AND operation.\r
88\r
89**/\r
90VOID\r
91EFIAPI\r
92AhciAndReg (\r
93 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
94 IN UINT32 Offset,\r
95 IN UINT32 AndData\r
96 )\r
97{\r
98 UINT32 Data;\r
1aff716a 99\r
a41b5272 100 ASSERT (PciIo != NULL);\r
101\r
102 Data = AhciReadReg (PciIo, Offset);\r
103\r
104 Data &= AndData;\r
105\r
106 AhciWriteReg (PciIo, Offset, Data);\r
107}\r
108\r
109/**\r
110 Do OR operation with the value of AHCI Operation register.\r
111\r
112 @param PciIo The PCI IO protocol instance.\r
113 @param Offset The operation register offset.\r
114 @param OrData The data used to do OR operation.\r
115\r
116**/\r
117VOID\r
118EFIAPI\r
119AhciOrReg (\r
120 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
121 IN UINT32 Offset,\r
122 IN UINT32 OrData\r
123 )\r
124{\r
125 UINT32 Data;\r
126\r
127 ASSERT (PciIo != NULL);\r
128\r
129 Data = AhciReadReg (PciIo, Offset);\r
130\r
131 Data |= OrData;\r
132\r
133 AhciWriteReg (PciIo, Offset, Data);\r
134}\r
135\r
136/**\r
8536cc4b 137 Wait for the value of the specified MMIO register set to the test value.\r
1aff716a 138\r
aca84419 139 @param PciIo The PCI IO protocol instance.\r
8536cc4b 140 @param Offset The MMIO address to test.\r
aca84419 141 @param MaskValue The mask value of memory.\r
142 @param TestValue The test value of memory.\r
8536cc4b 143 @param Timeout The time out value for wait memory set, uses 100ns as a unit.\r
a41b5272 144\r
8536cc4b 145 @retval EFI_TIMEOUT The MMIO setting is time out.\r
146 @retval EFI_SUCCESS The MMIO is correct set.\r
a41b5272 147\r
148**/\r
149EFI_STATUS\r
150EFIAPI\r
8536cc4b 151AhciWaitMmioSet (\r
a41b5272 152 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
8536cc4b 153 IN UINTN Offset,\r
a41b5272 154 IN UINT32 MaskValue,\r
155 IN UINT32 TestValue,\r
156 IN UINT64 Timeout\r
157 )\r
158{\r
1aff716a 159 UINT32 Value;\r
ab82122d
TF
160 UINT64 Delay;\r
161 BOOLEAN InfiniteWait;\r
a41b5272 162\r
ab82122d
TF
163 if (Timeout == 0) {\r
164 InfiniteWait = TRUE;\r
165 } else {\r
166 InfiniteWait = FALSE;\r
167 }\r
168\r
169 Delay = DivU64x32 (Timeout, 1000) + 1;\r
a41b5272 170\r
171 do {\r
8536cc4b 172 //\r
173 // Access PCI MMIO space to see if the value is the tested one.\r
174 //\r
175 Value = AhciReadReg (PciIo, (UINT32) Offset) & MaskValue;\r
a41b5272 176\r
177 if (Value == TestValue) {\r
178 return EFI_SUCCESS;\r
179 }\r
180\r
181 //\r
182 // Stall for 100 microseconds.\r
183 //\r
184 MicroSecondDelay (100);\r
185\r
186 Delay--;\r
187\r
ab82122d 188 } while (InfiniteWait || (Delay > 0));\r
a41b5272 189\r
8536cc4b 190 return EFI_TIMEOUT;\r
191}\r
192\r
193/**\r
194 Wait for the value of the specified system memory set to the test value.\r
1aff716a 195\r
8536cc4b 196 @param Address The system memory address to test.\r
197 @param MaskValue The mask value of memory.\r
198 @param TestValue The test value of memory.\r
199 @param Timeout The time out value for wait memory set, uses 100ns as a unit.\r
200\r
201 @retval EFI_TIMEOUT The system memory setting is time out.\r
202 @retval EFI_SUCCESS The system memory is correct set.\r
203\r
204**/\r
205EFI_STATUS\r
206EFIAPI\r
207AhciWaitMemSet (\r
208 IN EFI_PHYSICAL_ADDRESS Address,\r
209 IN UINT32 MaskValue,\r
210 IN UINT32 TestValue,\r
211 IN UINT64 Timeout\r
212 )\r
213{\r
1aff716a 214 UINT32 Value;\r
ab82122d
TF
215 UINT64 Delay;\r
216 BOOLEAN InfiniteWait;\r
217\r
218 if (Timeout == 0) {\r
219 InfiniteWait = TRUE;\r
220 } else {\r
221 InfiniteWait = FALSE;\r
222 }\r
8536cc4b 223\r
ab82122d 224 Delay = DivU64x32 (Timeout, 1000) + 1;\r
8536cc4b 225\r
226 do {\r
227 //\r
228 // Access sytem memory to see if the value is the tested one.\r
229 //\r
230 // The system memory pointed by Address will be updated by the\r
231 // SATA Host Controller, "volatile" is introduced to prevent\r
232 // compiler from optimizing the access to the memory address\r
233 // to only read once.\r
234 //\r
235 Value = *(volatile UINT32 *) (UINTN) Address;\r
236 Value &= MaskValue;\r
237\r
238 if (Value == TestValue) {\r
239 return EFI_SUCCESS;\r
240 }\r
241\r
242 //\r
243 // Stall for 100 microseconds.\r
244 //\r
245 MicroSecondDelay (100);\r
246\r
247 Delay--;\r
a41b5272 248\r
ab82122d 249 } while (InfiniteWait || (Delay > 0));\r
8536cc4b 250\r
251 return EFI_TIMEOUT;\r
a41b5272 252}\r
253\r
490b5ea1 254/**\r
255 Check the memory status to the test value.\r
1aff716a 256\r
8536cc4b 257 @param[in] Address The memory address to test.\r
490b5ea1 258 @param[in] MaskValue The mask value of memory.\r
259 @param[in] TestValue The test value of memory.\r
ab82122d
TF
260 @param[in, out] Task Optional. Pointer to the ATA_NONBLOCK_TASK used by\r
261 non-blocking mode. If NULL, then just try once.\r
490b5ea1 262\r
263 @retval EFI_NOTREADY The memory is not set.\r
264 @retval EFI_TIMEOUT The memory setting retry times out.\r
265 @retval EFI_SUCCESS The memory is correct set.\r
266\r
267**/\r
268EFI_STATUS\r
269EFIAPI\r
270AhciCheckMemSet (\r
8536cc4b 271 IN UINTN Address,\r
86d8e199 272 IN UINT32 MaskValue,\r
273 IN UINT32 TestValue,\r
ab82122d 274 IN OUT ATA_NONBLOCK_TASK *Task\r
490b5ea1 275 )\r
276{\r
277 UINT32 Value;\r
278\r
ab82122d
TF
279 if (Task != NULL) {\r
280 Task->RetryTimes--;\r
8536cc4b 281 }\r
1aff716a 282\r
8536cc4b 283 Value = *(volatile UINT32 *) Address;\r
284 Value &= MaskValue;\r
490b5ea1 285\r
286 if (Value == TestValue) {\r
287 return EFI_SUCCESS;\r
288 }\r
289\r
ab82122d 290 if ((Task != NULL) && !Task->InfiniteWait && (Task->RetryTimes == 0)) {\r
490b5ea1 291 return EFI_TIMEOUT;\r
292 } else {\r
293 return EFI_NOT_READY;\r
294 }\r
295}\r
296\r
a41b5272 297\r
298/**\r
299\r
300 Clear the port interrupt and error status. It will also clear\r
301 HBA interrupt status.\r
1aff716a 302\r
a41b5272 303 @param PciIo The PCI IO protocol instance.\r
304 @param Port The number of port.\r
1aff716a 305\r
306**/\r
a41b5272 307VOID\r
308EFIAPI\r
309AhciClearPortStatus (\r
310 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
311 IN UINT8 Port\r
1aff716a 312 )\r
a41b5272 313{\r
314 UINT32 Offset;\r
315\r
316 //\r
317 // Clear any error status\r
490b5ea1 318 //\r
a41b5272 319 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
320 AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset));\r
321\r
322 //\r
323 // Clear any port interrupt status\r
324 //\r
325 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
326 AhciWriteReg (PciIo, Offset, AhciReadReg (PciIo, Offset));\r
327\r
328 //\r
329 // Clear any HBA interrupt status\r
330 //\r
331 AhciWriteReg (PciIo, EFI_AHCI_IS_OFFSET, AhciReadReg (PciIo, EFI_AHCI_IS_OFFSET));\r
332}\r
333\r
e519983a 334/**\r
335 This function is used to dump the Status Registers and if there is ERR bit set\r
336 in the Status Register, the Error Register's value is also be dumped.\r
337\r
338 @param PciIo The PCI IO protocol instance.\r
a7b3f90f 339 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
e519983a 340 @param Port The number of port.\r
341 @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
342\r
343**/\r
344VOID\r
345EFIAPI\r
346AhciDumpPortStatus (\r
347 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
a7b3f90f 348 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
e519983a 349 IN UINT8 Port,\r
350 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
351 )\r
352{\r
a7b3f90f 353 UINTN Offset;\r
e519983a 354 UINT32 Data;\r
a7b3f90f
FT
355 UINTN FisBaseAddr;\r
356 EFI_STATUS Status;\r
e519983a 357\r
358 ASSERT (PciIo != NULL);\r
359\r
e519983a 360 if (AtaStatusBlock != NULL) {\r
361 ZeroMem (AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
362\r
a7b3f90f
FT
363 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
364 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
365\r
366 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
367 if (!EFI_ERROR (Status)) {\r
368 //\r
369 // If D2H FIS is received, update StatusBlock with its content.\r
370 //\r
371 CopyMem (AtaStatusBlock, (UINT8 *)Offset, sizeof (EFI_ATA_STATUS_BLOCK));\r
372 } else {\r
373 //\r
374 // If D2H FIS is not received, only update Status & Error field through PxTFD\r
375 // as there is no other way to get the content of the Shadow Register Block.\r
376 //\r
377 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
378 Data = AhciReadReg (PciIo, (UINT32)Offset);\r
379\r
380 AtaStatusBlock->AtaStatus = (UINT8)Data;\r
381 if ((AtaStatusBlock->AtaStatus & BIT0) != 0) {\r
382 AtaStatusBlock->AtaError = (UINT8)(Data >> 8);\r
383 }\r
e519983a 384 }\r
385 }\r
386}\r
387\r
388\r
a41b5272 389/**\r
390 Enable the FIS running for giving port.\r
1aff716a 391\r
a41b5272 392 @param PciIo The PCI IO protocol instance.\r
393 @param Port The number of port.\r
8536cc4b 394 @param Timeout The timeout value of enabling FIS, uses 100ns as a unit.\r
a41b5272 395\r
396 @retval EFI_DEVICE_ERROR The FIS enable setting fails.\r
397 @retval EFI_TIMEOUT The FIS enable setting is time out.\r
398 @retval EFI_SUCCESS The FIS enable successfully.\r
399\r
400**/\r
401EFI_STATUS\r
402EFIAPI\r
403AhciEnableFisReceive (\r
404 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
405 IN UINT8 Port,\r
406 IN UINT64 Timeout\r
490b5ea1 407 )\r
408{\r
a41b5272 409 UINT32 Offset;\r
410\r
411 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
412 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);\r
413\r
5e90aa1e 414 return EFI_SUCCESS;\r
a41b5272 415}\r
416\r
417/**\r
418 Disable the FIS running for giving port.\r
419\r
420 @param PciIo The PCI IO protocol instance.\r
421 @param Port The number of port.\r
8536cc4b 422 @param Timeout The timeout value of disabling FIS, uses 100ns as a unit.\r
a41b5272 423\r
424 @retval EFI_DEVICE_ERROR The FIS disable setting fails.\r
425 @retval EFI_TIMEOUT The FIS disable setting is time out.\r
426 @retval EFI_UNSUPPORTED The port is in running state.\r
427 @retval EFI_SUCCESS The FIS disable successfully.\r
428\r
429**/\r
430EFI_STATUS\r
431EFIAPI\r
432AhciDisableFisReceive (\r
433 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
434 IN UINT8 Port,\r
435 IN UINT64 Timeout\r
1aff716a 436 )\r
a41b5272 437{\r
438 UINT32 Offset;\r
439 UINT32 Data;\r
440\r
441 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
442 Data = AhciReadReg (PciIo, Offset);\r
443\r
444 //\r
445 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.\r
446 //\r
447 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {\r
448 return EFI_UNSUPPORTED;\r
449 }\r
1aff716a 450\r
a41b5272 451 //\r
452 // Check if the Fis receive DMA engine for the port is running.\r
453 //\r
454 if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {\r
455 return EFI_SUCCESS;\r
456 }\r
457\r
458 AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));\r
459\r
8536cc4b 460 return AhciWaitMmioSet (\r
490b5ea1 461 PciIo,\r
a41b5272 462 Offset,\r
463 EFI_AHCI_PORT_CMD_FR,\r
464 0,\r
465 Timeout\r
490b5ea1 466 );\r
a41b5272 467}\r
468\r
469\r
470\r
471/**\r
472 Build the command list, command table and prepare the fis receiver.\r
1aff716a 473\r
aca84419 474 @param PciIo The PCI IO protocol instance.\r
a41b5272 475 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
aca84419 476 @param Port The number of port.\r
477 @param PortMultiplier The timeout value of stop.\r
478 @param CommandFis The control fis will be used for the transfer.\r
479 @param CommandList The command list will be used for the transfer.\r
480 @param AtapiCommand The atapi command will be used for the transfer.\r
481 @param AtapiCommandLength The length of the atapi command.\r
482 @param CommandSlotNumber The command slot will be used for the transfer.\r
a41b5272 483 @param DataPhysicalAddr The pointer to the data buffer pci bus master address.\r
484 @param DataLength The data count to be transferred.\r
485\r
1aff716a 486**/\r
a41b5272 487VOID\r
488EFIAPI\r
489AhciBuildCommand (\r
490 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
491 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
492 IN UINT8 Port,\r
493 IN UINT8 PortMultiplier,\r
494 IN EFI_AHCI_COMMAND_FIS *CommandFis,\r
495 IN EFI_AHCI_COMMAND_LIST *CommandList,\r
496 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
497 IN UINT8 AtapiCommandLength,\r
498 IN UINT8 CommandSlotNumber,\r
499 IN OUT VOID *DataPhysicalAddr,\r
e0e7f80c 500 IN UINT32 DataLength\r
1aff716a 501 )\r
a41b5272 502{\r
490b5ea1 503 UINT64 BaseAddr;\r
e0e7f80c
LG
504 UINT32 PrdtNumber;\r
505 UINT32 PrdtIndex;\r
a41b5272 506 UINTN RemainedData;\r
507 UINTN MemAddr;\r
508 DATA_64 Data64;\r
509 UINT32 Offset;\r
510\r
511 //\r
512 // Filling the PRDT\r
1aff716a 513 //\r
f1bc233a 514 PrdtNumber = (UINT32)DivU64x32 (((UINT64)DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1), EFI_AHCI_MAX_DATA_PER_PRDT);\r
a41b5272 515\r
516 //\r
517 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.\r
518 // It also limits that the maximum amount of the PRDT entry in the command table\r
519 // is 65535.\r
520 //\r
521 ASSERT (PrdtNumber <= 65535);\r
522\r
523 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port;\r
524\r
525 BaseAddr = Data64.Uint64;\r
1aff716a 526\r
490b5ea1 527 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
1aff716a 528\r
a41b5272 529 ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
530\r
531 CommandFis->AhciCFisPmNum = PortMultiplier;\r
1aff716a 532\r
a41b5272 533 CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
534\r
535 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
536 if (AtapiCommand != NULL) {\r
537 CopyMem (\r
538 &AhciRegisters->AhciCommandTable->AtapiCmd,\r
539 AtapiCommand,\r
540 AtapiCommandLength\r
541 );\r
542\r
543 CommandList->AhciCmdA = 1;\r
544 CommandList->AhciCmdP = 1;\r
a41b5272 545\r
546 AhciOrReg (PciIo, Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
547 } else {\r
548 AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
549 }\r
1aff716a 550\r
5dec0c68 551 RemainedData = (UINTN) DataLength;\r
a41b5272 552 MemAddr = (UINTN) DataPhysicalAddr;\r
e0e7f80c 553 CommandList->AhciCmdPrdtl = PrdtNumber;\r
1aff716a 554\r
a41b5272 555 for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
490b5ea1 556 if (RemainedData < EFI_AHCI_MAX_DATA_PER_PRDT) {\r
a41b5272 557 AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;\r
558 } else {\r
559 AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbc = EFI_AHCI_MAX_DATA_PER_PRDT - 1;\r
560 }\r
561\r
562 Data64.Uint64 = (UINT64)MemAddr;\r
563 AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDba = Data64.Uint32.Lower32;\r
564 AhciRegisters->AhciCommandTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;\r
490b5ea1 565 RemainedData -= EFI_AHCI_MAX_DATA_PER_PRDT;\r
a41b5272 566 MemAddr += EFI_AHCI_MAX_DATA_PER_PRDT;\r
567 }\r
568\r
569 //\r
570 // Set the last PRDT to Interrupt On Complete\r
571 //\r
572 if (PrdtNumber > 0) {\r
573 AhciRegisters->AhciCommandTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;\r
574 }\r
575\r
576 CopyMem (\r
577 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
578 CommandList,\r
579 sizeof (EFI_AHCI_COMMAND_LIST)\r
1aff716a 580 );\r
a41b5272 581\r
582 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTablePciAddr;\r
583 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;\r
584 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
585 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;\r
586\r
587}\r
588\r
589/**\r
590 Buid a command FIS.\r
1aff716a 591\r
aca84419 592 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data structure.\r
a41b5272 593 @param AtaCommandBlock A pointer to the AhciBuildCommandFis data structure.\r
594\r
595**/\r
596VOID\r
597EFIAPI\r
598AhciBuildCommandFis (\r
599 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,\r
600 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock\r
601 )\r
602{\r
603 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
604\r
605 CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;\r
606 //\r
607 // Indicator it's a command\r
608 //\r
1aff716a 609 CmdFis->AhciCFisCmdInd = 0x1;\r
a41b5272 610 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;\r
611\r
612 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;\r
613 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;\r
614\r
615 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;\r
616 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;\r
617\r
618 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;\r
619 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;\r
620\r
621 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;\r
622 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;\r
623\r
624 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;\r
625 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
626\r
aca84419 627 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
a41b5272 628}\r
629\r
630/**\r
631 Start a PIO data transfer on specific port.\r
1aff716a 632\r
490b5ea1 633 @param[in] PciIo The PCI IO protocol instance.\r
634 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
635 @param[in] Port The number of port.\r
636 @param[in] PortMultiplier The timeout value of stop.\r
637 @param[in] AtapiCommand The atapi command will be used for the\r
638 transfer.\r
639 @param[in] AtapiCommandLength The length of the atapi command.\r
640 @param[in] Read The transfer direction.\r
641 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
642 @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
643 @param[in, out] MemoryAddr The pointer to the data buffer.\r
644 @param[in] DataCount The data count to be transferred.\r
8536cc4b 645 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
490b5ea1 646 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
647 used by non-blocking mode.\r
a41b5272 648\r
649 @retval EFI_DEVICE_ERROR The PIO data transfer abort with error occurs.\r
650 @retval EFI_TIMEOUT The operation is time out.\r
651 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
652 @retval EFI_SUCCESS The PIO data transfer executes successfully.\r
653\r
654**/\r
655EFI_STATUS\r
aca84419 656EFIAPI\r
a41b5272 657AhciPioTransfer (\r
658 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
659 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
660 IN UINT8 Port,\r
661 IN UINT8 PortMultiplier,\r
662 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
1aff716a 663 IN UINT8 AtapiCommandLength,\r
664 IN BOOLEAN Read,\r
a41b5272 665 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
666 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
667 IN OUT VOID *MemoryAddr,\r
668 IN UINT32 DataCount,\r
490b5ea1 669 IN UINT64 Timeout,\r
670 IN ATA_NONBLOCK_TASK *Task\r
a41b5272 671 )\r
672{\r
673 EFI_STATUS Status;\r
674 UINTN FisBaseAddr;\r
8536cc4b 675 UINTN Offset;\r
a41b5272 676 EFI_PHYSICAL_ADDRESS PhyAddr;\r
677 VOID *Map;\r
678 UINTN MapLength;\r
679 EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
ab82122d 680 UINT64 Delay;\r
a41b5272 681 EFI_AHCI_COMMAND_FIS CFis;\r
1aff716a 682 EFI_AHCI_COMMAND_LIST CmdList;\r
8536cc4b 683 UINT32 PortTfd;\r
684 UINT32 PrdCount;\r
ab82122d 685 BOOLEAN InfiniteWait;\r
43654b1c
RJ
686 BOOLEAN PioFisReceived;\r
687 BOOLEAN D2hFisReceived;\r
ab82122d
TF
688\r
689 if (Timeout == 0) {\r
690 InfiniteWait = TRUE;\r
691 } else {\r
692 InfiniteWait = FALSE;\r
693 }\r
a41b5272 694\r
695 if (Read) {\r
696 Flag = EfiPciIoOperationBusMasterWrite;\r
697 } else {\r
698 Flag = EfiPciIoOperationBusMasterRead;\r
699 }\r
700\r
701 //\r
702 // construct command list and command table with pci bus address\r
703 //\r
704 MapLength = DataCount;\r
705 Status = PciIo->Map (\r
706 PciIo,\r
707 Flag,\r
708 MemoryAddr,\r
709 &MapLength,\r
710 &PhyAddr,\r
711 &Map\r
712 );\r
713\r
714 if (EFI_ERROR (Status) || (DataCount != MapLength)) {\r
9e70c18b 715 return EFI_BAD_BUFFER_SIZE;\r
a41b5272 716 }\r
1aff716a 717\r
a41b5272 718 //\r
719 // Package read needed\r
720 //\r
721 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
722\r
723 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
724\r
725 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
726 CmdList.AhciCmdW = Read ? 0 : 1;\r
727\r
728 AhciBuildCommand (\r
729 PciIo,\r
730 AhciRegisters,\r
731 Port,\r
732 PortMultiplier,\r
733 &CFis,\r
734 &CmdList,\r
735 AtapiCommand,\r
736 AtapiCommandLength,\r
737 0,\r
738 (VOID *)(UINTN)PhyAddr,\r
739 DataCount\r
1aff716a 740 );\r
741\r
a41b5272 742 Status = AhciStartCommand (\r
490b5ea1 743 PciIo,\r
744 Port,\r
a41b5272 745 0,\r
746 Timeout\r
747 );\r
748 if (EFI_ERROR (Status)) {\r
749 goto Exit;\r
750 }\r
490b5ea1 751\r
a41b5272 752 //\r
490b5ea1 753 // Check the status and wait the driver sending data\r
a41b5272 754 //\r
755 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
a41b5272 756\r
8536cc4b 757 if (Read && (AtapiCommand == 0)) {\r
a41b5272 758 //\r
8536cc4b 759 // Wait device sends the PIO setup fis before data transfer\r
a41b5272 760 //\r
8536cc4b 761 Status = EFI_TIMEOUT;\r
ab82122d 762 Delay = DivU64x32 (Timeout, 1000) + 1;\r
8536cc4b 763 do {\r
43654b1c
RJ
764 PioFisReceived = FALSE;\r
765 D2hFisReceived = FALSE;\r
8536cc4b 766 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
ab82122d 767 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL);\r
8536cc4b 768 if (!EFI_ERROR (Status)) {\r
43654b1c
RJ
769 PioFisReceived = TRUE;\r
770 }\r
771 //\r
772 // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
773 // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from device\r
774 // after the transaction is finished successfully.\r
775 // To get better device compatibilities, we further check if the PxTFD's ERR bit is set.\r
776 // By this way, we can know if there is a real error happened.\r
777 //\r
778 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
779 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
780 if (!EFI_ERROR (Status)) {\r
781 D2hFisReceived = TRUE;\r
782 }\r
783\r
784 if (PioFisReceived || D2hFisReceived) {\r
7fb60a98 785 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
786 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
787 //\r
d1102dba 788 // PxTFD will be updated if there is a D2H or SetupFIS received.\r
7fb60a98 789 //\r
790 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
791 Status = EFI_DEVICE_ERROR;\r
792 break;\r
793 }\r
794\r
8536cc4b 795 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
796 if (PrdCount == DataCount) {\r
43654b1c 797 Status = EFI_SUCCESS;\r
8536cc4b 798 break;\r
799 }\r
800 }\r
a41b5272 801\r
8536cc4b 802 //\r
803 // Stall for 100 microseconds.\r
804 //\r
805 MicroSecondDelay(100);\r
a41b5272 806\r
8536cc4b 807 Delay--;\r
43654b1c
RJ
808 if (Delay == 0) {\r
809 Status = EFI_TIMEOUT;\r
810 }\r
ab82122d 811 } while (InfiniteWait || (Delay > 0));\r
8536cc4b 812 } else {\r
813 //\r
814 // Wait for D2H Fis is received\r
815 //\r
816 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
817 Status = AhciWaitMemSet (\r
818 Offset,\r
819 EFI_AHCI_FIS_TYPE_MASK,\r
820 EFI_AHCI_FIS_REGISTER_D2H,\r
821 Timeout\r
822 );\r
a41b5272 823\r
8536cc4b 824 if (EFI_ERROR (Status)) {\r
825 goto Exit;\r
826 }\r
827\r
828 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
829 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
830 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
831 Status = EFI_DEVICE_ERROR;\r
832 }\r
a41b5272 833 }\r
834\r
490b5ea1 835Exit:\r
a41b5272 836 AhciStopCommand (\r
490b5ea1 837 PciIo,\r
a41b5272 838 Port,\r
839 Timeout\r
840 );\r
1aff716a 841\r
a41b5272 842 AhciDisableFisReceive (\r
490b5ea1 843 PciIo,\r
a41b5272 844 Port,\r
845 Timeout\r
846 );\r
847\r
848 PciIo->Unmap (\r
849 PciIo,\r
850 Map\r
851 );\r
852\r
a7b3f90f 853 AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
e519983a 854\r
a41b5272 855 return Status;\r
856}\r
857\r
858/**\r
859 Start a DMA data transfer on specific port\r
860\r
490b5ea1 861 @param[in] Instance The ATA_ATAPI_PASS_THRU_INSTANCE protocol instance.\r
862 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
863 @param[in] Port The number of port.\r
864 @param[in] PortMultiplier The timeout value of stop.\r
865 @param[in] AtapiCommand The atapi command will be used for the\r
866 transfer.\r
867 @param[in] AtapiCommandLength The length of the atapi command.\r
868 @param[in] Read The transfer direction.\r
869 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
870 @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
871 @param[in, out] MemoryAddr The pointer to the data buffer.\r
872 @param[in] DataCount The data count to be transferred.\r
8536cc4b 873 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
490b5ea1 874 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
875 used by non-blocking mode.\r
aca84419 876\r
877 @retval EFI_DEVICE_ERROR The DMA data transfer abort with error occurs.\r
878 @retval EFI_TIMEOUT The operation is time out.\r
879 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
880 @retval EFI_SUCCESS The DMA data transfer executes successfully.\r
490b5ea1 881\r
a41b5272 882**/\r
883EFI_STATUS\r
884EFIAPI\r
885AhciDmaTransfer (\r
490b5ea1 886 IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance,\r
a41b5272 887 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
888 IN UINT8 Port,\r
889 IN UINT8 PortMultiplier,\r
890 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
891 IN UINT8 AtapiCommandLength,\r
1aff716a 892 IN BOOLEAN Read,\r
a41b5272 893 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
894 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
895 IN OUT VOID *MemoryAddr,\r
8536cc4b 896 IN UINT32 DataCount,\r
490b5ea1 897 IN UINT64 Timeout,\r
898 IN ATA_NONBLOCK_TASK *Task\r
a41b5272 899 )\r
900{\r
901 EFI_STATUS Status;\r
8536cc4b 902 UINTN Offset;\r
a41b5272 903 EFI_PHYSICAL_ADDRESS PhyAddr;\r
904 VOID *Map;\r
905 UINTN MapLength;\r
906 EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
907 EFI_AHCI_COMMAND_FIS CFis;\r
908 EFI_AHCI_COMMAND_LIST CmdList;\r
8536cc4b 909 UINTN FisBaseAddr;\r
910 UINT32 PortTfd;\r
a41b5272 911\r
8536cc4b 912 EFI_PCI_IO_PROTOCOL *PciIo;\r
913 EFI_TPL OldTpl;\r
a41b5272 914\r
490b5ea1 915 Map = NULL;\r
916 PciIo = Instance->PciIo;\r
a41b5272 917\r
490b5ea1 918 if (PciIo == NULL) {\r
919 return EFI_INVALID_PARAMETER;\r
a41b5272 920 }\r
921\r
922 //\r
490b5ea1 923 // Before starting the Blocking BlockIO operation, push to finish all non-blocking\r
924 // BlockIO tasks.\r
925 // Delay 100us to simulate the blocking time out checking.\r
a41b5272 926 //\r
1aff716a 927 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
490b5ea1 928 while ((Task == NULL) && (!IsListEmpty (&Instance->NonBlockingTaskList))) {\r
490b5ea1 929 AsyncNonBlockingTransferRoutine (NULL, Instance);\r
490b5ea1 930 //\r
931 // Stall for 100us.\r
932 //\r
933 MicroSecondDelay (100);\r
934 }\r
1aff716a 935 gBS->RestoreTPL (OldTpl);\r
a41b5272 936\r
490b5ea1 937 if ((Task == NULL) || ((Task != NULL) && (!Task->IsStart))) {\r
938 //\r
939 // Mark the Task to indicate that it has been started.\r
940 //\r
941 if (Task != NULL) {\r
942 Task->IsStart = TRUE;\r
490b5ea1 943 }\r
944 if (Read) {\r
945 Flag = EfiPciIoOperationBusMasterWrite;\r
946 } else {\r
947 Flag = EfiPciIoOperationBusMasterRead;\r
948 }\r
a41b5272 949\r
490b5ea1 950 //\r
951 // Construct command list and command table with pci bus address.\r
952 //\r
953 MapLength = DataCount;\r
954 Status = PciIo->Map (\r
955 PciIo,\r
956 Flag,\r
957 MemoryAddr,\r
958 &MapLength,\r
959 &PhyAddr,\r
960 &Map\r
961 );\r
962\r
963 if (EFI_ERROR (Status) || (DataCount != MapLength)) {\r
9e70c18b 964 return EFI_BAD_BUFFER_SIZE;\r
490b5ea1 965 }\r
a41b5272 966\r
490b5ea1 967 if (Task != NULL) {\r
968 Task->Map = Map;\r
969 }\r
970 //\r
971 // Package read needed\r
972 //\r
973 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
974\r
975 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
976\r
977 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
978 CmdList.AhciCmdW = Read ? 0 : 1;\r
979\r
980 AhciBuildCommand (\r
981 PciIo,\r
982 AhciRegisters,\r
983 Port,\r
984 PortMultiplier,\r
985 &CFis,\r
986 &CmdList,\r
987 AtapiCommand,\r
988 AtapiCommandLength,\r
989 0,\r
990 (VOID *)(UINTN)PhyAddr,\r
991 DataCount\r
992 );\r
993\r
994 Status = AhciStartCommand (\r
995 PciIo,\r
996 Port,\r
997 0,\r
998 Timeout\r
999 );\r
1000 if (EFI_ERROR (Status)) {\r
1001 goto Exit;\r
1002 }\r
a41b5272 1003 }\r
1004\r
1005 //\r
1006 // Wait for command compelte\r
1007 //\r
8536cc4b 1008 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1009 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
490b5ea1 1010 if (Task != NULL) {\r
1011 //\r
1012 // For Non-blocking\r
1013 //\r
1014 Status = AhciCheckMemSet (\r
490b5ea1 1015 Offset,\r
8536cc4b 1016 EFI_AHCI_FIS_TYPE_MASK,\r
1017 EFI_AHCI_FIS_REGISTER_D2H,\r
ab82122d 1018 Task\r
490b5ea1 1019 );\r
1020 } else {\r
1021 Status = AhciWaitMemSet (\r
490b5ea1 1022 Offset,\r
8536cc4b 1023 EFI_AHCI_FIS_TYPE_MASK,\r
1024 EFI_AHCI_FIS_REGISTER_D2H,\r
490b5ea1 1025 Timeout\r
1026 );\r
1027 }\r
1028\r
a41b5272 1029 if (EFI_ERROR (Status)) {\r
1030 goto Exit;\r
1031 }\r
1032\r
8536cc4b 1033 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1034 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
1035 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
1036 Status = EFI_DEVICE_ERROR;\r
a41b5272 1037 }\r
1038\r
490b5ea1 1039Exit:\r
1040 //\r
1041 // For Blocking mode, the command should be stopped, the Fis should be disabled\r
1042 // and the PciIo should be unmapped.\r
1aff716a 1043 // For non-blocking mode, only when a error is happened (if the return status is\r
1044 // EFI_NOT_READY that means the command doesn't finished, try again.), first do the\r
490b5ea1 1045 // context cleanup, then set the packet's Asb status.\r
1046 //\r
1047 if (Task == NULL ||\r
1048 ((Task != NULL) && (Status != EFI_NOT_READY))\r
1049 ) {\r
1050 AhciStopCommand (\r
1aff716a 1051 PciIo,\r
490b5ea1 1052 Port,\r
1053 Timeout\r
1054 );\r
a41b5272 1055\r
490b5ea1 1056 AhciDisableFisReceive (\r
1aff716a 1057 PciIo,\r
490b5ea1 1058 Port,\r
1059 Timeout\r
1060 );\r
a41b5272 1061\r
490b5ea1 1062 PciIo->Unmap (\r
1063 PciIo,\r
1064 (Task != NULL) ? Task->Map : Map\r
1065 );\r
e519983a 1066\r
490b5ea1 1067 if (Task != NULL) {\r
1068 Task->Packet->Asb->AtaStatus = 0x01;\r
1069 }\r
1070 }\r
1071\r
a7b3f90f 1072 AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
a41b5272 1073 return Status;\r
1074}\r
1075\r
1076/**\r
1077 Start a non data transfer on specific port.\r
1aff716a 1078\r
490b5ea1 1079 @param[in] PciIo The PCI IO protocol instance.\r
1080 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1081 @param[in] Port The number of port.\r
1082 @param[in] PortMultiplier The timeout value of stop.\r
1083 @param[in] AtapiCommand The atapi command will be used for the\r
1084 transfer.\r
1085 @param[in] AtapiCommandLength The length of the atapi command.\r
1086 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
1087 @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
8536cc4b 1088 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
490b5ea1 1089 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
1090 used by non-blocking mode.\r
a41b5272 1091\r
1092 @retval EFI_DEVICE_ERROR The non data transfer abort with error occurs.\r
1093 @retval EFI_TIMEOUT The operation is time out.\r
1094 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
1095 @retval EFI_SUCCESS The non data transfer executes successfully.\r
1096\r
1aff716a 1097**/\r
a41b5272 1098EFI_STATUS\r
1099EFIAPI\r
1100AhciNonDataTransfer (\r
1101 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1102 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1103 IN UINT8 Port,\r
1104 IN UINT8 PortMultiplier,\r
1105 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
1106 IN UINT8 AtapiCommandLength,\r
1107 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
1108 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
490b5ea1 1109 IN UINT64 Timeout,\r
1110 IN ATA_NONBLOCK_TASK *Task\r
1111 )\r
a41b5272 1112{\r
490b5ea1 1113 EFI_STATUS Status;\r
a41b5272 1114 UINTN FisBaseAddr;\r
8536cc4b 1115 UINTN Offset;\r
1116 UINT32 PortTfd;\r
a41b5272 1117 EFI_AHCI_COMMAND_FIS CFis;\r
1118 EFI_AHCI_COMMAND_LIST CmdList;\r
1119\r
1120 //\r
1121 // Package read needed\r
1122 //\r
1123 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
1124\r
1125 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
1126\r
1127 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
1128\r
1129 AhciBuildCommand (\r
1130 PciIo,\r
1131 AhciRegisters,\r
1132 Port,\r
1133 PortMultiplier,\r
1134 &CFis,\r
1135 &CmdList,\r
1136 AtapiCommand,\r
1137 AtapiCommandLength,\r
1138 0,\r
1139 NULL,\r
1140 0\r
490b5ea1 1141 );\r
1142\r
a41b5272 1143 Status = AhciStartCommand (\r
490b5ea1 1144 PciIo,\r
1145 Port,\r
a41b5272 1146 0,\r
1147 Timeout\r
1148 );\r
1149 if (EFI_ERROR (Status)) {\r
1150 goto Exit;\r
1151 }\r
490b5ea1 1152\r
a41b5272 1153 //\r
1154 // Wait device sends the Response Fis\r
1155 //\r
1156 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
8536cc4b 1157 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
1158 Status = AhciWaitMemSet (\r
1159 Offset,\r
1160 EFI_AHCI_FIS_TYPE_MASK,\r
1161 EFI_AHCI_FIS_REGISTER_D2H,\r
1162 Timeout\r
1163 );\r
a41b5272 1164\r
8536cc4b 1165 if (EFI_ERROR (Status)) {\r
a41b5272 1166 goto Exit;\r
1167 }\r
1168\r
8536cc4b 1169 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1170 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
1171 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
1172 Status = EFI_DEVICE_ERROR;\r
1173 }\r
490b5ea1 1174\r
1175Exit:\r
a41b5272 1176 AhciStopCommand (\r
490b5ea1 1177 PciIo,\r
a41b5272 1178 Port,\r
1179 Timeout\r
1180 );\r
1181\r
1182 AhciDisableFisReceive (\r
490b5ea1 1183 PciIo,\r
a41b5272 1184 Port,\r
1185 Timeout\r
1186 );\r
1187\r
a7b3f90f 1188 AhciDumpPortStatus (PciIo, AhciRegisters, Port, AtaStatusBlock);\r
e519983a 1189\r
a41b5272 1190 return Status;\r
1191}\r
1192\r
1193/**\r
1194 Stop command running for giving port\r
1aff716a 1195\r
a41b5272 1196 @param PciIo The PCI IO protocol instance.\r
1197 @param Port The number of port.\r
8536cc4b 1198 @param Timeout The timeout value of stop, uses 100ns as a unit.\r
1aff716a 1199\r
a41b5272 1200 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
1201 @retval EFI_TIMEOUT The operation is time out.\r
1202 @retval EFI_SUCCESS The command stop successfully.\r
1203\r
1204**/\r
1205EFI_STATUS\r
1206EFIAPI\r
1207AhciStopCommand (\r
1208 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1209 IN UINT8 Port,\r
1210 IN UINT64 Timeout\r
1211 )\r
1212{\r
1213 UINT32 Offset;\r
1214 UINT32 Data;\r
1215\r
1216 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1217 Data = AhciReadReg (PciIo, Offset);\r
1218\r
1219 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {\r
490b5ea1 1220 return EFI_SUCCESS;\r
a41b5272 1221 }\r
1222\r
1223 if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {\r
1224 AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));\r
1225 }\r
1226\r
8536cc4b 1227 return AhciWaitMmioSet (\r
490b5ea1 1228 PciIo,\r
a41b5272 1229 Offset,\r
1230 EFI_AHCI_PORT_CMD_CR,\r
1231 0,\r
1232 Timeout\r
490b5ea1 1233 );\r
a41b5272 1234}\r
1235\r
1236/**\r
1237 Start command for give slot on specific port.\r
490b5ea1 1238\r
a41b5272 1239 @param PciIo The PCI IO protocol instance.\r
1240 @param Port The number of port.\r
490b5ea1 1241 @param CommandSlot The number of Command Slot.\r
8536cc4b 1242 @param Timeout The timeout value of start, uses 100ns as a unit.\r
490b5ea1 1243\r
a41b5272 1244 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
1245 @retval EFI_TIMEOUT The operation is time out.\r
1246 @retval EFI_SUCCESS The command start successfully.\r
1247\r
1248**/\r
1249EFI_STATUS\r
1250EFIAPI\r
1251AhciStartCommand (\r
1252 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1253 IN UINT8 Port,\r
1254 IN UINT8 CommandSlot,\r
1255 IN UINT64 Timeout\r
1256 )\r
1257{\r
1258 UINT32 CmdSlotBit;\r
1259 EFI_STATUS Status;\r
1260 UINT32 PortStatus;\r
1261 UINT32 StartCmd;\r
1262 UINT32 PortTfd;\r
1263 UINT32 Offset;\r
1264 UINT32 Capability;\r
1265\r
1266 //\r
1267 // Collect AHCI controller information\r
1268 //\r
1269 Capability = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
1270\r
1271 CmdSlotBit = (UINT32) (1 << CommandSlot);\r
1272\r
1273 AhciClearPortStatus (\r
1274 PciIo,\r
1275 Port\r
1276 );\r
1277\r
1278 Status = AhciEnableFisReceive (\r
1aff716a 1279 PciIo,\r
a41b5272 1280 Port,\r
1281 Timeout\r
1282 );\r
490b5ea1 1283\r
a41b5272 1284 if (EFI_ERROR (Status)) {\r
1285 return Status;\r
1286 }\r
1287\r
a41b5272 1288 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1289 PortStatus = AhciReadReg (PciIo, Offset);\r
490b5ea1 1290\r
a41b5272 1291 StartCmd = 0;\r
1292 if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {\r
1293 StartCmd = AhciReadReg (PciIo, Offset);\r
1294 StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;\r
1295 StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;\r
1296 }\r
1297\r
1298 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1299 PortTfd = AhciReadReg (PciIo, Offset);\r
1300\r
1301 if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {\r
1302 if ((Capability & BIT24) != 0) {\r
1303 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
cbd2a4b3 1304 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);\r
a41b5272 1305\r
8536cc4b 1306 AhciWaitMmioSet (\r
490b5ea1 1307 PciIo,\r
a41b5272 1308 Offset,\r
cbd2a4b3 1309 EFI_AHCI_PORT_CMD_CLO,\r
a41b5272 1310 0,\r
1311 Timeout\r
490b5ea1 1312 );\r
a41b5272 1313 }\r
1314 }\r
1315\r
1316 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1317 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);\r
1318\r
e519983a 1319 //\r
1320 // Setting the command\r
1321 //\r
e519983a 1322 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;\r
1323 AhciAndReg (PciIo, Offset, 0);\r
1324 AhciOrReg (PciIo, Offset, CmdSlotBit);\r
1325\r
a41b5272 1326 return EFI_SUCCESS;\r
1327}\r
1328\r
a41b5272 1329\r
1330/**\r
1331 Do AHCI HBA reset.\r
490b5ea1 1332\r
a41b5272 1333 @param PciIo The PCI IO protocol instance.\r
8536cc4b 1334 @param Timeout The timeout value of reset, uses 100ns as a unit.\r
490b5ea1 1335\r
a41b5272 1336 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
1337 @retval EFI_TIMEOUT The reset operation is time out.\r
1338 @retval EFI_SUCCESS AHCI controller is reset successfully.\r
1339\r
1340**/\r
1341EFI_STATUS\r
1342EFIAPI\r
1343AhciReset (\r
1344 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1345 IN UINT64 Timeout\r
1aff716a 1346 )\r
a41b5272 1347{\r
ab82122d 1348 UINT64 Delay;\r
a41b5272 1349 UINT32 Value;\r
1350\r
1ff1dd0f 1351 //\r
6052a15f 1352 // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
1ff1dd0f 1353 //\r
6052a15f
MW
1354 Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
1355\r
1356 if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {\r
1ff1dd0f
FT
1357 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
1358 }\r
a41b5272 1359\r
1360 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
1361\r
ab82122d 1362 Delay = DivU64x32(Timeout, 1000) + 1;\r
a41b5272 1363\r
1364 do {\r
1365 Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
1366\r
1367 if ((Value & EFI_AHCI_GHC_RESET) == 0) {\r
1368 break;\r
1369 }\r
1370\r
1371 //\r
1372 // Stall for 100 microseconds.\r
1373 //\r
1374 MicroSecondDelay(100);\r
1375\r
1376 Delay--;\r
1377 } while (Delay > 0);\r
1378\r
1379 if (Delay == 0) {\r
1380 return EFI_TIMEOUT;\r
1381 }\r
1382\r
1383 return EFI_SUCCESS;\r
1384}\r
1385\r
12873d57 1386/**\r
1387 Send SMART Return Status command to check if the execution of SMART cmd is successful or not.\r
1388\r
1389 @param PciIo The PCI IO protocol instance.\r
1390 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1391 @param Port The number of port.\r
79992d6e 1392 @param PortMultiplier The port multiplier port number.\r
12873d57 1393 @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
1394\r
1395 @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution.\r
1396 @retval Others Fail to get return status data.\r
1397\r
1398**/\r
1399EFI_STATUS\r
1400EFIAPI\r
1401AhciAtaSmartReturnStatusCheck (\r
1402 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1403 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1404 IN UINT8 Port,\r
1405 IN UINT8 PortMultiplier,\r
1406 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
1407 )\r
1408{\r
1409 EFI_STATUS Status;\r
1410 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1411 UINT8 LBAMid;\r
1412 UINT8 LBAHigh;\r
1413 UINTN FisBaseAddr;\r
1414 UINT32 Value;\r
1415\r
1416 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1417\r
1418 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1419 AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS;\r
1420 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1421 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1422\r
1423 //\r
1424 // Send S.M.A.R.T Read Return Status command to device\r
1425 //\r
1426 Status = AhciNonDataTransfer (\r
1427 PciIo,\r
1428 AhciRegisters,\r
1429 (UINT8)Port,\r
1430 (UINT8)PortMultiplier,\r
1431 NULL,\r
1432 0,\r
1433 &AtaCommandBlock,\r
1434 AtaStatusBlock,\r
490b5ea1 1435 ATA_ATAPI_TIMEOUT,\r
1436 NULL\r
12873d57 1437 );\r
1438\r
1439 if (EFI_ERROR (Status)) {\r
cffd2171
EL
1440 REPORT_STATUS_CODE (\r
1441 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1442 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)\r
1443 );\r
12873d57 1444 return EFI_DEVICE_ERROR;\r
1445 }\r
1446\r
cffd2171
EL
1447 REPORT_STATUS_CODE (\r
1448 EFI_PROGRESS_CODE,\r
1449 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)\r
1450 );\r
1451\r
12873d57 1452 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1453\r
1454 Value = *(UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);\r
1455\r
1456 if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) {\r
1457 LBAMid = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[5];\r
1458 LBAHigh = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[6];\r
1459\r
1460 if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {\r
1461 //\r
1462 // The threshold exceeded condition is not detected by the device\r
1463 //\r
1464 DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));\r
cffd2171
EL
1465 REPORT_STATUS_CODE (\r
1466 EFI_PROGRESS_CODE,\r
1467 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)\r
1468 );\r
12873d57 1469 } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {\r
1470 //\r
1471 // The threshold exceeded condition is detected by the device\r
1472 //\r
1473 DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));\r
cffd2171
EL
1474 REPORT_STATUS_CODE (\r
1475 EFI_PROGRESS_CODE,\r
1476 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)\r
1477 );\r
12873d57 1478 }\r
1479 }\r
1480\r
1481 return EFI_SUCCESS;\r
1482}\r
1483\r
1484/**\r
1485 Enable SMART command of the disk if supported.\r
1486\r
1487 @param PciIo The PCI IO protocol instance.\r
1488 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1489 @param Port The number of port.\r
79992d6e 1490 @param PortMultiplier The port multiplier port number.\r
12873d57 1491 @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.\r
1492 @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
1493\r
1494**/\r
1495VOID\r
1496EFIAPI\r
1497AhciAtaSmartSupport (\r
1498 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1499 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1500 IN UINT8 Port,\r
1501 IN UINT8 PortMultiplier,\r
490b5ea1 1502 IN EFI_IDENTIFY_DATA *IdentifyData,\r
12873d57 1503 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
1504 )\r
1505{\r
1506 EFI_STATUS Status;\r
1507 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1508\r
1509 //\r
1510 // Detect if the device supports S.M.A.R.T.\r
1511 //\r
1512 if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {\r
1513 //\r
1514 // S.M.A.R.T is not supported by the device\r
1515 //\r
1aff716a 1516 DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",\r
12873d57 1517 Port, PortMultiplier));\r
cffd2171
EL
1518 REPORT_STATUS_CODE (\r
1519 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1520 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)\r
1521 );\r
12873d57 1522 } else {\r
1523 //\r
1524 // Check if the feature is enabled. If not, then enable S.M.A.R.T.\r
1525 //\r
1526 if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) {\r
cffd2171
EL
1527\r
1528 REPORT_STATUS_CODE (\r
1529 EFI_PROGRESS_CODE,\r
1530 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)\r
1531 );\r
1532\r
12873d57 1533 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1534\r
1535 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1536 AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION;\r
1537 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1538 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1539\r
1540 //\r
1541 // Send S.M.A.R.T Enable command to device\r
1542 //\r
1543 Status = AhciNonDataTransfer (\r
1544 PciIo,\r
1545 AhciRegisters,\r
1546 (UINT8)Port,\r
1547 (UINT8)PortMultiplier,\r
1548 NULL,\r
1549 0,\r
1550 &AtaCommandBlock,\r
1551 AtaStatusBlock,\r
490b5ea1 1552 ATA_ATAPI_TIMEOUT,\r
1553 NULL\r
12873d57 1554 );\r
1555\r
1556\r
1557 if (!EFI_ERROR (Status)) {\r
1558 //\r
1559 // Send S.M.A.R.T AutoSave command to device\r
1560 //\r
1561 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1562\r
1563 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1564 AtaCommandBlock.AtaFeatures = 0xD2;\r
1565 AtaCommandBlock.AtaSectorCount = 0xF1;\r
1566 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1567 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1568\r
1569 Status = AhciNonDataTransfer (\r
1570 PciIo,\r
1571 AhciRegisters,\r
1572 (UINT8)Port,\r
1573 (UINT8)PortMultiplier,\r
1574 NULL,\r
1575 0,\r
1576 &AtaCommandBlock,\r
1577 AtaStatusBlock,\r
490b5ea1 1578 ATA_ATAPI_TIMEOUT,\r
1579 NULL\r
12873d57 1580 );\r
1581\r
1582 if (!EFI_ERROR (Status)) {\r
1583 Status = AhciAtaSmartReturnStatusCheck (\r
1584 PciIo,\r
1585 AhciRegisters,\r
1586 (UINT8)Port,\r
1587 (UINT8)PortMultiplier,\r
1588 AtaStatusBlock\r
1589 );\r
1590 }\r
1591 }\r
1592 }\r
490b5ea1 1593 DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",\r
12873d57 1594 Port, PortMultiplier));\r
1595 }\r
1596\r
1597 return ;\r
1598}\r
1599\r
a41b5272 1600/**\r
1601 Send Buffer cmd to specific device.\r
1aff716a 1602\r
aca84419 1603 @param PciIo The PCI IO protocol instance.\r
1604 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1605 @param Port The number of port.\r
79992d6e 1606 @param PortMultiplier The port multiplier port number.\r
aca84419 1607 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
a41b5272 1608\r
1609 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1610 @retval EFI_TIMEOUT The operation is time out.\r
1611 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1612 @retval EFI_SUCCESS The cmd executes successfully.\r
1613\r
1614**/\r
1615EFI_STATUS\r
1616EFIAPI\r
1617AhciIdentify (\r
1618 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1619 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1620 IN UINT8 Port,\r
1621 IN UINT8 PortMultiplier,\r
1aff716a 1622 IN OUT EFI_IDENTIFY_DATA *Buffer\r
a41b5272 1623 )\r
1624{\r
1625 EFI_STATUS Status;\r
1626 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1627 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1628\r
1629 if (PciIo == NULL || AhciRegisters == NULL || Buffer == NULL) {\r
1630 return EFI_INVALID_PARAMETER;\r
1631 }\r
1632\r
1633 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1634 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
490b5ea1 1635\r
a41b5272 1636 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
1637 AtaCommandBlock.AtaSectorCount = 1;\r
1638\r
1639 Status = AhciPioTransfer (\r
1640 PciIo,\r
1641 AhciRegisters,\r
1642 Port,\r
1643 PortMultiplier,\r
1644 NULL,\r
1645 0,\r
1646 TRUE,\r
1647 &AtaCommandBlock,\r
1648 &AtaStatusBlock,\r
1649 Buffer,\r
1650 sizeof (EFI_IDENTIFY_DATA),\r
1aff716a 1651 ATA_ATAPI_TIMEOUT,\r
490b5ea1 1652 NULL\r
a41b5272 1653 );\r
1654\r
1655 return Status;\r
1656}\r
1657\r
1658/**\r
1659 Send Buffer cmd to specific device.\r
1aff716a 1660\r
aca84419 1661 @param PciIo The PCI IO protocol instance.\r
1662 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1663 @param Port The number of port.\r
79992d6e 1664 @param PortMultiplier The port multiplier port number.\r
aca84419 1665 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
a41b5272 1666\r
1667 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1668 @retval EFI_TIMEOUT The operation is time out.\r
1669 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1670 @retval EFI_SUCCESS The cmd executes successfully.\r
1671\r
1672**/\r
1673EFI_STATUS\r
1674EFIAPI\r
1675AhciIdentifyPacket (\r
1676 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1677 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1678 IN UINT8 Port,\r
1679 IN UINT8 PortMultiplier,\r
1aff716a 1680 IN OUT EFI_IDENTIFY_DATA *Buffer\r
a41b5272 1681 )\r
1682{\r
1683 EFI_STATUS Status;\r
1684 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1685 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1686\r
1687 if (PciIo == NULL || AhciRegisters == NULL) {\r
1688 return EFI_INVALID_PARAMETER;\r
1689 }\r
490b5ea1 1690\r
a41b5272 1691 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1692 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
1693\r
1694 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DEVICE;\r
1695 AtaCommandBlock.AtaSectorCount = 1;\r
1696\r
1697 Status = AhciPioTransfer (\r
1698 PciIo,\r
1699 AhciRegisters,\r
1700 Port,\r
1701 PortMultiplier,\r
1702 NULL,\r
1703 0,\r
1704 TRUE,\r
1705 &AtaCommandBlock,\r
1706 &AtaStatusBlock,\r
1707 Buffer,\r
1708 sizeof (EFI_IDENTIFY_DATA),\r
490b5ea1 1709 ATA_ATAPI_TIMEOUT,\r
1710 NULL\r
a41b5272 1711 );\r
1712\r
1713 return Status;\r
1714}\r
1715\r
1716/**\r
1717 Send SET FEATURE cmd on specific device.\r
1aff716a 1718\r
aca84419 1719 @param PciIo The PCI IO protocol instance.\r
1720 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1721 @param Port The number of port.\r
79992d6e 1722 @param PortMultiplier The port multiplier port number.\r
aca84419 1723 @param Feature The data to send Feature register.\r
1724 @param FeatureSpecificData The specific data for SET FEATURE cmd.\r
8d3c4b55 1725 @param Timeout The timeout value of SET FEATURE cmd, uses 100ns as a unit.\r
a41b5272 1726\r
1727 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1728 @retval EFI_TIMEOUT The operation is time out.\r
1729 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1730 @retval EFI_SUCCESS The cmd executes successfully.\r
1731\r
1732**/\r
1733EFI_STATUS\r
1734EFIAPI\r
1735AhciDeviceSetFeature (\r
1736 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1737 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1738 IN UINT8 Port,\r
1739 IN UINT8 PortMultiplier,\r
1740 IN UINT16 Feature,\r
8d3c4b55
RN
1741 IN UINT32 FeatureSpecificData,\r
1742 IN UINT64 Timeout\r
a41b5272 1743 )\r
1744{\r
1745 EFI_STATUS Status;\r
1746 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1747 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1748\r
1749 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1750 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
490b5ea1 1751\r
a41b5272 1752 AtaCommandBlock.AtaCommand = ATA_CMD_SET_FEATURES;\r
1753 AtaCommandBlock.AtaFeatures = (UINT8) Feature;\r
1754 AtaCommandBlock.AtaFeaturesExp = (UINT8) (Feature >> 8);\r
1755 AtaCommandBlock.AtaSectorCount = (UINT8) FeatureSpecificData;\r
1756 AtaCommandBlock.AtaSectorNumber = (UINT8) (FeatureSpecificData >> 8);\r
1757 AtaCommandBlock.AtaCylinderLow = (UINT8) (FeatureSpecificData >> 16);\r
1758 AtaCommandBlock.AtaCylinderHigh = (UINT8) (FeatureSpecificData >> 24);\r
1759\r
1760 Status = AhciNonDataTransfer (\r
1761 PciIo,\r
1762 AhciRegisters,\r
1763 (UINT8)Port,\r
1764 (UINT8)PortMultiplier,\r
1765 NULL,\r
1766 0,\r
1767 &AtaCommandBlock,\r
1768 &AtaStatusBlock,\r
8d3c4b55 1769 Timeout,\r
490b5ea1 1770 NULL\r
a41b5272 1771 );\r
1772\r
1773 return Status;\r
1774}\r
1775\r
1776/**\r
1aff716a 1777 This function is used to send out ATAPI commands conforms to the Packet Command\r
a41b5272 1778 with PIO Protocol.\r
1779\r
1780 @param PciIo The PCI IO protocol instance.\r
1781 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1aff716a 1782 @param Port The number of port.\r
a41b5272 1783 @param PortMultiplier The number of port multiplier.\r
1784 @param Packet A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET structure.\r
1785\r
1786 @retval EFI_SUCCESS send out the ATAPI packet command successfully\r
1787 and device sends data successfully.\r
1788 @retval EFI_DEVICE_ERROR the device failed to send data.\r
1789\r
1790**/\r
1791EFI_STATUS\r
1792EFIAPI\r
1793AhciPacketCommandExecute (\r
1794 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1795 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1796 IN UINT8 Port,\r
1797 IN UINT8 PortMultiplier,\r
1798 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
1799 )\r
1800{\r
1801 EFI_STATUS Status;\r
1802 VOID *Buffer;\r
1803 UINT32 Length;\r
1804 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1805 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1806 BOOLEAN Read;\r
a41b5272 1807\r
1808 if (Packet == NULL || Packet->Cdb == NULL) {\r
1809 return EFI_INVALID_PARAMETER;\r
1810 }\r
1811\r
1812 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1813 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
1814 AtaCommandBlock.AtaCommand = ATA_CMD_PACKET;\r
1815 //\r
1816 // No OVL; No DMA\r
1817 //\r
1818 AtaCommandBlock.AtaFeatures = 0x00;\r
1819 //\r
1820 // set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device\r
1821 // determine how many data should be transferred.\r
1822 //\r
1823 AtaCommandBlock.AtaCylinderLow = (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff);\r
1824 AtaCommandBlock.AtaCylinderHigh = (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8);\r
1825\r
1826 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
1827 Buffer = Packet->InDataBuffer;\r
1828 Length = Packet->InTransferLength;\r
1829 Read = TRUE;\r
1830 } else {\r
1831 Buffer = Packet->OutDataBuffer;\r
1832 Length = Packet->OutTransferLength;\r
1833 Read = FALSE;\r
1834 }\r
1835\r
490b5ea1 1836 if (Length == 0) {\r
a41b5272 1837 Status = AhciNonDataTransfer (\r
1838 PciIo,\r
1839 AhciRegisters,\r
1840 Port,\r
1841 PortMultiplier,\r
1842 Packet->Cdb,\r
1843 Packet->CdbLength,\r
1844 &AtaCommandBlock,\r
1845 &AtaStatusBlock,\r
1aff716a 1846 Packet->Timeout,\r
490b5ea1 1847 NULL\r
a41b5272 1848 );\r
1849 } else {\r
cbd2a4b3 1850 Status = AhciPioTransfer (\r
1851 PciIo,\r
1852 AhciRegisters,\r
1853 Port,\r
1854 PortMultiplier,\r
1855 Packet->Cdb,\r
1856 Packet->CdbLength,\r
1857 Read,\r
1858 &AtaCommandBlock,\r
1859 &AtaStatusBlock,\r
1860 Buffer,\r
1861 Length,\r
1aff716a 1862 Packet->Timeout,\r
cbd2a4b3 1863 NULL\r
1864 );\r
a41b5272 1865 }\r
1866 return Status;\r
1867}\r
1868\r
1869/**\r
1870 Allocate transfer-related data struct which is used at AHCI mode.\r
1aff716a 1871\r
a41b5272 1872 @param PciIo The PCI IO protocol instance.\r
1873 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1874\r
1875**/\r
1876EFI_STATUS\r
1877EFIAPI\r
1878AhciCreateTransferDescriptor (\r
1879 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1880 IN OUT EFI_AHCI_REGISTERS *AhciRegisters\r
1881 )\r
1882{\r
1883 EFI_STATUS Status;\r
1884 UINTN Bytes;\r
1885 VOID *Buffer;\r
1886\r
1887 UINT32 Capability;\r
467cacbf 1888 UINT32 PortImplementBitMap;\r
a41b5272 1889 UINT8 MaxPortNumber;\r
1890 UINT8 MaxCommandSlotNumber;\r
1891 BOOLEAN Support64Bit;\r
1892 UINT64 MaxReceiveFisSize;\r
1893 UINT64 MaxCommandListSize;\r
1894 UINT64 MaxCommandTableSize;\r
ed365e93 1895 EFI_PHYSICAL_ADDRESS AhciRFisPciAddr;\r
1896 EFI_PHYSICAL_ADDRESS AhciCmdListPciAddr;\r
1897 EFI_PHYSICAL_ADDRESS AhciCommandTablePciAddr;\r
a41b5272 1898\r
1899 Buffer = NULL;\r
1900 //\r
1901 // Collect AHCI controller information\r
1902 //\r
1903 Capability = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
a41b5272 1904 //\r
1905 // Get the number of command slots per port supported by this HBA.\r
1906 //\r
1907 MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
aca84419 1908 Support64Bit = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);\r
d1102dba 1909\r
467cacbf 1910 PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
1911 //\r
1912 // Get the highest bit of implemented ports which decides how many bytes are allocated for recived FIS.\r
1913 //\r
1914 MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
1915 if (MaxPortNumber == 0) {\r
1916 return EFI_DEVICE_ERROR;\r
1917 }\r
a41b5272 1918\r
1919 MaxReceiveFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1920 Status = PciIo->AllocateBuffer (\r
1921 PciIo,\r
1922 AllocateAnyPages,\r
1923 EfiBootServicesData,\r
ed365e93 1924 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
a41b5272 1925 &Buffer,\r
1926 0\r
1927 );\r
1928\r
1929 if (EFI_ERROR (Status)) {\r
1930 return EFI_OUT_OF_RESOURCES;\r
1931 }\r
1932\r
5dec0c68 1933 ZeroMem (Buffer, (UINTN)MaxReceiveFisSize);\r
a41b5272 1934\r
1935 AhciRegisters->AhciRFis = Buffer;\r
1936 AhciRegisters->MaxReceiveFisSize = MaxReceiveFisSize;\r
5dec0c68 1937 Bytes = (UINTN)MaxReceiveFisSize;\r
a41b5272 1938\r
1939 Status = PciIo->Map (\r
1940 PciIo,\r
1941 EfiPciIoOperationBusMasterCommonBuffer,\r
1942 Buffer,\r
1943 &Bytes,\r
ed365e93 1944 &AhciRFisPciAddr,\r
a41b5272 1945 &AhciRegisters->MapRFis\r
1946 );\r
1947\r
1948 if (EFI_ERROR (Status) || (Bytes != MaxReceiveFisSize)) {\r
1949 //\r
1aff716a 1950 // Map error or unable to map the whole RFis buffer into a contiguous region.\r
a41b5272 1951 //\r
1952 Status = EFI_OUT_OF_RESOURCES;\r
1953 goto Error6;\r
1954 }\r
1955\r
ed365e93 1956 if ((!Support64Bit) && (AhciRFisPciAddr > 0x100000000ULL)) {\r
a41b5272 1957 //\r
1958 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
1959 //\r
1960 Status = EFI_DEVICE_ERROR;\r
1961 goto Error5;\r
1962 }\r
ed365e93 1963 AhciRegisters->AhciRFisPciAddr = (EFI_AHCI_RECEIVED_FIS *)(UINTN)AhciRFisPciAddr;\r
a41b5272 1964\r
1965 //\r
1966 // Allocate memory for command list\r
1967 // Note that the implemenation is a single task model which only use a command list for all ports.\r
1968 //\r
1969 Buffer = NULL;\r
1970 MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);\r
1971 Status = PciIo->AllocateBuffer (\r
1972 PciIo,\r
1973 AllocateAnyPages,\r
1974 EfiBootServicesData,\r
ed365e93 1975 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
a41b5272 1976 &Buffer,\r
1977 0\r
1978 );\r
1979\r
1980 if (EFI_ERROR (Status)) {\r
1981 //\r
1aff716a 1982 // Free mapped resource.\r
a41b5272 1983 //\r
1984 Status = EFI_OUT_OF_RESOURCES;\r
1985 goto Error5;\r
1986 }\r
1987\r
5dec0c68 1988 ZeroMem (Buffer, (UINTN)MaxCommandListSize);\r
a41b5272 1989\r
1990 AhciRegisters->AhciCmdList = Buffer;\r
1991 AhciRegisters->MaxCommandListSize = MaxCommandListSize;\r
5dec0c68 1992 Bytes = (UINTN)MaxCommandListSize;\r
a41b5272 1993\r
1994 Status = PciIo->Map (\r
1995 PciIo,\r
1996 EfiPciIoOperationBusMasterCommonBuffer,\r
1997 Buffer,\r
1998 &Bytes,\r
ed365e93 1999 &AhciCmdListPciAddr,\r
a41b5272 2000 &AhciRegisters->MapCmdList\r
2001 );\r
2002\r
2003 if (EFI_ERROR (Status) || (Bytes != MaxCommandListSize)) {\r
2004 //\r
2005 // Map error or unable to map the whole cmd list buffer into a contiguous region.\r
2006 //\r
2007 Status = EFI_OUT_OF_RESOURCES;\r
2008 goto Error4;\r
2009 }\r
2010\r
ed365e93 2011 if ((!Support64Bit) && (AhciCmdListPciAddr > 0x100000000ULL)) {\r
a41b5272 2012 //\r
2013 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
2014 //\r
2015 Status = EFI_DEVICE_ERROR;\r
2016 goto Error3;\r
2017 }\r
ed365e93 2018 AhciRegisters->AhciCmdListPciAddr = (EFI_AHCI_COMMAND_LIST *)(UINTN)AhciCmdListPciAddr;\r
a41b5272 2019\r
2020 //\r
2021 // Allocate memory for command table\r
2022 // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
2023 //\r
2024 Buffer = NULL;\r
2025 MaxCommandTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
2026\r
2027 Status = PciIo->AllocateBuffer (\r
2028 PciIo,\r
2029 AllocateAnyPages,\r
2030 EfiBootServicesData,\r
ed365e93 2031 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
a41b5272 2032 &Buffer,\r
2033 0\r
2034 );\r
2035\r
2036 if (EFI_ERROR (Status)) {\r
2037 //\r
1aff716a 2038 // Free mapped resource.\r
a41b5272 2039 //\r
2040 Status = EFI_OUT_OF_RESOURCES;\r
2041 goto Error3;\r
2042 }\r
2043\r
5dec0c68 2044 ZeroMem (Buffer, (UINTN)MaxCommandTableSize);\r
a41b5272 2045\r
2046 AhciRegisters->AhciCommandTable = Buffer;\r
2047 AhciRegisters->MaxCommandTableSize = MaxCommandTableSize;\r
5dec0c68 2048 Bytes = (UINTN)MaxCommandTableSize;\r
a41b5272 2049\r
2050 Status = PciIo->Map (\r
2051 PciIo,\r
2052 EfiPciIoOperationBusMasterCommonBuffer,\r
2053 Buffer,\r
2054 &Bytes,\r
ed365e93 2055 &AhciCommandTablePciAddr,\r
a41b5272 2056 &AhciRegisters->MapCommandTable\r
2057 );\r
2058\r
2059 if (EFI_ERROR (Status) || (Bytes != MaxCommandTableSize)) {\r
2060 //\r
2061 // Map error or unable to map the whole cmd list buffer into a contiguous region.\r
2062 //\r
2063 Status = EFI_OUT_OF_RESOURCES;\r
2064 goto Error2;\r
2065 }\r
2066\r
ed365e93 2067 if ((!Support64Bit) && (AhciCommandTablePciAddr > 0x100000000ULL)) {\r
a41b5272 2068 //\r
2069 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
2070 //\r
2071 Status = EFI_DEVICE_ERROR;\r
2072 goto Error1;\r
2073 }\r
ed365e93 2074 AhciRegisters->AhciCommandTablePciAddr = (EFI_AHCI_COMMAND_TABLE *)(UINTN)AhciCommandTablePciAddr;\r
a41b5272 2075\r
2076 return EFI_SUCCESS;\r
2077 //\r
1aff716a 2078 // Map error or unable to map the whole CmdList buffer into a contiguous region.\r
a41b5272 2079 //\r
2080Error1:\r
2081 PciIo->Unmap (\r
2082 PciIo,\r
2083 AhciRegisters->MapCommandTable\r
2084 );\r
2085Error2:\r
2086 PciIo->FreeBuffer (\r
2087 PciIo,\r
ed365e93 2088 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
a41b5272 2089 AhciRegisters->AhciCommandTable\r
2090 );\r
2091Error3:\r
2092 PciIo->Unmap (\r
2093 PciIo,\r
2094 AhciRegisters->MapCmdList\r
2095 );\r
2096Error4:\r
2097 PciIo->FreeBuffer (\r
2098 PciIo,\r
ed365e93 2099 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
a41b5272 2100 AhciRegisters->AhciCmdList\r
2101 );\r
2102Error5:\r
2103 PciIo->Unmap (\r
2104 PciIo,\r
2105 AhciRegisters->MapRFis\r
2106 );\r
2107Error6:\r
2108 PciIo->FreeBuffer (\r
2109 PciIo,\r
ed365e93 2110 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
a41b5272 2111 AhciRegisters->AhciRFis\r
2112 );\r
2113\r
2114 return Status;\r
2115}\r
2116\r
f3100a1a
RN
2117/**\r
2118 Read logs from SATA device.\r
2119\r
2120 @param PciIo The PCI IO protocol instance.\r
2121 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
2122 @param Port The number of port.\r
2123 @param PortMultiplier The multiplier of port.\r
2124 @param Buffer The data buffer to store SATA logs.\r
2125 @param LogNumber The address of the log.\r
2126 @param PageNumber The page number of the log.\r
2127\r
2128 @retval EFI_INVALID_PARAMETER PciIo, AhciRegisters or Buffer is NULL.\r
2129 @retval others Return status of AhciPioTransfer().\r
2130**/\r
2131EFI_STATUS\r
2132AhciReadLogExt (\r
2133 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
2134 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
2135 IN UINT8 Port,\r
2136 IN UINT8 PortMultiplier,\r
2137 IN OUT UINT8 *Buffer,\r
2138 IN UINT8 LogNumber,\r
2139 IN UINT8 PageNumber\r
2140 )\r
2141{\r
2142 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
2143 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
2144\r
2145 if (PciIo == NULL || AhciRegisters == NULL || Buffer == NULL) {\r
2146 return EFI_INVALID_PARAMETER;\r
2147 }\r
2148\r
2149 ///\r
2150 /// Read log from device\r
2151 ///\r
2152 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
2153 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
2154 ZeroMem (Buffer, 512);\r
2155\r
2156 AtaCommandBlock.AtaCommand = ATA_CMD_READ_LOG_EXT;\r
2157 AtaCommandBlock.AtaSectorCount = 1;\r
2158 AtaCommandBlock.AtaSectorNumber = LogNumber;\r
2159 AtaCommandBlock.AtaCylinderLow = PageNumber;\r
2160\r
2161 return AhciPioTransfer (\r
2162 PciIo,\r
2163 AhciRegisters,\r
2164 Port,\r
2165 PortMultiplier,\r
2166 NULL,\r
2167 0,\r
2168 TRUE,\r
2169 &AtaCommandBlock,\r
2170 &AtaStatusBlock,\r
2171 Buffer,\r
2172 512,\r
2173 ATA_ATAPI_TIMEOUT,\r
2174 NULL\r
2175 );\r
2176}\r
2177\r
2178/**\r
2179 Enable DEVSLP of the disk if supported.\r
2180\r
2181 @param PciIo The PCI IO protocol instance.\r
2182 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
2183 @param Port The number of port.\r
2184 @param PortMultiplier The multiplier of port.\r
2185 @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.\r
2186\r
2187 @retval EFI_SUCCESS The DEVSLP is enabled per policy successfully.\r
2188 @retval EFI_UNSUPPORTED The DEVSLP isn't supported by the controller/device and policy requires to enable it.\r
2189**/\r
2190EFI_STATUS\r
2191AhciEnableDevSlp (\r
2192 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
2193 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
2194 IN UINT8 Port,\r
2195 IN UINT8 PortMultiplier,\r
2196 IN EFI_IDENTIFY_DATA *IdentifyData\r
2197 )\r
2198{\r
2199 EFI_STATUS Status;\r
2200 UINT32 Offset;\r
2201 UINT32 Capability2;\r
2202 UINT8 LogData[512];\r
2203 DEVSLP_TIMING_VARIABLES DevSlpTiming;\r
2204 UINT32 PortCmd;\r
2205 UINT32 PortDevSlp;\r
2206\r
2207 if (mAtaAtapiPolicy->DeviceSleepEnable != 1) {\r
2208 return EFI_SUCCESS;\r
2209 }\r
2210\r
2211 //\r
2212 // Do not enable DevSlp if DevSlp is not supported.\r
2213 //\r
2214 Capability2 = AhciReadReg (PciIo, AHCI_CAPABILITY2_OFFSET);\r
2215 DEBUG ((DEBUG_INFO, "AHCI CAPABILITY2 = %08x\n", Capability2));\r
2216 if ((Capability2 & AHCI_CAP2_SDS) == 0) {\r
2217 return EFI_UNSUPPORTED;\r
2218 }\r
2219\r
2220 //\r
2221 // Do not enable DevSlp if DevSlp is not present\r
2222 // Do not enable DevSlp if Hot Plug or Mechanical Presence Switch is supported\r
2223 //\r
2224 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH;\r
2225 PortCmd = AhciReadReg (PciIo, Offset + EFI_AHCI_PORT_CMD);\r
2226 PortDevSlp = AhciReadReg (PciIo, Offset + AHCI_PORT_DEVSLP);\r
2227 DEBUG ((DEBUG_INFO, "Port CMD/DEVSLP = %08x / %08x\n", PortCmd, PortDevSlp));\r
2228 if (((PortDevSlp & AHCI_PORT_DEVSLP_DSP) == 0) ||\r
2229 ((PortCmd & (EFI_AHCI_PORT_CMD_HPCP | EFI_AHCI_PORT_CMD_MPSP)) != 0)\r
2230 ) {\r
2231 return EFI_UNSUPPORTED;\r
2232 }\r
2233\r
2234 //\r
2235 // Do not enable DevSlp if the device doesn't support DevSlp\r
2236 //\r
2237 DEBUG ((DEBUG_INFO, "IDENTIFY DEVICE: [77] = %04x, [78] = %04x, [79] = %04x\n",\r
2238 IdentifyData->AtaData.reserved_77,\r
2239 IdentifyData->AtaData.serial_ata_features_supported, IdentifyData->AtaData.serial_ata_features_enabled));\r
2240 if ((IdentifyData->AtaData.serial_ata_features_supported & BIT8) == 0) {\r
2241 DEBUG ((DEBUG_INFO, "DevSlp feature is not supported for device at port [%d] PortMultiplier [%d]!\n",\r
2242 Port, PortMultiplier));\r
2243 return EFI_UNSUPPORTED;\r
2244 }\r
2245\r
2246 //\r
2247 // Enable DevSlp when it is not enabled.\r
2248 //\r
2249 if ((IdentifyData->AtaData.serial_ata_features_enabled & BIT8) != 0) {\r
2250 Status = AhciDeviceSetFeature (\r
2251 PciIo, AhciRegisters, Port, 0, ATA_SUB_CMD_ENABLE_SATA_FEATURE, 0x09, ATA_ATAPI_TIMEOUT\r
2252 );\r
2253 DEBUG ((DEBUG_INFO, "DevSlp set feature for device at port [%d] PortMultiplier [%d] - %r\n",\r
2254 Port, PortMultiplier, Status));\r
2255 if (EFI_ERROR (Status)) {\r
2256 return Status;\r
2257 }\r
2258 }\r
2259\r
2260 Status = AhciReadLogExt(PciIo, AhciRegisters, Port, PortMultiplier, LogData, 0x30, 0x08);\r
2261\r
2262 //\r
2263 // Clear PxCMD.ST and PxDEVSLP.ADSE before updating PxDEVSLP.DITO and PxDEVSLP.MDAT.\r
2264 //\r
2265 AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd & ~EFI_AHCI_PORT_CMD_ST);\r
2266 PortDevSlp &= ~AHCI_PORT_DEVSLP_ADSE;\r
2267 AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
2268\r
2269 //\r
2270 // Set PxDEVSLP.DETO and PxDEVSLP.MDAT to 0.\r
2271 //\r
2272 PortDevSlp &= ~AHCI_PORT_DEVSLP_DETO_MASK;\r
2273 PortDevSlp &= ~AHCI_PORT_DEVSLP_MDAT_MASK;\r
2274 AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
2275 DEBUG ((DEBUG_INFO, "Read Log Ext at port [%d] PortMultiplier [%d] - %r\n", Port, PortMultiplier, Status));\r
2276 if (EFI_ERROR (Status)) {\r
2277 //\r
2278 // Assume DEVSLP TIMING VARIABLES is not supported if the Identify Device Data log (30h, 8) fails\r
2279 //\r
4e738cd4 2280 ZeroMem (&DevSlpTiming, sizeof (DevSlpTiming));\r
f3100a1a
RN
2281 } else {\r
2282 CopyMem (&DevSlpTiming, &LogData[48], sizeof (DevSlpTiming));\r
2283 DEBUG ((DEBUG_INFO, "DevSlpTiming: Supported(%d), Deto(%d), Madt(%d)\n",\r
2284 DevSlpTiming.Supported, DevSlpTiming.Deto, DevSlpTiming.Madt));\r
2285 }\r
2286\r
2287 //\r
2288 // Use 20ms as default DETO when DEVSLP TIMING VARIABLES is not supported or the DETO is 0.\r
2289 //\r
2290 if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Deto == 0)) {\r
2291 DevSlpTiming.Deto = 20;\r
2292 }\r
2293\r
2294 //\r
2295 // Use 10ms as default MADT when DEVSLP TIMING VARIABLES is not supported or the MADT is 0.\r
2296 //\r
2297 if ((DevSlpTiming.Supported == 0) || (DevSlpTiming.Madt == 0)) {\r
2298 DevSlpTiming.Madt = 10;\r
2299 }\r
2300\r
2301 PortDevSlp |= DevSlpTiming.Deto << 2;\r
2302 PortDevSlp |= DevSlpTiming.Madt << 10;\r
2303 AhciOrReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
2304\r
2305 if (mAtaAtapiPolicy->AggressiveDeviceSleepEnable == 1) {\r
2306 if ((Capability2 & AHCI_CAP2_SADM) != 0) {\r
2307 PortDevSlp &= ~AHCI_PORT_DEVSLP_DITO_MASK;\r
2308 PortDevSlp |= (625 << 15);\r
2309 AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
2310\r
2311 PortDevSlp |= AHCI_PORT_DEVSLP_ADSE;\r
2312 AhciWriteReg (PciIo, Offset + AHCI_PORT_DEVSLP, PortDevSlp);\r
2313 }\r
2314 }\r
2315\r
2316\r
2317 AhciWriteReg (PciIo, Offset + EFI_AHCI_PORT_CMD, PortCmd);\r
2318\r
2319 DEBUG ((DEBUG_INFO, "Enabled DevSlp feature at port [%d] PortMultiplier [%d], Port CMD/DEVSLP = %08x / %08x\n",\r
2320 Port, PortMultiplier, PortCmd, PortDevSlp));\r
2321\r
2322 return EFI_SUCCESS;\r
2323}\r
8d3c4b55
RN
2324\r
2325/**\r
2326 Spin-up disk if IDD was incomplete or PUIS feature is enabled\r
2327\r
2328 @param PciIo The PCI IO protocol instance.\r
2329 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
2330 @param Port The number of port.\r
2331 @param PortMultiplier The multiplier of port.\r
2332 @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.\r
2333\r
2334**/\r
2335EFI_STATUS\r
2336AhciSpinUpDisk (\r
2337 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
2338 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
2339 IN UINT8 Port,\r
2340 IN UINT8 PortMultiplier,\r
2341 IN OUT EFI_IDENTIFY_DATA *IdentifyData\r
2342 )\r
2343{\r
2344 EFI_STATUS Status;\r
2345 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
2346 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
2347 UINT8 Buffer[512];\r
2348\r
2349 if (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_REQUIRED_IDD_INCOMPLETE) {\r
2350 //\r
2351 // Use SET_FEATURE subcommand to spin up the device.\r
2352 //\r
2353 Status = AhciDeviceSetFeature (\r
2354 PciIo, AhciRegisters, Port, PortMultiplier,\r
2355 ATA_SUB_CMD_PUIS_SET_DEVICE_SPINUP, 0x00, ATA_SPINUP_TIMEOUT\r
2356 );\r
2357 DEBUG ((DEBUG_INFO, "CMD_PUIS_SET_DEVICE_SPINUP for device at port [%d] PortMultiplier [%d] - %r!\n",\r
2358 Port, PortMultiplier, Status));\r
2359 if (EFI_ERROR (Status)) {\r
2360 return Status;\r
2361 }\r
2362 } else {\r
2363 ASSERT (IdentifyData->AtaData.specific_config == ATA_SPINUP_CFG_NOT_REQUIRED_IDD_INCOMPLETE);\r
2364\r
2365 //\r
2366 // Use READ_SECTORS to spin up the device if SpinUp SET FEATURE subcommand is not supported\r
2367 //\r
2368 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
2369 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
2370 //\r
2371 // Perform READ SECTORS PIO Data-In command to Read LBA 0\r
2372 //\r
2373 AtaCommandBlock.AtaCommand = ATA_CMD_READ_SECTORS;\r
2374 AtaCommandBlock.AtaSectorCount = 0x1;\r
2375\r
2376 Status = AhciPioTransfer (\r
2377 PciIo,\r
2378 AhciRegisters,\r
2379 Port,\r
2380 PortMultiplier,\r
2381 NULL,\r
2382 0,\r
2383 TRUE,\r
2384 &AtaCommandBlock,\r
2385 &AtaStatusBlock,\r
2386 &Buffer,\r
2387 sizeof (Buffer),\r
2388 ATA_SPINUP_TIMEOUT,\r
2389 NULL\r
2390 );\r
2391 DEBUG ((DEBUG_INFO, "Read LBA 0 for device at port [%d] PortMultiplier [%d] - %r!\n",\r
2392 Port, PortMultiplier, Status));\r
2393 if (EFI_ERROR (Status)) {\r
2394 return Status;\r
2395 }\r
2396 }\r
2397\r
2398 //\r
2399 // Read the complete IDENTIFY DEVICE data.\r
2400 //\r
2401 ZeroMem (IdentifyData, sizeof (*IdentifyData));\r
2402 Status = AhciIdentify (PciIo, AhciRegisters, Port, PortMultiplier, IdentifyData);\r
2403 if (EFI_ERROR (Status)) {\r
2404 DEBUG ((DEBUG_ERROR, "Read IDD failed for device at port [%d] PortMultiplier [%d] - %r!\n",\r
2405 Port, PortMultiplier, Status));\r
2406 return Status;\r
2407 }\r
2408\r
2409 DEBUG ((DEBUG_INFO, "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",\r
2410 IdentifyData->AtaData.config, IdentifyData->AtaData.specific_config,\r
2411 IdentifyData->AtaData.command_set_supported_83, IdentifyData->AtaData.command_set_feature_enb_86));\r
2412 //\r
2413 // Check if IDD is incomplete\r
2414 //\r
2415 if ((IdentifyData->AtaData.config & BIT2) != 0) {\r
2416 return EFI_DEVICE_ERROR;\r
2417 }\r
2418\r
2419 return EFI_SUCCESS;\r
2420}\r
2421\r
06766c0e
RN
2422/**\r
2423 Enable/disable/skip PUIS of the disk according to policy.\r
2424\r
2425 @param PciIo The PCI IO protocol instance.\r
2426 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
2427 @param Port The number of port.\r
2428 @param PortMultiplier The multiplier of port.\r
2429\r
2430**/\r
2431EFI_STATUS\r
2432AhciPuisEnable (\r
2433 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
2434 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
2435 IN UINT8 Port,\r
2436 IN UINT8 PortMultiplier\r
2437 )\r
2438{\r
2439 EFI_STATUS Status;\r
2440\r
2441 Status = EFI_SUCCESS;\r
2442 if (mAtaAtapiPolicy->PuisEnable == 0) {\r
2443 Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_DISABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);\r
2444 } else if (mAtaAtapiPolicy->PuisEnable == 1) {\r
2445 Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, PortMultiplier, ATA_SUB_CMD_ENABLE_PUIS, 0x00, ATA_ATAPI_TIMEOUT);\r
2446 }\r
2447 DEBUG ((DEBUG_INFO, "%a PUIS feature at port [%d] PortMultiplier [%d] - %r!\n",\r
2448 (mAtaAtapiPolicy->PuisEnable == 0) ? "Disable" : (\r
2449 (mAtaAtapiPolicy->PuisEnable == 1) ? "Enable" : "Skip"\r
2450 ), Port, PortMultiplier, Status));\r
2451 return Status;\r
2452}\r
2453\r
a41b5272 2454/**\r
2455 Initialize ATA host controller at AHCI mode.\r
2456\r
1aff716a 2457 The function is designed to initialize ATA host controller.\r
2458\r
a41b5272 2459 @param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance.\r
2460\r
2461**/\r
2462EFI_STATUS\r
2463EFIAPI\r
2464AhciModeInitialization (\r
2465 IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance\r
2466 )\r
2467{\r
2468 EFI_STATUS Status;\r
2469 EFI_PCI_IO_PROTOCOL *PciIo;\r
2470 EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit;\r
2471 UINT32 Capability;\r
2472 UINT8 MaxPortNumber;\r
2473 UINT32 PortImplementBitMap;\r
a41b5272 2474\r
2475 EFI_AHCI_REGISTERS *AhciRegisters;\r
2476\r
2477 UINT8 Port;\r
2478 DATA_64 Data64;\r
2479 UINT32 Offset;\r
2480 UINT32 Data;\r
2481 EFI_IDENTIFY_DATA Buffer;\r
2482 EFI_ATA_DEVICE_TYPE DeviceType;\r
2483 EFI_ATA_COLLECTIVE_MODE *SupportedModes;\r
2484 EFI_ATA_TRANSFER_MODE TransferMode;\r
cbd2a4b3 2485 UINT32 PhyDetectDelay;\r
6052a15f 2486 UINT32 Value;\r
cbd2a4b3 2487\r
a41b5272 2488 if (Instance == NULL) {\r
2489 return EFI_INVALID_PARAMETER;\r
2490 }\r
2491\r
2492 PciIo = Instance->PciIo;\r
2493 IdeInit = Instance->IdeControllerInit;\r
2494\r
1aff716a 2495 Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);\r
a41b5272 2496\r
2497 if (EFI_ERROR (Status)) {\r
2498 return EFI_DEVICE_ERROR;\r
2499 }\r
2500\r
2501 //\r
1ff1dd0f 2502 // Collect AHCI controller information\r
a41b5272 2503 //\r
1ff1dd0f 2504 Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
6052a15f 2505\r
a41b5272 2506 //\r
6052a15f 2507 // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
a41b5272 2508 //\r
6052a15f
MW
2509 Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
2510\r
2511 if ((Value & EFI_AHCI_GHC_ENABLE) == 0) {\r
1ff1dd0f
FT
2512 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
2513 }\r
a2c9b087
AB
2514\r
2515 //\r
2516 // Enable 64-bit DMA support in the PCI layer if this controller\r
2517 // supports it.\r
2518 //\r
2519 if ((Capability & EFI_AHCI_CAP_S64A) != 0) {\r
2520 Status = PciIo->Attributes (\r
2521 PciIo,\r
2522 EfiPciIoAttributeOperationEnable,\r
2523 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
2524 NULL\r
2525 );\r
2526 if (EFI_ERROR (Status)) {\r
2527 DEBUG ((EFI_D_WARN,\r
2528 "AhciModeInitialization: failed to enable 64-bit DMA on 64-bit capable controller (%r)\n",\r
2529 Status));\r
2530 }\r
2531 }\r
2532\r
a41b5272 2533 //\r
2534 // Get the number of command slots per port supported by this HBA.\r
2535 //\r
cbd2a4b3 2536 MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
a41b5272 2537\r
2538 //\r
2539 // Get the bit map of those ports exposed by this HBA.\r
1aff716a 2540 // It indicates which ports that the HBA supports are available for software to use.\r
a41b5272 2541 //\r
2542 PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
1aff716a 2543\r
a41b5272 2544 AhciRegisters = &Instance->AhciRegisters;\r
2545 Status = AhciCreateTransferDescriptor (PciIo, AhciRegisters);\r
2546\r
2547 if (EFI_ERROR (Status)) {\r
2548 return EFI_OUT_OF_RESOURCES;\r
2549 }\r
2550\r
6b13aa60 2551 for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port ++) {\r
27daa865 2552 if ((PortImplementBitMap & (((UINT32)BIT0) << Port)) != 0) {\r
6b13aa60 2553 //\r
2554 // According to AHCI spec, MaxPortNumber should be equal or greater than the number of implemented ports.\r
2555 //\r
2556 if ((MaxPortNumber--) == 0) {\r
2557 //\r
2558 // Should never be here.\r
2559 //\r
2560 ASSERT (FALSE);\r
2561 return EFI_SUCCESS;\r
2562 }\r
2563\r
cbd2a4b3 2564 IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port);\r
2565\r
2566 //\r
2567 // Initialize FIS Base Address Register and Command List Base Address Register for use.\r
2568 //\r
2569 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFisPciAddr) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port;\r
2570 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
2571 AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);\r
2572 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
2573 AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);\r
2574\r
2575 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdListPciAddr);\r
2576 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
2577 AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);\r
2578 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
2579 AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);\r
2580\r
a41b5272 2581 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
a41b5272 2582 Data = AhciReadReg (PciIo, Offset);\r
2583 if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {\r
2584 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_POD);\r
2585 }\r
a41b5272 2586\r
cbd2a4b3 2587 if ((Capability & EFI_AHCI_CAP_SSS) != 0) {\r
2588 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_SUD);\r
2589 }\r
2590\r
2591 //\r
2592 // Disable aggressive power management.\r
2593 //\r
2594 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
2595 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);\r
2596 //\r
2597 // Disable the reporting of the corresponding interrupt to system software.\r
2598 //\r
2599 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;\r
2600 AhciAndReg (PciIo, Offset, 0);\r
a41b5272 2601\r
cbd2a4b3 2602 //\r
2603 // Now inform the IDE Controller Init Module.\r
2604 //\r
2605 IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, Port);\r
2606\r
2607 //\r
2608 // Enable FIS Receive DMA engine for the first D2H FIS.\r
2609 //\r
2610 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
2611 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);\r
cbd2a4b3 2612\r
a41b5272 2613 //\r
1fb805b1 2614 // Wait for the Phy to detect the presence of a device.\r
a41b5272 2615 //\r
cbd2a4b3 2616 PhyDetectDelay = EFI_AHCI_BUS_PHY_DETECT_TIMEOUT;\r
2617 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
2618 do {\r
2619 Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK;\r
2620 if ((Data == EFI_AHCI_PORT_SSTS_DET_PCE) || (Data == EFI_AHCI_PORT_SSTS_DET)) {\r
2621 break;\r
a41b5272 2622 }\r
2623\r
cbd2a4b3 2624 MicroSecondDelay (1000);\r
2625 PhyDetectDelay--;\r
2626 } while (PhyDetectDelay > 0);\r
2627\r
2628 if (PhyDetectDelay == 0) {\r
a41b5272 2629 //\r
cbd2a4b3 2630 // No device detected at this port.\r
2721fabc 2631 // Clear PxCMD.SUD for those ports at which there are no device present.\r
a41b5272 2632 //\r
2721fabc 2633 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
2634 AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_SUD));\r
cbd2a4b3 2635 continue;\r
2636 }\r
a41b5272 2637\r
cbd2a4b3 2638 //\r
2639 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
2640 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
2641 //\r
2642 PhyDetectDelay = 16 * 1000;\r
2643 do {\r
2644 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
2645 if (AhciReadReg(PciIo, Offset) != 0) {\r
2646 AhciWriteReg (PciIo, Offset, AhciReadReg(PciIo, Offset));\r
2647 }\r
2648 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
a41b5272 2649\r
cbd2a4b3 2650 Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_TFD_MASK;\r
2651 if (Data == 0) {\r
2652 break;\r
2653 }\r
a41b5272 2654\r
cbd2a4b3 2655 MicroSecondDelay (1000);\r
2656 PhyDetectDelay--;\r
2657 } while (PhyDetectDelay > 0);\r
1aff716a 2658\r
cbd2a4b3 2659 if (PhyDetectDelay == 0) {\r
9c32277a 2660 DEBUG ((EFI_D_ERROR, "Port %d Device presence detected but phy not ready (TFD=0x%X)\n", Port, Data));\r
cbd2a4b3 2661 continue;\r
2662 }\r
a41b5272 2663\r
cbd2a4b3 2664 //\r
2665 // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
2666 //\r
2667 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;\r
8536cc4b 2668 Status = AhciWaitMmioSet (\r
1aff716a 2669 PciIo,\r
cbd2a4b3 2670 Offset,\r
2671 0x0000FFFF,\r
2672 0x00000101,\r
2673 EFI_TIMER_PERIOD_SECONDS(16)\r
2674 );\r
2675 if (EFI_ERROR (Status)) {\r
2676 continue;\r
2677 }\r
a41b5272 2678\r
cbd2a4b3 2679 Data = AhciReadReg (PciIo, Offset);\r
2680 if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) {\r
2681 Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer);\r
a41b5272 2682\r
cbd2a4b3 2683 if (EFI_ERROR (Status)) {\r
a41b5272 2684 continue;\r
2685 }\r
aca84419 2686\r
cbd2a4b3 2687 DeviceType = EfiIdeCdrom;\r
2688 } else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) {\r
2689 Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer);\r
a41b5272 2690\r
a41b5272 2691 if (EFI_ERROR (Status)) {\r
cbd2a4b3 2692 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED));\r
a41b5272 2693 continue;\r
2694 }\r
2695\r
8d3c4b55
RN
2696 DEBUG ((\r
2697 DEBUG_INFO, "IDENTIFY DEVICE: [0] = %016x, [2] = %016x, [83] = %016x, [86] = %016x\n",\r
2698 Buffer.AtaData.config, Buffer.AtaData.specific_config,\r
2699 Buffer.AtaData.command_set_supported_83, Buffer.AtaData.command_set_feature_enb_86\r
2700 ));\r
2701 if ((Buffer.AtaData.config & BIT2) != 0) {\r
2702 //\r
2703 // SpinUp disk if device reported incomplete IDENTIFY DEVICE.\r
2704 //\r
2705 Status = AhciSpinUpDisk (\r
2706 PciIo,\r
2707 AhciRegisters,\r
2708 Port,\r
2709 0,\r
2710 &Buffer\r
2711 );\r
2712 if (EFI_ERROR (Status)) {\r
2713 DEBUG ((DEBUG_ERROR, "Spin up standby device failed - %r\n", Status));\r
2714 continue;\r
2715 }\r
2716 }\r
2717\r
cbd2a4b3 2718 DeviceType = EfiIdeHarddisk;\r
2719 } else {\r
2720 continue;\r
2721 }\r
1aff716a 2722 DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n",\r
cbd2a4b3 2723 Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"));\r
a41b5272 2724\r
cbd2a4b3 2725 //\r
2726 // If the device is a hard disk, then try to enable S.M.A.R.T feature\r
2727 //\r
fc80ee69 2728 if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {\r
cbd2a4b3 2729 AhciAtaSmartSupport (\r
2730 PciIo,\r
2731 AhciRegisters,\r
2732 Port,\r
2733 0,\r
2734 &Buffer,\r
2735 NULL\r
2736 );\r
2737 }\r
a41b5272 2738\r
cbd2a4b3 2739 //\r
2740 // Submit identify data to IDE controller init driver\r
2741 //\r
2742 IdeInit->SubmitData (IdeInit, Port, 0, &Buffer);\r
aca84419 2743\r
cbd2a4b3 2744 //\r
2745 // Now start to config ide device parameter and transfer mode.\r
2746 //\r
2747 Status = IdeInit->CalculateMode (\r
2748 IdeInit,\r
2749 Port,\r
2750 0,\r
2751 &SupportedModes\r
2752 );\r
2753 if (EFI_ERROR (Status)) {\r
2754 DEBUG ((EFI_D_ERROR, "Calculate Mode Fail, Status = %r\n", Status));\r
2755 continue;\r
2756 }\r
2757\r
2758 //\r
2759 // Set best supported PIO mode on this IDE device\r
2760 //\r
2761 if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) {\r
2762 TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO;\r
2763 } else {\r
2764 TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO;\r
2765 }\r
2766\r
2767 TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode);\r
2768\r
2769 //\r
2770 // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't\r
2771 // be set together. Only one DMA mode can be set to a device. If setting\r
2772 // DMA mode operation fails, we can continue moving on because we only use\r
2773 // PIO mode at boot time. DMA modes are used by certain kind of OS booting\r
2774 //\r
2775 if (SupportedModes->UdmaMode.Valid) {\r
2776 TransferMode.ModeCategory = EFI_ATA_MODE_UDMA;\r
2777 TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode);\r
2778 } else if (SupportedModes->MultiWordDmaMode.Valid) {\r
2779 TransferMode.ModeCategory = EFI_ATA_MODE_MDMA;\r
1aff716a 2780 TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode;\r
cbd2a4b3 2781 }\r
2782\r
8d3c4b55 2783 Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode), ATA_ATAPI_TIMEOUT);\r
cbd2a4b3 2784 if (EFI_ERROR (Status)) {\r
2785 DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status));\r
2786 continue;\r
2787 }\r
2788\r
2789 //\r
2790 // Found a ATA or ATAPI device, add it into the device list.\r
2791 //\r
23a596db 2792 CreateNewDeviceInfo (Instance, Port, 0xFFFF, DeviceType, &Buffer);\r
cbd2a4b3 2793 if (DeviceType == EfiIdeHarddisk) {\r
2794 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));\r
f3100a1a
RN
2795 AhciEnableDevSlp (\r
2796 PciIo,\r
2797 AhciRegisters,\r
2798 Port,\r
2799 0,\r
2800 &Buffer\r
2801 );\r
a41b5272 2802 }\r
06766c0e
RN
2803\r
2804 //\r
2805 // Enable/disable PUIS according to policy setting if PUIS is capable (Word[83].BIT5 is set).\r
2806 //\r
2807 if ((Buffer.AtaData.command_set_supported_83 & BIT5) != 0) {\r
2808 Status = AhciPuisEnable (\r
2809 PciIo,\r
2810 AhciRegisters,\r
2811 Port,\r
2812 0\r
2813 );\r
2814 if (EFI_ERROR (Status)) {\r
2815 DEBUG ((DEBUG_ERROR, "PUIS enable/disable failed, Status = %r\n", Status));\r
2816 continue;\r
2817 }\r
2818 }\r
a41b5272 2819 }\r
2820 }\r
cbd2a4b3 2821\r
a41b5272 2822 return EFI_SUCCESS;\r
2823}\r
2824\r