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