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