]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdeModulePkg: Convert non DOS format files to DOS format and remove unused module...
[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
TF
706 BOOLEAN InfiniteWait;\r
707\r
708 if (Timeout == 0) {\r
709 InfiniteWait = TRUE;\r
710 } else {\r
711 InfiniteWait = FALSE;\r
712 }\r
a41b5272 713\r
714 if (Read) {\r
715 Flag = EfiPciIoOperationBusMasterWrite;\r
716 } else {\r
717 Flag = EfiPciIoOperationBusMasterRead;\r
718 }\r
719\r
720 //\r
721 // construct command list and command table with pci bus address\r
722 //\r
723 MapLength = DataCount;\r
724 Status = PciIo->Map (\r
725 PciIo,\r
726 Flag,\r
727 MemoryAddr,\r
728 &MapLength,\r
729 &PhyAddr,\r
730 &Map\r
731 );\r
732\r
733 if (EFI_ERROR (Status) || (DataCount != MapLength)) {\r
9e70c18b 734 return EFI_BAD_BUFFER_SIZE;\r
a41b5272 735 }\r
1aff716a 736\r
a41b5272 737 //\r
738 // Package read needed\r
739 //\r
740 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
741\r
742 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
743\r
744 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
745 CmdList.AhciCmdW = Read ? 0 : 1;\r
746\r
747 AhciBuildCommand (\r
748 PciIo,\r
749 AhciRegisters,\r
750 Port,\r
751 PortMultiplier,\r
752 &CFis,\r
753 &CmdList,\r
754 AtapiCommand,\r
755 AtapiCommandLength,\r
756 0,\r
757 (VOID *)(UINTN)PhyAddr,\r
758 DataCount\r
1aff716a 759 );\r
760\r
a41b5272 761 Status = AhciStartCommand (\r
490b5ea1 762 PciIo,\r
763 Port,\r
a41b5272 764 0,\r
765 Timeout\r
766 );\r
767 if (EFI_ERROR (Status)) {\r
768 goto Exit;\r
769 }\r
490b5ea1 770\r
a41b5272 771 //\r
490b5ea1 772 // Check the status and wait the driver sending data\r
a41b5272 773 //\r
774 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
a41b5272 775\r
8536cc4b 776 if (Read && (AtapiCommand == 0)) {\r
a41b5272 777 //\r
8536cc4b 778 // Wait device sends the PIO setup fis before data transfer\r
a41b5272 779 //\r
8536cc4b 780 Status = EFI_TIMEOUT;\r
ab82122d 781 Delay = DivU64x32 (Timeout, 1000) + 1;\r
8536cc4b 782 do {\r
8536cc4b 783 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
a41b5272 784\r
ab82122d 785 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL);\r
8536cc4b 786 if (!EFI_ERROR (Status)) {\r
7fb60a98 787 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
788 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
789 //\r
790 // PxTFD will be updated if there is a D2H or SetupFIS received. \r
791 // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.\r
792 //\r
793 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
794 Status = EFI_DEVICE_ERROR;\r
795 break;\r
796 }\r
797\r
8536cc4b 798 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
799 if (PrdCount == DataCount) {\r
800 break;\r
801 }\r
802 }\r
a41b5272 803\r
8536cc4b 804 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
ab82122d 805 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, NULL);\r
8536cc4b 806 if (!EFI_ERROR (Status)) {\r
807 Status = EFI_DEVICE_ERROR;\r
808 break;\r
809 }\r
a41b5272 810\r
8536cc4b 811 //\r
812 // Stall for 100 microseconds.\r
813 //\r
814 MicroSecondDelay(100);\r
a41b5272 815\r
8536cc4b 816 Delay--;\r
ab82122d 817 } while (InfiniteWait || (Delay > 0));\r
8536cc4b 818 } else {\r
819 //\r
820 // Wait for D2H Fis is received\r
821 //\r
822 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
823 Status = AhciWaitMemSet (\r
824 Offset,\r
825 EFI_AHCI_FIS_TYPE_MASK,\r
826 EFI_AHCI_FIS_REGISTER_D2H,\r
827 Timeout\r
828 );\r
a41b5272 829\r
8536cc4b 830 if (EFI_ERROR (Status)) {\r
831 goto Exit;\r
832 }\r
833\r
834 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
835 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
836 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
837 Status = EFI_DEVICE_ERROR;\r
838 }\r
a41b5272 839 }\r
840\r
490b5ea1 841Exit:\r
a41b5272 842 AhciStopCommand (\r
490b5ea1 843 PciIo,\r
a41b5272 844 Port,\r
845 Timeout\r
846 );\r
1aff716a 847\r
a41b5272 848 AhciDisableFisReceive (\r
490b5ea1 849 PciIo,\r
a41b5272 850 Port,\r
851 Timeout\r
852 );\r
853\r
854 PciIo->Unmap (\r
855 PciIo,\r
856 Map\r
857 );\r
858\r
e519983a 859 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
860\r
a41b5272 861 return Status;\r
862}\r
863\r
864/**\r
865 Start a DMA data transfer on specific port\r
866\r
490b5ea1 867 @param[in] Instance The ATA_ATAPI_PASS_THRU_INSTANCE protocol instance.\r
868 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
869 @param[in] Port The number of port.\r
870 @param[in] PortMultiplier The timeout value of stop.\r
871 @param[in] AtapiCommand The atapi command will be used for the\r
872 transfer.\r
873 @param[in] AtapiCommandLength The length of the atapi command.\r
874 @param[in] Read The transfer direction.\r
875 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
876 @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
877 @param[in, out] MemoryAddr The pointer to the data buffer.\r
878 @param[in] DataCount The data count to be transferred.\r
8536cc4b 879 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
490b5ea1 880 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
881 used by non-blocking mode.\r
aca84419 882\r
883 @retval EFI_DEVICE_ERROR The DMA data transfer abort with error occurs.\r
884 @retval EFI_TIMEOUT The operation is time out.\r
885 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
886 @retval EFI_SUCCESS The DMA data transfer executes successfully.\r
490b5ea1 887\r
a41b5272 888**/\r
889EFI_STATUS\r
890EFIAPI\r
891AhciDmaTransfer (\r
490b5ea1 892 IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance,\r
a41b5272 893 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
894 IN UINT8 Port,\r
895 IN UINT8 PortMultiplier,\r
896 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
897 IN UINT8 AtapiCommandLength,\r
1aff716a 898 IN BOOLEAN Read,\r
a41b5272 899 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
900 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
901 IN OUT VOID *MemoryAddr,\r
8536cc4b 902 IN UINT32 DataCount,\r
490b5ea1 903 IN UINT64 Timeout,\r
904 IN ATA_NONBLOCK_TASK *Task\r
a41b5272 905 )\r
906{\r
907 EFI_STATUS Status;\r
8536cc4b 908 UINTN Offset;\r
a41b5272 909 EFI_PHYSICAL_ADDRESS PhyAddr;\r
910 VOID *Map;\r
911 UINTN MapLength;\r
912 EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
913 EFI_AHCI_COMMAND_FIS CFis;\r
914 EFI_AHCI_COMMAND_LIST CmdList;\r
8536cc4b 915 UINTN FisBaseAddr;\r
916 UINT32 PortTfd;\r
a41b5272 917\r
8536cc4b 918 EFI_PCI_IO_PROTOCOL *PciIo;\r
919 EFI_TPL OldTpl;\r
a41b5272 920\r
490b5ea1 921 Map = NULL;\r
922 PciIo = Instance->PciIo;\r
a41b5272 923\r
490b5ea1 924 if (PciIo == NULL) {\r
925 return EFI_INVALID_PARAMETER;\r
a41b5272 926 }\r
927\r
928 //\r
490b5ea1 929 // Before starting the Blocking BlockIO operation, push to finish all non-blocking\r
930 // BlockIO tasks.\r
931 // Delay 100us to simulate the blocking time out checking.\r
a41b5272 932 //\r
1aff716a 933 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
490b5ea1 934 while ((Task == NULL) && (!IsListEmpty (&Instance->NonBlockingTaskList))) {\r
490b5ea1 935 AsyncNonBlockingTransferRoutine (NULL, Instance);\r
490b5ea1 936 //\r
937 // Stall for 100us.\r
938 //\r
939 MicroSecondDelay (100);\r
940 }\r
1aff716a 941 gBS->RestoreTPL (OldTpl);\r
a41b5272 942\r
490b5ea1 943 if ((Task == NULL) || ((Task != NULL) && (!Task->IsStart))) {\r
944 //\r
945 // Mark the Task to indicate that it has been started.\r
946 //\r
947 if (Task != NULL) {\r
948 Task->IsStart = TRUE;\r
490b5ea1 949 }\r
950 if (Read) {\r
951 Flag = EfiPciIoOperationBusMasterWrite;\r
952 } else {\r
953 Flag = EfiPciIoOperationBusMasterRead;\r
954 }\r
a41b5272 955\r
490b5ea1 956 //\r
957 // Construct command list and command table with pci bus address.\r
958 //\r
959 MapLength = DataCount;\r
960 Status = PciIo->Map (\r
961 PciIo,\r
962 Flag,\r
963 MemoryAddr,\r
964 &MapLength,\r
965 &PhyAddr,\r
966 &Map\r
967 );\r
968\r
969 if (EFI_ERROR (Status) || (DataCount != MapLength)) {\r
9e70c18b 970 return EFI_BAD_BUFFER_SIZE;\r
490b5ea1 971 }\r
a41b5272 972\r
490b5ea1 973 if (Task != NULL) {\r
974 Task->Map = Map;\r
975 }\r
976 //\r
977 // Package read needed\r
978 //\r
979 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
980\r
981 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
982\r
983 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
984 CmdList.AhciCmdW = Read ? 0 : 1;\r
985\r
986 AhciBuildCommand (\r
987 PciIo,\r
988 AhciRegisters,\r
989 Port,\r
990 PortMultiplier,\r
991 &CFis,\r
992 &CmdList,\r
993 AtapiCommand,\r
994 AtapiCommandLength,\r
995 0,\r
996 (VOID *)(UINTN)PhyAddr,\r
997 DataCount\r
998 );\r
999\r
1000 Status = AhciStartCommand (\r
1001 PciIo,\r
1002 Port,\r
1003 0,\r
1004 Timeout\r
1005 );\r
1006 if (EFI_ERROR (Status)) {\r
1007 goto Exit;\r
1008 }\r
a41b5272 1009 }\r
1010\r
1011 //\r
1012 // Wait for command compelte\r
1013 //\r
8536cc4b 1014 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1015 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
490b5ea1 1016 if (Task != NULL) {\r
1017 //\r
1018 // For Non-blocking\r
1019 //\r
1020 Status = AhciCheckMemSet (\r
490b5ea1 1021 Offset,\r
8536cc4b 1022 EFI_AHCI_FIS_TYPE_MASK,\r
1023 EFI_AHCI_FIS_REGISTER_D2H,\r
ab82122d 1024 Task\r
490b5ea1 1025 );\r
1026 } else {\r
1027 Status = AhciWaitMemSet (\r
490b5ea1 1028 Offset,\r
8536cc4b 1029 EFI_AHCI_FIS_TYPE_MASK,\r
1030 EFI_AHCI_FIS_REGISTER_D2H,\r
490b5ea1 1031 Timeout\r
1032 );\r
1033 }\r
1034\r
a41b5272 1035 if (EFI_ERROR (Status)) {\r
1036 goto Exit;\r
1037 }\r
1038\r
8536cc4b 1039 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1040 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
1041 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
1042 Status = EFI_DEVICE_ERROR;\r
a41b5272 1043 }\r
1044\r
490b5ea1 1045Exit:\r
1046 //\r
1047 // For Blocking mode, the command should be stopped, the Fis should be disabled\r
1048 // and the PciIo should be unmapped.\r
1aff716a 1049 // For non-blocking mode, only when a error is happened (if the return status is\r
1050 // EFI_NOT_READY that means the command doesn't finished, try again.), first do the\r
490b5ea1 1051 // context cleanup, then set the packet's Asb status.\r
1052 //\r
1053 if (Task == NULL ||\r
1054 ((Task != NULL) && (Status != EFI_NOT_READY))\r
1055 ) {\r
1056 AhciStopCommand (\r
1aff716a 1057 PciIo,\r
490b5ea1 1058 Port,\r
1059 Timeout\r
1060 );\r
a41b5272 1061\r
490b5ea1 1062 AhciDisableFisReceive (\r
1aff716a 1063 PciIo,\r
490b5ea1 1064 Port,\r
1065 Timeout\r
1066 );\r
a41b5272 1067\r
490b5ea1 1068 PciIo->Unmap (\r
1069 PciIo,\r
1070 (Task != NULL) ? Task->Map : Map\r
1071 );\r
e519983a 1072\r
490b5ea1 1073 if (Task != NULL) {\r
1074 Task->Packet->Asb->AtaStatus = 0x01;\r
1075 }\r
1076 }\r
1077\r
1078 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
a41b5272 1079 return Status;\r
1080}\r
1081\r
1082/**\r
1083 Start a non data transfer on specific port.\r
1aff716a 1084\r
490b5ea1 1085 @param[in] PciIo The PCI IO protocol instance.\r
1086 @param[in] AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1087 @param[in] Port The number of port.\r
1088 @param[in] PortMultiplier The timeout value of stop.\r
1089 @param[in] AtapiCommand The atapi command will be used for the\r
1090 transfer.\r
1091 @param[in] AtapiCommandLength The length of the atapi command.\r
1092 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
1093 @param[in, out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
8536cc4b 1094 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
490b5ea1 1095 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
1096 used by non-blocking mode.\r
a41b5272 1097\r
1098 @retval EFI_DEVICE_ERROR The non data transfer abort with error occurs.\r
1099 @retval EFI_TIMEOUT The operation is time out.\r
1100 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
1101 @retval EFI_SUCCESS The non data transfer executes successfully.\r
1102\r
1aff716a 1103**/\r
a41b5272 1104EFI_STATUS\r
1105EFIAPI\r
1106AhciNonDataTransfer (\r
1107 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1108 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1109 IN UINT8 Port,\r
1110 IN UINT8 PortMultiplier,\r
1111 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
1112 IN UINT8 AtapiCommandLength,\r
1113 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
1114 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
490b5ea1 1115 IN UINT64 Timeout,\r
1116 IN ATA_NONBLOCK_TASK *Task\r
1117 )\r
a41b5272 1118{\r
490b5ea1 1119 EFI_STATUS Status;\r
a41b5272 1120 UINTN FisBaseAddr;\r
8536cc4b 1121 UINTN Offset;\r
1122 UINT32 PortTfd;\r
a41b5272 1123 EFI_AHCI_COMMAND_FIS CFis;\r
1124 EFI_AHCI_COMMAND_LIST CmdList;\r
1125\r
1126 //\r
1127 // Package read needed\r
1128 //\r
1129 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
1130\r
1131 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
1132\r
1133 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
1134\r
1135 AhciBuildCommand (\r
1136 PciIo,\r
1137 AhciRegisters,\r
1138 Port,\r
1139 PortMultiplier,\r
1140 &CFis,\r
1141 &CmdList,\r
1142 AtapiCommand,\r
1143 AtapiCommandLength,\r
1144 0,\r
1145 NULL,\r
1146 0\r
490b5ea1 1147 );\r
1148\r
a41b5272 1149 Status = AhciStartCommand (\r
490b5ea1 1150 PciIo,\r
1151 Port,\r
a41b5272 1152 0,\r
1153 Timeout\r
1154 );\r
1155 if (EFI_ERROR (Status)) {\r
1156 goto Exit;\r
1157 }\r
490b5ea1 1158\r
a41b5272 1159 //\r
1160 // Wait device sends the Response Fis\r
1161 //\r
1162 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
8536cc4b 1163 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
1164 Status = AhciWaitMemSet (\r
1165 Offset,\r
1166 EFI_AHCI_FIS_TYPE_MASK,\r
1167 EFI_AHCI_FIS_REGISTER_D2H,\r
1168 Timeout\r
1169 );\r
a41b5272 1170\r
8536cc4b 1171 if (EFI_ERROR (Status)) {\r
a41b5272 1172 goto Exit;\r
1173 }\r
1174\r
8536cc4b 1175 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1176 PortTfd = AhciReadReg (PciIo, (UINT32) Offset);\r
1177 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
1178 Status = EFI_DEVICE_ERROR;\r
1179 }\r
490b5ea1 1180\r
1181Exit:\r
a41b5272 1182 AhciStopCommand (\r
490b5ea1 1183 PciIo,\r
a41b5272 1184 Port,\r
1185 Timeout\r
1186 );\r
1187\r
1188 AhciDisableFisReceive (\r
490b5ea1 1189 PciIo,\r
a41b5272 1190 Port,\r
1191 Timeout\r
1192 );\r
1193\r
e519983a 1194 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
1195\r
a41b5272 1196 return Status;\r
1197}\r
1198\r
1199/**\r
1200 Stop command running for giving port\r
1aff716a 1201\r
a41b5272 1202 @param PciIo The PCI IO protocol instance.\r
1203 @param Port The number of port.\r
8536cc4b 1204 @param Timeout The timeout value of stop, uses 100ns as a unit.\r
1aff716a 1205\r
a41b5272 1206 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
1207 @retval EFI_TIMEOUT The operation is time out.\r
1208 @retval EFI_SUCCESS The command stop successfully.\r
1209\r
1210**/\r
1211EFI_STATUS\r
1212EFIAPI\r
1213AhciStopCommand (\r
1214 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1215 IN UINT8 Port,\r
1216 IN UINT64 Timeout\r
1217 )\r
1218{\r
1219 UINT32 Offset;\r
1220 UINT32 Data;\r
1221\r
1222 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1223 Data = AhciReadReg (PciIo, Offset);\r
1224\r
1225 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {\r
490b5ea1 1226 return EFI_SUCCESS;\r
a41b5272 1227 }\r
1228\r
1229 if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {\r
1230 AhciAndReg (PciIo, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));\r
1231 }\r
1232\r
8536cc4b 1233 return AhciWaitMmioSet (\r
490b5ea1 1234 PciIo,\r
a41b5272 1235 Offset,\r
1236 EFI_AHCI_PORT_CMD_CR,\r
1237 0,\r
1238 Timeout\r
490b5ea1 1239 );\r
a41b5272 1240}\r
1241\r
1242/**\r
1243 Start command for give slot on specific port.\r
490b5ea1 1244\r
a41b5272 1245 @param PciIo The PCI IO protocol instance.\r
1246 @param Port The number of port.\r
490b5ea1 1247 @param CommandSlot The number of Command Slot.\r
8536cc4b 1248 @param Timeout The timeout value of start, uses 100ns as a unit.\r
490b5ea1 1249\r
a41b5272 1250 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
1251 @retval EFI_TIMEOUT The operation is time out.\r
1252 @retval EFI_SUCCESS The command start successfully.\r
1253\r
1254**/\r
1255EFI_STATUS\r
1256EFIAPI\r
1257AhciStartCommand (\r
1258 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1259 IN UINT8 Port,\r
1260 IN UINT8 CommandSlot,\r
1261 IN UINT64 Timeout\r
1262 )\r
1263{\r
1264 UINT32 CmdSlotBit;\r
1265 EFI_STATUS Status;\r
1266 UINT32 PortStatus;\r
1267 UINT32 StartCmd;\r
1268 UINT32 PortTfd;\r
1269 UINT32 Offset;\r
1270 UINT32 Capability;\r
1271\r
1272 //\r
1273 // Collect AHCI controller information\r
1274 //\r
1275 Capability = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
1276\r
1277 CmdSlotBit = (UINT32) (1 << CommandSlot);\r
1278\r
1279 AhciClearPortStatus (\r
1280 PciIo,\r
1281 Port\r
1282 );\r
1283\r
1284 Status = AhciEnableFisReceive (\r
1aff716a 1285 PciIo,\r
a41b5272 1286 Port,\r
1287 Timeout\r
1288 );\r
490b5ea1 1289\r
a41b5272 1290 if (EFI_ERROR (Status)) {\r
1291 return Status;\r
1292 }\r
1293\r
a41b5272 1294 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1295 PortStatus = AhciReadReg (PciIo, Offset);\r
490b5ea1 1296\r
a41b5272 1297 StartCmd = 0;\r
1298 if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {\r
1299 StartCmd = AhciReadReg (PciIo, Offset);\r
1300 StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;\r
1301 StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;\r
1302 }\r
1303\r
1304 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1305 PortTfd = AhciReadReg (PciIo, Offset);\r
1306\r
1307 if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {\r
1308 if ((Capability & BIT24) != 0) {\r
1309 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
cbd2a4b3 1310 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);\r
a41b5272 1311\r
8536cc4b 1312 AhciWaitMmioSet (\r
490b5ea1 1313 PciIo,\r
a41b5272 1314 Offset,\r
cbd2a4b3 1315 EFI_AHCI_PORT_CMD_CLO,\r
a41b5272 1316 0,\r
1317 Timeout\r
490b5ea1 1318 );\r
a41b5272 1319 }\r
1320 }\r
1321\r
1322 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1323 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);\r
1324\r
e519983a 1325 //\r
1326 // Setting the command\r
1327 //\r
1328 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;\r
1329 AhciAndReg (PciIo, Offset, 0);\r
1330 AhciOrReg (PciIo, Offset, CmdSlotBit);\r
1331\r
1332 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;\r
1333 AhciAndReg (PciIo, Offset, 0);\r
1334 AhciOrReg (PciIo, Offset, CmdSlotBit);\r
1335\r
a41b5272 1336 return EFI_SUCCESS;\r
1337}\r
1338\r
1339/**\r
1340 Do AHCI port reset.\r
1341\r
1342 @param PciIo The PCI IO protocol instance.\r
1343 @param Port The number of port.\r
8536cc4b 1344 @param Timeout The timeout value of reset, uses 100ns as a unit.\r
1aff716a 1345\r
a41b5272 1346 @retval EFI_DEVICE_ERROR The port reset unsuccessfully\r
1347 @retval EFI_TIMEOUT The reset operation is time out.\r
1348 @retval EFI_SUCCESS The port reset successfully.\r
1349\r
1350**/\r
1351EFI_STATUS\r
1352EFIAPI\r
1353AhciPortReset (\r
1354 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1355 IN UINT8 Port,\r
1356 IN UINT64 Timeout\r
1357 )\r
1358{\r
1359 EFI_STATUS Status;\r
490b5ea1 1360 UINT32 Offset;\r
1361\r
a41b5272 1362 AhciClearPortStatus (PciIo, Port);\r
1363\r
1364 AhciStopCommand (PciIo, Port, Timeout);\r
1365\r
1366 AhciDisableFisReceive (PciIo, Port, Timeout);\r
1367\r
1368 AhciEnableFisReceive (PciIo, Port, Timeout);\r
1369\r
1370 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
1371\r
1372 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_DET_INIT);\r
1373\r
1374 //\r
490b5ea1 1375 // wait 5 millisecond before de-assert DET\r
a41b5272 1376 //\r
1377 MicroSecondDelay (5000);\r
1378\r
1379 AhciAndReg (PciIo, Offset, (UINT32)EFI_AHCI_PORT_SCTL_MASK);\r
1380\r
1381 //\r
490b5ea1 1382 // wait 5 millisecond before de-assert DET\r
a41b5272 1383 //\r
1384 MicroSecondDelay (5000);\r
1385\r
1386 //\r
1387 // Wait for communication to be re-established\r
1388 //\r
1389 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
8536cc4b 1390 Status = AhciWaitMmioSet (\r
a41b5272 1391 PciIo,\r
1392 Offset,\r
1393 EFI_AHCI_PORT_SSTS_DET_MASK,\r
1394 EFI_AHCI_PORT_SSTS_DET_PCE,\r
1395 Timeout\r
490b5ea1 1396 );\r
a41b5272 1397\r
1398 if (EFI_ERROR (Status)) {\r
1399 return Status;\r
1400 }\r
1401\r
1402 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
1403 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_ERR_CLEAR);\r
1404\r
1405 return EFI_SUCCESS;\r
1406}\r
1407\r
1408/**\r
1409 Do AHCI HBA reset.\r
490b5ea1 1410\r
a41b5272 1411 @param PciIo The PCI IO protocol instance.\r
8536cc4b 1412 @param Timeout The timeout value of reset, uses 100ns as a unit.\r
490b5ea1 1413\r
a41b5272 1414 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
1415 @retval EFI_TIMEOUT The reset operation is time out.\r
1416 @retval EFI_SUCCESS AHCI controller is reset successfully.\r
1417\r
1418**/\r
1419EFI_STATUS\r
1420EFIAPI\r
1421AhciReset (\r
1422 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1423 IN UINT64 Timeout\r
1aff716a 1424 )\r
a41b5272 1425{\r
ab82122d 1426 UINT64 Delay;\r
a41b5272 1427 UINT32 Value;\r
1428\r
1429 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
1430\r
1431 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
1432\r
ab82122d 1433 Delay = DivU64x32(Timeout, 1000) + 1;\r
a41b5272 1434\r
1435 do {\r
1436 Value = AhciReadReg(PciIo, EFI_AHCI_GHC_OFFSET);\r
1437\r
1438 if ((Value & EFI_AHCI_GHC_RESET) == 0) {\r
1439 break;\r
1440 }\r
1441\r
1442 //\r
1443 // Stall for 100 microseconds.\r
1444 //\r
1445 MicroSecondDelay(100);\r
1446\r
1447 Delay--;\r
1448 } while (Delay > 0);\r
1449\r
1450 if (Delay == 0) {\r
1451 return EFI_TIMEOUT;\r
1452 }\r
1453\r
1454 return EFI_SUCCESS;\r
1455}\r
1456\r
12873d57 1457/**\r
1458 Send SMART Return Status command to check if the execution of SMART cmd is successful or not.\r
1459\r
1460 @param PciIo The PCI IO protocol instance.\r
1461 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1462 @param Port The number of port.\r
1463 @param PortMultiplier The timeout value of stop.\r
1464 @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
1465\r
1466 @retval EFI_SUCCESS Successfully get the return status of S.M.A.R.T command execution.\r
1467 @retval Others Fail to get return status data.\r
1468\r
1469**/\r
1470EFI_STATUS\r
1471EFIAPI\r
1472AhciAtaSmartReturnStatusCheck (\r
1473 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1474 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1475 IN UINT8 Port,\r
1476 IN UINT8 PortMultiplier,\r
1477 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
1478 )\r
1479{\r
1480 EFI_STATUS Status;\r
1481 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1482 UINT8 LBAMid;\r
1483 UINT8 LBAHigh;\r
1484 UINTN FisBaseAddr;\r
1485 UINT32 Value;\r
1486\r
1487 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1488\r
1489 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1490 AtaCommandBlock.AtaFeatures = ATA_SMART_RETURN_STATUS;\r
1491 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1492 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1493\r
1494 //\r
1495 // Send S.M.A.R.T Read Return Status command to device\r
1496 //\r
1497 Status = AhciNonDataTransfer (\r
1498 PciIo,\r
1499 AhciRegisters,\r
1500 (UINT8)Port,\r
1501 (UINT8)PortMultiplier,\r
1502 NULL,\r
1503 0,\r
1504 &AtaCommandBlock,\r
1505 AtaStatusBlock,\r
490b5ea1 1506 ATA_ATAPI_TIMEOUT,\r
1507 NULL\r
12873d57 1508 );\r
1509\r
1510 if (EFI_ERROR (Status)) {\r
cffd2171
EL
1511 REPORT_STATUS_CODE (\r
1512 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1513 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLED)\r
1514 );\r
12873d57 1515 return EFI_DEVICE_ERROR;\r
1516 }\r
1517\r
cffd2171
EL
1518 REPORT_STATUS_CODE (\r
1519 EFI_PROGRESS_CODE,\r
1520 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_ENABLE)\r
1521 );\r
1522\r
12873d57 1523 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1524\r
1525 Value = *(UINT32 *) (FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET);\r
1526\r
1527 if ((Value & EFI_AHCI_FIS_TYPE_MASK) == EFI_AHCI_FIS_REGISTER_D2H) {\r
1528 LBAMid = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[5];\r
1529 LBAHigh = ((UINT8 *)(UINTN)(FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET))[6];\r
1530\r
1531 if ((LBAMid == 0x4f) && (LBAHigh == 0xc2)) {\r
1532 //\r
1533 // The threshold exceeded condition is not detected by the device\r
1534 //\r
1535 DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is not detected\n"));\r
cffd2171
EL
1536 REPORT_STATUS_CODE (\r
1537 EFI_PROGRESS_CODE,\r
1538 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_UNDERTHRESHOLD)\r
1539 );\r
12873d57 1540 } else if ((LBAMid == 0xf4) && (LBAHigh == 0x2c)) {\r
1541 //\r
1542 // The threshold exceeded condition is detected by the device\r
1543 //\r
1544 DEBUG ((EFI_D_INFO, "The S.M.A.R.T threshold exceeded condition is detected\n"));\r
cffd2171
EL
1545 REPORT_STATUS_CODE (\r
1546 EFI_PROGRESS_CODE,\r
1547 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_OVERTHRESHOLD)\r
1548 );\r
12873d57 1549 }\r
1550 }\r
1551\r
1552 return EFI_SUCCESS;\r
1553}\r
1554\r
1555/**\r
1556 Enable SMART command of the disk if supported.\r
1557\r
1558 @param PciIo The PCI IO protocol instance.\r
1559 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1560 @param Port The number of port.\r
1561 @param PortMultiplier The timeout value of stop.\r
1562 @param IdentifyData A pointer to data buffer which is used to contain IDENTIFY data.\r
1563 @param AtaStatusBlock A pointer to EFI_ATA_STATUS_BLOCK data structure.\r
1564\r
1565**/\r
1566VOID\r
1567EFIAPI\r
1568AhciAtaSmartSupport (\r
1569 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1570 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1571 IN UINT8 Port,\r
1572 IN UINT8 PortMultiplier,\r
490b5ea1 1573 IN EFI_IDENTIFY_DATA *IdentifyData,\r
12873d57 1574 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock\r
1575 )\r
1576{\r
1577 EFI_STATUS Status;\r
1578 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1579\r
1580 //\r
1581 // Detect if the device supports S.M.A.R.T.\r
1582 //\r
1583 if ((IdentifyData->AtaData.command_set_supported_82 & 0x0001) != 0x0001) {\r
1584 //\r
1585 // S.M.A.R.T is not supported by the device\r
1586 //\r
1aff716a 1587 DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",\r
12873d57 1588 Port, PortMultiplier));\r
cffd2171
EL
1589 REPORT_STATUS_CODE (\r
1590 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1591 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_NOTSUPPORTED)\r
1592 );\r
12873d57 1593 } else {\r
1594 //\r
1595 // Check if the feature is enabled. If not, then enable S.M.A.R.T.\r
1596 //\r
1597 if ((IdentifyData->AtaData.command_set_feature_enb_85 & 0x0001) != 0x0001) {\r
cffd2171
EL
1598\r
1599 REPORT_STATUS_CODE (\r
1600 EFI_PROGRESS_CODE,\r
1601 (EFI_IO_BUS_ATA_ATAPI | EFI_IOB_ATA_BUS_SMART_DISABLE)\r
1602 );\r
1603\r
12873d57 1604 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1605\r
1606 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1607 AtaCommandBlock.AtaFeatures = ATA_SMART_ENABLE_OPERATION;\r
1608 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1609 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1610\r
1611 //\r
1612 // Send S.M.A.R.T Enable command to device\r
1613 //\r
1614 Status = AhciNonDataTransfer (\r
1615 PciIo,\r
1616 AhciRegisters,\r
1617 (UINT8)Port,\r
1618 (UINT8)PortMultiplier,\r
1619 NULL,\r
1620 0,\r
1621 &AtaCommandBlock,\r
1622 AtaStatusBlock,\r
490b5ea1 1623 ATA_ATAPI_TIMEOUT,\r
1624 NULL\r
12873d57 1625 );\r
1626\r
1627\r
1628 if (!EFI_ERROR (Status)) {\r
1629 //\r
1630 // Send S.M.A.R.T AutoSave command to device\r
1631 //\r
1632 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1633\r
1634 AtaCommandBlock.AtaCommand = ATA_CMD_SMART;\r
1635 AtaCommandBlock.AtaFeatures = 0xD2;\r
1636 AtaCommandBlock.AtaSectorCount = 0xF1;\r
1637 AtaCommandBlock.AtaCylinderLow = ATA_CONSTANT_4F;\r
1638 AtaCommandBlock.AtaCylinderHigh = ATA_CONSTANT_C2;\r
1639\r
1640 Status = AhciNonDataTransfer (\r
1641 PciIo,\r
1642 AhciRegisters,\r
1643 (UINT8)Port,\r
1644 (UINT8)PortMultiplier,\r
1645 NULL,\r
1646 0,\r
1647 &AtaCommandBlock,\r
1648 AtaStatusBlock,\r
490b5ea1 1649 ATA_ATAPI_TIMEOUT,\r
1650 NULL\r
12873d57 1651 );\r
1652\r
1653 if (!EFI_ERROR (Status)) {\r
1654 Status = AhciAtaSmartReturnStatusCheck (\r
1655 PciIo,\r
1656 AhciRegisters,\r
1657 (UINT8)Port,\r
1658 (UINT8)PortMultiplier,\r
1659 AtaStatusBlock\r
1660 );\r
1661 }\r
1662 }\r
1663 }\r
490b5ea1 1664 DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",\r
12873d57 1665 Port, PortMultiplier));\r
1666 }\r
1667\r
1668 return ;\r
1669}\r
1670\r
a41b5272 1671/**\r
1672 Send Buffer cmd to specific device.\r
1aff716a 1673\r
aca84419 1674 @param PciIo The PCI IO protocol instance.\r
1675 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1676 @param Port The number of port.\r
a41b5272 1677 @param PortMultiplier The timeout value of stop.\r
aca84419 1678 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
a41b5272 1679\r
1680 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1681 @retval EFI_TIMEOUT The operation is time out.\r
1682 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1683 @retval EFI_SUCCESS The cmd executes successfully.\r
1684\r
1685**/\r
1686EFI_STATUS\r
1687EFIAPI\r
1688AhciIdentify (\r
1689 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1690 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1691 IN UINT8 Port,\r
1692 IN UINT8 PortMultiplier,\r
1aff716a 1693 IN OUT EFI_IDENTIFY_DATA *Buffer\r
a41b5272 1694 )\r
1695{\r
1696 EFI_STATUS Status;\r
1697 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1698 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1699\r
1700 if (PciIo == NULL || AhciRegisters == NULL || Buffer == NULL) {\r
1701 return EFI_INVALID_PARAMETER;\r
1702 }\r
1703\r
1704 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1705 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
490b5ea1 1706\r
a41b5272 1707 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
1708 AtaCommandBlock.AtaSectorCount = 1;\r
1709\r
1710 Status = AhciPioTransfer (\r
1711 PciIo,\r
1712 AhciRegisters,\r
1713 Port,\r
1714 PortMultiplier,\r
1715 NULL,\r
1716 0,\r
1717 TRUE,\r
1718 &AtaCommandBlock,\r
1719 &AtaStatusBlock,\r
1720 Buffer,\r
1721 sizeof (EFI_IDENTIFY_DATA),\r
1aff716a 1722 ATA_ATAPI_TIMEOUT,\r
490b5ea1 1723 NULL\r
a41b5272 1724 );\r
1725\r
1726 return Status;\r
1727}\r
1728\r
1729/**\r
1730 Send Buffer cmd to specific device.\r
1aff716a 1731\r
aca84419 1732 @param PciIo The PCI IO protocol instance.\r
1733 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1734 @param Port The number of port.\r
a41b5272 1735 @param PortMultiplier The timeout value of stop.\r
aca84419 1736 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
a41b5272 1737\r
1738 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1739 @retval EFI_TIMEOUT The operation is time out.\r
1740 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1741 @retval EFI_SUCCESS The cmd executes successfully.\r
1742\r
1743**/\r
1744EFI_STATUS\r
1745EFIAPI\r
1746AhciIdentifyPacket (\r
1747 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1748 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1749 IN UINT8 Port,\r
1750 IN UINT8 PortMultiplier,\r
1aff716a 1751 IN OUT EFI_IDENTIFY_DATA *Buffer\r
a41b5272 1752 )\r
1753{\r
1754 EFI_STATUS Status;\r
1755 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1756 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1757\r
1758 if (PciIo == NULL || AhciRegisters == NULL) {\r
1759 return EFI_INVALID_PARAMETER;\r
1760 }\r
490b5ea1 1761\r
a41b5272 1762 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1763 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
1764\r
1765 AtaCommandBlock.AtaCommand = ATA_CMD_IDENTIFY_DEVICE;\r
1766 AtaCommandBlock.AtaSectorCount = 1;\r
1767\r
1768 Status = AhciPioTransfer (\r
1769 PciIo,\r
1770 AhciRegisters,\r
1771 Port,\r
1772 PortMultiplier,\r
1773 NULL,\r
1774 0,\r
1775 TRUE,\r
1776 &AtaCommandBlock,\r
1777 &AtaStatusBlock,\r
1778 Buffer,\r
1779 sizeof (EFI_IDENTIFY_DATA),\r
490b5ea1 1780 ATA_ATAPI_TIMEOUT,\r
1781 NULL\r
a41b5272 1782 );\r
1783\r
1784 return Status;\r
1785}\r
1786\r
1787/**\r
1788 Send SET FEATURE cmd on specific device.\r
1aff716a 1789\r
aca84419 1790 @param PciIo The PCI IO protocol instance.\r
1791 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1792 @param Port The number of port.\r
a41b5272 1793 @param PortMultiplier The timeout value of stop.\r
aca84419 1794 @param Feature The data to send Feature register.\r
1795 @param FeatureSpecificData The specific data for SET FEATURE cmd.\r
a41b5272 1796\r
1797 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1798 @retval EFI_TIMEOUT The operation is time out.\r
1799 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1800 @retval EFI_SUCCESS The cmd executes successfully.\r
1801\r
1802**/\r
1803EFI_STATUS\r
1804EFIAPI\r
1805AhciDeviceSetFeature (\r
1806 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1807 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1808 IN UINT8 Port,\r
1809 IN UINT8 PortMultiplier,\r
1810 IN UINT16 Feature,\r
1811 IN UINT32 FeatureSpecificData\r
1812 )\r
1813{\r
1814 EFI_STATUS Status;\r
1815 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1816 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1817\r
1818 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1819 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
490b5ea1 1820\r
a41b5272 1821 AtaCommandBlock.AtaCommand = ATA_CMD_SET_FEATURES;\r
1822 AtaCommandBlock.AtaFeatures = (UINT8) Feature;\r
1823 AtaCommandBlock.AtaFeaturesExp = (UINT8) (Feature >> 8);\r
1824 AtaCommandBlock.AtaSectorCount = (UINT8) FeatureSpecificData;\r
1825 AtaCommandBlock.AtaSectorNumber = (UINT8) (FeatureSpecificData >> 8);\r
1826 AtaCommandBlock.AtaCylinderLow = (UINT8) (FeatureSpecificData >> 16);\r
1827 AtaCommandBlock.AtaCylinderHigh = (UINT8) (FeatureSpecificData >> 24);\r
1828\r
1829 Status = AhciNonDataTransfer (\r
1830 PciIo,\r
1831 AhciRegisters,\r
1832 (UINT8)Port,\r
1833 (UINT8)PortMultiplier,\r
1834 NULL,\r
1835 0,\r
1836 &AtaCommandBlock,\r
1837 &AtaStatusBlock,\r
1aff716a 1838 ATA_ATAPI_TIMEOUT,\r
490b5ea1 1839 NULL\r
a41b5272 1840 );\r
1841\r
1842 return Status;\r
1843}\r
1844\r
1845/**\r
1aff716a 1846 This function is used to send out ATAPI commands conforms to the Packet Command\r
a41b5272 1847 with PIO Protocol.\r
1848\r
1849 @param PciIo The PCI IO protocol instance.\r
1850 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1aff716a 1851 @param Port The number of port.\r
a41b5272 1852 @param PortMultiplier The number of port multiplier.\r
1853 @param Packet A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET structure.\r
1854\r
1855 @retval EFI_SUCCESS send out the ATAPI packet command successfully\r
1856 and device sends data successfully.\r
1857 @retval EFI_DEVICE_ERROR the device failed to send data.\r
1858\r
1859**/\r
1860EFI_STATUS\r
1861EFIAPI\r
1862AhciPacketCommandExecute (\r
1863 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1864 IN EFI_AHCI_REGISTERS *AhciRegisters,\r
1865 IN UINT8 Port,\r
1866 IN UINT8 PortMultiplier,\r
1867 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
1868 )\r
1869{\r
1870 EFI_STATUS Status;\r
1871 VOID *Buffer;\r
1872 UINT32 Length;\r
1873 EFI_ATA_COMMAND_BLOCK AtaCommandBlock;\r
1874 EFI_ATA_STATUS_BLOCK AtaStatusBlock;\r
1875 BOOLEAN Read;\r
a41b5272 1876\r
1877 if (Packet == NULL || Packet->Cdb == NULL) {\r
1878 return EFI_INVALID_PARAMETER;\r
1879 }\r
1880\r
1881 ZeroMem (&AtaCommandBlock, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1882 ZeroMem (&AtaStatusBlock, sizeof (EFI_ATA_STATUS_BLOCK));\r
1883 AtaCommandBlock.AtaCommand = ATA_CMD_PACKET;\r
1884 //\r
1885 // No OVL; No DMA\r
1886 //\r
1887 AtaCommandBlock.AtaFeatures = 0x00;\r
1888 //\r
1889 // set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device\r
1890 // determine how many data should be transferred.\r
1891 //\r
1892 AtaCommandBlock.AtaCylinderLow = (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff);\r
1893 AtaCommandBlock.AtaCylinderHigh = (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8);\r
1894\r
1895 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
1896 Buffer = Packet->InDataBuffer;\r
1897 Length = Packet->InTransferLength;\r
1898 Read = TRUE;\r
1899 } else {\r
1900 Buffer = Packet->OutDataBuffer;\r
1901 Length = Packet->OutTransferLength;\r
1902 Read = FALSE;\r
1903 }\r
1904\r
490b5ea1 1905 if (Length == 0) {\r
a41b5272 1906 Status = AhciNonDataTransfer (\r
1907 PciIo,\r
1908 AhciRegisters,\r
1909 Port,\r
1910 PortMultiplier,\r
1911 Packet->Cdb,\r
1912 Packet->CdbLength,\r
1913 &AtaCommandBlock,\r
1914 &AtaStatusBlock,\r
1aff716a 1915 Packet->Timeout,\r
490b5ea1 1916 NULL\r
a41b5272 1917 );\r
1918 } else {\r
cbd2a4b3 1919 Status = AhciPioTransfer (\r
1920 PciIo,\r
1921 AhciRegisters,\r
1922 Port,\r
1923 PortMultiplier,\r
1924 Packet->Cdb,\r
1925 Packet->CdbLength,\r
1926 Read,\r
1927 &AtaCommandBlock,\r
1928 &AtaStatusBlock,\r
1929 Buffer,\r
1930 Length,\r
1aff716a 1931 Packet->Timeout,\r
cbd2a4b3 1932 NULL\r
1933 );\r
a41b5272 1934 }\r
1935 return Status;\r
1936}\r
1937\r
1938/**\r
1939 Allocate transfer-related data struct which is used at AHCI mode.\r
1aff716a 1940\r
a41b5272 1941 @param PciIo The PCI IO protocol instance.\r
1942 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
1943\r
1944**/\r
1945EFI_STATUS\r
1946EFIAPI\r
1947AhciCreateTransferDescriptor (\r
1948 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
1949 IN OUT EFI_AHCI_REGISTERS *AhciRegisters\r
1950 )\r
1951{\r
1952 EFI_STATUS Status;\r
1953 UINTN Bytes;\r
1954 VOID *Buffer;\r
1955\r
1956 UINT32 Capability;\r
467cacbf 1957 UINT32 PortImplementBitMap;\r
a41b5272 1958 UINT8 MaxPortNumber;\r
1959 UINT8 MaxCommandSlotNumber;\r
1960 BOOLEAN Support64Bit;\r
1961 UINT64 MaxReceiveFisSize;\r
1962 UINT64 MaxCommandListSize;\r
1963 UINT64 MaxCommandTableSize;\r
ed365e93 1964 EFI_PHYSICAL_ADDRESS AhciRFisPciAddr;\r
1965 EFI_PHYSICAL_ADDRESS AhciCmdListPciAddr;\r
1966 EFI_PHYSICAL_ADDRESS AhciCommandTablePciAddr;\r
a41b5272 1967\r
1968 Buffer = NULL;\r
1969 //\r
1970 // Collect AHCI controller information\r
1971 //\r
1972 Capability = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
a41b5272 1973 //\r
1974 // Get the number of command slots per port supported by this HBA.\r
1975 //\r
1976 MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
aca84419 1977 Support64Bit = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);\r
467cacbf 1978 \r
1979 PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
1980 //\r
1981 // Get the highest bit of implemented ports which decides how many bytes are allocated for recived FIS.\r
1982 //\r
1983 MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
1984 if (MaxPortNumber == 0) {\r
1985 return EFI_DEVICE_ERROR;\r
1986 }\r
a41b5272 1987\r
1988 MaxReceiveFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1989 Status = PciIo->AllocateBuffer (\r
1990 PciIo,\r
1991 AllocateAnyPages,\r
1992 EfiBootServicesData,\r
ed365e93 1993 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
a41b5272 1994 &Buffer,\r
1995 0\r
1996 );\r
1997\r
1998 if (EFI_ERROR (Status)) {\r
1999 return EFI_OUT_OF_RESOURCES;\r
2000 }\r
2001\r
5dec0c68 2002 ZeroMem (Buffer, (UINTN)MaxReceiveFisSize);\r
a41b5272 2003\r
2004 AhciRegisters->AhciRFis = Buffer;\r
2005 AhciRegisters->MaxReceiveFisSize = MaxReceiveFisSize;\r
5dec0c68 2006 Bytes = (UINTN)MaxReceiveFisSize;\r
a41b5272 2007\r
2008 Status = PciIo->Map (\r
2009 PciIo,\r
2010 EfiPciIoOperationBusMasterCommonBuffer,\r
2011 Buffer,\r
2012 &Bytes,\r
ed365e93 2013 &AhciRFisPciAddr,\r
a41b5272 2014 &AhciRegisters->MapRFis\r
2015 );\r
2016\r
2017 if (EFI_ERROR (Status) || (Bytes != MaxReceiveFisSize)) {\r
2018 //\r
1aff716a 2019 // Map error or unable to map the whole RFis buffer into a contiguous region.\r
a41b5272 2020 //\r
2021 Status = EFI_OUT_OF_RESOURCES;\r
2022 goto Error6;\r
2023 }\r
2024\r
ed365e93 2025 if ((!Support64Bit) && (AhciRFisPciAddr > 0x100000000ULL)) {\r
a41b5272 2026 //\r
2027 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
2028 //\r
2029 Status = EFI_DEVICE_ERROR;\r
2030 goto Error5;\r
2031 }\r
ed365e93 2032 AhciRegisters->AhciRFisPciAddr = (EFI_AHCI_RECEIVED_FIS *)(UINTN)AhciRFisPciAddr;\r
a41b5272 2033\r
2034 //\r
2035 // Allocate memory for command list\r
2036 // Note that the implemenation is a single task model which only use a command list for all ports.\r
2037 //\r
2038 Buffer = NULL;\r
2039 MaxCommandListSize = MaxCommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST);\r
2040 Status = PciIo->AllocateBuffer (\r
2041 PciIo,\r
2042 AllocateAnyPages,\r
2043 EfiBootServicesData,\r
ed365e93 2044 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
a41b5272 2045 &Buffer,\r
2046 0\r
2047 );\r
2048\r
2049 if (EFI_ERROR (Status)) {\r
2050 //\r
1aff716a 2051 // Free mapped resource.\r
a41b5272 2052 //\r
2053 Status = EFI_OUT_OF_RESOURCES;\r
2054 goto Error5;\r
2055 }\r
2056\r
5dec0c68 2057 ZeroMem (Buffer, (UINTN)MaxCommandListSize);\r
a41b5272 2058\r
2059 AhciRegisters->AhciCmdList = Buffer;\r
2060 AhciRegisters->MaxCommandListSize = MaxCommandListSize;\r
5dec0c68 2061 Bytes = (UINTN)MaxCommandListSize;\r
a41b5272 2062\r
2063 Status = PciIo->Map (\r
2064 PciIo,\r
2065 EfiPciIoOperationBusMasterCommonBuffer,\r
2066 Buffer,\r
2067 &Bytes,\r
ed365e93 2068 &AhciCmdListPciAddr,\r
a41b5272 2069 &AhciRegisters->MapCmdList\r
2070 );\r
2071\r
2072 if (EFI_ERROR (Status) || (Bytes != MaxCommandListSize)) {\r
2073 //\r
2074 // Map error or unable to map the whole cmd list buffer into a contiguous region.\r
2075 //\r
2076 Status = EFI_OUT_OF_RESOURCES;\r
2077 goto Error4;\r
2078 }\r
2079\r
ed365e93 2080 if ((!Support64Bit) && (AhciCmdListPciAddr > 0x100000000ULL)) {\r
a41b5272 2081 //\r
2082 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
2083 //\r
2084 Status = EFI_DEVICE_ERROR;\r
2085 goto Error3;\r
2086 }\r
ed365e93 2087 AhciRegisters->AhciCmdListPciAddr = (EFI_AHCI_COMMAND_LIST *)(UINTN)AhciCmdListPciAddr;\r
a41b5272 2088\r
2089 //\r
2090 // Allocate memory for command table\r
2091 // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
2092 //\r
2093 Buffer = NULL;\r
2094 MaxCommandTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
2095\r
2096 Status = PciIo->AllocateBuffer (\r
2097 PciIo,\r
2098 AllocateAnyPages,\r
2099 EfiBootServicesData,\r
ed365e93 2100 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
a41b5272 2101 &Buffer,\r
2102 0\r
2103 );\r
2104\r
2105 if (EFI_ERROR (Status)) {\r
2106 //\r
1aff716a 2107 // Free mapped resource.\r
a41b5272 2108 //\r
2109 Status = EFI_OUT_OF_RESOURCES;\r
2110 goto Error3;\r
2111 }\r
2112\r
5dec0c68 2113 ZeroMem (Buffer, (UINTN)MaxCommandTableSize);\r
a41b5272 2114\r
2115 AhciRegisters->AhciCommandTable = Buffer;\r
2116 AhciRegisters->MaxCommandTableSize = MaxCommandTableSize;\r
5dec0c68 2117 Bytes = (UINTN)MaxCommandTableSize;\r
a41b5272 2118\r
2119 Status = PciIo->Map (\r
2120 PciIo,\r
2121 EfiPciIoOperationBusMasterCommonBuffer,\r
2122 Buffer,\r
2123 &Bytes,\r
ed365e93 2124 &AhciCommandTablePciAddr,\r
a41b5272 2125 &AhciRegisters->MapCommandTable\r
2126 );\r
2127\r
2128 if (EFI_ERROR (Status) || (Bytes != MaxCommandTableSize)) {\r
2129 //\r
2130 // Map error or unable to map the whole cmd list buffer into a contiguous region.\r
2131 //\r
2132 Status = EFI_OUT_OF_RESOURCES;\r
2133 goto Error2;\r
2134 }\r
2135\r
ed365e93 2136 if ((!Support64Bit) && (AhciCommandTablePciAddr > 0x100000000ULL)) {\r
a41b5272 2137 //\r
2138 // The AHCI HBA doesn't support 64bit addressing, so should not get a >4G pci bus master address.\r
2139 //\r
2140 Status = EFI_DEVICE_ERROR;\r
2141 goto Error1;\r
2142 }\r
ed365e93 2143 AhciRegisters->AhciCommandTablePciAddr = (EFI_AHCI_COMMAND_TABLE *)(UINTN)AhciCommandTablePciAddr;\r
a41b5272 2144\r
2145 return EFI_SUCCESS;\r
2146 //\r
1aff716a 2147 // Map error or unable to map the whole CmdList buffer into a contiguous region.\r
a41b5272 2148 //\r
2149Error1:\r
2150 PciIo->Unmap (\r
2151 PciIo,\r
2152 AhciRegisters->MapCommandTable\r
2153 );\r
2154Error2:\r
2155 PciIo->FreeBuffer (\r
2156 PciIo,\r
ed365e93 2157 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
a41b5272 2158 AhciRegisters->AhciCommandTable\r
2159 );\r
2160Error3:\r
2161 PciIo->Unmap (\r
2162 PciIo,\r
2163 AhciRegisters->MapCmdList\r
2164 );\r
2165Error4:\r
2166 PciIo->FreeBuffer (\r
2167 PciIo,\r
ed365e93 2168 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
a41b5272 2169 AhciRegisters->AhciCmdList\r
2170 );\r
2171Error5:\r
2172 PciIo->Unmap (\r
2173 PciIo,\r
2174 AhciRegisters->MapRFis\r
2175 );\r
2176Error6:\r
2177 PciIo->FreeBuffer (\r
2178 PciIo,\r
ed365e93 2179 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
a41b5272 2180 AhciRegisters->AhciRFis\r
2181 );\r
2182\r
2183 return Status;\r
2184}\r
2185\r
2186/**\r
2187 Initialize ATA host controller at AHCI mode.\r
2188\r
1aff716a 2189 The function is designed to initialize ATA host controller.\r
2190\r
a41b5272 2191 @param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance.\r
2192\r
2193**/\r
2194EFI_STATUS\r
2195EFIAPI\r
2196AhciModeInitialization (\r
2197 IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance\r
2198 )\r
2199{\r
2200 EFI_STATUS Status;\r
2201 EFI_PCI_IO_PROTOCOL *PciIo;\r
2202 EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeInit;\r
2203 UINT32 Capability;\r
2204 UINT8 MaxPortNumber;\r
2205 UINT32 PortImplementBitMap;\r
a41b5272 2206\r
2207 EFI_AHCI_REGISTERS *AhciRegisters;\r
2208\r
2209 UINT8 Port;\r
2210 DATA_64 Data64;\r
2211 UINT32 Offset;\r
2212 UINT32 Data;\r
2213 EFI_IDENTIFY_DATA Buffer;\r
2214 EFI_ATA_DEVICE_TYPE DeviceType;\r
2215 EFI_ATA_COLLECTIVE_MODE *SupportedModes;\r
2216 EFI_ATA_TRANSFER_MODE TransferMode;\r
cbd2a4b3 2217 UINT32 PhyDetectDelay;\r
2218\r
a41b5272 2219 if (Instance == NULL) {\r
2220 return EFI_INVALID_PARAMETER;\r
2221 }\r
2222\r
2223 PciIo = Instance->PciIo;\r
2224 IdeInit = Instance->IdeControllerInit;\r
2225\r
1aff716a 2226 Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);\r
a41b5272 2227\r
2228 if (EFI_ERROR (Status)) {\r
2229 return EFI_DEVICE_ERROR;\r
2230 }\r
2231\r
2232 //\r
2233 // Enable AE before accessing any AHCI registers\r
2234 //\r
2235 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
2236\r
2237 //\r
2238 // Collect AHCI controller information\r
2239 //\r
2240 Capability = AhciReadReg(PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
2241\r
2242 //\r
2243 // Get the number of command slots per port supported by this HBA.\r
2244 //\r
cbd2a4b3 2245 MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
a41b5272 2246\r
2247 //\r
2248 // Get the bit map of those ports exposed by this HBA.\r
1aff716a 2249 // It indicates which ports that the HBA supports are available for software to use.\r
a41b5272 2250 //\r
2251 PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
1aff716a 2252\r
a41b5272 2253 AhciRegisters = &Instance->AhciRegisters;\r
2254 Status = AhciCreateTransferDescriptor (PciIo, AhciRegisters);\r
2255\r
2256 if (EFI_ERROR (Status)) {\r
2257 return EFI_OUT_OF_RESOURCES;\r
2258 }\r
2259\r
6b13aa60 2260 for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port ++) {\r
a41b5272 2261 if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
6b13aa60 2262 //\r
2263 // According to AHCI spec, MaxPortNumber should be equal or greater than the number of implemented ports.\r
2264 //\r
2265 if ((MaxPortNumber--) == 0) {\r
2266 //\r
2267 // Should never be here.\r
2268 //\r
2269 ASSERT (FALSE);\r
2270 return EFI_SUCCESS;\r
2271 }\r
2272\r
cbd2a4b3 2273 IdeInit->NotifyPhase (IdeInit, EfiIdeBeforeChannelEnumeration, Port);\r
2274\r
2275 //\r
2276 // Initialize FIS Base Address Register and Command List Base Address Register for use.\r
2277 //\r
2278 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFisPciAddr) + sizeof (EFI_AHCI_RECEIVED_FIS) * Port;\r
2279 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
2280 AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);\r
2281 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
2282 AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);\r
2283\r
2284 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdListPciAddr);\r
2285 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
2286 AhciWriteReg (PciIo, Offset, Data64.Uint32.Lower32);\r
2287 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
2288 AhciWriteReg (PciIo, Offset, Data64.Uint32.Upper32);\r
2289\r
a41b5272 2290 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
a41b5272 2291 Data = AhciReadReg (PciIo, Offset);\r
2292 if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {\r
2293 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_POD);\r
2294 }\r
a41b5272 2295\r
cbd2a4b3 2296 if ((Capability & EFI_AHCI_CAP_SSS) != 0) {\r
2297 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_SUD);\r
2298 }\r
2299\r
2300 //\r
2301 // Disable aggressive power management.\r
2302 //\r
2303 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
2304 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);\r
2305 //\r
2306 // Disable the reporting of the corresponding interrupt to system software.\r
2307 //\r
2308 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;\r
2309 AhciAndReg (PciIo, Offset, 0);\r
a41b5272 2310\r
cbd2a4b3 2311 //\r
2312 // Now inform the IDE Controller Init Module.\r
2313 //\r
2314 IdeInit->NotifyPhase (IdeInit, EfiIdeBusBeforeDevicePresenceDetection, Port);\r
2315\r
2316 //\r
2317 // Enable FIS Receive DMA engine for the first D2H FIS.\r
2318 //\r
2319 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
2320 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_FRE);\r
8536cc4b 2321 Status = AhciWaitMmioSet (\r
1aff716a 2322 PciIo,\r
cbd2a4b3 2323 Offset,\r
2324 EFI_AHCI_PORT_CMD_FR,\r
2325 EFI_AHCI_PORT_CMD_FR,\r
2326 EFI_AHCI_PORT_CMD_FR_CLEAR_TIMEOUT\r
2327 );\r
2328 if (EFI_ERROR (Status)) {\r
a41b5272 2329 continue;\r
2330 }\r
cbd2a4b3 2331\r
a41b5272 2332 //\r
cbd2a4b3 2333 // Wait no longer than 10 ms to wait the Phy to detect the presence of a device.\r
2334 // It's the requirment from SATA1.0a spec section 5.2.\r
a41b5272 2335 //\r
cbd2a4b3 2336 PhyDetectDelay = EFI_AHCI_BUS_PHY_DETECT_TIMEOUT;\r
2337 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SSTS;\r
2338 do {\r
2339 Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_SSTS_DET_MASK;\r
2340 if ((Data == EFI_AHCI_PORT_SSTS_DET_PCE) || (Data == EFI_AHCI_PORT_SSTS_DET)) {\r
2341 break;\r
a41b5272 2342 }\r
2343\r
cbd2a4b3 2344 MicroSecondDelay (1000);\r
2345 PhyDetectDelay--;\r
2346 } while (PhyDetectDelay > 0);\r
2347\r
2348 if (PhyDetectDelay == 0) {\r
a41b5272 2349 //\r
cbd2a4b3 2350 // No device detected at this port.\r
2721fabc 2351 // Clear PxCMD.SUD for those ports at which there are no device present.\r
a41b5272 2352 //\r
2721fabc 2353 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
2354 AhciAndReg (PciIo, Offset, (UINT32) ~(EFI_AHCI_PORT_CMD_SUD));\r
cbd2a4b3 2355 continue;\r
2356 }\r
a41b5272 2357\r
cbd2a4b3 2358 //\r
2359 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
2360 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
2361 //\r
2362 PhyDetectDelay = 16 * 1000;\r
2363 do {\r
2364 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
2365 if (AhciReadReg(PciIo, Offset) != 0) {\r
2366 AhciWriteReg (PciIo, Offset, AhciReadReg(PciIo, Offset));\r
2367 }\r
2368 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
a41b5272 2369\r
cbd2a4b3 2370 Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_TFD_MASK;\r
2371 if (Data == 0) {\r
2372 break;\r
2373 }\r
a41b5272 2374\r
cbd2a4b3 2375 MicroSecondDelay (1000);\r
2376 PhyDetectDelay--;\r
2377 } while (PhyDetectDelay > 0);\r
1aff716a 2378\r
cbd2a4b3 2379 if (PhyDetectDelay == 0) {\r
2380 continue;\r
2381 }\r
a41b5272 2382\r
cbd2a4b3 2383 //\r
2384 // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
2385 //\r
2386 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;\r
8536cc4b 2387 Status = AhciWaitMmioSet (\r
1aff716a 2388 PciIo,\r
cbd2a4b3 2389 Offset,\r
2390 0x0000FFFF,\r
2391 0x00000101,\r
2392 EFI_TIMER_PERIOD_SECONDS(16)\r
2393 );\r
2394 if (EFI_ERROR (Status)) {\r
2395 continue;\r
2396 }\r
a41b5272 2397\r
cbd2a4b3 2398 Data = AhciReadReg (PciIo, Offset);\r
2399 if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATAPI_DEVICE_SIG) {\r
2400 Status = AhciIdentifyPacket (PciIo, AhciRegisters, Port, 0, &Buffer);\r
a41b5272 2401\r
cbd2a4b3 2402 if (EFI_ERROR (Status)) {\r
a41b5272 2403 continue;\r
2404 }\r
aca84419 2405\r
cbd2a4b3 2406 DeviceType = EfiIdeCdrom;\r
2407 } else if ((Data & EFI_AHCI_ATAPI_SIG_MASK) == EFI_AHCI_ATA_DEVICE_SIG) {\r
2408 Status = AhciIdentify (PciIo, AhciRegisters, Port, 0, &Buffer);\r
a41b5272 2409\r
a41b5272 2410 if (EFI_ERROR (Status)) {\r
cbd2a4b3 2411 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED));\r
a41b5272 2412 continue;\r
2413 }\r
2414\r
cbd2a4b3 2415 DeviceType = EfiIdeHarddisk;\r
2416 } else {\r
2417 continue;\r
2418 }\r
1aff716a 2419 DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n",\r
cbd2a4b3 2420 Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"));\r
a41b5272 2421\r
cbd2a4b3 2422 //\r
2423 // If the device is a hard disk, then try to enable S.M.A.R.T feature\r
2424 //\r
fc80ee69 2425 if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {\r
cbd2a4b3 2426 AhciAtaSmartSupport (\r
2427 PciIo,\r
2428 AhciRegisters,\r
2429 Port,\r
2430 0,\r
2431 &Buffer,\r
2432 NULL\r
2433 );\r
2434 }\r
a41b5272 2435\r
cbd2a4b3 2436 //\r
2437 // Submit identify data to IDE controller init driver\r
2438 //\r
2439 IdeInit->SubmitData (IdeInit, Port, 0, &Buffer);\r
aca84419 2440\r
cbd2a4b3 2441 //\r
2442 // Now start to config ide device parameter and transfer mode.\r
2443 //\r
2444 Status = IdeInit->CalculateMode (\r
2445 IdeInit,\r
2446 Port,\r
2447 0,\r
2448 &SupportedModes\r
2449 );\r
2450 if (EFI_ERROR (Status)) {\r
2451 DEBUG ((EFI_D_ERROR, "Calculate Mode Fail, Status = %r\n", Status));\r
2452 continue;\r
2453 }\r
2454\r
2455 //\r
2456 // Set best supported PIO mode on this IDE device\r
2457 //\r
2458 if (SupportedModes->PioMode.Mode <= EfiAtaPioMode2) {\r
2459 TransferMode.ModeCategory = EFI_ATA_MODE_DEFAULT_PIO;\r
2460 } else {\r
2461 TransferMode.ModeCategory = EFI_ATA_MODE_FLOW_PIO;\r
2462 }\r
2463\r
2464 TransferMode.ModeNumber = (UINT8) (SupportedModes->PioMode.Mode);\r
2465\r
2466 //\r
2467 // Set supported DMA mode on this IDE device. Note that UDMA & MDMA cann't\r
2468 // be set together. Only one DMA mode can be set to a device. If setting\r
2469 // DMA mode operation fails, we can continue moving on because we only use\r
2470 // PIO mode at boot time. DMA modes are used by certain kind of OS booting\r
2471 //\r
2472 if (SupportedModes->UdmaMode.Valid) {\r
2473 TransferMode.ModeCategory = EFI_ATA_MODE_UDMA;\r
2474 TransferMode.ModeNumber = (UINT8) (SupportedModes->UdmaMode.Mode);\r
2475 } else if (SupportedModes->MultiWordDmaMode.Valid) {\r
2476 TransferMode.ModeCategory = EFI_ATA_MODE_MDMA;\r
1aff716a 2477 TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode;\r
cbd2a4b3 2478 }\r
2479\r
2480 Status = AhciDeviceSetFeature (PciIo, AhciRegisters, Port, 0, 0x03, (UINT32)(*(UINT8 *)&TransferMode));\r
2481 if (EFI_ERROR (Status)) {\r
2482 DEBUG ((EFI_D_ERROR, "Set transfer Mode Fail, Status = %r\n", Status));\r
2483 continue;\r
2484 }\r
2485\r
2486 //\r
2487 // Found a ATA or ATAPI device, add it into the device list.\r
2488 //\r
2489 CreateNewDeviceInfo (Instance, Port, 0, DeviceType, &Buffer);\r
2490 if (DeviceType == EfiIdeHarddisk) {\r
2491 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));\r
a41b5272 2492 }\r
2493 }\r
2494 }\r
cbd2a4b3 2495\r
a41b5272 2496 return EFI_SUCCESS;\r
2497}\r
2498\r