]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Ata/AhciPei/AhciMode.c
MdeModulePkg/AhciPei: Add PEI BlockIO support
[mirror_edk2.git] / MdeModulePkg / Bus / Ata / AhciPei / AhciMode.c
CommitLineData
87bc3f19
HW
1/** @file\r
2 The AhciPei driver is used to manage ATA hard disk device working under AHCI\r
3 mode at PEI phase.\r
4\r
5 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
6\r
9d510e61 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
87bc3f19
HW
8\r
9**/\r
10\r
11#include "AhciPei.h"\r
12\r
13#define ATA_CMD_TRUST_NON_DATA 0x5B\r
14#define ATA_CMD_TRUST_RECEIVE 0x5C\r
15#define ATA_CMD_TRUST_SEND 0x5E\r
16\r
17//\r
18// Look up table (IsWrite) for EFI_ATA_PASS_THRU_CMD_PROTOCOL\r
19//\r
20EFI_ATA_PASS_THRU_CMD_PROTOCOL mAtaPassThruCmdProtocols[2] = {\r
21 EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN,\r
22 EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT\r
23};\r
24\r
25//\r
26// Look up table (Lba48Bit, IsIsWrite) for ATA_CMD\r
27//\r
28UINT8 mAtaCommands[2][2] = {\r
29 {\r
30 ATA_CMD_READ_SECTORS, // 28-bit LBA; PIO read\r
31 ATA_CMD_WRITE_SECTORS // 28-bit LBA; PIO write\r
32 },\r
33 {\r
34 ATA_CMD_READ_SECTORS_EXT, // 48-bit LBA; PIO read\r
35 ATA_CMD_WRITE_SECTORS_EXT // 48-bit LBA; PIO write\r
36 }\r
37};\r
38\r
39//\r
40// Look up table (IsTrustSend) for ATA_CMD\r
41//\r
42UINT8 mAtaTrustCommands[2] = {\r
43 ATA_CMD_TRUST_RECEIVE, // PIO read\r
44 ATA_CMD_TRUST_SEND // PIO write\r
45};\r
46\r
47//\r
48// Look up table (Lba48Bit) for maximum transfer block number\r
49//\r
50#define MAX_28BIT_TRANSFER_BLOCK_NUM 0x100\r
04c7a5fe
HW
51//\r
52// Due to limited resource for VTd PEI DMA buffer on platforms, the driver\r
53// limits the maximum transfer block number for 48-bit addressing.\r
54// Here, setting to 0x800 means that for device with 512-byte block size, the\r
55// maximum buffer for DMA mapping will be 1M bytes in size.\r
56//\r
57#define MAX_48BIT_TRANSFER_BLOCK_NUM 0x800\r
87bc3f19
HW
58\r
59UINT32 mMaxTransferBlockNumber[2] = {\r
60 MAX_28BIT_TRANSFER_BLOCK_NUM,\r
61 MAX_48BIT_TRANSFER_BLOCK_NUM\r
62};\r
63\r
64//\r
65// The maximum total sectors count in 28 bit addressing mode\r
66//\r
67#define MAX_28BIT_ADDRESSING_CAPACITY 0xfffffff\r
68\r
69\r
70/**\r
71 Read AHCI Operation register.\r
72\r
73 @param[in] AhciBar AHCI bar address.\r
74 @param[in] Offset The operation register offset.\r
75\r
76 @return The register content read.\r
77\r
78**/\r
79UINT32\r
80AhciReadReg (\r
81 IN UINTN AhciBar,\r
82 IN UINT32 Offset\r
83 )\r
84{\r
85 UINT32 Data;\r
86\r
87 Data = 0;\r
88 Data = MmioRead32 (AhciBar + Offset);\r
89\r
90 return Data;\r
91}\r
92\r
93/**\r
94 Write AHCI Operation register.\r
95\r
96 @param[in] AhciBar AHCI bar address.\r
97 @param[in] Offset The operation register offset.\r
98 @param[in] Data The Data used to write down.\r
99\r
100**/\r
101VOID\r
102AhciWriteReg (\r
103 IN UINTN AhciBar,\r
104 IN UINT32 Offset,\r
105 IN UINT32 Data\r
106 )\r
107{\r
108 MmioWrite32 (AhciBar + Offset, Data);\r
109}\r
110\r
111/**\r
112 Do AND operation with the value of AHCI Operation register.\r
113\r
114 @param[in] AhciBar AHCI bar address.\r
115 @param[in] Offset The operation register offset.\r
116 @param[in] AndData The data used to do AND operation.\r
117\r
118**/\r
119VOID\r
120AhciAndReg (\r
121 IN UINTN AhciBar,\r
122 IN UINT32 Offset,\r
123 IN UINT32 AndData\r
124 )\r
125{\r
126 UINT32 Data;\r
127\r
128 Data = AhciReadReg (AhciBar, Offset);\r
129 Data &= AndData;\r
130\r
131 AhciWriteReg (AhciBar, Offset, Data);\r
132}\r
133\r
134/**\r
135 Do OR operation with the Value of AHCI Operation register.\r
136\r
137 @param[in] AhciBar AHCI bar address.\r
138 @param[in] Offset The operation register offset.\r
139 @param[in] OrData The Data used to do OR operation.\r
140\r
141**/\r
142VOID\r
143AhciOrReg (\r
144 IN UINTN AhciBar,\r
145 IN UINT32 Offset,\r
146 IN UINT32 OrData\r
147 )\r
148{\r
149 UINT32 Data;\r
150\r
151 Data = AhciReadReg (AhciBar, Offset);\r
152 Data |= OrData;\r
153\r
154 AhciWriteReg (AhciBar, Offset, Data);\r
155}\r
156\r
157/**\r
158 Wait for memory set to the test Value.\r
159\r
160 @param[in] AhciBar AHCI bar address.\r
161 @param[in] Offset The memory offset to test.\r
162 @param[in] MaskValue The mask Value of memory.\r
163 @param[in] TestValue The test Value of memory.\r
164 @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
165\r
166 @retval EFI_DEVICE_ERROR The memory is not set.\r
167 @retval EFI_TIMEOUT The memory setting is time out.\r
168 @retval EFI_SUCCESS The memory is correct set.\r
169\r
170**/\r
171EFI_STATUS\r
172EFIAPI\r
173AhciWaitMmioSet (\r
174 IN UINTN AhciBar,\r
175 IN UINT32 Offset,\r
176 IN UINT32 MaskValue,\r
177 IN UINT32 TestValue,\r
178 IN UINT64 Timeout\r
179 )\r
180{\r
181 UINT32 Value;\r
182 UINT32 Delay;\r
183\r
184 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
185\r
186 do {\r
187 Value = AhciReadReg (AhciBar, Offset) & MaskValue;\r
188\r
189 if (Value == TestValue) {\r
190 return EFI_SUCCESS;\r
191 }\r
192\r
193 //\r
194 // Stall for 100 microseconds.\r
195 //\r
196 MicroSecondDelay (100);\r
197\r
198 Delay--;\r
199\r
200 } while (Delay > 0);\r
201\r
202 return EFI_TIMEOUT;\r
203}\r
204\r
205/**\r
206 Check the memory status to the test value.\r
207\r
208 @param[in] Address The memory address to test.\r
209 @param[in] MaskValue The mask value of memory.\r
210 @param[in] TestValue The test value of memory.\r
211\r
212 @retval EFI_NOT_READY The memory is not set.\r
213 @retval EFI_SUCCESS The memory is correct set.\r
214\r
215**/\r
216EFI_STATUS\r
217AhciCheckMemSet (\r
218 IN UINTN Address,\r
219 IN UINT32 MaskValue,\r
220 IN UINT32 TestValue\r
221 )\r
222{\r
223 UINT32 Value;\r
224\r
225 Value = *(volatile UINT32 *) Address;\r
226 Value &= MaskValue;\r
227\r
228 if (Value == TestValue) {\r
229 return EFI_SUCCESS;\r
230 } else {\r
231 return EFI_NOT_READY;\r
232 }\r
233}\r
234\r
235/**\r
236 Wait for the value of the specified system memory set to the test value.\r
237\r
238 @param[in] Address The system memory address to test.\r
239 @param[in] MaskValue The mask value of memory.\r
240 @param[in] TestValue The test value of memory.\r
241 @param[in] Timeout The timeout, in 100ns units, for wait memory set.\r
242\r
243 @retval EFI_TIMEOUT The system memory setting is time out.\r
244 @retval EFI_SUCCESS The system memory is correct set.\r
245\r
246**/\r
247EFI_STATUS\r
248AhciWaitMemSet (\r
249 IN EFI_PHYSICAL_ADDRESS Address,\r
250 IN UINT32 MaskValue,\r
251 IN UINT32 TestValue,\r
252 IN UINT64 Timeout\r
253 )\r
254{\r
255 UINT32 Value;\r
256 UINT64 Delay;\r
257 BOOLEAN InfiniteWait;\r
258\r
259 if (Timeout == 0) {\r
260 InfiniteWait = TRUE;\r
261 } else {\r
262 InfiniteWait = FALSE;\r
263 }\r
264\r
265 Delay = DivU64x32 (Timeout, 1000) + 1;\r
266\r
267 do {\r
268 //\r
269 // Access sytem memory to see if the value is the tested one.\r
270 //\r
271 // The system memory pointed by Address will be updated by the\r
272 // SATA Host Controller, "volatile" is introduced to prevent\r
273 // compiler from optimizing the access to the memory address\r
274 // to only read once.\r
275 //\r
276 Value = *(volatile UINT32 *) (UINTN) Address;\r
277 Value &= MaskValue;\r
278\r
279 if (Value == TestValue) {\r
280 return EFI_SUCCESS;\r
281 }\r
282\r
283 //\r
284 // Stall for 100 microseconds.\r
285 //\r
286 MicroSecondDelay (100);\r
287\r
288 Delay--;\r
289\r
290 } while (InfiniteWait || (Delay > 0));\r
291\r
292 return EFI_TIMEOUT;\r
293}\r
294\r
295/**\r
296\r
297 Clear the port interrupt and error status. It will also clear HBA interrupt\r
298 status.\r
299\r
300 @param[in] AhciBar AHCI bar address.\r
301 @param[in] Port The number of port.\r
302\r
303**/\r
304VOID\r
305AhciClearPortStatus (\r
306 IN UINTN AhciBar,\r
307 IN UINT8 Port\r
308 )\r
309{\r
310 UINT32 Offset;\r
311\r
312 //\r
313 // Clear any error status\r
314 //\r
315 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
316 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
317\r
318 //\r
319 // Clear any port interrupt status\r
320 //\r
321 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IS;\r
322 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
323\r
324 //\r
325 // Clear any HBA interrupt status\r
326 //\r
327 AhciWriteReg (AhciBar, AHCI_IS_OFFSET, AhciReadReg (AhciBar, AHCI_IS_OFFSET));\r
328}\r
329\r
330/**\r
331 Enable the FIS running for giving port.\r
332\r
333 @param[in] AhciBar AHCI bar address.\r
334 @param[in] Port The number of port.\r
335 @param[in] Timeout The timeout, in 100ns units, to enabling FIS.\r
336\r
337 @retval EFI_DEVICE_ERROR The FIS enable setting fails.\r
338 @retval EFI_TIMEOUT The FIS enable setting is time out.\r
339 @retval EFI_SUCCESS The FIS enable successfully.\r
340\r
341**/\r
342EFI_STATUS\r
343AhciEnableFisReceive (\r
344 IN UINTN AhciBar,\r
345 IN UINT8 Port,\r
346 IN UINT64 Timeout\r
347 )\r
348{\r
349 UINT32 Offset;\r
350\r
351 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
352 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
353\r
354 return EFI_SUCCESS;\r
355}\r
356\r
357/**\r
358 Disable the FIS running for giving port.\r
359\r
360 @param[in] AhciBar AHCI bar address.\r
361 @param[in] Port The number of port.\r
362 @param[in] Timeout The timeout value of disabling FIS, uses 100ns as a unit.\r
363\r
364 @retval EFI_DEVICE_ERROR The FIS disable setting fails.\r
365 @retval EFI_TIMEOUT The FIS disable setting is time out.\r
366 @retval EFI_UNSUPPORTED The port is in running state.\r
367 @retval EFI_SUCCESS The FIS disable successfully.\r
368\r
369**/\r
370EFI_STATUS\r
371AhciDisableFisReceive (\r
372 IN UINTN AhciBar,\r
373 IN UINT8 Port,\r
374 IN UINT64 Timeout\r
375 )\r
376{\r
377 UINT32 Offset;\r
378 UINT32 Data;\r
379\r
380 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
381 Data = AhciReadReg (AhciBar, Offset);\r
382\r
383 //\r
384 // Before disabling Fis receive, the DMA engine of the port should NOT be in\r
385 // running status.\r
386 //\r
387 if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) != 0) {\r
388 return EFI_UNSUPPORTED;\r
389 }\r
390\r
391 //\r
392 // Check if the Fis receive DMA engine for the port is running.\r
393 //\r
394 if ((Data & AHCI_PORT_CMD_FR) != AHCI_PORT_CMD_FR) {\r
395 return EFI_SUCCESS;\r
396 }\r
397\r
398 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_FRE));\r
399\r
400 return AhciWaitMmioSet (\r
401 AhciBar,\r
402 Offset,\r
403 AHCI_PORT_CMD_FR,\r
404 0,\r
405 Timeout\r
406 );\r
407}\r
408\r
409/**\r
410 Build the command list, command table and prepare the fis receiver.\r
411\r
412 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
413 @param[in] Port The number of port.\r
414 @param[in] PortMultiplier The number of port multiplier.\r
415 @param[in] FisIndex The offset index of the FIS base address.\r
416 @param[in] CommandFis The control fis will be used for the transfer.\r
417 @param[in] CommandList The command list will be used for the transfer.\r
418 @param[in] CommandSlotNumber The command slot will be used for the transfer.\r
419 @param[in,out] DataPhysicalAddr The pointer to the data buffer pci bus master\r
420 address.\r
421 @param[in] DataLength The data count to be transferred.\r
422\r
423**/\r
424VOID\r
425AhciBuildCommand (\r
426 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
427 IN UINT8 Port,\r
428 IN UINT8 PortMultiplier,\r
429 IN UINT8 FisIndex,\r
430 IN EFI_AHCI_COMMAND_FIS *CommandFis,\r
431 IN EFI_AHCI_COMMAND_LIST *CommandList,\r
432 IN UINT8 CommandSlotNumber,\r
433 IN OUT VOID *DataPhysicalAddr,\r
434 IN UINT32 DataLength\r
435 )\r
436{\r
437 EFI_AHCI_REGISTERS *AhciRegisters;\r
438 UINTN AhciBar;\r
439 UINT64 BaseAddr;\r
440 UINT32 PrdtNumber;\r
441 UINT32 PrdtIndex;\r
442 UINTN RemainedData;\r
443 UINTN MemAddr;\r
444 DATA_64 Data64;\r
445 UINT32 Offset;\r
446\r
447 AhciRegisters = &Private->AhciRegisters;\r
448 AhciBar = Private->MmioBase;\r
449\r
450 //\r
451 // Filling the PRDT\r
452 //\r
453 PrdtNumber = (UINT32)DivU64x32 (\r
454 (UINT64)DataLength + AHCI_MAX_DATA_PER_PRDT - 1,\r
455 AHCI_MAX_DATA_PER_PRDT\r
456 );\r
457\r
458 //\r
459 // According to AHCI 1.3 spec, a PRDT entry can point to a maximum 4MB data block.\r
460 // It also limits that the maximum amount of the PRDT entry in the command table\r
461 // is 65535.\r
462 // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
463 // PRDT entries.\r
464 //\r
465 ASSERT (PrdtNumber <= AHCI_MAX_PRDT_NUMBER);\r
466 if (PrdtNumber > AHCI_MAX_PRDT_NUMBER) {\r
467 return;\r
468 }\r
469\r
470 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
471\r
472 BaseAddr = Data64.Uint64;\r
473\r
474 ZeroMem ((VOID *)((UINTN) BaseAddr), sizeof (EFI_AHCI_RECEIVED_FIS));\r
475\r
476 ZeroMem (AhciRegisters->AhciCmdTable, sizeof (EFI_AHCI_COMMAND_TABLE));\r
477\r
478 CommandFis->AhciCFisPmNum = PortMultiplier;\r
479\r
480 CopyMem (&AhciRegisters->AhciCmdTable->CommandFis, CommandFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
481\r
482 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
483 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_DLAE | AHCI_PORT_CMD_ATAPI));\r
484\r
485 RemainedData = (UINTN) DataLength;\r
486 MemAddr = (UINTN) DataPhysicalAddr;\r
487 CommandList->AhciCmdPrdtl = PrdtNumber;\r
488\r
489 for (PrdtIndex = 0; PrdtIndex < PrdtNumber; PrdtIndex++) {\r
490 if (RemainedData < AHCI_MAX_DATA_PER_PRDT) {\r
491 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = (UINT32)RemainedData - 1;\r
492 } else {\r
493 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbc = AHCI_MAX_DATA_PER_PRDT - 1;\r
494 }\r
495\r
496 Data64.Uint64 = (UINT64)MemAddr;\r
497 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDba = Data64.Uint32.Lower32;\r
498 AhciRegisters->AhciCmdTable->PrdtTable[PrdtIndex].AhciPrdtDbau = Data64.Uint32.Upper32;\r
499 RemainedData -= AHCI_MAX_DATA_PER_PRDT;\r
500 MemAddr += AHCI_MAX_DATA_PER_PRDT;\r
501 }\r
502\r
503 //\r
504 // Set the last PRDT to Interrupt On Complete\r
505 //\r
506 if (PrdtNumber > 0) {\r
507 AhciRegisters->AhciCmdTable->PrdtTable[PrdtNumber - 1].AhciPrdtIoc = 1;\r
508 }\r
509\r
510 CopyMem (\r
511 (VOID *) ((UINTN) AhciRegisters->AhciCmdList + (UINTN) CommandSlotNumber * sizeof (EFI_AHCI_COMMAND_LIST)),\r
512 CommandList,\r
513 sizeof (EFI_AHCI_COMMAND_LIST)\r
514 );\r
515\r
516 Data64.Uint64 = (UINT64)(UINTN) AhciRegisters->AhciCmdTable;\r
517 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtba = Data64.Uint32.Lower32;\r
518 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdCtbau = Data64.Uint32.Upper32;\r
519 AhciRegisters->AhciCmdList[CommandSlotNumber].AhciCmdPmp = PortMultiplier;\r
520}\r
521\r
522/**\r
523 Buid a command FIS.\r
524\r
525 @param[in,out] CmdFis A pointer to the EFI_AHCI_COMMAND_FIS data\r
526 structure.\r
527 @param[in] AtaCommandBlock A pointer to the EFI_ATA_COMMAND_BLOCK data\r
528 structure.\r
529\r
530**/\r
531VOID\r
532AhciBuildCommandFis (\r
533 IN OUT EFI_AHCI_COMMAND_FIS *CmdFis,\r
534 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock\r
535 )\r
536{\r
537 ZeroMem (CmdFis, sizeof (EFI_AHCI_COMMAND_FIS));\r
538\r
539 CmdFis->AhciCFisType = AHCI_FIS_REGISTER_H2D;\r
540 //\r
541 // Indicator it's a command\r
542 //\r
543 CmdFis->AhciCFisCmdInd = 0x1;\r
544 CmdFis->AhciCFisCmd = AtaCommandBlock->AtaCommand;\r
545\r
546 CmdFis->AhciCFisFeature = AtaCommandBlock->AtaFeatures;\r
547 CmdFis->AhciCFisFeatureExp = AtaCommandBlock->AtaFeaturesExp;\r
548\r
549 CmdFis->AhciCFisSecNum = AtaCommandBlock->AtaSectorNumber;\r
550 CmdFis->AhciCFisSecNumExp = AtaCommandBlock->AtaSectorNumberExp;\r
551\r
552 CmdFis->AhciCFisClyLow = AtaCommandBlock->AtaCylinderLow;\r
553 CmdFis->AhciCFisClyLowExp = AtaCommandBlock->AtaCylinderLowExp;\r
554\r
555 CmdFis->AhciCFisClyHigh = AtaCommandBlock->AtaCylinderHigh;\r
556 CmdFis->AhciCFisClyHighExp = AtaCommandBlock->AtaCylinderHighExp;\r
557\r
558 CmdFis->AhciCFisSecCount = AtaCommandBlock->AtaSectorCount;\r
559 CmdFis->AhciCFisSecCountExp = AtaCommandBlock->AtaSectorCountExp;\r
560\r
561 CmdFis->AhciCFisDevHead = (UINT8) (AtaCommandBlock->AtaDeviceHead | 0xE0);\r
562}\r
563\r
564/**\r
565 Stop command running for giving port\r
566\r
567 @param[in] AhciBar AHCI bar address.\r
568 @param[in] Port The number of port.\r
569 @param[in] Timeout The timeout value, in 100ns units, to stop.\r
570\r
571 @retval EFI_DEVICE_ERROR The command stop unsuccessfully.\r
572 @retval EFI_TIMEOUT The operation is time out.\r
573 @retval EFI_SUCCESS The command stop successfully.\r
574\r
575**/\r
576EFI_STATUS\r
577AhciStopCommand (\r
578 IN UINTN AhciBar,\r
579 IN UINT8 Port,\r
580 IN UINT64 Timeout\r
581 )\r
582{\r
583 UINT32 Offset;\r
584 UINT32 Data;\r
585\r
586 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
587 Data = AhciReadReg (AhciBar, Offset);\r
588\r
589 if ((Data & (AHCI_PORT_CMD_ST | AHCI_PORT_CMD_CR)) == 0) {\r
590 return EFI_SUCCESS;\r
591 }\r
592\r
593 if ((Data & AHCI_PORT_CMD_ST) != 0) {\r
594 AhciAndReg (AhciBar, Offset, (UINT32)~(AHCI_PORT_CMD_ST));\r
595 }\r
596\r
597 return AhciWaitMmioSet (\r
598 AhciBar,\r
599 Offset,\r
600 AHCI_PORT_CMD_CR,\r
601 0,\r
602 Timeout\r
603 );\r
604}\r
605\r
606/**\r
607 Start command for give slot on specific port.\r
608\r
609 @param[in] AhciBar AHCI bar address.\r
610 @param[in] Port The number of port.\r
611 @param[in] CommandSlot The number of Command Slot.\r
612 @param[in] Timeout The timeout value, in 100ns units, to start.\r
613\r
614 @retval EFI_DEVICE_ERROR The command start unsuccessfully.\r
615 @retval EFI_TIMEOUT The operation is time out.\r
616 @retval EFI_SUCCESS The command start successfully.\r
617\r
618**/\r
619EFI_STATUS\r
620AhciStartCommand (\r
621 IN UINTN AhciBar,\r
622 IN UINT8 Port,\r
623 IN UINT8 CommandSlot,\r
624 IN UINT64 Timeout\r
625 )\r
626{\r
627 UINT32 CmdSlotBit;\r
628 EFI_STATUS Status;\r
629 UINT32 PortStatus;\r
630 UINT32 StartCmd;\r
631 UINT32 PortTfd;\r
632 UINT32 Offset;\r
633 UINT32 Capability;\r
634\r
635 //\r
636 // Collect AHCI controller information\r
637 //\r
638 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
639\r
640 CmdSlotBit = (UINT32) (1 << CommandSlot);\r
641\r
642 AhciClearPortStatus (\r
643 AhciBar,\r
644 Port\r
645 );\r
646\r
647 Status = AhciEnableFisReceive (\r
648 AhciBar,\r
649 Port,\r
650 Timeout\r
651 );\r
652 if (EFI_ERROR (Status)) {\r
653 return Status;\r
654 }\r
655\r
656 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
657 PortStatus = AhciReadReg (AhciBar, Offset);\r
658\r
659 StartCmd = 0;\r
660 if ((PortStatus & AHCI_PORT_CMD_ALPE) != 0) {\r
661 StartCmd = AhciReadReg (AhciBar, Offset);\r
662 StartCmd &= ~AHCI_PORT_CMD_ICC_MASK;\r
663 StartCmd |= AHCI_PORT_CMD_ACTIVE;\r
664 }\r
665\r
666 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
667 PortTfd = AhciReadReg (AhciBar, Offset);\r
668\r
669 if ((PortTfd & (AHCI_PORT_TFD_BSY | AHCI_PORT_TFD_DRQ)) != 0) {\r
670 if ((Capability & BIT24) != 0) {\r
671 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
672 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_CLO);\r
673\r
674 AhciWaitMmioSet (\r
675 AhciBar,\r
676 Offset,\r
677 AHCI_PORT_CMD_CLO,\r
678 0,\r
679 Timeout\r
680 );\r
681 }\r
682 }\r
683\r
684 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
685 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_ST | StartCmd);\r
686\r
687 //\r
688 // Setting the command\r
689 //\r
690 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CI;\r
691 AhciAndReg (AhciBar, Offset, 0);\r
692 AhciOrReg (AhciBar, Offset, CmdSlotBit);\r
693\r
694 return EFI_SUCCESS;\r
695}\r
696\r
697/**\r
698 Start a PIO Data transfer on specific port.\r
699\r
700 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
701 @param[in] Port The number of port.\r
702 @param[in] PortMultiplier The number of port multiplier.\r
703 @param[in] FisIndex The offset index of the FIS base address.\r
704 @param[in] Read The transfer direction.\r
705 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
706 @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
707 @param[in,out] MemoryAddr The pointer to the data buffer.\r
708 @param[in] DataCount The data count to be transferred.\r
709 @param[in] Timeout The timeout value of PIO data transfer, uses\r
710 100ns as a unit.\r
711\r
712 @retval EFI_DEVICE_ERROR The PIO data transfer abort with error occurs.\r
713 @retval EFI_TIMEOUT The operation is time out.\r
714 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
715 @retval EFI_OUT_OF_RESOURCES The operation fails due to lack of resources.\r
716 @retval EFI_SUCCESS The PIO data transfer executes successfully.\r
717\r
718**/\r
719EFI_STATUS\r
720AhciPioTransfer (\r
721 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
722 IN UINT8 Port,\r
723 IN UINT8 PortMultiplier,\r
724 IN UINT8 FisIndex,\r
725 IN BOOLEAN Read,\r
726 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
727 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
728 IN OUT VOID *MemoryAddr,\r
729 IN UINT32 DataCount,\r
730 IN UINT64 Timeout\r
731 )\r
732{\r
733 EFI_STATUS Status;\r
734 EDKII_IOMMU_OPERATION MapOp;\r
735 UINTN MapLength;\r
736 EFI_PHYSICAL_ADDRESS PhyAddr;\r
737 VOID *MapData;\r
738 EFI_AHCI_REGISTERS *AhciRegisters;\r
739 UINTN AhciBar;\r
740 BOOLEAN InfiniteWait;\r
741 UINT32 Offset;\r
742 UINT32 OldRfisLo;\r
743 UINT32 OldRfisHi;\r
744 UINT32 OldCmdListLo;\r
745 UINT32 OldCmdListHi;\r
746 DATA_64 Data64;\r
747 UINT32 FisBaseAddr;\r
748 UINT32 Delay;\r
749 EFI_AHCI_COMMAND_FIS CFis;\r
750 EFI_AHCI_COMMAND_LIST CmdList;\r
751 UINT32 PortTfd;\r
752 UINT32 PrdCount;\r
753 BOOLEAN PioFisReceived;\r
754 BOOLEAN D2hFisReceived;\r
755\r
756 //\r
757 // Current driver implementation supports up to a maximum of AHCI_MAX_PRDT_NUMBER\r
758 // PRDT entries.\r
759 //\r
760 if (DataCount / (UINT32)AHCI_MAX_PRDT_NUMBER > AHCI_MAX_DATA_PER_PRDT) {\r
761 DEBUG ((\r
762 DEBUG_ERROR,\r
763 "%a: Driver only support a maximum of 0x%x PRDT entries, "\r
764 "current number of data byte 0x%x is too large, maximum allowed is 0x%x.\n",\r
765 __FUNCTION__, AHCI_MAX_PRDT_NUMBER, DataCount,\r
766 AHCI_MAX_PRDT_NUMBER * AHCI_MAX_DATA_PER_PRDT\r
767 ));\r
768 return EFI_UNSUPPORTED;\r
769 }\r
770\r
771 MapOp = Read ? EdkiiIoMmuOperationBusMasterWrite :\r
772 EdkiiIoMmuOperationBusMasterRead;\r
773 MapLength = DataCount;\r
774 Status = IoMmuMap (\r
775 MapOp,\r
776 MemoryAddr,\r
777 &MapLength,\r
778 &PhyAddr,\r
779 &MapData\r
780 );\r
781 if (EFI_ERROR (Status) || (MapLength != DataCount)) {\r
782 DEBUG ((DEBUG_ERROR, "%a: Fail to map data buffer.\n", __FUNCTION__));\r
783 return EFI_OUT_OF_RESOURCES;\r
784 }\r
785\r
786 AhciRegisters = &Private->AhciRegisters;\r
787 AhciBar = Private->MmioBase;\r
788 InfiniteWait = (Timeout == 0) ? TRUE : FALSE;\r
789\r
790 //\r
791 // Fill FIS base address register\r
792 //\r
793 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
794 OldRfisLo = AhciReadReg (AhciBar, Offset);\r
795 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
796 OldRfisHi = AhciReadReg (AhciBar, Offset);\r
797 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
798 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
799 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
800 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
801 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
802\r
803 //\r
804 // Single task envrionment, we only use one command table for all port\r
805 //\r
806 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
807 OldCmdListLo = AhciReadReg (AhciBar, Offset);\r
808 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
809 OldCmdListHi = AhciReadReg (AhciBar, Offset);\r
810 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
811 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
812 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
813 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
814 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
815\r
816 //\r
817 // Package read needed\r
818 //\r
819 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
820\r
821 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
822\r
823 CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
824 CmdList.AhciCmdW = Read ? 0 : 1;\r
825\r
826 AhciBuildCommand (\r
827 Private,\r
828 Port,\r
829 PortMultiplier,\r
830 FisIndex,\r
831 &CFis,\r
832 &CmdList,\r
833 0,\r
834 (VOID *)(UINTN)PhyAddr,\r
835 DataCount\r
836 );\r
837\r
838 Status = AhciStartCommand (\r
839 AhciBar,\r
840 Port,\r
841 0,\r
842 Timeout\r
843 );\r
844 if (EFI_ERROR (Status)) {\r
845 goto Exit;\r
846 }\r
847\r
848 //\r
849 // Checking the status and wait the driver sending Data\r
850 //\r
851 FisBaseAddr = (UINT32)(UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
852 if (Read) {\r
853 //\r
854 // Wait device sends the PIO setup fis before data transfer\r
855 //\r
856 Status = EFI_TIMEOUT;\r
857 Delay = (UINT32) DivU64x32 (Timeout, 1000) + 1;\r
858 do {\r
859 PioFisReceived = FALSE;\r
860 D2hFisReceived = FALSE;\r
861 Offset = FisBaseAddr + AHCI_PIO_FIS_OFFSET;\r
862 Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_PIO_SETUP);\r
863 if (!EFI_ERROR (Status)) {\r
864 DEBUG ((DEBUG_INFO, "%a: PioFisReceived.\n", __FUNCTION__));\r
865 PioFisReceived = TRUE;\r
866 }\r
867 //\r
868 // According to SATA 2.6 spec section 11.7, D2h FIS means an error encountered.\r
869 // But Qemu and Marvel 9230 sata controller may just receive a D2h FIS from\r
870 // device after the transaction is finished successfully.\r
871 // To get better device compatibilities, we further check if the PxTFD's\r
872 // ERR bit is set. By this way, we can know if there is a real error happened.\r
873 //\r
874 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
875 Status = AhciCheckMemSet (Offset, AHCI_FIS_TYPE_MASK, AHCI_FIS_REGISTER_D2H);\r
876 if (!EFI_ERROR (Status)) {\r
877 DEBUG ((DEBUG_INFO, "%a: D2hFisReceived.\n", __FUNCTION__));\r
878 D2hFisReceived = TRUE;\r
879 }\r
880\r
881 if (PioFisReceived || D2hFisReceived) {\r
882 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
883 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
884 //\r
885 // PxTFD will be updated if there is a D2H or SetupFIS received.\r
886 //\r
887 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
888 Status = EFI_DEVICE_ERROR;\r
889 break;\r
890 }\r
891\r
892 PrdCount = *(volatile UINT32 *) (&(AhciRegisters->AhciCmdList[0].AhciCmdPrdbc));\r
893 if (PrdCount == DataCount) {\r
894 Status = EFI_SUCCESS;\r
895 break;\r
896 }\r
897 }\r
898\r
899 //\r
900 // Stall for 100 microseconds.\r
901 //\r
902 MicroSecondDelay(100);\r
903\r
904 Delay--;\r
905 if (Delay == 0) {\r
906 Status = EFI_TIMEOUT;\r
907 }\r
908 } while (InfiniteWait || (Delay > 0));\r
909 } else {\r
910 //\r
911 // Wait for D2H Fis is received\r
912 //\r
913 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
914 Status = AhciWaitMemSet (\r
915 Offset,\r
916 AHCI_FIS_TYPE_MASK,\r
917 AHCI_FIS_REGISTER_D2H,\r
918 Timeout\r
919 );\r
920 if (EFI_ERROR (Status)) {\r
921 DEBUG ((DEBUG_ERROR, "%a: AhciWaitMemSet (%r)\n", __FUNCTION__, Status));\r
922 goto Exit;\r
923 }\r
924\r
925 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
926 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
927 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
928 Status = EFI_DEVICE_ERROR;\r
929 }\r
930 }\r
931\r
932Exit:\r
933 AhciStopCommand (\r
934 AhciBar,\r
935 Port,\r
936 Timeout\r
937 );\r
938\r
939 AhciDisableFisReceive (\r
940 AhciBar,\r
941 Port,\r
942 Timeout\r
943 );\r
944\r
945 if (MapData != NULL) {\r
946 IoMmuUnmap (MapData);\r
947 }\r
948\r
949 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
950 AhciWriteReg (AhciBar, Offset, OldRfisLo);\r
951 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
952 AhciWriteReg (AhciBar, Offset, OldRfisHi);\r
953\r
954 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
955 AhciWriteReg (AhciBar, Offset, OldCmdListLo);\r
956 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
957 AhciWriteReg (AhciBar, Offset, OldCmdListHi);\r
958\r
959 return Status;\r
960}\r
961\r
962/**\r
963 Start a non data transfer on specific port.\r
964\r
965 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
966 @param[in] Port The number of port.\r
967 @param[in] PortMultiplier The number of port multiplier.\r
968 @param[in] FisIndex The offset index of the FIS base address.\r
969 @param[in] AtaCommandBlock The EFI_ATA_COMMAND_BLOCK data.\r
970 @param[in,out] AtaStatusBlock The EFI_ATA_STATUS_BLOCK data.\r
971 @param[in] Timeout The timeout value of non data transfer, uses\r
972 100ns as a unit.\r
973\r
974 @retval EFI_DEVICE_ERROR The non data transfer abort with error occurs.\r
975 @retval EFI_TIMEOUT The operation is time out.\r
976 @retval EFI_UNSUPPORTED The device is not ready for transfer.\r
977 @retval EFI_SUCCESS The non data transfer executes successfully.\r
978\r
979**/\r
980EFI_STATUS\r
981AhciNonDataTransfer (\r
982 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
983 IN UINT8 Port,\r
984 IN UINT8 PortMultiplier,\r
985 IN UINT8 FisIndex,\r
986 IN EFI_ATA_COMMAND_BLOCK *AtaCommandBlock,\r
987 IN OUT EFI_ATA_STATUS_BLOCK *AtaStatusBlock,\r
988 IN UINT64 Timeout\r
989 )\r
990{\r
991 EFI_STATUS Status;\r
992 UINTN AhciBar;\r
993 EFI_AHCI_REGISTERS *AhciRegisters;\r
994 UINTN FisBaseAddr;\r
995 UINTN Offset;\r
996 UINT32 PortTfd;\r
997 EFI_AHCI_COMMAND_FIS CFis;\r
998 EFI_AHCI_COMMAND_LIST CmdList;\r
999\r
1000 AhciBar = Private->MmioBase;\r
1001 AhciRegisters = &Private->AhciRegisters;\r
1002\r
1003 //\r
1004 // Package read needed\r
1005 //\r
1006 AhciBuildCommandFis (&CFis, AtaCommandBlock);\r
1007\r
1008 ZeroMem (&CmdList, sizeof (EFI_AHCI_COMMAND_LIST));\r
1009\r
1010 CmdList.AhciCmdCfl = AHCI_FIS_REGISTER_H2D_LENGTH / 4;\r
1011\r
1012 AhciBuildCommand (\r
1013 Private,\r
1014 Port,\r
1015 PortMultiplier,\r
1016 FisIndex,\r
1017 &CFis,\r
1018 &CmdList,\r
1019 0,\r
1020 NULL,\r
1021 0\r
1022 );\r
1023\r
1024 Status = AhciStartCommand (\r
1025 AhciBar,\r
1026 Port,\r
1027 0,\r
1028 Timeout\r
1029 );\r
1030 if (EFI_ERROR (Status)) {\r
1031 goto Exit;\r
1032 }\r
1033\r
1034 //\r
1035 // Wait device sends the Response Fis\r
1036 //\r
1037 FisBaseAddr = (UINTN)AhciRegisters->AhciRFis + sizeof (EFI_AHCI_RECEIVED_FIS) * FisIndex;\r
1038 Offset = FisBaseAddr + AHCI_D2H_FIS_OFFSET;\r
1039 Status = AhciWaitMemSet (\r
1040 Offset,\r
1041 AHCI_FIS_TYPE_MASK,\r
1042 AHCI_FIS_REGISTER_D2H,\r
1043 Timeout\r
1044 );\r
1045\r
1046 if (EFI_ERROR (Status)) {\r
1047 goto Exit;\r
1048 }\r
1049\r
1050 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
1051 PortTfd = AhciReadReg (AhciBar, (UINT32) Offset);\r
1052 if ((PortTfd & AHCI_PORT_TFD_ERR) != 0) {\r
1053 Status = EFI_DEVICE_ERROR;\r
1054 }\r
1055\r
1056Exit:\r
1057 AhciStopCommand (\r
1058 AhciBar,\r
1059 Port,\r
1060 Timeout\r
1061 );\r
1062\r
1063 AhciDisableFisReceive (\r
1064 AhciBar,\r
1065 Port,\r
1066 Timeout\r
1067 );\r
1068\r
1069 return Status;\r
1070}\r
1071\r
1072/**\r
1073 Do AHCI HBA reset.\r
1074\r
1075 @param[in] AhciBar AHCI bar address.\r
1076 @param[in] Timeout The timeout, in 100ns units, to reset.\r
1077\r
1078 @retval EFI_DEVICE_ERROR AHCI controller is failed to complete hardware reset.\r
1079 @retval EFI_TIMEOUT The reset operation is time out.\r
1080 @retval EFI_SUCCESS AHCI controller is reset successfully.\r
1081\r
1082**/\r
1083EFI_STATUS\r
1084AhciReset (\r
1085 IN UINTN AhciBar,\r
1086 IN UINT64 Timeout\r
1087 )\r
1088{\r
1089 UINT32 Delay;\r
1090 UINT32 Value;\r
1091 UINT32 Capability;\r
1092\r
1093 //\r
1094 // Collect AHCI controller information\r
1095 //\r
1096 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1097\r
1098 //\r
1099 // Enable AE before accessing any AHCI registers if Supports AHCI Mode Only is not set\r
1100 //\r
1101 if ((Capability & AHCI_CAP_SAM) == 0) {\r
1102 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
1103 }\r
1104\r
1105 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_RESET);\r
1106\r
1107 Delay = (UINT32) (DivU64x32(Timeout, 1000) + 1);\r
1108\r
1109 do {\r
1110 Value = AhciReadReg(AhciBar, AHCI_GHC_OFFSET);\r
1111 if ((Value & AHCI_GHC_RESET) == 0) {\r
1112 return EFI_SUCCESS;\r
1113 }\r
1114\r
1115 //\r
1116 // Stall for 100 microseconds.\r
1117 //\r
1118 MicroSecondDelay(100);\r
1119\r
1120 Delay--;\r
1121 } while (Delay > 0);\r
1122\r
1123 return EFI_TIMEOUT;\r
1124}\r
1125\r
1126/**\r
1127 Send Identify Drive command to a specific device.\r
1128\r
1129 @param[in] Private The pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA.\r
1130 @param[in] Port The number of port.\r
1131 @param[in] PortMultiplier The port multiplier port number.\r
1132 @param[in] FisIndex The offset index of the FIS base address.\r
1133 @param[in] Buffer The data buffer to store IDENTIFY PACKET data.\r
1134\r
1135 @retval EFI_SUCCESS The cmd executes successfully.\r
1136 @retval EFI_INVALID_PARAMETER Buffer is NULL.\r
1137 @retval EFI_DEVICE_ERROR The cmd abort with error occurs.\r
1138 @retval EFI_TIMEOUT The operation is time out.\r
1139 @retval EFI_UNSUPPORTED The device is not ready for executing.\r
1140\r
1141**/\r
1142EFI_STATUS\r
1143AhciIdentify (\r
1144 IN PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
1145 IN UINT8 Port,\r
1146 IN UINT8 PortMultiplier,\r
1147 IN UINT8 FisIndex,\r
1148 IN ATA_IDENTIFY_DATA *Buffer\r
1149 )\r
1150{\r
1151 EFI_STATUS Status;\r
1152 EFI_ATA_COMMAND_BLOCK Acb;\r
1153 EFI_ATA_STATUS_BLOCK Asb;\r
1154\r
1155 if (Buffer == NULL) {\r
1156 return EFI_INVALID_PARAMETER;\r
1157 }\r
1158\r
1159 ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1160 ZeroMem (&Asb, sizeof (EFI_ATA_STATUS_BLOCK));\r
1161\r
1162 Acb.AtaCommand = ATA_CMD_IDENTIFY_DRIVE;\r
1163 Acb.AtaSectorCount = 1;\r
1164\r
1165 Status = AhciPioTransfer (\r
1166 Private,\r
1167 Port,\r
1168 PortMultiplier,\r
1169 FisIndex,\r
1170 TRUE,\r
1171 &Acb,\r
1172 &Asb,\r
1173 Buffer,\r
1174 sizeof (ATA_IDENTIFY_DATA),\r
1175 ATA_TIMEOUT\r
1176 );\r
1177\r
1178 return Status;\r
1179}\r
1180\r
1181\r
1182/**\r
1183 Collect the number of bits set within a port bitmap.\r
1184\r
1185 @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
1186\r
1187 @retval The number of bits set in the bitmap.\r
1188\r
1189**/\r
1190UINT8\r
1191AhciGetNumberOfPortsFromMap (\r
1192 IN UINT32 PortBitMap\r
1193 )\r
1194{\r
1195 UINT8 NumberOfPorts;\r
1196\r
1197 NumberOfPorts = 0;\r
1198\r
1199 while (PortBitMap != 0) {\r
1200 if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
1201 NumberOfPorts++;\r
1202 }\r
1203 PortBitMap = PortBitMap >> 1;\r
1204 }\r
1205\r
1206 return NumberOfPorts;\r
1207}\r
1208\r
1209/**\r
1210 Get the specified port number from a port bitmap.\r
1211\r
1212 @param[in] PortBitMap A 32-bit wide bit map of ATA AHCI ports.\r
1213 @param[in] PortIndex The specified port index.\r
1214 @param[out] Port The port number of the port specified by PortIndex.\r
1215\r
1216 @retval EFI_SUCCESS The specified port is found and its port number is\r
1217 in Port.\r
1218 @retval EFI_NOT_FOUND Cannot find the specified port within the port bitmap.\r
1219\r
1220**/\r
1221EFI_STATUS\r
1222AhciGetPortFromMap (\r
1223 IN UINT32 PortBitMap,\r
1224 IN UINT8 PortIndex,\r
1225 OUT UINT8 *Port\r
1226 )\r
1227{\r
1228 if (PortIndex == 0) {\r
1229 return EFI_NOT_FOUND;\r
1230 }\r
1231\r
1232 *Port = 0;\r
1233\r
1234 while (PortBitMap != 0) {\r
1235 if ((PortBitMap & ((UINT32)BIT0)) != 0) {\r
1236 PortIndex--;\r
1237\r
1238 //\r
1239 // Found the port specified by PortIndex.\r
1240 //\r
1241 if (PortIndex == 0) {\r
1242 return EFI_SUCCESS;\r
1243 }\r
1244 }\r
1245 PortBitMap = PortBitMap >> 1;\r
1246 *Port = *Port + 1;\r
1247 }\r
1248\r
1249 return EFI_NOT_FOUND;\r
1250}\r
1251\r
1252/**\r
1253 Allocate transfer-related data struct which is used at AHCI mode.\r
1254\r
1255 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
1256\r
1257 @retval EFI_SUCCESS Data structures are allocated successfully.\r
1258 @retval Others Data structures are not allocated successfully.\r
1259\r
1260**/\r
1261EFI_STATUS\r
1262AhciCreateTransferDescriptor (\r
1263 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
1264 )\r
1265{\r
1266 EFI_STATUS Status;\r
1267 UINTN AhciBar;\r
1268 EFI_AHCI_REGISTERS *AhciRegisters;\r
1269 EFI_PHYSICAL_ADDRESS DeviceAddress;\r
1270 VOID *Base;\r
1271 VOID *Mapping;\r
1272 UINT32 Capability;\r
1273 UINT32 PortImplementBitMap;\r
1274 UINT8 MaxPortNumber;\r
1275 UINT8 MaxCommandSlotNumber;\r
1276 UINTN MaxRFisSize;\r
1277 UINTN MaxCmdListSize;\r
1278 UINTN MaxCmdTableSize;\r
1279\r
1280 AhciBar = Private->MmioBase;\r
1281 AhciRegisters = &Private->AhciRegisters;\r
1282\r
1283 //\r
1284 // Collect AHCI controller information\r
1285 //\r
1286 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1287\r
1288 //\r
1289 // Get the number of command slots per port supported by this HBA.\r
1290 //\r
1291 MaxCommandSlotNumber = (UINT8) (((Capability & 0x1F00) >> 8) + 1);\r
1292 ASSERT (MaxCommandSlotNumber > 0);\r
1293 if (MaxCommandSlotNumber == 0) {\r
1294 return EFI_DEVICE_ERROR;\r
1295 }\r
1296\r
1297 //\r
1298 // Get the highest bit of implemented ports which decides how many bytes are\r
1299 // allocated for recived FIS.\r
1300 //\r
1301 PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
1302 MaxPortNumber = (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1);\r
1303 if (MaxPortNumber == 0) {\r
1304 return EFI_DEVICE_ERROR;\r
1305 }\r
1306 //\r
1307 // Get the number of ports that actually needed to be initialized.\r
1308 //\r
1309 MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
1310\r
1311 //\r
1312 // Allocate memory for received FIS.\r
1313 //\r
1314 MaxRFisSize = MaxPortNumber * sizeof (EFI_AHCI_RECEIVED_FIS);\r
1315 Status = IoMmuAllocateBuffer (\r
1316 EFI_SIZE_TO_PAGES (MaxRFisSize),\r
1317 &Base,\r
1318 &DeviceAddress,\r
1319 &Mapping\r
1320 );\r
1321 if (EFI_ERROR (Status)) {\r
1322 return EFI_OUT_OF_RESOURCES;\r
1323 }\r
1324 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1325 AhciRegisters->AhciRFis = Base;\r
1326 AhciRegisters->AhciRFisMap = Mapping;\r
1327 AhciRegisters->MaxRFisSize = MaxRFisSize;\r
1328 ZeroMem (AhciRegisters->AhciRFis, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxRFisSize));\r
1329\r
1330 //\r
1331 // Allocate memory for command list.\r
1332 // Note that the implemenation is a single task model which only use a command\r
1333 // list for each port.\r
1334 //\r
1335 MaxCmdListSize = 1 * sizeof (EFI_AHCI_COMMAND_LIST);\r
1336 Status = IoMmuAllocateBuffer (\r
1337 EFI_SIZE_TO_PAGES (MaxCmdListSize),\r
1338 &Base,\r
1339 &DeviceAddress,\r
1340 &Mapping\r
1341 );\r
1342 if (EFI_ERROR (Status)) {\r
1343 Status = EFI_OUT_OF_RESOURCES;\r
1344 goto ErrorExit;\r
1345 }\r
1346 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1347 AhciRegisters->AhciCmdList = Base;\r
1348 AhciRegisters->AhciCmdListMap = Mapping;\r
1349 AhciRegisters->MaxCmdListSize = MaxCmdListSize;\r
1350 ZeroMem (AhciRegisters->AhciCmdList, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdListSize));\r
1351\r
1352 //\r
1353 // Allocate memory for command table\r
1354 // According to AHCI 1.3 spec, a PRD table can contain maximum 65535 entries.\r
1355 //\r
1356 MaxCmdTableSize = sizeof (EFI_AHCI_COMMAND_TABLE);\r
1357 Status = IoMmuAllocateBuffer (\r
1358 EFI_SIZE_TO_PAGES (MaxCmdTableSize),\r
1359 &Base,\r
1360 &DeviceAddress,\r
1361 &Mapping\r
1362 );\r
1363 if (EFI_ERROR (Status)) {\r
1364 Status = EFI_OUT_OF_RESOURCES;\r
1365 goto ErrorExit;\r
1366 }\r
1367 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Base));\r
1368 AhciRegisters->AhciCmdTable = Base;\r
1369 AhciRegisters->AhciCmdTableMap = Mapping;\r
1370 AhciRegisters->MaxCmdTableSize = MaxCmdTableSize;\r
1371 ZeroMem (AhciRegisters->AhciCmdTable, EFI_PAGE_SIZE * EFI_SIZE_TO_PAGES (MaxCmdTableSize));\r
1372\r
1373 return EFI_SUCCESS;\r
1374\r
1375ErrorExit:\r
1376 if (AhciRegisters->AhciRFisMap != NULL) {\r
1377 IoMmuFreeBuffer (\r
1378 EFI_SIZE_TO_PAGES (AhciRegisters->MaxRFisSize),\r
1379 AhciRegisters->AhciRFis,\r
1380 AhciRegisters->AhciRFisMap\r
1381 );\r
1382 AhciRegisters->AhciRFis = NULL;\r
1383 }\r
1384\r
1385 if (AhciRegisters->AhciCmdListMap != NULL) {\r
1386 IoMmuFreeBuffer (\r
1387 EFI_SIZE_TO_PAGES (AhciRegisters->MaxCmdListSize),\r
1388 AhciRegisters->AhciCmdList,\r
1389 AhciRegisters->AhciCmdListMap\r
1390 );\r
1391 AhciRegisters->AhciCmdList = NULL;\r
1392 }\r
1393\r
1394 return Status;\r
1395}\r
1396\r
1397/**\r
1398 Gets ATA device Capacity according to ATA 6.\r
1399\r
1400 This function returns the capacity of the ATA device if it follows\r
1401 ATA 6 to support 48 bit addressing.\r
1402\r
1403 @param[in] IdentifyData A pointer to ATA_IDENTIFY_DATA structure.\r
1404\r
1405 @return The capacity of the ATA device or 0 if the device does not support\r
1406 48-bit addressing defined in ATA 6.\r
1407\r
1408**/\r
1409EFI_LBA\r
1410GetAtapi6Capacity (\r
1411 IN ATA_IDENTIFY_DATA *IdentifyData\r
1412 )\r
1413{\r
1414 EFI_LBA Capacity;\r
1415 EFI_LBA TmpLba;\r
1416 UINTN Index;\r
1417\r
1418 if ((IdentifyData->command_set_supported_83 & BIT10) == 0) {\r
1419 //\r
1420 // The device doesn't support 48 bit addressing\r
1421 //\r
1422 return 0;\r
1423 }\r
1424\r
1425 //\r
1426 // 48 bit address feature set is supported, get maximum capacity\r
1427 //\r
1428 Capacity = 0;\r
1429 for (Index = 0; Index < 4; Index++) {\r
1430 //\r
1431 // Lower byte goes first: word[100] is the lowest word, word[103] is highest\r
1432 //\r
1433 TmpLba = IdentifyData->maximum_lba_for_48bit_addressing[Index];\r
1434 Capacity |= LShiftU64 (TmpLba, 16 * Index);\r
1435 }\r
1436\r
1437 return Capacity;\r
1438}\r
1439\r
1440/**\r
1441 Identifies ATA device via the Identify data.\r
1442\r
1443 This function identifies the ATA device and initializes the media information.\r
1444\r
1445 @attention This is boundary function that may receive untrusted input.\r
1446 @attention The input is from peripheral hardware device.\r
1447\r
1448 The Identify Drive command response data from an ATA device is the peripheral\r
1449 hardware input, so this routine will do basic validation for the Identify Drive\r
1450 command response data.\r
1451\r
1452 @param[in,out] DeviceData A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
1453\r
1454 @retval EFI_SUCCESS The device is successfully identified and media\r
1455 information is correctly initialized.\r
1456 @retval EFI_UNSUPPORTED The device is not a valid ATA device (hard disk).\r
1457\r
1458**/\r
1459EFI_STATUS\r
1460IdentifyAtaDevice (\r
1461 IN OUT PEI_AHCI_ATA_DEVICE_DATA *DeviceData\r
1462 )\r
1463{\r
1464 ATA_IDENTIFY_DATA *IdentifyData;\r
1465 EFI_PEI_BLOCK_IO2_MEDIA *Media;\r
1466 EFI_LBA Capacity;\r
1467 UINT32 MaxSectorCount;\r
1468 UINT16 PhyLogicSectorSupport;\r
1469\r
1470 IdentifyData = DeviceData->IdentifyData;\r
1471 Media = &DeviceData->Media;\r
1472\r
1473 if ((IdentifyData->config & BIT15) != 0) {\r
1474 DEBUG ((\r
1475 DEBUG_ERROR, "%a: Not a hard disk device on Port 0x%x PortMultiplierPort 0x%x\n",\r
1476 __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
1477 ));\r
1478 return EFI_UNSUPPORTED;\r
1479 }\r
1480\r
1481 DEBUG ((\r
1482 DEBUG_INFO, "%a: Identify Device: Port 0x%x PortMultiplierPort 0x%x\n",\r
1483 __FUNCTION__, DeviceData->Port, DeviceData->PortMultiplier\r
1484 ));\r
1485\r
1486 //\r
1487 // Skip checking whether the WORD 88 (supported UltraDMA by drive), since the\r
1488 // driver only support PIO data transfer for now.\r
1489 //\r
1490\r
1491 //\r
1492 // Get the capacity information of the device.\r
1493 //\r
1494 Capacity = GetAtapi6Capacity (IdentifyData);\r
1495 if (Capacity > MAX_28BIT_ADDRESSING_CAPACITY) {\r
1496 //\r
1497 // Capacity exceeds 120GB. 48-bit addressing is really needed\r
1498 //\r
1499 DeviceData->Lba48Bit = TRUE;\r
1500 } else {\r
1501 //\r
1502 // This is a hard disk <= 120GB capacity, treat it as normal hard disk\r
1503 //\r
1504 Capacity = ((UINT32)IdentifyData->user_addressable_sectors_hi << 16) |\r
1505 IdentifyData->user_addressable_sectors_lo;\r
1506 DeviceData->Lba48Bit = FALSE;\r
1507 }\r
1508\r
1509 if (Capacity == 0) {\r
1510 DEBUG ((DEBUG_ERROR, "%a: Invalid Capacity (0) for ATA device.\n", __FUNCTION__));\r
1511 return EFI_UNSUPPORTED;\r
1512 }\r
1513 Media->LastBlock = (EFI_PEI_LBA) (Capacity - 1);\r
1514\r
1515 Media->BlockSize = 0x200;\r
1516 //\r
1517 // Check whether Long Physical Sector Feature is supported\r
1518 //\r
1519 PhyLogicSectorSupport = IdentifyData->phy_logic_sector_support;\r
1520 DEBUG ((\r
1521 DEBUG_INFO, "%a: PhyLogicSectorSupport = 0x%x\n",\r
1522 __FUNCTION__, PhyLogicSectorSupport\r
1523 ));\r
1524 if ((PhyLogicSectorSupport & (BIT14 | BIT15)) == BIT14) {\r
1525 //\r
1526 // Check logical block size\r
1527 //\r
1528 if ((PhyLogicSectorSupport & BIT12) != 0) {\r
1529 Media->BlockSize = (UINT32) (((IdentifyData->logic_sector_size_hi << 16) |\r
1530 IdentifyData->logic_sector_size_lo) * sizeof (UINT16));\r
1531 }\r
1532 }\r
1533\r
1534 //\r
1535 // Check BlockSize validity\r
1536 //\r
1537 MaxSectorCount = mMaxTransferBlockNumber[DeviceData->Lba48Bit];\r
1538 if ((Media->BlockSize == 0) || (Media->BlockSize > MAX_UINT32 / MaxSectorCount)) {\r
1539 DEBUG ((DEBUG_ERROR, "%a: Invalid BlockSize (0x%x).\n", __FUNCTION__, Media->BlockSize));\r
1540 return EFI_UNSUPPORTED;\r
1541 }\r
1542\r
1543 DEBUG ((\r
1544 DEBUG_INFO, "%a: BlockSize = 0x%x, LastBlock = 0x%lx\n",\r
1545 __FUNCTION__, Media->BlockSize, Media->LastBlock\r
1546 ));\r
1547\r
1548 if ((IdentifyData->trusted_computing_support & BIT0) != 0) {\r
1549 DEBUG ((DEBUG_INFO, "%a: Found Trust Computing feature support.\n", __FUNCTION__));\r
1550 DeviceData->TrustComputing = TRUE;\r
1551 }\r
1552\r
1553 Media->InterfaceType = MSG_SATA_DP;\r
1554 Media->RemovableMedia = FALSE;\r
1555 Media->MediaPresent = TRUE;\r
1556 Media->ReadOnly = FALSE;\r
1557\r
1558 return EFI_SUCCESS;\r
1559}\r
1560\r
1561/**\r
1562 Allocate device information data structure to contain device information.\r
1563 And insert the data structure to the tail of device list for tracing.\r
1564\r
1565 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA\r
1566 instance.\r
1567 @param[in] DeviceIndex The device index.\r
1568 @param[in] Port The port number of the ATA device to send\r
1569 the command.\r
1570 @param[in] PortMultiplierPort The port multiplier port number of the ATA\r
1571 device to send the command.\r
1572 If there is no port multiplier, then specify\r
1573 0xFFFF.\r
1574 @param[in] FisIndex The index of the FIS of the ATA device to\r
1575 send the command.\r
1576 @param[in] IdentifyData The data buffer to store the output of the\r
1577 IDENTIFY command.\r
1578\r
1579 @retval EFI_SUCCESS Successfully insert the ATA device to the\r
1580 tail of device list.\r
1581 @retval EFI_OUT_OF_RESOURCES Not enough resource.\r
1582\r
1583**/\r
1584EFI_STATUS\r
1585CreateNewDevice (\r
1586 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private,\r
1587 IN UINTN DeviceIndex,\r
1588 IN UINT16 Port,\r
1589 IN UINT16 PortMultiplier,\r
1590 IN UINT8 FisIndex,\r
1591 IN ATA_IDENTIFY_DATA *IdentifyData\r
1592 )\r
1593{\r
1594 PEI_AHCI_ATA_DEVICE_DATA *DeviceData;\r
1595 EFI_STATUS Status;\r
1596\r
1597 DeviceData = AllocateZeroPool (sizeof (PEI_AHCI_ATA_DEVICE_DATA));\r
1598 if (DeviceData == NULL) {\r
1599 return EFI_OUT_OF_RESOURCES;\r
1600 }\r
1601\r
1602 if (IdentifyData != NULL) {\r
1603 DeviceData->IdentifyData = AllocateCopyPool (sizeof (ATA_IDENTIFY_DATA), IdentifyData);\r
1604 if (DeviceData->IdentifyData == NULL) {\r
1605 return EFI_OUT_OF_RESOURCES;\r
1606 }\r
1607 }\r
1608\r
1609 DeviceData->Signature = AHCI_PEI_ATA_DEVICE_DATA_SIGNATURE;\r
1610 DeviceData->Port = Port;\r
1611 DeviceData->PortMultiplier = PortMultiplier;\r
1612 DeviceData->FisIndex = FisIndex;\r
1613 DeviceData->DeviceIndex = DeviceIndex;\r
1614 DeviceData->Private = Private;\r
1615\r
1616 Status = IdentifyAtaDevice (DeviceData);\r
1617 if (EFI_ERROR (Status)) {\r
1618 return Status;\r
1619 }\r
1620\r
1621 if (DeviceData->TrustComputing) {\r
1622 Private->TrustComputingDevices++;\r
1623 DeviceData->TrustComputingDeviceIndex = Private->TrustComputingDevices;\r
1624 }\r
1625 Private->ActiveDevices++;\r
1626 InsertTailList (&Private->DeviceList, &DeviceData->Link);\r
1627\r
1628 return EFI_SUCCESS;\r
1629}\r
1630\r
1631/**\r
1632 Initialize ATA host controller at AHCI mode.\r
1633\r
1634 The function is designed to initialize ATA host controller.\r
1635\r
1636 @param[in,out] Private A pointer to the PEI_AHCI_CONTROLLER_PRIVATE_DATA instance.\r
1637\r
1638 @retval EFI_SUCCESS The ATA AHCI controller is initialized successfully.\r
1639 @retval EFI_OUT_OF_RESOURCES Not enough resource to complete while initializing\r
1640 the controller.\r
1641 @retval Others A device error occurred while initializing the\r
1642 controller.\r
1643\r
1644**/\r
1645EFI_STATUS\r
1646AhciModeInitialization (\r
1647 IN OUT PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private\r
1648 )\r
1649{\r
1650 EFI_STATUS Status;\r
1651 UINTN AhciBar;\r
1652 UINT32 Capability;\r
1653 UINT32 Value;\r
1654 UINT8 MaxPortNumber;\r
1655 UINT32 PortImplementBitMap;\r
1656 UINT32 PortInitializeBitMap;\r
1657 EFI_AHCI_REGISTERS *AhciRegisters;\r
1658 UINT8 PortIndex;\r
1659 UINT8 Port;\r
1660 DATA_64 Data64;\r
1661 UINT32 Data;\r
1662 UINT32 Offset;\r
1663 UINT32 PhyDetectDelay;\r
1664 UINTN DeviceIndex;\r
1665 ATA_IDENTIFY_DATA IdentifyData;\r
1666\r
1667 AhciBar = Private->MmioBase;\r
1668\r
1669 Status = AhciReset (AhciBar, AHCI_PEI_RESET_TIMEOUT);\r
1670 if (EFI_ERROR (Status)) {\r
1671 DEBUG ((DEBUG_ERROR, "%a: AHCI HBA reset failed with %r.\n", __FUNCTION__, Status));\r
1672 return EFI_DEVICE_ERROR;\r
1673 }\r
1674\r
1675 //\r
1676 // Collect AHCI controller information\r
1677 //\r
1678 Capability = AhciReadReg (AhciBar, AHCI_CAPABILITY_OFFSET);\r
1679\r
1680 //\r
1681 // Make sure that GHC.AE bit is set before accessing any AHCI registers.\r
1682 //\r
1683 Value = AhciReadReg (AhciBar, AHCI_GHC_OFFSET);\r
1684 if ((Value & AHCI_GHC_ENABLE) == 0) {\r
1685 AhciOrReg (AhciBar, AHCI_GHC_OFFSET, AHCI_GHC_ENABLE);\r
1686 }\r
1687\r
1688 Status = AhciCreateTransferDescriptor (Private);\r
1689 if (EFI_ERROR (Status)) {\r
1690 DEBUG ((\r
1691 DEBUG_ERROR,\r
1692 "%a: Transfer-related data allocation failed with %r.\n",\r
1693 __FUNCTION__, Status\r
1694 ));\r
1695 return EFI_OUT_OF_RESOURCES;\r
1696 }\r
1697\r
1698 //\r
1699 // Get the number of command slots per port supported by this HBA.\r
1700 //\r
1701 MaxPortNumber = (UINT8) ((Capability & 0x1F) + 1);\r
1702\r
1703 //\r
1704 // Get the bit map of those ports exposed by this HBA.\r
1705 // It indicates which ports that the HBA supports are available for software\r
1706 // to use.\r
1707 //\r
1708 PortImplementBitMap = AhciReadReg (AhciBar, AHCI_PI_OFFSET);\r
1709\r
1710 //\r
1711 // Get the number of ports that actually needed to be initialized.\r
1712 //\r
1713 MaxPortNumber = MIN (MaxPortNumber, (UINT8)(UINTN)(HighBitSet32(PortImplementBitMap) + 1));\r
1714 MaxPortNumber = MIN (MaxPortNumber, AhciGetNumberOfPortsFromMap (Private->PortBitMap));\r
1715\r
1716 PortInitializeBitMap = Private->PortBitMap;\r
1717 AhciRegisters = &Private->AhciRegisters;\r
1718 DeviceIndex = 0;\r
1719 //\r
1720 // Enumerate ATA ports\r
1721 //\r
1722 for (PortIndex = 1; PortIndex <= MaxPortNumber; PortIndex ++) {\r
1723 Status = AhciGetPortFromMap (PortInitializeBitMap, PortIndex, &Port);\r
1724 if ((PortImplementBitMap & (BIT0 << Port)) != 0) {\r
1725 //\r
1726 // Initialize FIS Base Address Register and Command List Base Address\r
1727 // Register for use.\r
1728 //\r
1729 Data64.Uint64 = (UINTN) (AhciRegisters->AhciRFis) +\r
1730 sizeof (EFI_AHCI_RECEIVED_FIS) * (PortIndex - 1);\r
1731 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FB;\r
1732 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
1733 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_FBU;\r
1734 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
1735\r
1736 Data64.Uint64 = (UINTN) (AhciRegisters->AhciCmdList);\r
1737 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLB;\r
1738 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Lower32);\r
1739 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CLBU;\r
1740 AhciWriteReg (AhciBar, Offset, Data64.Uint32.Upper32);\r
1741\r
1742 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1743 Data = AhciReadReg (AhciBar, Offset);\r
1744 if ((Data & AHCI_PORT_CMD_CPD) != 0) {\r
1745 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_POD);\r
1746 }\r
1747\r
1748 if ((Capability & AHCI_CAP_SSS) != 0) {\r
1749 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_SUD);\r
1750 }\r
1751\r
1752 //\r
1753 // Disable aggressive power management.\r
1754 //\r
1755 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SCTL;\r
1756 AhciOrReg (AhciBar, Offset, AHCI_PORT_SCTL_IPM_INIT);\r
1757 //\r
1758 // Disable the reporting of the corresponding interrupt to system software.\r
1759 //\r
1760 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_IE;\r
1761 AhciAndReg (AhciBar, Offset, 0);\r
1762\r
1763 //\r
1764 // Enable FIS Receive DMA engine for the first D2H FIS.\r
1765 //\r
1766 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1767 AhciOrReg (AhciBar, Offset, AHCI_PORT_CMD_FRE);\r
1768\r
1769 //\r
1770 // Wait no longer than 15 ms to wait the Phy to detect the presence of a device.\r
1771 //\r
1772 PhyDetectDelay = AHCI_BUS_PHY_DETECT_TIMEOUT;\r
1773 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SSTS;\r
1774 do {\r
1775 Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_SSTS_DET_MASK;\r
1776 if ((Data == AHCI_PORT_SSTS_DET_PCE) || (Data == AHCI_PORT_SSTS_DET)) {\r
1777 break;\r
1778 }\r
1779\r
1780 MicroSecondDelay (1000);\r
1781 PhyDetectDelay--;\r
1782 } while (PhyDetectDelay > 0);\r
1783\r
1784 if (PhyDetectDelay == 0) {\r
1785 //\r
1786 // No device detected at this port.\r
1787 // Clear PxCMD.SUD for those ports at which there are no device present.\r
1788 //\r
1789 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_CMD;\r
1790 AhciAndReg (AhciBar, Offset, (UINT32) ~(AHCI_PORT_CMD_SUD));\r
1791 DEBUG ((DEBUG_ERROR, "%a: No device detected at Port %d.\n", __FUNCTION__, Port));\r
1792 continue;\r
1793 }\r
1794\r
1795 //\r
1796 // According to SATA1.0a spec section 5.2, we need to wait for PxTFD.BSY and PxTFD.DRQ\r
1797 // and PxTFD.ERR to be zero. The maximum wait time is 16s which is defined at ATA spec.\r
1798 //\r
1799 PhyDetectDelay = 16 * 1000;\r
1800 do {\r
1801 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SERR;\r
1802 if (AhciReadReg(AhciBar, Offset) != 0) {\r
1803 AhciWriteReg (AhciBar, Offset, AhciReadReg (AhciBar, Offset));\r
1804 }\r
1805 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_TFD;\r
1806\r
1807 Data = AhciReadReg (AhciBar, Offset) & AHCI_PORT_TFD_MASK;\r
1808 if (Data == 0) {\r
1809 break;\r
1810 }\r
1811\r
1812 MicroSecondDelay (1000);\r
1813 PhyDetectDelay--;\r
1814 } while (PhyDetectDelay > 0);\r
1815\r
1816 if (PhyDetectDelay == 0) {\r
1817 DEBUG ((\r
1818 DEBUG_ERROR,\r
1819 "%a: Port %d device presence detected but phy not ready (TFD=0x%x).\n",\r
1820 __FUNCTION__, Port, Data\r
1821 ));\r
1822 continue;\r
1823 }\r
1824\r
1825 //\r
1826 // When the first D2H register FIS is received, the content of PxSIG register is updated.\r
1827 //\r
1828 Offset = AHCI_PORT_START + Port * AHCI_PORT_REG_WIDTH + AHCI_PORT_SIG;\r
1829 Status = AhciWaitMmioSet (\r
1830 AhciBar,\r
1831 Offset,\r
1832 0x0000FFFF,\r
1833 0x00000101,\r
1834 160000000\r
1835 );\r
1836 if (EFI_ERROR (Status)) {\r
1837 DEBUG ((\r
1838 DEBUG_ERROR,\r
1839 "%a: Error occured when waiting for the first D2H register FIS - %r\n",\r
1840 __FUNCTION__, Status\r
1841 ));\r
1842 continue;\r
1843 }\r
1844\r
1845 Data = AhciReadReg (AhciBar, Offset);\r
1846 if ((Data & AHCI_ATAPI_SIG_MASK) == AHCI_ATA_DEVICE_SIG) {\r
1847 Status = AhciIdentify (Private, Port, 0, PortIndex - 1, &IdentifyData);\r
1848 if (EFI_ERROR (Status)) {\r
1849 DEBUG ((DEBUG_ERROR, "%a: AhciIdentify() failed with %r\n", __FUNCTION__, Status));\r
1850 continue;\r
1851 }\r
1852 DEBUG ((DEBUG_INFO, "%a: ATA hard disk found on Port %d.\n", __FUNCTION__, Port));\r
1853 } else {\r
1854 continue;\r
1855 }\r
1856\r
1857 //\r
1858 // Found an ATA hard disk device, add it into the device list.\r
1859 //\r
1860 DeviceIndex++;\r
1861 CreateNewDevice (\r
1862 Private,\r
1863 DeviceIndex,\r
1864 Port,\r
1865 0xFFFF,\r
1866 PortIndex - 1,\r
1867 &IdentifyData\r
1868 );\r
1869 }\r
1870 }\r
1871\r
1872 return EFI_SUCCESS;\r
1873}\r
1874\r
b2b8e872
HW
1875/**\r
1876 Transfer data from ATA device.\r
1877\r
1878 This function performs one ATA pass through transaction to transfer data from/to\r
1879 ATA device. It chooses the appropriate ATA command and protocol to invoke PassThru\r
1880 interface of ATA pass through.\r
1881\r
1882 @param[in] DeviceData A pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
1883 @param[in,out] Buffer The pointer to the current transaction buffer.\r
1884 @param[in] StartLba The starting logical block address to be accessed.\r
1885 @param[in] TransferLength The block number or sector count of the transfer.\r
1886 @param[in] IsWrite Indicates whether it is a write operation.\r
1887\r
1888 @retval EFI_SUCCESS The data transfer is complete successfully.\r
1889 @return others Some error occurs when transferring data.\r
1890\r
1891**/\r
1892EFI_STATUS\r
1893TransferAtaDevice (\r
1894 IN PEI_AHCI_ATA_DEVICE_DATA *DeviceData,\r
1895 IN OUT VOID *Buffer,\r
1896 IN EFI_LBA StartLba,\r
1897 IN UINT32 TransferLength,\r
1898 IN BOOLEAN IsWrite\r
1899 )\r
1900{\r
1901 PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
1902 EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru;\r
1903 EFI_ATA_COMMAND_BLOCK Acb;\r
1904 EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
1905\r
1906 Private = DeviceData->Private;\r
1907 AtaPassThru = &Private->AtaPassThruPpi;\r
1908\r
1909 //\r
1910 // Ensure Lba48Bit and IsWrite are valid boolean values\r
1911 //\r
1912 ASSERT ((UINTN) DeviceData->Lba48Bit < 2);\r
1913 ASSERT ((UINTN) IsWrite < 2);\r
1914 if (((UINTN) DeviceData->Lba48Bit >= 2) ||\r
1915 ((UINTN) IsWrite >= 2)) {\r
1916 return EFI_INVALID_PARAMETER;\r
1917 }\r
1918\r
1919 //\r
1920 // Prepare for ATA command block.\r
1921 //\r
1922 ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
1923 Acb.AtaCommand = mAtaCommands[DeviceData->Lba48Bit][IsWrite];\r
1924 Acb.AtaSectorNumber = (UINT8) StartLba;\r
1925 Acb.AtaCylinderLow = (UINT8) RShiftU64 (StartLba, 8);\r
1926 Acb.AtaCylinderHigh = (UINT8) RShiftU64 (StartLba, 16);\r
1927 Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 |\r
1928 (DeviceData->PortMultiplier == 0xFFFF ?\r
1929 0 : (DeviceData->PortMultiplier << 4)));\r
1930 Acb.AtaSectorCount = (UINT8) TransferLength;\r
1931 if (DeviceData->Lba48Bit) {\r
1932 Acb.AtaSectorNumberExp = (UINT8) RShiftU64 (StartLba, 24);\r
1933 Acb.AtaCylinderLowExp = (UINT8) RShiftU64 (StartLba, 32);\r
1934 Acb.AtaCylinderHighExp = (UINT8) RShiftU64 (StartLba, 40);\r
1935 Acb.AtaSectorCountExp = (UINT8) (TransferLength >> 8);\r
1936 } else {\r
1937 Acb.AtaDeviceHead = (UINT8) (Acb.AtaDeviceHead | RShiftU64 (StartLba, 24));\r
1938 }\r
1939\r
1940 //\r
1941 // Prepare for ATA pass through packet.\r
1942 //\r
1943 ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
1944 if (IsWrite) {\r
1945 Packet.OutDataBuffer = Buffer;\r
1946 Packet.OutTransferLength = TransferLength;\r
1947 } else {\r
1948 Packet.InDataBuffer = Buffer;\r
1949 Packet.InTransferLength = TransferLength;\r
1950 }\r
1951 Packet.Asb = NULL;\r
1952 Packet.Acb = &Acb;\r
1953 Packet.Protocol = mAtaPassThruCmdProtocols[IsWrite];\r
1954 Packet.Length = EFI_ATA_PASS_THRU_LENGTH_SECTOR_COUNT;\r
1955 //\r
1956 // |------------------------|-----------------|\r
1957 // | ATA PIO Transfer Mode | Transfer Rate |\r
1958 // |------------------------|-----------------|\r
1959 // | PIO Mode 0 | 3.3Mbytes/sec |\r
1960 // |------------------------|-----------------|\r
1961 // | PIO Mode 1 | 5.2Mbytes/sec |\r
1962 // |------------------------|-----------------|\r
1963 // | PIO Mode 2 | 8.3Mbytes/sec |\r
1964 // |------------------------|-----------------|\r
1965 // | PIO Mode 3 | 11.1Mbytes/sec |\r
1966 // |------------------------|-----------------|\r
1967 // | PIO Mode 4 | 16.6Mbytes/sec |\r
1968 // |------------------------|-----------------|\r
1969 //\r
1970 // As AtaBus is used to manage ATA devices, we have to use the lowest transfer\r
1971 // rate to calculate the possible maximum timeout value for each read/write\r
1972 // operation. The timout value is rounded up to nearest integar and here an\r
1973 // additional 30s is added to follow ATA spec in which it mentioned that the\r
1974 // device may take up to 30s to respond commands in the Standby/Idle mode.\r
1975 //\r
1976 // Calculate the maximum timeout value for PIO read/write operation.\r
1977 //\r
1978 Packet.Timeout = TIMER_PERIOD_SECONDS (\r
1979 DivU64x32 (\r
1980 MultU64x32 (TransferLength, DeviceData->Media.BlockSize),\r
1981 3300000\r
1982 ) + 31\r
1983 );\r
1984\r
1985 return AtaPassThru->PassThru (\r
1986 AtaPassThru,\r
1987 DeviceData->Port,\r
1988 DeviceData->PortMultiplier,\r
1989 &Packet\r
1990 );\r
1991}\r
1992\r
87bc3f19
HW
1993/**\r
1994 Trust transfer data from/to ATA device.\r
1995\r
1996 This function performs one ATA pass through transaction to do a trust transfer\r
1997 from/to ATA device. It chooses the appropriate ATA command and protocol to invoke\r
1998 PassThru interface of ATA pass through.\r
1999\r
2000 @param[in] DeviceData Pointer to PEI_AHCI_ATA_DEVICE_DATA structure.\r
2001 @param[in,out] Buffer The pointer to the current transaction buffer.\r
2002 @param[in] SecurityProtocolId\r
2003 The value of the "Security Protocol" parameter\r
2004 of the security protocol command to be sent.\r
2005 @param[in] SecurityProtocolSpecificData\r
2006 The value of the "Security Protocol Specific"\r
2007 parameter of the security protocol command to\r
2008 be sent.\r
2009 @param[in] TransferLength The block number or sector count of the transfer.\r
2010 @param[in] IsTrustSend Indicates whether it is a trust send operation\r
2011 or not.\r
2012 @param[in] Timeout The timeout, in 100ns units, to use for the execution\r
2013 of the security protocol command. A Timeout value\r
2014 of 0 means that this function will wait indefinitely\r
2015 for the security protocol command to execute. If\r
2016 Timeout is greater than zero, then this function\r
2017 will return EFI_TIMEOUT if the time required to\r
2018 execute the receive data command is greater than\r
2019 Timeout.\r
2020 @param[out] TransferLengthOut\r
2021 A pointer to a buffer to store the size in bytes\r
2022 of the data written to the buffer. Ignore it when\r
2023 IsTrustSend is TRUE.\r
2024\r
2025 @retval EFI_SUCCESS The data transfer is complete successfully.\r
2026 @return others Some error occurs when transferring data.\r
2027\r
2028**/\r
2029EFI_STATUS\r
2030TrustTransferAtaDevice (\r
2031 IN PEI_AHCI_ATA_DEVICE_DATA *DeviceData,\r
2032 IN OUT VOID *Buffer,\r
2033 IN UINT8 SecurityProtocolId,\r
2034 IN UINT16 SecurityProtocolSpecificData,\r
2035 IN UINTN TransferLength,\r
2036 IN BOOLEAN IsTrustSend,\r
2037 IN UINT64 Timeout,\r
2038 OUT UINTN *TransferLengthOut\r
2039 )\r
2040{\r
2041 PEI_AHCI_CONTROLLER_PRIVATE_DATA *Private;\r
2042 EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru;\r
2043 EFI_ATA_COMMAND_BLOCK Acb;\r
2044 EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;\r
2045 EFI_STATUS Status;\r
2046 VOID *NewBuffer;\r
2047\r
2048 Private = DeviceData->Private;\r
2049 AtaPassThru = &Private->AtaPassThruPpi;\r
2050\r
2051 //\r
2052 // Ensure IsTrustSend are valid boolean values\r
2053 //\r
2054 ASSERT ((UINTN) IsTrustSend < 2);\r
2055 if ((UINTN) IsTrustSend >= 2) {\r
2056 return EFI_INVALID_PARAMETER;\r
2057 }\r
2058\r
2059 //\r
2060 // Prepare for ATA command block.\r
2061 //\r
2062 ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK));\r
2063 if (TransferLength == 0) {\r
2064 Acb.AtaCommand = ATA_CMD_TRUST_NON_DATA;\r
2065 } else {\r
2066 Acb.AtaCommand = mAtaTrustCommands[IsTrustSend];\r
2067 }\r
2068 Acb.AtaFeatures = SecurityProtocolId;\r
2069 Acb.AtaSectorCount = (UINT8) (TransferLength / 512);\r
2070 Acb.AtaSectorNumber = (UINT8) ((TransferLength / 512) >> 8);\r
2071 //\r
2072 // NOTE: ATA Spec has no explicitly definition for Security Protocol Specific layout.\r
2073 // Here use big endian for Cylinder register.\r
2074 //\r
2075 Acb.AtaCylinderHigh = (UINT8) SecurityProtocolSpecificData;\r
2076 Acb.AtaCylinderLow = (UINT8) (SecurityProtocolSpecificData >> 8);\r
2077 Acb.AtaDeviceHead = (UINT8) (BIT7 | BIT6 | BIT5 |\r
2078 (DeviceData->PortMultiplier == 0xFFFF ?\r
2079 0 : (DeviceData->PortMultiplier << 4)));\r
2080\r
2081 //\r
2082 // Prepare for ATA pass through packet.\r
2083 //\r
2084 ZeroMem (&Packet, sizeof (EFI_ATA_PASS_THRU_COMMAND_PACKET));\r
2085 if (TransferLength == 0) {\r
2086 Packet.InTransferLength = 0;\r
2087 Packet.OutTransferLength = 0;\r
2088 Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;\r
2089 } else if (IsTrustSend) {\r
2090 //\r
2091 // Check the alignment of the incoming buffer prior to invoking underlying\r
2092 // ATA PassThru PPI.\r
2093 //\r
2094 if ((AtaPassThru->Mode->IoAlign > 1) &&\r
2095 !IS_ALIGNED (Buffer, AtaPassThru->Mode->IoAlign)) {\r
2096 NewBuffer = AllocateAlignedPages (\r
2097 EFI_SIZE_TO_PAGES (TransferLength),\r
2098 AtaPassThru->Mode->IoAlign\r
2099 );\r
2100 if (NewBuffer == NULL) {\r
2101 return EFI_OUT_OF_RESOURCES;\r
2102 }\r
2103\r
2104 CopyMem (NewBuffer, Buffer, TransferLength);\r
2105 Buffer = NewBuffer;\r
2106 }\r
2107 Packet.OutDataBuffer = Buffer;\r
2108 Packet.OutTransferLength = (UINT32) TransferLength;\r
2109 Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
2110 } else {\r
2111 Packet.InDataBuffer = Buffer;\r
2112 Packet.InTransferLength = (UINT32) TransferLength;\r
2113 Packet.Protocol = mAtaPassThruCmdProtocols[IsTrustSend];\r
2114 }\r
2115 Packet.Asb = NULL;\r
2116 Packet.Acb = &Acb;\r
2117 Packet.Timeout = Timeout;\r
2118 Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;\r
2119\r
2120 Status = AtaPassThru->PassThru (\r
2121 AtaPassThru,\r
2122 DeviceData->Port,\r
2123 DeviceData->PortMultiplier,\r
2124 &Packet\r
2125 );\r
2126 if (TransferLengthOut != NULL) {\r
2127 if (!IsTrustSend) {\r
2128 *TransferLengthOut = Packet.InTransferLength;\r
2129 }\r
2130 }\r
2131 return Status;\r
2132}\r