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