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