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