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