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