]> git.proxmox.com Git - mirror_edk2.git/blame - SecurityPkg/Tcg/Opal/OpalPassword/OpalAhciMode.c
SecurityPkg: Remove dead code and inf redundant definitions.
[mirror_edk2.git] / SecurityPkg / Tcg / Opal / OpalPassword / OpalAhciMode.c
CommitLineData
112e584b
SZ
1/** @file\r
2 This driver is used for Opal Password Feature support at AHCI mode.\r
3\r
4Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15\r
16#include "OpalPasswordPei.h"\r
17\r
18/**\r
19 Start command for give slot on specific port.\r
20\r
21 @param AhciBar AHCI bar address.\r
22 @param Port The number of port.\r
23 @param CommandSlot The number of CommandSlot.\r
24 @param Timeout The timeout Value of start.\r
25\r
26 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
27 @retval EFI_TIMEOUT The operation is time out.\r
28 @retval EFI_SUCCESS The command start successfully.\r
29\r
30**/\r
31EFI_STATUS\r
32EFIAPI\r
33AhciStartCommand (\r
34 IN UINT32 AhciBar,\r
35 IN UINT8 Port,\r
36 IN UINT8 CommandSlot,\r
37 IN UINT64 Timeout\r
38 );\r
39\r
40/**\r
41 Stop command running for giving port\r
42\r
43 @param AhciBar AHCI bar address.\r
44 @param Port The number of port.\r
45 @param Timeout The timeout Value of stop.\r
46\r
47 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
48 @retval EFI_TIMEOUT The operation is time out.\r
49 @retval EFI_SUCCESS The command stop successfully.\r
50\r
51**/\r
52EFI_STATUS\r
53EFIAPI\r
54AhciStopCommand (\r
55 IN UINT32 AhciBar,\r
56 IN UINT8 Port,\r
57 IN UINT64 Timeout\r
58 );\r
59\r
60/**\r
61 Read AHCI Operation register.\r
62\r
63 @param AhciBar AHCI bar address.\r
64 @param Offset The operation register offset.\r
65\r
66 @return The register content read.\r
67\r
68**/\r
69UINT32\r
70EFIAPI\r
71AhciReadReg (\r
72 IN UINT32 AhciBar,\r
73 IN UINT32 Offset\r
74 )\r
75{\r
76 UINT32 Data;\r
77\r
78 Data = 0;\r
79\r
80 Data = MmioRead32 (AhciBar + Offset);\r
81\r
82 return Data;\r
83}\r
84\r
85/**\r
86 Write AHCI Operation register.\r
87\r
88 @param AhciBar AHCI bar address.\r
89 @param Offset The operation register offset.\r
90 @param Data The Data used to write down.\r
91\r
92**/\r
93VOID\r
94EFIAPI\r
95AhciWriteReg (\r
96 IN UINT32 AhciBar,\r
97 IN UINT32 Offset,\r
98 IN UINT32 Data\r
99 )\r
100{\r
101 MmioWrite32 (AhciBar + Offset, Data);\r
102\r
103 return ;\r
104}\r
105\r
106/**\r
107 Do AND operation with the Value of AHCI Operation register.\r
108\r
109 @param AhciBar AHCI bar address.\r
110 @param Offset The operation register offset.\r
111 @param AndData The Data used to do AND operation.\r
112\r
113**/\r
114VOID\r
115EFIAPI\r
116AhciAndReg (\r
117 IN UINT32 AhciBar,\r
118 IN UINT32 Offset,\r
119 IN UINT32 AndData\r
120 )\r
121{\r
122 UINT32 Data;\r
123\r
124 Data = AhciReadReg (AhciBar, Offset);\r
125\r
126 Data &= AndData;\r
127\r
128 AhciWriteReg (AhciBar, Offset, Data);\r
129}\r
130\r
131/**\r
132 Do OR operation with the Value of AHCI Operation register.\r
133\r
134 @param AhciBar AHCI bar address.\r
135 @param Offset The operation register offset.\r
136 @param OrData The Data used to do OR operation.\r
137\r
138**/\r
139VOID\r
140EFIAPI\r
141AhciOrReg (\r
142 IN UINT32 AhciBar,\r
143 IN UINT32 Offset,\r
144 IN UINT32 OrData\r
145 )\r
146{\r
147 UINT32 Data;\r
148\r
149 Data = AhciReadReg (AhciBar, Offset);\r
150\r
151 Data |= OrData;\r
152\r
153 AhciWriteReg (AhciBar, Offset, Data);\r
154}\r
155\r
156/**\r
157 Wait for memory set to the test Value.\r
158\r
159 @param AhciBar AHCI bar address.\r
160 @param Offset The memory offset to test.\r
161 @param MaskValue The mask Value of memory.\r
162 @param TestValue The test Value of memory.\r
163 @param Timeout The time out Value for wait memory set.\r
164\r
165 @retval EFI_DEVICE_ERROR The memory is not set.\r
166 @retval EFI_TIMEOUT The memory setting is time out.\r
167 @retval EFI_SUCCESS The memory is correct set.\r
168\r
169**/\r
170EFI_STATUS\r
171EFIAPI\r
172AhciWaitMmioSet (\r
173 IN UINT32 AhciBar,\r
174 IN UINT32 Offset,\r
175 IN UINT32 MaskValue,\r
176 IN UINT32 TestValue,\r
177 IN UINT64 Timeout\r
178 )\r
179{\r
180 UINT32 Value;\r
181 UINT32 Delay;\r
182\r
183 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
184\r
185 do {\r
186 Value = AhciReadReg (AhciBar, Offset) & MaskValue;\r
187\r
188 if (Value == TestValue) {\r
189 return EFI_SUCCESS;\r
190 }\r
191\r
192 //\r
193 // Stall for 100 microseconds.\r
194 //\r
195 MicroSecondDelay (100);\r
196\r
197 Delay--;\r
198\r
199 } while (Delay > 0);\r
200\r
201 return EFI_TIMEOUT;\r
202}\r
203/**\r
204 Wait for the Value of the specified system memory set to the test Value.\r
205\r
206 @param Address The system memory address to test.\r
207 @param MaskValue The mask Value of memory.\r
208 @param TestValue The test Value of memory.\r
209 @param Timeout The time out Value for wait memory set, uses 100ns as a unit.\r
210\r
211 @retval EFI_TIMEOUT The system memory setting is time out.\r
212 @retval EFI_SUCCESS The system memory is correct set.\r
213\r
214**/\r
215EFI_STATUS\r
216EFIAPI\r
217AhciWaitMemSet (\r
218 IN EFI_PHYSICAL_ADDRESS Address,\r
219 IN UINT32 MaskValue,\r
220 IN UINT32 TestValue,\r
221 IN UINT64 Timeout\r
222 )\r
223{\r
224 UINT32 Value;\r
225 UINT32 Delay;\r
226\r
227 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
228\r
229 do {\r
230 //\r
231 // Access sytem memory to see if the Value is the tested one.\r
232 //\r
233 // The system memory pointed by Address will be updated by the\r
234 // SATA Host Controller, "volatile" is introduced to prevent\r
235 // compiler from optimizing the access to the memory address\r
236 // to only read once.\r
237 //\r
238 Value = *(volatile UINT32 *) (UINTN) Address;\r
239 Value &= MaskValue;\r
240\r
241 if (Value == TestValue) {\r
242 return EFI_SUCCESS;\r
243 }\r
244\r
245 //\r
246 // Stall for 100 microseconds.\r
247 //\r
248 MicroSecondDelay (100);\r
249\r
250 Delay--;\r
251\r
252 } while (Delay > 0);\r
253\r
254 return EFI_TIMEOUT;\r
255}\r
256\r
257/**\r
258 Check the memory status to the test Value.\r
259\r
260 @param[in] Address The memory address to test.\r
261 @param[in] MaskValue The mask Value of memory.\r
262 @param[in] TestValue The test Value of memory.\r
263 @param[in, out] RetryTimes The retry times Value for waitting memory set. If 0, then just try once.\r
264\r
265 @retval EFI_NOTREADY The memory is not set.\r
266 @retval EFI_TIMEOUT The memory setting retry times out.\r
267 @retval EFI_SUCCESS The memory is correct set.\r
268\r
269**/\r
270EFI_STATUS\r
271EFIAPI\r
272AhciCheckMemSet (\r
273 IN UINTN Address,\r
274 IN UINT32 MaskValue,\r
275 IN UINT32 TestValue,\r
276 IN OUT UINTN *RetryTimes OPTIONAL\r
277 )\r
278{\r
279 UINT32 Value;\r
280\r
281 if (RetryTimes != NULL) {\r
282 (*RetryTimes)--;\r
283 }\r
284\r
285 Value = *(volatile UINT32 *) Address;\r
286 Value &= MaskValue;\r
287\r
288 if (Value == TestValue) {\r
289 return EFI_SUCCESS;\r
290 }\r
291\r
292 if ((RetryTimes != NULL) && (*RetryTimes == 0)) {\r
293 return EFI_TIMEOUT;\r
294 } else {\r
295 return EFI_NOT_READY;\r
296 }\r
297}\r
298\r
299/**\r
300 Clear the port interrupt and error status. It will also clear\r
301 HBA interrupt status.\r
302\r
303 @param AhciBar AHCI bar address.\r
304 @param Port The number of port.\r
305\r
306**/\r
307VOID\r
308EFIAPI\r
309AhciClearPortStatus (\r
310 IN UINT32 AhciBar,\r
311 IN UINT8 Port\r
312 )\r
313{\r
314 UINT32 Offset;\r
315\r
316 //\r
317 // Clear any error status\r
318 //\r
319 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
320 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
321\r
322 //\r
323 // Clear any port interrupt status\r
324 //\r
325 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IS;\r
326 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
327\r
328 //\r
329 // Clear any HBA interrupt status\r
330 //\r
331 AhciWriteReg (AhciBar, EFI_AHCI_IS_OFFSET, AhciReadReg (AhciBar, EFI_AHCI_IS_OFFSET));\r
332}\r
333\r
334/**\r
335 Enable the FIS running for giving port.\r
336\r
337 @param AhciBar AHCI bar address.\r
338 @param Port The number of port.\r
339 @param Timeout The timeout Value of enabling FIS.\r
340\r
341 @retval EFI_DEVICE_ERROR The FIS enable setting fails.\r
342 @retval EFI_TIMEOUT The FIS enable setting is time out.\r
343 @retval EFI_SUCCESS The FIS enable successfully.\r
344\r
345**/\r
346EFI_STATUS\r
347EFIAPI\r
348AhciEnableFisReceive (\r
349 IN UINT32 AhciBar,\r
350 IN UINT8 Port,\r
351 IN UINT64 Timeout\r
352 )\r
353{\r
354 UINT32 Offset;\r
355\r
356 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
357 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_FRE);\r
358\r
359 return AhciWaitMmioSet (\r
360 AhciBar,\r
361 Offset,\r
362 EFI_AHCI_PORT_CMD_FR,\r
363 EFI_AHCI_PORT_CMD_FR,\r
364 Timeout\r
365 );\r
366}\r
367\r
368/**\r
369 Disable the FIS running for giving port.\r
370\r
371 @param AhciBar AHCI bar address.\r
372 @param Port The number of port.\r
373 @param Timeout The timeout Value of disabling FIS.\r
374\r
375 @retval EFI_DEVICE_ERROR The FIS disable setting fails.\r
376 @retval EFI_TIMEOUT The FIS disable setting is time out.\r
377 @retval EFI_UNSUPPORTED The port is in running state.\r
378 @retval EFI_SUCCESS The FIS disable successfully.\r
379\r
380**/\r
381EFI_STATUS\r
382EFIAPI\r
383AhciDisableFisReceive (\r
384 IN UINT32 AhciBar,\r
385 IN UINT8 Port,\r
386 IN UINT64 Timeout\r
387 )\r
388{\r
389 UINT32 Offset;\r
390 UINT32 Data;\r
391\r
392 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
393 Data = AhciReadReg (AhciBar, Offset);\r
394\r
395 //\r
396 // Before disabling Fis receive, the DMA engine of the port should NOT be in running status.\r
397 //\r
398 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) != 0) {\r
399 return EFI_UNSUPPORTED;\r
400 }\r
401\r
402 //\r
403 // Check if the Fis receive DMA engine for the port is running.\r
404 //\r
405 if ((Data & EFI_AHCI_PORT_CMD_FR) != EFI_AHCI_PORT_CMD_FR) {\r
406 return EFI_SUCCESS;\r
407 }\r
408\r
409 AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_FRE));\r
410\r
411 return AhciWaitMmioSet (\r
412 AhciBar,\r
413 Offset,\r
414 EFI_AHCI_PORT_CMD_FR,\r
415 0,\r
416 Timeout\r
417 );\r
418}\r
419\r
420/**\r
421 Build the command list, command table and prepare the fis receiver.\r
422\r
423 @param AhciContext The pointer to the AHCI_CONTEXT.\r
424 @param Port The number of port.\r
425 @param PortMultiplier The timeout Value of stop.\r
426 @param CommandFis The control fis will be used for the transfer.\r
427 @param CommandList The command list will be used for the transfer.\r
428 @param AtapiCommand The atapi command will be used for the transfer.\r
429 @param AtapiCommandLength The Length of the atapi command.\r
430 @param CommandSlotNumber The command slot will be used for the transfer.\r
431 @param DataPhysicalAddr The pointer to the Data Buffer pci bus master address.\r
432 @param DataLength The Data count to be transferred.\r
433\r
434**/\r
435VOID\r
436EFIAPI\r
437AhciBuildCommand (\r
438 IN AHCI_CONTEXT *AhciContext,\r
439 IN UINT8 Port,\r
440 IN UINT8 PortMultiplier,\r
441 IN EFI_AHCI_COMMAND_FIS *CommandFis,\r
442 IN EFI_AHCI_COMMAND_LIST *CommandList,\r
443 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
444 IN UINT8 AtapiCommandLength,\r
445 IN UINT8 CommandSlotNumber,\r
446 IN OUT VOID *DataPhysicalAddr,\r
447 IN UINT64 DataLength\r
448 )\r
449{\r
450 EFI_AHCI_REGISTERS *AhciRegisters;\r
451 UINT32 AhciBar;\r
452 UINT64 BaseAddr;\r
453 UINT64 PrdtNumber;\r
454 UINTN RemainedData;\r
455 UINTN MemAddr;\r
456 DATA_64 Data64;\r
457 UINT32 Offset;\r
458\r
459 AhciRegisters = &AhciContext->AhciRegisters;\r
460 AhciBar = AhciContext->AhciBar;\r
461\r
462 //\r
463 // Filling the PRDT\r
464 //\r
465 PrdtNumber = DivU64x32 (DataLength + EFI_AHCI_MAX_DATA_PER_PRDT - 1, EFI_AHCI_MAX_DATA_PER_PRDT);\r
466\r
467 //\r
468 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB Data block.\r
469 // It also limits that the maximum amount of the PRDT entry in the command table\r
470 // is 65535.\r
471 //\r
472 ASSERT (PrdtNumber <= 1);\r
473\r
474 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis);\r
475\r
476 BaseAddr = Data64.Uint64;\r
477\r
478 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
479\r
480 ZeroMem (AhciRegisters->AhciCommandTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
481\r
482 CommandFis->AhciCFisPmNum = PortMultiplier;\r
483\r
484 CopyMem (&AhciRegisters->AhciCommandTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
485\r
486 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
487 if (AtapiCommand != NULL) {\r
488 CopyMem (\r
489 &AhciRegisters->AhciCommandTable->AtapiCmd,\r
490 AtapiCommand,\r
491 AtapiCommandLength\r
492 );\r
493\r
494 CommandList->AhciCmdA = 1;\r
495 CommandList->AhciCmdP = 1;\r
496\r
497 AhciOrReg (AhciBar, Offset, (EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
498 } else {\r
499 AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_DLAE | EFI_AHCI_PORT_CMD_ATAPI));\r
500 }\r
501\r
502 RemainedData = (UINTN) DataLength;\r
503 MemAddr = (UINTN) DataPhysicalAddr;\r
504 CommandList->AhciCmdPrdtl = (UINT32)PrdtNumber;\r
505\r
506 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbc = (UINT32)RemainedData - 1;\r
507\r
508 Data64.Uint64 = (UINT64)MemAddr;\r
509 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDba = Data64.Uint32.Lower32;\r
510 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtDbau = Data64.Uint32.Upper32;\r
511\r
512 //\r
513 // Set the last PRDT to Interrupt On Complete\r
514 //\r
515 AhciRegisters->AhciCommandTable->PrdtTable.AhciPrdtIoc = 1;\r
516\r
517 CopyMem (\r
518 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
519 CommandList,\r
520 sizeof (EFI_AHCI_COMMAND_LIST)\r
521 );\r
522\r
523 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCommandTable;\r
524 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;\r
525 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
526 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;\r
527\r
528}\r
529\r
530/**\r
531 Buid a command FIS.\r
532\r
533 @param CmdFis A pointer to the EFI_AHCI_COMMAND_FIS Data structure.\r
534 @param AtaCommandBlock A pointer to the AhciBuildCommandFis Data structure.\r
535\r
536**/\r
537VOID\r
538EFIAPI\r
539AhciBuildCommandFis (\r
540 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,\r
541 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock\r
542 )\r
543{\r
544 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
545\r
546 CmdFis->AhciCFisType = EFI_AHCI_FIS_REGISTER_H2D;\r
547 //\r
548 // Indicator it's a command\r
549 //\r
550 CmdFis->AhciCFisCmdInd = 0x1;\r
551 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;\r
552\r
553 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;\r
554 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;\r
555\r
556 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;\r
557 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;\r
558\r
559 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;\r
560 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;\r
561\r
562 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;\r
563 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;\r
564\r
565 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;\r
566 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
567\r
568 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
569}\r
570\r
571/**\r
572 Start a PIO Data transfer on specific port.\r
573\r
574 @param AhciContext The pointer to the AHCI_CONTEXT.\r
575 @param Port The number of port.\r
576 @param PortMultiplier The timeout Value of stop.\r
577 @param AtapiCommand The atapi command will be used for the transfer.\r
578 @param AtapiCommandLength The Length of the atapi command.\r
579 @param Read The transfer direction.\r
580 @param AtaCommandBlock The EFI_ATA_COMMAND_BLOCK Data.\r
581 @param AtaStatusBlock The EFI_ATA_STATUS_BLOCK Data.\r
582 @param MemoryAddr The pointer to the Data Buffer.\r
583 @param DataCount The Data count to be transferred.\r
584 @param Timeout The timeout Value of non Data transfer.\r
585\r
586 @retval EFI_DEVICE_ERROR The PIO Data transfer abort with error occurs.\r
587 @retval EFI_TIMEOUT The operation is time out.\r
588 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
589 @retval EFI_SUCCESS The PIO Data transfer executes successfully.\r
590\r
591**/\r
592EFI_STATUS\r
593EFIAPI\r
594AhciPioTransfer (\r
595 IN AHCI_CONTEXT *AhciContext,\r
596 IN UINT8 Port,\r
597 IN UINT8 PortMultiplier,\r
598 IN EFI_AHCI_ATAPI_COMMAND *AtapiCommand OPTIONAL,\r
599 IN UINT8 AtapiCommandLength,\r
600 IN BOOLEAN Read,\r
601 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
602 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
603 IN OUT VOID *MemoryAddr,\r
604 IN UINT32 DataCount,\r
605 IN UINT64 Timeout\r
606 )\r
607{\r
608 EFI_STATUS Status;\r
609 EFI_AHCI_REGISTERS *AhciRegisters;\r
610 UINT32 AhciBar;\r
611 UINT32 FisBaseAddr;\r
612 UINT32 Offset;\r
613 UINT32 Delay;\r
614 EFI_AHCI_COMMAND_FIS CFis;\r
615 EFI_AHCI_COMMAND_LIST CmdList;\r
616 UINT32 PortTfd;\r
617 UINT32 PrdCount;\r
618 UINT32 OldRfisLo;\r
619 UINT32 OldRfisHi;\r
620 UINT32 OldCmdListLo;\r
621 UINT32 OldCmdListHi;\r
622\r
623 AhciRegisters = &AhciContext->AhciRegisters;\r
624 AhciBar = AhciContext->AhciBar;\r
625\r
626 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
627 OldRfisLo = AhciReadReg (AhciBar, Offset);\r
628 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
629 OldRfisHi = AhciReadReg (AhciBar, Offset);\r
630 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
631 AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);\r
632 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
633 AhciWriteReg (AhciBar, Offset, 0);\r
634\r
635 //\r
636 // Single task envrionment, we only use one command table for all port\r
637 //\r
638 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
639 OldCmdListLo = AhciReadReg (AhciBar, Offset);\r
640 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
641 OldCmdListHi = AhciReadReg (AhciBar, Offset);\r
642 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
643 AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);\r
644 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
645 AhciWriteReg (AhciBar, Offset, 0);\r
646\r
647 //\r
648 // Package read needed\r
649 //\r
650 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
651\r
652 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
653\r
654 CmdList.AhciCmdCfl = EFI_AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
655 CmdList.AhciCmdW = Read ? 0 : 1;\r
656\r
657 AhciBuildCommand (\r
658 AhciContext,\r
659 Port,\r
660 PortMultiplier,\r
661 &CFis,\r
662 &CmdList,\r
663 AtapiCommand,\r
664 AtapiCommandLength,\r
665 0,\r
666 MemoryAddr,\r
667 DataCount\r
668 );\r
669\r
670 Status = AhciStartCommand (\r
671 AhciBar,\r
672 Port,\r
673 0,\r
674 Timeout\r
675 );\r
676 if (EFI_ERROR (Status)) {\r
677 goto Exit;\r
678 }\r
679\r
680 //\r
681 // Checking the status and wait the driver sending Data\r
682 //\r
683 FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis;\r
684 if (Read && (AtapiCommand == 0)) {\r
685 //\r
686 // Wait device sends the PIO setup fis before Data transfer\r
687 //\r
688 Status = EFI_TIMEOUT;\r
689 Delay = (UINT32) (DivU64x32 (Timeout, 1000) + 1);\r
690 do {\r
691 Offset = FisBaseAddr + EFI_AHCI_PIO_FIS_OFFSET;\r
692\r
693 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_PIO_SETUP, 0);\r
694 if (!EFI_ERROR (Status)) {\r
695 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
696 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
697 //\r
698 // PxTFD will be updated if there is a D2H or SetupFIS received.\r
699 // For PIO IN transfer, D2H means a device error. Therefore we only need to check the TFD after receiving a SetupFIS.\r
700 //\r
701 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
702 Status = EFI_DEVICE_ERROR;\r
703 break;\r
704 }\r
705\r
706 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
707 if (PrdCount == DataCount) {\r
708 break;\r
709 }\r
710 }\r
711\r
712 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
713 Status = AhciCheckMemSet (Offset, EFI_AHCI_FIS_TYPE_MASK, EFI_AHCI_FIS_REGISTER_D2H, 0);\r
714 if (!EFI_ERROR (Status)) {\r
715 Status = EFI_DEVICE_ERROR;\r
716 break;\r
717 }\r
718\r
719 //\r
720 // Stall for 100 microseconds.\r
721 //\r
722 MicroSecondDelay(100);\r
723\r
724 Delay--;\r
725 } while (Delay > 0);\r
726 } else {\r
727 //\r
728 // Wait for D2H Fis is received\r
729 //\r
730 Offset = FisBaseAddr + EFI_AHCI_D2H_FIS_OFFSET;\r
731 Status = AhciWaitMemSet (\r
732 Offset,\r
733 EFI_AHCI_FIS_TYPE_MASK,\r
734 EFI_AHCI_FIS_REGISTER_D2H,\r
735 Timeout\r
736 );\r
737\r
738 if (EFI_ERROR (Status)) {\r
739 goto Exit;\r
740 }\r
741\r
742 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
743 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
744 if ((PortTfd & EFI_AHCI_PORT_TFD_ERR) != 0) {\r
745 Status = EFI_DEVICE_ERROR;\r
746 }\r
747 }\r
748\r
749Exit:\r
750 AhciStopCommand (\r
751 AhciBar,\r
752 Port,\r
753 Timeout\r
754 );\r
755\r
756 AhciDisableFisReceive (\r
757 AhciBar,\r
758 Port,\r
759 Timeout\r
760 );\r
761\r
762 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
763 AhciWriteReg (AhciBar, Offset, OldRfisLo);\r
764 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FBU;\r
765 AhciWriteReg (AhciBar, Offset, OldRfisHi);\r
766\r
767 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
768 AhciWriteReg (AhciBar, Offset, OldCmdListLo);\r
769 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLBU;\r
770 AhciWriteReg (AhciBar, Offset, OldCmdListHi);\r
771\r
772 return Status;\r
773}\r
774\r
775/**\r
776 Stop command running for giving port\r
777\r
778 @param AhciBar AHCI bar address.\r
779 @param Port The number of port.\r
780 @param Timeout The timeout Value of stop.\r
781\r
782 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
783 @retval EFI_TIMEOUT The operation is time out.\r
784 @retval EFI_SUCCESS The command stop successfully.\r
785\r
786**/\r
787EFI_STATUS\r
788EFIAPI\r
789AhciStopCommand (\r
790 IN UINT32 AhciBar,\r
791 IN UINT8 Port,\r
792 IN UINT64 Timeout\r
793 )\r
794{\r
795 UINT32 Offset;\r
796 UINT32 Data;\r
797\r
798 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
799 Data = AhciReadReg (AhciBar, Offset);\r
800\r
801 if ((Data & (EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_CR)) == 0) {\r
802 return EFI_SUCCESS;\r
803 }\r
804\r
805 if ((Data & EFI_AHCI_PORT_CMD_ST) != 0) {\r
806 AhciAndReg (AhciBar, Offset, (UINT32)~(EFI_AHCI_PORT_CMD_ST));\r
807 }\r
808\r
809 return AhciWaitMmioSet (\r
810 AhciBar,\r
811 Offset,\r
812 EFI_AHCI_PORT_CMD_CR,\r
813 0,\r
814 Timeout\r
815 );\r
816}\r
817\r
818/**\r
819 Start command for give slot on specific port.\r
820\r
821 @param AhciBar AHCI bar address.\r
822 @param Port The number of port.\r
823 @param CommandSlot The number of CommandSlot.\r
824 @param Timeout The timeout Value of start.\r
825\r
826 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
827 @retval EFI_TIMEOUT The operation is time out.\r
828 @retval EFI_SUCCESS The command start successfully.\r
829\r
830**/\r
831EFI_STATUS\r
832EFIAPI\r
833AhciStartCommand (\r
834 IN UINT32 AhciBar,\r
835 IN UINT8 Port,\r
836 IN UINT8 CommandSlot,\r
837 IN UINT64 Timeout\r
838 )\r
839{\r
840 UINT32 CmdSlotBit;\r
841 EFI_STATUS Status;\r
842 UINT32 PortStatus;\r
843 UINT32 StartCmd;\r
844 UINT32 PortTfd;\r
845 UINT32 Offset;\r
846 UINT32 Capability;\r
847\r
848 //\r
849 // Collect AHCI controller information\r
850 //\r
851 Capability = AhciReadReg(AhciBar, EFI_AHCI_CAPABILITY_OFFSET);\r
852\r
853 CmdSlotBit = (UINT32) (1 << CommandSlot);\r
854\r
855 AhciClearPortStatus (\r
856 AhciBar,\r
857 Port\r
858 );\r
859\r
860 Status = AhciEnableFisReceive (\r
861 AhciBar,\r
862 Port,\r
863 Timeout\r
864 );\r
865\r
866 if (EFI_ERROR (Status)) {\r
867 return Status;\r
868 }\r
869\r
870 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
871 PortStatus = AhciReadReg (AhciBar, Offset);\r
872\r
873 StartCmd = 0;\r
874 if ((PortStatus & EFI_AHCI_PORT_CMD_ALPE) != 0) {\r
875 StartCmd = AhciReadReg (AhciBar, Offset);\r
876 StartCmd &= ~EFI_AHCI_PORT_CMD_ICC_MASK;\r
877 StartCmd |= EFI_AHCI_PORT_CMD_ACTIVE;\r
878 }\r
879\r
880 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
881 PortTfd = AhciReadReg (AhciBar, Offset);\r
882\r
883 if ((PortTfd & (EFI_AHCI_PORT_TFD_BSY | EFI_AHCI_PORT_TFD_DRQ)) != 0) {\r
884 if ((Capability & BIT24) != 0) {\r
885 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
886 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_COL);\r
887\r
888 AhciWaitMmioSet (\r
889 AhciBar,\r
890 Offset,\r
891 EFI_AHCI_PORT_CMD_COL,\r
892 0,\r
893 Timeout\r
894 );\r
895 }\r
896 }\r
897\r
898 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
899 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_ST | StartCmd);\r
900\r
901 //\r
902 // Setting the command\r
903 //\r
904 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SACT;\r
905 AhciAndReg (AhciBar, Offset, 0);\r
906 AhciOrReg (AhciBar, Offset, CmdSlotBit);\r
907\r
908 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CI;\r
909 AhciAndReg (AhciBar, Offset, 0);\r
910 AhciOrReg (AhciBar, Offset, CmdSlotBit);\r
911 return EFI_SUCCESS;\r
912}\r
913\r
914\r
915/**\r
916 Do AHCI HBA reset.\r
917\r
918 @param[in] AhciBar AHCI bar address.\r
919 @param[in] Timeout The timeout Value of reset.\r
920\r
921 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
922 @retval EFI_TIMEOUT The reset operation is time out.\r
923 @retval EFI_SUCCESS AHCI controller is reset successfully.\r
924\r
925**/\r
926EFI_STATUS\r
927EFIAPI\r
928AhciReset (\r
929 IN UINT32 AhciBar,\r
930 IN UINT64 Timeout\r
931 )\r
932{\r
933 UINT32 Delay;\r
934 UINT32 Value;\r
935 UINT32 Capability;\r
936\r
937 //\r
938 // Collect AHCI controller information\r
939 //\r
940 Capability = AhciReadReg (AhciBar, EFI_AHCI_CAPABILITY_OFFSET);\r
941\r
942 //\r
943 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
944 //\r
945 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\r
946 AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
947 }\r
948\r
949 AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_RESET);\r
950\r
951 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
952\r
953 do {\r
954 Value = AhciReadReg(AhciBar, EFI_AHCI_GHC_OFFSET);\r
955 if ((Value & EFI_AHCI_GHC_RESET) == 0) {\r
956 return EFI_SUCCESS;\r
957 }\r
958\r
959 //\r
960 // Stall for 100 microseconds.\r
961 //\r
962 MicroSecondDelay(100);\r
963\r
964 Delay--;\r
965 } while (Delay > 0);\r
966\r
967 return EFI_TIMEOUT;\r
968\r
969\r
970}\r
971\r
112e584b
SZ
972/**\r
973 Allocate transfer-related data struct which is used at AHCI mode.\r
974\r
975 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.\r
976\r
977 @retval EFI_OUT_OF_RESOURCE No enough resource.\r
978 @retval EFI_SUCCESS Successful to allocate resource.\r
979\r
980**/\r
981EFI_STATUS\r
982EFIAPI\r
983AhciAllocateResource (\r
984 IN OUT AHCI_CONTEXT *AhciContext\r
985 )\r
986{\r
987 EFI_STATUS Status;\r
988 EFI_AHCI_REGISTERS *AhciRegisters;\r
989 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
990 VOID *Base;\r
991 VOID *Mapping;\r
992\r
993 AhciRegisters = &AhciContext->AhciRegisters;\r
994\r
995 //\r
996 // Allocate resources required by AHCI host controller.\r
997 //\r
998 Status = IoMmuAllocateBuffer (\r
999 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1000 &Base,\r
1001 &DeviceAddress,\r
1002 &Mapping\r
1003 );\r
1004 if (EFI_ERROR (Status)) {\r
1005 return EFI_OUT_OF_RESOURCES;\r
1006 }\r
1007 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1008 AhciRegisters->AhciRFisMapping = Mapping;\r
1009 AhciRegisters->AhciRFis = Base;\r
1010 ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)));\r
1011\r
1012 Status = IoMmuAllocateBuffer (\r
1013 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),\r
1014 &Base,\r
1015 &DeviceAddress,\r
1016 &Mapping\r
1017 );\r
1018 if (EFI_ERROR (Status)) {\r
1019 IoMmuFreeBuffer (\r
1020 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1021 AhciRegisters->AhciRFis,\r
1022 AhciRegisters->AhciRFisMapping\r
1023 );\r
1024 AhciRegisters->AhciRFis = NULL;\r
1025 return EFI_OUT_OF_RESOURCES;\r
1026 }\r
1027 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1028 AhciRegisters->AhciCmdListMapping = Mapping;\r
1029 AhciRegisters->AhciCmdList = Base;\r
1030 ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)));\r
1031\r
1032 Status = IoMmuAllocateBuffer (\r
1033 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),\r
1034 &Base,\r
1035 &DeviceAddress,\r
1036 &Mapping\r
1037 );\r
1038 if (EFI_ERROR (Status)) {\r
1039 IoMmuFreeBuffer (\r
1040 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),\r
1041 AhciRegisters->AhciCmdList,\r
1042 AhciRegisters->AhciCmdListMapping\r
1043 );\r
1044 AhciRegisters->AhciCmdList = NULL;\r
1045 IoMmuFreeBuffer (\r
1046 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1047 AhciRegisters->AhciRFis,\r
1048 AhciRegisters->AhciRFisMapping\r
1049 );\r
1050 AhciRegisters->AhciRFis = NULL;\r
1051 return EFI_OUT_OF_RESOURCES;\r
1052 }\r
1053 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1054 AhciRegisters->AhciCommandTableMapping = Mapping;\r
1055 AhciRegisters->AhciCommandTable = Base;\r
1056 ZeroMem (AhciRegisters->AhciCommandTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)));\r
1057\r
1058 //\r
1059 // Allocate resources for data transfer.\r
1060 //\r
1061 Status = IoMmuAllocateBuffer (\r
1062 EFI_SIZE_TO_PAGES (HDD_PAYLOAD),\r
1063 &Base,\r
1064 &DeviceAddress,\r
1065 &Mapping\r
1066 );\r
1067 if (EFI_ERROR (Status)) {\r
1068 IoMmuFreeBuffer (\r
1069 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1070 AhciRegisters->AhciCommandTable,\r
1071 AhciRegisters->AhciCommandTableMapping\r
1072 );\r
1073 AhciRegisters->AhciCommandTable = NULL;\r
1074 IoMmuFreeBuffer (\r
1075 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),\r
1076 AhciRegisters->AhciCmdList,\r
1077 AhciRegisters->AhciCmdListMapping\r
1078 );\r
1079 AhciRegisters->AhciCmdList = NULL;\r
1080 IoMmuFreeBuffer (\r
1081 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1082 AhciRegisters->AhciRFis,\r
1083 AhciRegisters->AhciRFisMapping\r
1084 );\r
1085 AhciRegisters->AhciRFis = NULL;\r
1086 return EFI_OUT_OF_RESOURCES;\r
1087 }\r
1088 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1089 AhciContext->BufferMapping = Mapping;\r
1090 AhciContext->Buffer = Base;\r
1091 ZeroMem (AhciContext->Buffer, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (HDD_PAYLOAD));\r
1092\r
1093 DEBUG ((\r
1094 DEBUG_INFO,\r
1095 "%a() AhciContext 0x%x 0x%x 0x%x 0x%x\n",\r
1096 __FUNCTION__,\r
1097 AhciContext->Buffer,\r
1098 AhciRegisters->AhciRFis,\r
1099 AhciRegisters->AhciCmdList,\r
1100 AhciRegisters->AhciCommandTable\r
1101 ));\r
1102 return EFI_SUCCESS;\r
1103}\r
1104\r
1105/**\r
1106 Free allocated transfer-related data struct which is used at AHCI mode.\r
1107\r
1108 @param[in, out] AhciContext The pointer to the AHCI_CONTEXT.\r
1109\r
1110**/\r
1111VOID\r
1112EFIAPI\r
1113AhciFreeResource (\r
1114 IN OUT AHCI_CONTEXT *AhciContext\r
1115 )\r
1116{\r
1117 EFI_AHCI_REGISTERS *AhciRegisters;\r
1118\r
1119 AhciRegisters = &AhciContext->AhciRegisters;\r
1120\r
1121 if (AhciContext->Buffer != NULL) {\r
1122 IoMmuFreeBuffer (\r
1123 EFI_SIZE_TO_PAGES (HDD_PAYLOAD),\r
1124 AhciContext->Buffer,\r
1125 AhciContext->BufferMapping\r
1126 );\r
1127 AhciContext->Buffer = NULL;\r
1128 }\r
1129\r
1130 if (AhciRegisters->AhciCommandTable != NULL) {\r
1131 IoMmuFreeBuffer (\r
1132 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_TABLE)),\r
1133 AhciRegisters->AhciCommandTable,\r
1134 AhciRegisters->AhciCommandTableMapping\r
1135 );\r
1136 AhciRegisters->AhciCommandTable = NULL;\r
1137 }\r
1138\r
1139 if (AhciRegisters->AhciCmdList != NULL) {\r
1140 IoMmuFreeBuffer (\r
1141 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_COMMAND_LIST)),\r
1142 AhciRegisters->AhciCmdList,\r
1143 AhciRegisters->AhciCmdListMapping\r
1144 );\r
1145 AhciRegisters->AhciCmdList = NULL;\r
1146 }\r
1147\r
1148 if (AhciRegisters->AhciRFis != NULL) {\r
1149 IoMmuFreeBuffer (\r
1150 EFI_SIZE_TO_PAGES (sizeof (EFI_AHCI_RECEIVED_FIS)),\r
1151 AhciRegisters->AhciRFis,\r
1152 AhciRegisters->AhciRFisMapping\r
1153 );\r
1154 AhciRegisters->AhciRFis = NULL;\r
1155 }\r
1156}\r
1157\r
1158/**\r
1159 Initialize ATA host controller at AHCI mode.\r
1160\r
1161 The function is designed to initialize ATA host controller.\r
1162\r
1163 @param[in] AhciContext The pointer to the AHCI_CONTEXT.\r
1164 @param[in] Port The port number to do initialization.\r
1165\r
1166**/\r
1167EFI_STATUS\r
1168EFIAPI\r
1169AhciModeInitialize (\r
1170 IN AHCI_CONTEXT *AhciContext,\r
1171 IN UINT8 Port\r
1172 )\r
1173{\r
1174 EFI_STATUS Status;\r
1175 EFI_AHCI_REGISTERS *AhciRegisters;\r
1176 UINT32 AhciBar;\r
1177 UINT32 Capability;\r
1178 UINT32 Offset;\r
1179 UINT32 Data;\r
1180 UINT32 PhyDetectDelay;\r
1181\r
1182 AhciRegisters = &AhciContext->AhciRegisters;\r
1183 AhciBar = AhciContext->AhciBar;\r
1184\r
1185 Status = AhciReset (AhciBar, ATA_TIMEOUT);\r
1186 if (EFI_ERROR (Status)) {\r
1187 return Status;\r
1188 }\r
1189\r
1190 //\r
1191 // Collect AHCI controller information\r
1192 //\r
1193 Capability = AhciReadReg (AhciBar, EFI_AHCI_CAPABILITY_OFFSET);\r
1194\r
1195 //\r
1196 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
1197 //\r
1198 if ((Capability & EFI_AHCI_CAP_SAM) == 0) {\r
1199 AhciOrReg (AhciBar, EFI_AHCI_GHC_OFFSET, EFI_AHCI_GHC_ENABLE);\r
1200 }\r
1201\r
1202 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_FB;\r
1203 AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciRFis);\r
1204\r
1205 //\r
1206 // Single task envrionment, we only use one command table for all port\r
1207 //\r
1208 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CLB;\r
1209 AhciWriteReg (AhciBar, Offset, (UINT32)(UINTN)AhciRegisters->AhciCmdList);\r
1210\r
1211 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_CMD;\r
1212 Data = AhciReadReg (AhciBar, Offset);\r
1213 if ((Data & EFI_AHCI_PORT_CMD_CPD) != 0) {\r
1214 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_POD);\r
1215 }\r
1216\r
1217 if ((Capability & BIT27) != 0) {\r
1218 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_CMD_SUD);\r
1219 }\r
1220\r
1221 //\r
1222 // Disable aggressive power management.\r
1223 //\r
1224 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SCTL;\r
1225 AhciOrReg (AhciBar, Offset, EFI_AHCI_PORT_SCTL_IPM_INIT);\r
1226 //\r
1227 // Disable the reporting of the corresponding interrupt to system software.\r
1228 //\r
1229 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_IE;\r
1230 AhciAndReg (AhciBar, Offset, 0);\r
1231\r
1232 Status = AhciEnableFisReceive (\r
1233 AhciBar,\r
1234 Port,\r
1235 5000000\r
1236 );\r
1237 ASSERT_EFI_ERROR (Status);\r
1238 if (EFI_ERROR (Status)) {\r
1239 return Status;\r
1240 }\r
1241\r
1242 //\r
1243 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
1244 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
1245 //\r
1246 PhyDetectDelay = 16 * 1000;\r
1247 do {\r
1248 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SERR;\r
1249 if (AhciReadReg(AhciBar, Offset) != 0) {\r
1250 AhciWriteReg (AhciBar, Offset, AhciReadReg(AhciBar, Offset));\r
1251 }\r
1252 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_TFD;\r
1253\r
1254 Data = AhciReadReg (AhciBar, Offset) & EFI_AHCI_PORT_TFD_MASK;\r
1255 if (Data == 0) {\r
1256 break;\r
1257 }\r
1258\r
1259 MicroSecondDelay (1000);\r
1260 PhyDetectDelay--;\r
1261 } while (PhyDetectDelay > 0);\r
1262\r
1263 if (PhyDetectDelay == 0) {\r
1264 return EFI_NOT_FOUND;\r
1265 }\r
1266\r
1267 Offset = EFI_AHCI_PORT_START + Port * EFI_AHCI_PORT_REG_WIDTH + EFI_AHCI_PORT_SIG;\r
1268 Status = AhciWaitMmioSet (\r
1269 AhciBar,\r
1270 Offset,\r
1271 0x0000FFFF,\r
1272 0x00000101,\r
1273 160000000\r
1274 );\r
1275\r
1276 if (EFI_ERROR (Status)) {\r
1277 return Status;\r
1278 }\r
1279\r
1280 return Status;\r
1281}\r
1282\r