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