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