]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AhciMode.c
MdePkg Protocol/AtaPassThru.h: Update PortMultiplierPort related comments
[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 - 2014, 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 AhciWaitMmioSet (\r
431 PciIo,\r
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
444 @param Timeout The timeout value of disabling FIS, uses 100ns as a unit.\r
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
458 )\r
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
472\r
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
482 return AhciWaitMmioSet (\r
483 PciIo,\r
484 Offset,\r
485 EFI_AHCI_PORT_CMD_FR,\r
486 0,\r
487 Timeout\r
488 );\r
489}\r
490\r
491\r
492\r
493/**\r
494 Build the command list, command table and prepare the fis receiver.\r
495\r
496 @param PciIo The PCI IO protocol instance.\r
497 @param AhciRegisters The pointer to the EFI_AHCI_REGISTERS.\r
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
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
508**/\r
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
522 IN UINT32 DataLength\r
523 )\r
524{\r
525 UINT64 BaseAddr;\r
526 UINT32 PrdtNumber;\r
527 UINT32 PrdtIndex;\r
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
535 //\r
536 PrdtNumber = (UINT32)DivU64x32 (((UINT64)DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1), EFI_AHCI_MAX_DATA_PER_PRDT);\r
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
548\r
549 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
550\r
551 ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
552\r
553 CommandFis->AhciCFisPmNum = PortMultiplier;\r
554\r
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
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
572\r
573 RemainedData = (UINTN) DataLength;\r
574 MemAddr = (UINTN) DataPhysicalAddr;\r
575 CommandList->AhciCmdPrdtl = PrdtNumber;\r
576\r
577 for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
578 if (RemainedData < EFI_AHCI_MAX_DATA_PER_PRDT) {\r
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
587 RemainedData -= EFI_AHCI_MAX_DATA_PER_PRDT;\r
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
602 );\r
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
613\r
614 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data structure.\r
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
631 CmdFis->AhciCFisCmdInd = 0x1;\r
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
649 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
650}\r
651\r
652/**\r
653 Start a PIO data transfer on specific port.\r
654\r
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
667 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
668 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
669 used by non-blocking mode.\r
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
678EFIAPI\r
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
685 IN UINT8 AtapiCommandLength,\r
686 IN BOOLEAN Read,\r
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
691 IN UINT64 Timeout,\r
692 IN ATA_NONBLOCK_TASK *Task\r
693 )\r
694{\r
695 EFI_STATUS Status;\r
696 UINTN FisBaseAddr;\r
697 UINTN Offset;\r
698 EFI_PHYSICAL_ADDRESS PhyAddr;\r
699 VOID *Map;\r
700 UINTN MapLength;\r
701 EFI_PCI_IO_PROTOCOL_OPERATION Flag;\r
702 UINT64 Delay;\r
703 EFI_AHCI_COMMAND_FIS CFis;\r
704 EFI_AHCI_COMMAND_LIST CmdList;\r
705 UINT32 PortTfd;\r
706 UINT32 PrdCount;\r
707 BOOLEAN InfiniteWait;\r
708 BOOLEAN PioFisReceived;\r
709 BOOLEAN D2hFisReceived;\r
710\r
711 if (Timeout == 0) {\r
712 InfiniteWait = TRUE;\r
713 } else {\r
714 InfiniteWait = FALSE;\r
715 }\r
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
737 return EFI_BAD_BUFFER_SIZE;\r
738 }\r
739\r
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
762 );\r
763\r
764 Status = AhciStartCommand (\r
765 PciIo,\r
766 Port,\r
767 0,\r
768 Timeout\r
769 );\r
770 if (EFI_ERROR (Status)) {\r
771 goto Exit;\r
772 }\r
773\r
774 //\r
775 // Check the status and wait the driver sending data\r
776 //\r
777 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
778\r
779 if (Read && (AtapiCommand == 0)) {\r
780 //\r
781 // Wait device sends the PIO setup fis before data transfer\r
782 //\r
783 Status = EFI_TIMEOUT;\r
784 Delay = DivU64x32 (Timeout, 1000) + 1;\r
785 do {\r
786 PioFisReceived = FALSE;\r
787 D2hFisReceived = FALSE;\r
788 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
789 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, NULL);\r
790 if (!EFI_ERROR (Status)) {\r
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
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
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
817 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
818 if (PrdCount == DataCount) {\r
819 Status = EFI_SUCCESS;\r
820 break;\r
821 }\r
822 }\r
823\r
824 //\r
825 // Stall for 100 microseconds.\r
826 //\r
827 MicroSecondDelay(100);\r
828\r
829 Delay--;\r
830 if (Delay == 0) {\r
831 Status = EFI_TIMEOUT;\r
832 }\r
833 } while (InfiniteWait || (Delay > 0));\r
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
845\r
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
855 }\r
856\r
857Exit:\r
858 AhciStopCommand (\r
859 PciIo,\r
860 Port,\r
861 Timeout\r
862 );\r
863\r
864 AhciDisableFisReceive (\r
865 PciIo,\r
866 Port,\r
867 Timeout\r
868 );\r
869\r
870 PciIo->Unmap (\r
871 PciIo,\r
872 Map\r
873 );\r
874\r
875 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
876\r
877 return Status;\r
878}\r
879\r
880/**\r
881 Start a DMA data transfer on specific port\r
882\r
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
895 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
896 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
897 used by non-blocking mode.\r
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
903\r
904**/\r
905EFI_STATUS\r
906EFIAPI\r
907AhciDmaTransfer (\r
908 IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance,\r
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
914 IN BOOLEAN Read,\r
915 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
916 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
917 IN OUT VOID *MemoryAddr,\r
918 IN UINT32 DataCount,\r
919 IN UINT64 Timeout,\r
920 IN ATA_NONBLOCK_TASK *Task\r
921 )\r
922{\r
923 EFI_STATUS Status;\r
924 UINTN Offset;\r
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
931 UINTN FisBaseAddr;\r
932 UINT32 PortTfd;\r
933\r
934 EFI_PCI_IO_PROTOCOL *PciIo;\r
935 EFI_TPL OldTpl;\r
936\r
937 Map = NULL;\r
938 PciIo = Instance->PciIo;\r
939\r
940 if (PciIo == NULL) {\r
941 return EFI_INVALID_PARAMETER;\r
942 }\r
943\r
944 //\r
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
948 //\r
949 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
950 while ((Task == NULL) && (!IsListEmpty (&Instance->NonBlockingTaskList))) {\r
951 AsyncNonBlockingTransferRoutine (NULL, Instance);\r
952 //\r
953 // Stall for 100us.\r
954 //\r
955 MicroSecondDelay (100);\r
956 }\r
957 gBS->RestoreTPL (OldTpl);\r
958\r
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
965 }\r
966 if (Read) {\r
967 Flag = EfiPciIoOperationBusMasterWrite;\r
968 } else {\r
969 Flag = EfiPciIoOperationBusMasterRead;\r
970 }\r
971\r
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
986 return EFI_BAD_BUFFER_SIZE;\r
987 }\r
988\r
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
1025 }\r
1026\r
1027 //\r
1028 // Wait for command compelte\r
1029 //\r
1030 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1031 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
1032 if (Task != NULL) {\r
1033 //\r
1034 // For Non-blocking\r
1035 //\r
1036 Status = AhciCheckMemSet (\r
1037 Offset,\r
1038 EFI_AHCI_FIS_TYPE_MASK,\r
1039 EFI_AHCI_FIS_REGISTER_D2H,\r
1040 Task\r
1041 );\r
1042 } else {\r
1043 Status = AhciWaitMemSet (\r
1044 Offset,\r
1045 EFI_AHCI_FIS_TYPE_MASK,\r
1046 EFI_AHCI_FIS_REGISTER_D2H,\r
1047 Timeout\r
1048 );\r
1049 }\r
1050\r
1051 if (EFI_ERROR (Status)) {\r
1052 goto Exit;\r
1053 }\r
1054\r
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
1059 }\r
1060\r
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
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
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
1073 PciIo,\r
1074 Port,\r
1075 Timeout\r
1076 );\r
1077\r
1078 AhciDisableFisReceive (\r
1079 PciIo,\r
1080 Port,\r
1081 Timeout\r
1082 );\r
1083\r
1084 PciIo->Unmap (\r
1085 PciIo,\r
1086 (Task != NULL) ? Task->Map : Map\r
1087 );\r
1088\r
1089 if (Task != NULL) {\r
1090 Task->Packet->Asb->AtaStatus = 0x01;\r
1091 }\r
1092 }\r
1093\r
1094 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
1095 return Status;\r
1096}\r
1097\r
1098/**\r
1099 Start a non data transfer on specific port.\r
1100\r
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
1110 @param[in] Timeout The timeout value of non data transfer, uses 100ns as a unit.\r
1111 @param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK\r
1112 used by non-blocking mode.\r
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
1119**/\r
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
1131 IN UINT64 Timeout,\r
1132 IN ATA_NONBLOCK_TASK *Task\r
1133 )\r
1134{\r
1135 EFI_STATUS Status;\r
1136 UINTN FisBaseAddr;\r
1137 UINTN Offset;\r
1138 UINT32 PortTfd;\r
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
1163 );\r
1164\r
1165 Status = AhciStartCommand (\r
1166 PciIo,\r
1167 Port,\r
1168 0,\r
1169 Timeout\r
1170 );\r
1171 if (EFI_ERROR (Status)) {\r
1172 goto Exit;\r
1173 }\r
1174\r
1175 //\r
1176 // Wait device sends the Response Fis\r
1177 //\r
1178 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + Port * sizeof (EFI_AHCI_RECEIVED_FIS);\r
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
1186\r
1187 if (EFI_ERROR (Status)) {\r
1188 goto Exit;\r
1189 }\r
1190\r
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
1196\r
1197Exit:\r
1198 AhciStopCommand (\r
1199 PciIo,\r
1200 Port,\r
1201 Timeout\r
1202 );\r
1203\r
1204 AhciDisableFisReceive (\r
1205 PciIo,\r
1206 Port,\r
1207 Timeout\r
1208 );\r
1209\r
1210 AhciDumpPortStatus (PciIo, Port, AtaStatusBlock);\r
1211\r
1212 return Status;\r
1213}\r
1214\r
1215/**\r
1216 Stop command running for giving port\r
1217\r
1218 @param PciIo The PCI IO protocol instance.\r
1219 @param Port The number of port.\r
1220 @param Timeout The timeout value of stop, uses 100ns as a unit.\r
1221\r
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
1242 return EFI_SUCCESS;\r
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
1249 return AhciWaitMmioSet (\r
1250 PciIo,\r
1251 Offset,\r
1252 EFI_AHCI_PORT_CMD_CR,\r
1253 0,\r
1254 Timeout\r
1255 );\r
1256}\r
1257\r
1258/**\r
1259 Start command for give slot on specific port.\r
1260\r
1261 @param PciIo The PCI IO protocol instance.\r
1262 @param Port The number of port.\r
1263 @param CommandSlot The number of Command Slot.\r
1264 @param Timeout The timeout value of start, uses 100ns as a unit.\r
1265\r
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
1301 PciIo,\r
1302 Port,\r
1303 Timeout\r
1304 );\r
1305\r
1306 if (EFI_ERROR (Status)) {\r
1307 return Status;\r
1308 }\r
1309\r
1310 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1311 PortStatus = AhciReadReg (PciIo, Offset);\r
1312\r
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
1326 AhciOrReg (PciIo, Offset, EFI_AHCI_PORT_CMD_CLO);\r
1327\r
1328 AhciWaitMmioSet (\r
1329 PciIo,\r
1330 Offset,\r
1331 EFI_AHCI_PORT_CMD_CLO,\r
1332 0,\r
1333 Timeout\r
1334 );\r
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
1341 //\r
1342 // Setting the command\r
1343 //\r
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
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
1356 @param Timeout The timeout value of reset, uses 100ns as a unit.\r
1357\r
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
1372 UINT32 Offset;\r
1373\r
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
1387 // wait 5 millisecond before de-assert DET\r
1388 //\r
1389 MicroSecondDelay (5000);\r
1390\r
1391 AhciAndReg (PciIo, Offset, (UINT32)EFI_AHCI_PORT_SCTL_MASK);\r
1392\r
1393 //\r
1394 // wait 5 millisecond before de-assert DET\r
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
1402 Status = AhciWaitMmioSet (\r
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
1408 );\r
1409\r
1410 if (EFI_ERROR (Status)) {\r
1411 DEBUG ((EFI_D_ERROR, "Port %d COMRESET failed: %r\n", Port, Status));\r
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
1423\r
1424 @param PciIo The PCI IO protocol instance.\r
1425 @param Timeout The timeout value of reset, uses 100ns as a unit.\r
1426\r
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
1437 )\r
1438{\r
1439 UINT64 Delay;\r
1440 UINT32 Value;\r
1441 UINT32 Capability;\r
1442\r
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
1454\r
1455 AhciOrReg (PciIo, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
1456\r
1457 Delay = DivU64x32(Timeout, 1000) + 1;\r
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
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
1487 @param PortMultiplier The timeout value of stop.\r
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
1530 ATA_ATAPI_TIMEOUT,\r
1531 NULL\r
1532 );\r
1533\r
1534 if (EFI_ERROR (Status)) {\r
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
1539 return EFI_DEVICE_ERROR;\r
1540 }\r
1541\r
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
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
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
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
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
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
1585 @param PortMultiplier The timeout value of stop.\r
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
1597 IN EFI_IDENTIFY_DATA *IdentifyData,\r
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
1611 DEBUG ((EFI_D_INFO, "S.M.A.R.T feature is not supported at port [%d] PortMultiplier [%d]!\n",\r
1612 Port, PortMultiplier));\r
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
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
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
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
1647 ATA_ATAPI_TIMEOUT,\r
1648 NULL\r
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
1673 ATA_ATAPI_TIMEOUT,\r
1674 NULL\r
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
1688 DEBUG ((EFI_D_INFO, "Enabled S.M.A.R.T feature at port [%d] PortMultiplier [%d]!\n",\r
1689 Port, PortMultiplier));\r
1690 }\r
1691\r
1692 return ;\r
1693}\r
1694\r
1695/**\r
1696 Send Buffer cmd to specific device.\r
1697\r
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
1701 @param PortMultiplier The timeout value of stop.\r
1702 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
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
1717 IN OUT EFI_IDENTIFY_DATA *Buffer\r
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
1730\r
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
1746 ATA_ATAPI_TIMEOUT,\r
1747 NULL\r
1748 );\r
1749\r
1750 return Status;\r
1751}\r
1752\r
1753/**\r
1754 Send Buffer cmd to specific device.\r
1755\r
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
1759 @param PortMultiplier The timeout value of stop.\r
1760 @param Buffer The data buffer to store IDENTIFY PACKET data.\r
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
1775 IN OUT EFI_IDENTIFY_DATA *Buffer\r
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
1785\r
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
1804 ATA_ATAPI_TIMEOUT,\r
1805 NULL\r
1806 );\r
1807\r
1808 return Status;\r
1809}\r
1810\r
1811/**\r
1812 Send SET FEATURE cmd on specific device.\r
1813\r
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
1817 @param PortMultiplier The timeout value of stop.\r
1818 @param Feature The data to send Feature register.\r
1819 @param FeatureSpecificData The specific data for SET FEATURE cmd.\r
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
1844\r
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
1862 ATA_ATAPI_TIMEOUT,\r
1863 NULL\r
1864 );\r
1865\r
1866 return Status;\r
1867}\r
1868\r
1869/**\r
1870 This function is used to send out ATAPI commands conforms to the Packet Command\r
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
1875 @param Port The number of port.\r
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
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
1929 if (Length == 0) {\r
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
1939 Packet->Timeout,\r
1940 NULL\r
1941 );\r
1942 } else {\r
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
1955 Packet->Timeout,\r
1956 NULL\r
1957 );\r
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
1964\r
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
1981 UINT32 PortImplementBitMap;\r
1982 UINT8 MaxPortNumber;\r
1983 UINT8 MaxCommandSlotNumber;\r
1984 BOOLEAN Support64Bit;\r
1985 UINT64 MaxReceiveFisSize;\r
1986 UINT64 MaxCommandListSize;\r
1987 UINT64 MaxCommandTableSize;\r
1988 EFI_PHYSICAL_ADDRESS AhciRFisPciAddr;\r
1989 EFI_PHYSICAL_ADDRESS AhciCmdListPciAddr;\r
1990 EFI_PHYSICAL_ADDRESS AhciCommandTablePciAddr;\r
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
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
2001 Support64Bit = (BOOLEAN) (((Capability & BIT31) != 0) ? TRUE : FALSE);\r
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
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
2017 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
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
2026 ZeroMem (Buffer, (UINTN)MaxReceiveFisSize);\r
2027\r
2028 AhciRegisters->AhciRFis = Buffer;\r
2029 AhciRegisters->MaxReceiveFisSize = MaxReceiveFisSize;\r
2030 Bytes = (UINTN)MaxReceiveFisSize;\r
2031\r
2032 Status = PciIo->Map (\r
2033 PciIo,\r
2034 EfiPciIoOperationBusMasterCommonBuffer,\r
2035 Buffer,\r
2036 &Bytes,\r
2037 &AhciRFisPciAddr,\r
2038 &AhciRegisters->MapRFis\r
2039 );\r
2040\r
2041 if (EFI_ERROR (Status) || (Bytes != MaxReceiveFisSize)) {\r
2042 //\r
2043 // Map error or unable to map the whole RFis buffer into a contiguous region.\r
2044 //\r
2045 Status = EFI_OUT_OF_RESOURCES;\r
2046 goto Error6;\r
2047 }\r
2048\r
2049 if ((!Support64Bit) && (AhciRFisPciAddr > 0x100000000ULL)) {\r
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
2056 AhciRegisters->AhciRFisPciAddr = (EFI_AHCI_RECEIVED_FIS *)(UINTN)AhciRFisPciAddr;\r
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
2068 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
2069 &Buffer,\r
2070 0\r
2071 );\r
2072\r
2073 if (EFI_ERROR (Status)) {\r
2074 //\r
2075 // Free mapped resource.\r
2076 //\r
2077 Status = EFI_OUT_OF_RESOURCES;\r
2078 goto Error5;\r
2079 }\r
2080\r
2081 ZeroMem (Buffer, (UINTN)MaxCommandListSize);\r
2082\r
2083 AhciRegisters->AhciCmdList = Buffer;\r
2084 AhciRegisters->MaxCommandListSize = MaxCommandListSize;\r
2085 Bytes = (UINTN)MaxCommandListSize;\r
2086\r
2087 Status = PciIo->Map (\r
2088 PciIo,\r
2089 EfiPciIoOperationBusMasterCommonBuffer,\r
2090 Buffer,\r
2091 &Bytes,\r
2092 &AhciCmdListPciAddr,\r
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
2104 if ((!Support64Bit) && (AhciCmdListPciAddr > 0x100000000ULL)) {\r
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
2111 AhciRegisters->AhciCmdListPciAddr = (EFI_AHCI_COMMAND_LIST *)(UINTN)AhciCmdListPciAddr;\r
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
2124 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
2125 &Buffer,\r
2126 0\r
2127 );\r
2128\r
2129 if (EFI_ERROR (Status)) {\r
2130 //\r
2131 // Free mapped resource.\r
2132 //\r
2133 Status = EFI_OUT_OF_RESOURCES;\r
2134 goto Error3;\r
2135 }\r
2136\r
2137 ZeroMem (Buffer, (UINTN)MaxCommandTableSize);\r
2138\r
2139 AhciRegisters->AhciCommandTable = Buffer;\r
2140 AhciRegisters->MaxCommandTableSize = MaxCommandTableSize;\r
2141 Bytes = (UINTN)MaxCommandTableSize;\r
2142\r
2143 Status = PciIo->Map (\r
2144 PciIo,\r
2145 EfiPciIoOperationBusMasterCommonBuffer,\r
2146 Buffer,\r
2147 &Bytes,\r
2148 &AhciCommandTablePciAddr,\r
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
2160 if ((!Support64Bit) && (AhciCommandTablePciAddr > 0x100000000ULL)) {\r
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
2167 AhciRegisters->AhciCommandTablePciAddr = (EFI_AHCI_COMMAND_TABLE *)(UINTN)AhciCommandTablePciAddr;\r
2168\r
2169 return EFI_SUCCESS;\r
2170 //\r
2171 // Map error or unable to map the whole CmdList buffer into a contiguous region.\r
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
2181 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandTableSize),\r
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
2192 EFI_SIZE_TO_PAGES ((UINTN) MaxCommandListSize),\r
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
2203 EFI_SIZE_TO_PAGES ((UINTN) MaxReceiveFisSize),\r
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
2213 The function is designed to initialize ATA host controller.\r
2214\r
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
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
2241 UINT32 PhyDetectDelay;\r
2242\r
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
2250 Status = AhciReset (PciIo, EFI_AHCI_BUS_RESET_TIMEOUT);\r
2251\r
2252 if (EFI_ERROR (Status)) {\r
2253 return EFI_DEVICE_ERROR;\r
2254 }\r
2255\r
2256 //\r
2257 // Collect AHCI controller information\r
2258 //\r
2259 Capability = AhciReadReg (PciIo, EFI_AHCI_CAPABILITY_OFFSET);\r
2260 \r
2261 //\r
2262 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
2263 //\r
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
2268 //\r
2269 // Get the number of command slots per port supported by this HBA.\r
2270 //\r
2271 MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
2272\r
2273 //\r
2274 // Get the bit map of those ports exposed by this HBA.\r
2275 // It indicates which ports that the HBA supports are available for software to use.\r
2276 //\r
2277 PortImplementBitMap = AhciReadReg(PciIo, EFI_AHCI_PI_OFFSET);\r
2278\r
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
2286 for (Port = 0; Port < EFI_AHCI_MAX_PORTS; Port ++) {\r
2287 if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
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
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
2316 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
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
2321\r
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
2336\r
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
2347 Status = AhciWaitMmioSet (\r
2348 PciIo,\r
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
2355 continue;\r
2356 }\r
2357\r
2358 //\r
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
2361 //\r
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
2368 }\r
2369\r
2370 MicroSecondDelay (1000);\r
2371 PhyDetectDelay--;\r
2372 } while (PhyDetectDelay > 0);\r
2373\r
2374 if (PhyDetectDelay == 0) {\r
2375 //\r
2376 // No device detected at this port.\r
2377 // Clear PxCMD.SUD for those ports at which there are no device present.\r
2378 //\r
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
2381 continue;\r
2382 }\r
2383\r
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
2395\r
2396 Data = AhciReadReg (PciIo, Offset) & EFI_AHCI_PORT_TFD_MASK;\r
2397 if (Data == 0) {\r
2398 break;\r
2399 }\r
2400\r
2401 MicroSecondDelay (1000);\r
2402 PhyDetectDelay--;\r
2403 } while (PhyDetectDelay > 0);\r
2404\r
2405 if (PhyDetectDelay == 0) {\r
2406 DEBUG ((EFI_D_ERROR, "Port %d Device presence detected but phy not ready (TFD=0x%X)\n", Port, Data));\r
2407 continue;\r
2408 }\r
2409\r
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
2414 Status = AhciWaitMmioSet (\r
2415 PciIo,\r
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
2424\r
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
2428\r
2429 if (EFI_ERROR (Status)) {\r
2430 continue;\r
2431 }\r
2432\r
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
2436\r
2437 if (EFI_ERROR (Status)) {\r
2438 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_EC_NOT_DETECTED));\r
2439 continue;\r
2440 }\r
2441\r
2442 DeviceType = EfiIdeHarddisk;\r
2443 } else {\r
2444 continue;\r
2445 }\r
2446 DEBUG ((EFI_D_INFO, "port [%d] port mulitplier [%d] has a [%a]\n",\r
2447 Port, 0, DeviceType == EfiIdeCdrom ? "cdrom" : "harddisk"));\r
2448\r
2449 //\r
2450 // If the device is a hard disk, then try to enable S.M.A.R.T feature\r
2451 //\r
2452 if ((DeviceType == EfiIdeHarddisk) && PcdGetBool (PcdAtaSmartEnable)) {\r
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
2462\r
2463 //\r
2464 // Submit identify data to IDE controller init driver\r
2465 //\r
2466 IdeInit->SubmitData (IdeInit, Port, 0, &Buffer);\r
2467\r
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
2504 TransferMode.ModeNumber = (UINT8) SupportedModes->MultiWordDmaMode.Mode;\r
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
2516 CreateNewDeviceInfo (Instance, Port, 0, DeviceType, &Buffer);\r
2517 if (DeviceType == EfiIdeHarddisk) {\r
2518 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_PERIPHERAL_FIXED_MEDIA | EFI_P_PC_ENABLE));\r
2519 }\r
2520 }\r
2521 }\r
2522\r
2523 return EFI_SUCCESS;\r
2524}\r
2525\r