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