]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressHci.c
MdeModulePkg/NvmExpressDxe: enable 64-bit PCI DMA
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpressHci.c
CommitLineData
eb290d02
FT
1/** @file\r
2 NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows\r
3 NVM Express specification.\r
4\r
c5921812 5 Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>\r
eb290d02
FT
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "NvmExpress.h"\r
17\r
18/**\r
19 Read Nvm Express controller capability register.\r
20\r
21 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
22 @param Cap The buffer used to store capability register content.\r
23\r
24 @return EFI_SUCCESS Successfully read the controller capability register content.\r
25 @return EFI_DEVICE_ERROR Fail to read the controller capability register.\r
26\r
27**/\r
28EFI_STATUS\r
29ReadNvmeControllerCapabilities (\r
30 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
31 IN NVME_CAP *Cap\r
32 )\r
33{\r
34 EFI_PCI_IO_PROTOCOL *PciIo;\r
35 EFI_STATUS Status;\r
7b8883c6 36 UINT64 Data;\r
eb290d02
FT
37\r
38 PciIo = Private->PciIo;\r
39 Status = PciIo->Mem.Read (\r
40 PciIo,\r
7b8883c6 41 EfiPciIoWidthUint32,\r
eb290d02
FT
42 NVME_BAR,\r
43 NVME_CAP_OFFSET,\r
7b8883c6
FT
44 2,\r
45 &Data\r
eb290d02
FT
46 );\r
47\r
48 if (EFI_ERROR(Status)) {\r
49 return Status;\r
50 }\r
51\r
7b8883c6 52 WriteUnaligned64 ((UINT64*)Cap, Data);\r
eb290d02
FT
53 return EFI_SUCCESS;\r
54}\r
55\r
56/**\r
57 Read Nvm Express controller configuration register.\r
58\r
59 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
60 @param Cc The buffer used to store configuration register content.\r
61\r
62 @return EFI_SUCCESS Successfully read the controller configuration register content.\r
63 @return EFI_DEVICE_ERROR Fail to read the controller configuration register.\r
64\r
65**/\r
66EFI_STATUS\r
67ReadNvmeControllerConfiguration (\r
68 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
69 IN NVME_CC *Cc\r
70 )\r
71{\r
72 EFI_PCI_IO_PROTOCOL *PciIo;\r
73 EFI_STATUS Status;\r
7b8883c6 74 UINT32 Data;\r
eb290d02
FT
75\r
76 PciIo = Private->PciIo;\r
77 Status = PciIo->Mem.Read (\r
78 PciIo,\r
79 EfiPciIoWidthUint32,\r
80 NVME_BAR,\r
81 NVME_CC_OFFSET,\r
82 1,\r
7b8883c6 83 &Data\r
eb290d02
FT
84 );\r
85\r
86 if (EFI_ERROR(Status)) {\r
87 return Status;\r
88 }\r
89\r
7b8883c6 90 WriteUnaligned32 ((UINT32*)Cc, Data);\r
eb290d02
FT
91 return EFI_SUCCESS;\r
92}\r
93\r
94/**\r
95 Write Nvm Express controller configuration register.\r
96\r
97 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
98 @param Cc The buffer used to store the content to be written into configuration register.\r
99\r
100 @return EFI_SUCCESS Successfully write data into the controller configuration register.\r
101 @return EFI_DEVICE_ERROR Fail to write data into the controller configuration register.\r
102\r
103**/\r
104EFI_STATUS\r
105WriteNvmeControllerConfiguration (\r
106 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
107 IN NVME_CC *Cc\r
108 )\r
109{\r
110 EFI_PCI_IO_PROTOCOL *PciIo;\r
111 EFI_STATUS Status;\r
7b8883c6 112 UINT32 Data;\r
eb290d02
FT
113\r
114 PciIo = Private->PciIo;\r
7b8883c6 115 Data = ReadUnaligned32 ((UINT32*)Cc);\r
eb290d02
FT
116 Status = PciIo->Mem.Write (\r
117 PciIo,\r
118 EfiPciIoWidthUint32,\r
119 NVME_BAR,\r
120 NVME_CC_OFFSET,\r
121 1,\r
7b8883c6 122 &Data\r
eb290d02
FT
123 );\r
124\r
125 if (EFI_ERROR(Status)) {\r
126 return Status;\r
127 }\r
128\r
129 DEBUG ((EFI_D_INFO, "Cc.En: %d\n", Cc->En));\r
130 DEBUG ((EFI_D_INFO, "Cc.Css: %d\n", Cc->Css));\r
131 DEBUG ((EFI_D_INFO, "Cc.Mps: %d\n", Cc->Mps));\r
132 DEBUG ((EFI_D_INFO, "Cc.Ams: %d\n", Cc->Ams));\r
133 DEBUG ((EFI_D_INFO, "Cc.Shn: %d\n", Cc->Shn));\r
134 DEBUG ((EFI_D_INFO, "Cc.Iosqes: %d\n", Cc->Iosqes));\r
135 DEBUG ((EFI_D_INFO, "Cc.Iocqes: %d\n", Cc->Iocqes));\r
136\r
137 return EFI_SUCCESS;\r
138}\r
139\r
140/**\r
141 Read Nvm Express controller status register.\r
142\r
143 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
144 @param Csts The buffer used to store status register content.\r
145\r
146 @return EFI_SUCCESS Successfully read the controller status register content.\r
147 @return EFI_DEVICE_ERROR Fail to read the controller status register.\r
148\r
149**/\r
150EFI_STATUS\r
151ReadNvmeControllerStatus (\r
152 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
153 IN NVME_CSTS *Csts\r
154 )\r
155{\r
156 EFI_PCI_IO_PROTOCOL *PciIo;\r
157 EFI_STATUS Status;\r
7b8883c6 158 UINT32 Data;\r
eb290d02
FT
159\r
160 PciIo = Private->PciIo;\r
161 Status = PciIo->Mem.Read (\r
162 PciIo,\r
163 EfiPciIoWidthUint32,\r
164 NVME_BAR,\r
165 NVME_CSTS_OFFSET,\r
166 1,\r
7b8883c6 167 &Data\r
eb290d02
FT
168 );\r
169\r
170 if (EFI_ERROR(Status)) {\r
171 return Status;\r
172 }\r
173\r
7b8883c6 174 WriteUnaligned32 ((UINT32*)Csts, Data);\r
eb290d02
FT
175 return EFI_SUCCESS;\r
176}\r
177\r
178/**\r
179 Read Nvm Express admin queue attributes register.\r
180\r
181 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
182 @param Aqa The buffer used to store admin queue attributes register content.\r
183\r
184 @return EFI_SUCCESS Successfully read the admin queue attributes register content.\r
185 @return EFI_DEVICE_ERROR Fail to read the admin queue attributes register.\r
186\r
187**/\r
188EFI_STATUS\r
189ReadNvmeAdminQueueAttributes (\r
190 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
191 IN NVME_AQA *Aqa\r
192 )\r
193{\r
194 EFI_PCI_IO_PROTOCOL *PciIo;\r
195 EFI_STATUS Status;\r
7b8883c6 196 UINT32 Data;\r
eb290d02
FT
197\r
198 PciIo = Private->PciIo;\r
199 Status = PciIo->Mem.Read (\r
200 PciIo,\r
201 EfiPciIoWidthUint32,\r
202 NVME_BAR,\r
203 NVME_AQA_OFFSET,\r
204 1,\r
7b8883c6 205 &Data\r
eb290d02
FT
206 );\r
207\r
208 if (EFI_ERROR(Status)) {\r
209 return Status;\r
210 }\r
211\r
7b8883c6 212 WriteUnaligned32 ((UINT32*)Aqa, Data);\r
eb290d02
FT
213 return EFI_SUCCESS;\r
214}\r
215\r
216/**\r
217 Write Nvm Express admin queue attributes register.\r
218\r
219 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
220 @param Aqa The buffer used to store the content to be written into admin queue attributes register.\r
221\r
222 @return EFI_SUCCESS Successfully write data into the admin queue attributes register.\r
223 @return EFI_DEVICE_ERROR Fail to write data into the admin queue attributes register.\r
224\r
225**/\r
226EFI_STATUS\r
227WriteNvmeAdminQueueAttributes (\r
228 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
229 IN NVME_AQA *Aqa\r
230 )\r
231{\r
232 EFI_PCI_IO_PROTOCOL *PciIo;\r
233 EFI_STATUS Status;\r
7b8883c6 234 UINT32 Data;\r
eb290d02
FT
235\r
236 PciIo = Private->PciIo;\r
7b8883c6 237 Data = ReadUnaligned32 ((UINT32*)Aqa);\r
eb290d02
FT
238 Status = PciIo->Mem.Write (\r
239 PciIo,\r
240 EfiPciIoWidthUint32,\r
241 NVME_BAR,\r
242 NVME_AQA_OFFSET,\r
243 1,\r
7b8883c6 244 &Data\r
eb290d02
FT
245 );\r
246\r
247 if (EFI_ERROR(Status)) {\r
248 return Status;\r
249 }\r
250\r
251 DEBUG ((EFI_D_INFO, "Aqa.Asqs: %d\n", Aqa->Asqs));\r
252 DEBUG ((EFI_D_INFO, "Aqa.Acqs: %d\n", Aqa->Acqs));\r
253\r
254 return EFI_SUCCESS;\r
255}\r
256\r
257/**\r
258 Read Nvm Express admin submission queue base address register.\r
259\r
260 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
261 @param Asq The buffer used to store admin submission queue base address register content.\r
262\r
263 @return EFI_SUCCESS Successfully read the admin submission queue base address register content.\r
264 @return EFI_DEVICE_ERROR Fail to read the admin submission queue base address register.\r
265\r
266**/\r
267EFI_STATUS\r
268ReadNvmeAdminSubmissionQueueBaseAddress (\r
269 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
270 IN NVME_ASQ *Asq\r
271 )\r
272{\r
273 EFI_PCI_IO_PROTOCOL *PciIo;\r
274 EFI_STATUS Status;\r
7b8883c6 275 UINT64 Data;\r
eb290d02
FT
276\r
277 PciIo = Private->PciIo;\r
278 Status = PciIo->Mem.Read (\r
279 PciIo,\r
7b8883c6 280 EfiPciIoWidthUint32,\r
eb290d02
FT
281 NVME_BAR,\r
282 NVME_ASQ_OFFSET,\r
7b8883c6
FT
283 2,\r
284 &Data\r
eb290d02
FT
285 );\r
286\r
287 if (EFI_ERROR(Status)) {\r
288 return Status;\r
289 }\r
290\r
7b8883c6 291 WriteUnaligned64 ((UINT64*)Asq, Data);\r
eb290d02
FT
292 return EFI_SUCCESS;\r
293}\r
294\r
295/**\r
296 Write Nvm Express admin submission queue base address register.\r
297\r
298 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
299 @param Asq The buffer used to store the content to be written into admin submission queue base address register.\r
300\r
301 @return EFI_SUCCESS Successfully write data into the admin submission queue base address register.\r
302 @return EFI_DEVICE_ERROR Fail to write data into the admin submission queue base address register.\r
303\r
304**/\r
305EFI_STATUS\r
306WriteNvmeAdminSubmissionQueueBaseAddress (\r
307 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
308 IN NVME_ASQ *Asq\r
309 )\r
310{\r
311 EFI_PCI_IO_PROTOCOL *PciIo;\r
312 EFI_STATUS Status;\r
7b8883c6 313 UINT64 Data;\r
eb290d02
FT
314\r
315 PciIo = Private->PciIo;\r
7b8883c6
FT
316 Data = ReadUnaligned64 ((UINT64*)Asq);\r
317\r
eb290d02
FT
318 Status = PciIo->Mem.Write (\r
319 PciIo,\r
7b8883c6 320 EfiPciIoWidthUint32,\r
eb290d02
FT
321 NVME_BAR,\r
322 NVME_ASQ_OFFSET,\r
7b8883c6
FT
323 2,\r
324 &Data\r
eb290d02
FT
325 );\r
326\r
327 if (EFI_ERROR(Status)) {\r
328 return Status;\r
329 }\r
330\r
c5921812 331 DEBUG ((EFI_D_INFO, "Asq: %lx\n", *Asq));\r
eb290d02
FT
332\r
333 return EFI_SUCCESS;\r
334}\r
335\r
336/**\r
337 Read Nvm Express admin completion queue base address register.\r
338\r
339 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
340 @param Acq The buffer used to store admin completion queue base address register content.\r
341\r
342 @return EFI_SUCCESS Successfully read the admin completion queue base address register content.\r
343 @return EFI_DEVICE_ERROR Fail to read the admin completion queue base address register.\r
344\r
345**/\r
346EFI_STATUS\r
347ReadNvmeAdminCompletionQueueBaseAddress (\r
348 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
349 IN NVME_ACQ *Acq\r
350 )\r
351{\r
352 EFI_PCI_IO_PROTOCOL *PciIo;\r
353 EFI_STATUS Status;\r
7b8883c6 354 UINT64 Data;\r
eb290d02
FT
355\r
356 PciIo = Private->PciIo;\r
7b8883c6 357\r
eb290d02
FT
358 Status = PciIo->Mem.Read (\r
359 PciIo,\r
7b8883c6 360 EfiPciIoWidthUint32,\r
eb290d02
FT
361 NVME_BAR,\r
362 NVME_ACQ_OFFSET,\r
7b8883c6
FT
363 2,\r
364 &Data\r
eb290d02
FT
365 );\r
366\r
367 if (EFI_ERROR(Status)) {\r
368 return Status;\r
369 }\r
370\r
7b8883c6 371 WriteUnaligned64 ((UINT64*)Acq, Data);\r
eb290d02
FT
372 return EFI_SUCCESS;\r
373}\r
374\r
375/**\r
376 Write Nvm Express admin completion queue base address register.\r
377\r
378 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
379 @param Acq The buffer used to store the content to be written into admin completion queue base address register.\r
380\r
381 @return EFI_SUCCESS Successfully write data into the admin completion queue base address register.\r
382 @return EFI_DEVICE_ERROR Fail to write data into the admin completion queue base address register.\r
383\r
384**/\r
385EFI_STATUS\r
386WriteNvmeAdminCompletionQueueBaseAddress (\r
387 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
388 IN NVME_ACQ *Acq\r
389 )\r
390{\r
391 EFI_PCI_IO_PROTOCOL *PciIo;\r
392 EFI_STATUS Status;\r
7b8883c6 393 UINT64 Data;\r
eb290d02
FT
394\r
395 PciIo = Private->PciIo;\r
7b8883c6
FT
396 Data = ReadUnaligned64 ((UINT64*)Acq);\r
397\r
eb290d02
FT
398 Status = PciIo->Mem.Write (\r
399 PciIo,\r
7b8883c6 400 EfiPciIoWidthUint32,\r
eb290d02
FT
401 NVME_BAR,\r
402 NVME_ACQ_OFFSET,\r
7b8883c6
FT
403 2,\r
404 &Data\r
eb290d02
FT
405 );\r
406\r
407 if (EFI_ERROR(Status)) {\r
408 return Status;\r
409 }\r
410\r
c5921812 411 DEBUG ((EFI_D_INFO, "Acq: %lxh\n", *Acq));\r
eb290d02
FT
412\r
413 return EFI_SUCCESS;\r
414}\r
415\r
416/**\r
417 Disable the Nvm Express controller.\r
418\r
419 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
420\r
421 @return EFI_SUCCESS Successfully disable the controller.\r
422 @return EFI_DEVICE_ERROR Fail to disable the controller.\r
423\r
424**/\r
425EFI_STATUS\r
426NvmeDisableController (\r
427 IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
428 )\r
429{\r
430 NVME_CC Cc;\r
431 NVME_CSTS Csts;\r
432 EFI_STATUS Status;\r
4ab4497c
TF
433 UINT32 Index;\r
434 UINT8 Timeout;\r
eb290d02
FT
435\r
436 //\r
437 // Read Controller Configuration Register.\r
438 //\r
439 Status = ReadNvmeControllerConfiguration (Private, &Cc);\r
440 if (EFI_ERROR(Status)) {\r
441 return Status;\r
442 }\r
443\r
444 Cc.En = 0;\r
445\r
446 //\r
447 // Disable the controller.\r
448 //\r
449 Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
450\r
451 if (EFI_ERROR(Status)) {\r
452 return Status;\r
453 }\r
454\r
eb290d02 455 //\r
4ab4497c
TF
456 // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to transition from 1 to 0 after\r
457 // Cc.Enable transition from 1 to 0. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.\r
eb290d02 458 //\r
4ab4497c
TF
459 if (Private->Cap.To == 0) {\r
460 Timeout = 1;\r
461 } else {\r
462 Timeout = Private->Cap.To;\r
463 }\r
eb290d02 464\r
4ab4497c
TF
465 for(Index = (Timeout * 500); Index != 0; --Index) {\r
466 gBS->Stall(1000);\r
467\r
468 //\r
469 // Check if the controller is initialized\r
470 //\r
471 Status = ReadNvmeControllerStatus (Private, &Csts);\r
472\r
473 if (EFI_ERROR(Status)) {\r
474 return Status;\r
475 }\r
476\r
477 if (Csts.Rdy == 0) {\r
478 break;\r
479 }\r
eb290d02
FT
480 }\r
481\r
4ab4497c
TF
482 if (Index == 0) {\r
483 Status = EFI_DEVICE_ERROR;\r
eb290d02
FT
484 }\r
485\r
486 DEBUG ((EFI_D_INFO, "NVMe controller is disabled with status [%r].\n", Status));\r
487 return Status;\r
488}\r
489\r
490/**\r
491 Enable the Nvm Express controller.\r
492\r
493 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
494\r
495 @return EFI_SUCCESS Successfully enable the controller.\r
496 @return EFI_DEVICE_ERROR Fail to enable the controller.\r
497 @return EFI_TIMEOUT Fail to enable the controller in given time slot.\r
498\r
499**/\r
500EFI_STATUS\r
501NvmeEnableController (\r
502 IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
503 )\r
504{\r
505 NVME_CC Cc;\r
506 NVME_CSTS Csts;\r
507 EFI_STATUS Status;\r
508 UINT32 Index;\r
509 UINT8 Timeout;\r
510\r
511 //\r
d6c55989
FT
512 // Enable the controller.\r
513 // CC.AMS, CC.MPS and CC.CSS are all set to 0.\r
eb290d02
FT
514 //\r
515 ZeroMem (&Cc, sizeof (NVME_CC));\r
166801d2
FT
516 Cc.En = 1;\r
517 Cc.Iosqes = 6;\r
518 Cc.Iocqes = 4;\r
eb290d02 519\r
166801d2 520 Status = WriteNvmeControllerConfiguration (Private, &Cc);\r
eb290d02
FT
521 if (EFI_ERROR(Status)) {\r
522 return Status;\r
523 }\r
524\r
525 //\r
526 // Cap.To specifies max delay time in 500ms increments for Csts.Rdy to set after\r
527 // Cc.Enable. Loop produces a 1 millisecond delay per itteration, up to 500 * Cap.To.\r
528 //\r
529 if (Private->Cap.To == 0) {\r
530 Timeout = 1;\r
531 } else {\r
532 Timeout = Private->Cap.To;\r
533 }\r
534\r
535 for(Index = (Timeout * 500); Index != 0; --Index) {\r
536 gBS->Stall(1000);\r
537\r
538 //\r
539 // Check if the controller is initialized\r
540 //\r
541 Status = ReadNvmeControllerStatus (Private, &Csts);\r
542\r
543 if (EFI_ERROR(Status)) {\r
544 return Status;\r
545 }\r
546\r
547 if (Csts.Rdy) {\r
548 break;\r
549 }\r
550 }\r
551\r
552 if (Index == 0) {\r
553 Status = EFI_TIMEOUT;\r
554 }\r
555\r
556 DEBUG ((EFI_D_INFO, "NVMe controller is enabled with status [%r].\n", Status));\r
557 return Status;\r
558}\r
559\r
560/**\r
561 Get identify controller data.\r
562\r
563 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
564 @param Buffer The buffer used to store the identify controller data.\r
565\r
566 @return EFI_SUCCESS Successfully get the identify controller data.\r
567 @return EFI_DEVICE_ERROR Fail to get the identify controller data.\r
568\r
569**/\r
570EFI_STATUS\r
571NvmeIdentifyController (\r
572 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
573 IN VOID *Buffer\r
574 )\r
575{\r
d6c55989
FT
576 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
577 EFI_NVM_EXPRESS_COMMAND Command;\r
578 EFI_NVM_EXPRESS_COMPLETION Completion;\r
eb290d02
FT
579 EFI_STATUS Status;\r
580\r
d6c55989
FT
581 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
582 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));\r
583 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
eb290d02 584\r
754b489b 585 Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
eb290d02
FT
586 //\r
587 // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.\r
588 // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.\r
589 //\r
590 Command.Nsid = 0;\r
591\r
592 CommandPacket.NvmeCmd = &Command;\r
d6c55989 593 CommandPacket.NvmeCompletion = &Completion;\r
eb290d02
FT
594 CommandPacket.TransferBuffer = Buffer;\r
595 CommandPacket.TransferLength = sizeof (NVME_ADMIN_CONTROLLER_DATA);\r
596 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
d6c55989 597 CommandPacket.QueueType = NVME_ADMIN_QUEUE;\r
eb290d02
FT
598 //\r
599 // Set bit 0 (Cns bit) to 1 to identify a controller\r
600 //\r
601 Command.Cdw10 = 1;\r
602 Command.Flags = CDW10_VALID;\r
603\r
604 Status = Private->Passthru.PassThru (\r
605 &Private->Passthru,\r
606 NVME_CONTROLLER_ID,\r
eb290d02
FT
607 &CommandPacket,\r
608 NULL\r
609 );\r
610\r
611 return Status;\r
612}\r
613\r
614/**\r
615 Get specified identify namespace data.\r
616\r
617 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
618 @param NamespaceId The specified namespace identifier.\r
619 @param Buffer The buffer used to store the identify namespace data.\r
620\r
621 @return EFI_SUCCESS Successfully get the identify namespace data.\r
622 @return EFI_DEVICE_ERROR Fail to get the identify namespace data.\r
623\r
624**/\r
625EFI_STATUS\r
626NvmeIdentifyNamespace (\r
627 IN NVME_CONTROLLER_PRIVATE_DATA *Private,\r
628 IN UINT32 NamespaceId,\r
629 IN VOID *Buffer\r
630 )\r
631{\r
d6c55989
FT
632 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
633 EFI_NVM_EXPRESS_COMMAND Command;\r
634 EFI_NVM_EXPRESS_COMPLETION Completion;\r
eb290d02
FT
635 EFI_STATUS Status;\r
636\r
d6c55989
FT
637 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
638 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));\r
639 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
eb290d02 640\r
d6c55989
FT
641 CommandPacket.NvmeCmd = &Command;\r
642 CommandPacket.NvmeCompletion = &Completion;\r
eb290d02 643\r
754b489b 644 Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;\r
eb290d02
FT
645 Command.Nsid = NamespaceId;\r
646 CommandPacket.TransferBuffer = Buffer;\r
647 CommandPacket.TransferLength = sizeof (NVME_ADMIN_NAMESPACE_DATA);\r
648 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
d6c55989 649 CommandPacket.QueueType = NVME_ADMIN_QUEUE;\r
eb290d02
FT
650 //\r
651 // Set bit 0 (Cns bit) to 1 to identify a namespace\r
652 //\r
653 CommandPacket.NvmeCmd->Cdw10 = 0;\r
654 CommandPacket.NvmeCmd->Flags = CDW10_VALID;\r
655\r
656 Status = Private->Passthru.PassThru (\r
657 &Private->Passthru,\r
658 NamespaceId,\r
eb290d02
FT
659 &CommandPacket,\r
660 NULL\r
661 );\r
662\r
663 return Status;\r
664}\r
665\r
666/**\r
667 Create io completion queue.\r
668\r
669 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
670\r
671 @return EFI_SUCCESS Successfully create io completion queue.\r
672 @return EFI_DEVICE_ERROR Fail to create io completion queue.\r
673\r
674**/\r
675EFI_STATUS\r
676NvmeCreateIoCompletionQueue (\r
677 IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
678 )\r
679{\r
d6c55989
FT
680 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
681 EFI_NVM_EXPRESS_COMMAND Command;\r
682 EFI_NVM_EXPRESS_COMPLETION Completion;\r
eb290d02
FT
683 EFI_STATUS Status;\r
684 NVME_ADMIN_CRIOCQ CrIoCq;\r
758ea946 685 UINT32 Index;\r
05bf4747 686 UINT16 QueueSize;\r
758ea946 687\r
6b571c4d
HW
688 Status = EFI_SUCCESS;\r
689\r
758ea946
HW
690 for (Index = 1; Index < NVME_MAX_QUEUES; Index++) {\r
691 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
692 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));\r
693 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
694 ZeroMem (&CrIoCq, sizeof(NVME_ADMIN_CRIOCQ));\r
695\r
696 CommandPacket.NvmeCmd = &Command;\r
697 CommandPacket.NvmeCompletion = &Completion;\r
698\r
699 Command.Cdw0.Opcode = NVME_ADMIN_CRIOCQ_CMD;\r
700 CommandPacket.TransferBuffer = Private->CqBufferPciAddr[Index];\r
701 CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
702 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
703 CommandPacket.QueueType = NVME_ADMIN_QUEUE;\r
704\r
05bf4747
HW
705 if (Index == 1) {\r
706 QueueSize = NVME_CCQ_SIZE;\r
707 } else {\r
708 if (Private->Cap.Mqes > NVME_ASYNC_CCQ_SIZE) {\r
709 QueueSize = NVME_ASYNC_CCQ_SIZE;\r
710 } else {\r
711 QueueSize = Private->Cap.Mqes;\r
712 }\r
713 }\r
714\r
758ea946 715 CrIoCq.Qid = Index;\r
05bf4747 716 CrIoCq.Qsize = QueueSize;\r
758ea946
HW
717 CrIoCq.Pc = 1;\r
718 CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoCq, sizeof (NVME_ADMIN_CRIOCQ));\r
719 CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
720\r
721 Status = Private->Passthru.PassThru (\r
722 &Private->Passthru,\r
723 0,\r
724 &CommandPacket,\r
725 NULL\r
726 );\r
727 if (EFI_ERROR (Status)) {\r
728 break;\r
729 }\r
730 }\r
eb290d02
FT
731\r
732 return Status;\r
733}\r
734\r
735/**\r
736 Create io submission queue.\r
737\r
738 @param Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
739\r
740 @return EFI_SUCCESS Successfully create io submission queue.\r
741 @return EFI_DEVICE_ERROR Fail to create io submission queue.\r
742\r
743**/\r
744EFI_STATUS\r
745NvmeCreateIoSubmissionQueue (\r
746 IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
747 )\r
748{\r
d6c55989
FT
749 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
750 EFI_NVM_EXPRESS_COMMAND Command;\r
751 EFI_NVM_EXPRESS_COMPLETION Completion;\r
eb290d02
FT
752 EFI_STATUS Status;\r
753 NVME_ADMIN_CRIOSQ CrIoSq;\r
758ea946 754 UINT32 Index;\r
05bf4747 755 UINT16 QueueSize;\r
758ea946 756\r
6b571c4d
HW
757 Status = EFI_SUCCESS;\r
758\r
758ea946
HW
759 for (Index = 1; Index < NVME_MAX_QUEUES; Index++) {\r
760 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
761 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));\r
762 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));\r
763 ZeroMem (&CrIoSq, sizeof(NVME_ADMIN_CRIOSQ));\r
764\r
765 CommandPacket.NvmeCmd = &Command;\r
766 CommandPacket.NvmeCompletion = &Completion;\r
767\r
768 Command.Cdw0.Opcode = NVME_ADMIN_CRIOSQ_CMD;\r
769 CommandPacket.TransferBuffer = Private->SqBufferPciAddr[Index];\r
770 CommandPacket.TransferLength = EFI_PAGE_SIZE;\r
771 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
772 CommandPacket.QueueType = NVME_ADMIN_QUEUE;\r
773\r
05bf4747
HW
774 if (Index == 1) {\r
775 QueueSize = NVME_CSQ_SIZE;\r
776 } else {\r
777 if (Private->Cap.Mqes > NVME_ASYNC_CSQ_SIZE) {\r
778 QueueSize = NVME_ASYNC_CSQ_SIZE;\r
779 } else {\r
780 QueueSize = Private->Cap.Mqes;\r
781 }\r
782 }\r
783\r
758ea946 784 CrIoSq.Qid = Index;\r
05bf4747 785 CrIoSq.Qsize = QueueSize;\r
758ea946
HW
786 CrIoSq.Pc = 1;\r
787 CrIoSq.Cqid = Index;\r
788 CrIoSq.Qprio = 0;\r
789 CopyMem (&CommandPacket.NvmeCmd->Cdw10, &CrIoSq, sizeof (NVME_ADMIN_CRIOSQ));\r
790 CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID;\r
791\r
792 Status = Private->Passthru.PassThru (\r
793 &Private->Passthru,\r
794 0,\r
795 &CommandPacket,\r
796 NULL\r
797 );\r
798 if (EFI_ERROR (Status)) {\r
799 break;\r
800 }\r
801 }\r
eb290d02
FT
802\r
803 return Status;\r
804}\r
805\r
806/**\r
807 Initialize the Nvm Express controller.\r
808\r
809 @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure.\r
810\r
811 @retval EFI_SUCCESS The NVM Express Controller is initialized successfully.\r
812 @retval Others A device error occurred while initializing the controller.\r
813\r
814**/\r
815EFI_STATUS\r
816NvmeControllerInit (\r
817 IN NVME_CONTROLLER_PRIVATE_DATA *Private\r
818 )\r
819{\r
820 EFI_STATUS Status;\r
821 EFI_PCI_IO_PROTOCOL *PciIo;\r
822 UINT64 Supports;\r
823 NVME_AQA Aqa;\r
824 NVME_ASQ Asq;\r
825 NVME_ACQ Acq;\r
da7c7274
FT
826 UINT8 Sn[21];\r
827 UINT8 Mn[41];\r
eb290d02
FT
828 //\r
829 // Save original PCI attributes and enable this controller.\r
830 //\r
831 PciIo = Private->PciIo;\r
832 Status = PciIo->Attributes (\r
833 PciIo,\r
834 EfiPciIoAttributeOperationGet,\r
835 0,\r
836 &Private->PciAttributes\r
837 );\r
838\r
839 if (EFI_ERROR (Status)) {\r
840 return Status;\r
841 }\r
842\r
843 Status = PciIo->Attributes (\r
844 PciIo,\r
845 EfiPciIoAttributeOperationSupported,\r
846 0,\r
847 &Supports\r
848 );\r
849\r
850 if (!EFI_ERROR (Status)) {\r
6e1e5405 851 Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;\r
eb290d02
FT
852 Status = PciIo->Attributes (\r
853 PciIo,\r
854 EfiPciIoAttributeOperationEnable,\r
855 Supports,\r
856 NULL\r
857 );\r
858 }\r
859\r
860 if (EFI_ERROR (Status)) {\r
861 DEBUG ((EFI_D_INFO, "NvmeControllerInit: failed to enable controller\n"));\r
862 return Status;\r
863 }\r
864\r
4e28ea2c
AB
865 //\r
866 // Enable 64-bit DMA support in the PCI layer.\r
867 //\r
868 Status = PciIo->Attributes (\r
869 PciIo,\r
870 EfiPciIoAttributeOperationEnable,\r
871 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
872 NULL\r
873 );\r
874 if (EFI_ERROR (Status)) {\r
875 DEBUG ((EFI_D_WARN, "NvmeControllerInit: failed to enable 64-bit DMA (%r)\n", Status));\r
876 }\r
877\r
eb290d02
FT
878 //\r
879 // Read the Controller Capabilities register and verify that the NVM command set is supported\r
880 //\r
881 Status = ReadNvmeControllerCapabilities (Private, &Private->Cap);\r
882 if (EFI_ERROR (Status)) {\r
883 return Status;\r
884 }\r
885\r
886 if (Private->Cap.Css != 0x01) {\r
887 DEBUG ((EFI_D_INFO, "NvmeControllerInit: the controller doesn't support NVMe command set\n"));\r
888 return EFI_UNSUPPORTED;\r
889 }\r
890\r
891 //\r
892 // Currently the driver only supports 4k page size.\r
893 //\r
894 ASSERT ((Private->Cap.Mpsmin + 12) <= EFI_PAGE_SHIFT);\r
895\r
896 Private->Cid[0] = 0;\r
897 Private->Cid[1] = 0;\r
758ea946 898 Private->Cid[2] = 0;\r
6523ae8b
TF
899 Private->Pt[0] = 0;\r
900 Private->Pt[1] = 0;\r
758ea946 901 Private->Pt[2] = 0;\r
6523ae8b
TF
902 Private->SqTdbl[0].Sqt = 0;\r
903 Private->SqTdbl[1].Sqt = 0;\r
758ea946 904 Private->SqTdbl[2].Sqt = 0;\r
6523ae8b
TF
905 Private->CqHdbl[0].Cqh = 0;\r
906 Private->CqHdbl[1].Cqh = 0;\r
758ea946
HW
907 Private->CqHdbl[2].Cqh = 0;\r
908 Private->AsyncSqHead = 0;\r
eb290d02
FT
909\r
910 Status = NvmeDisableController (Private);\r
911\r
912 if (EFI_ERROR(Status)) {\r
913 return Status;\r
914 }\r
915\r
916 //\r
917 // set number of entries admin submission & completion queues.\r
918 //\r
d45c8c30
FT
919 Aqa.Asqs = NVME_ASQ_SIZE;\r
920 Aqa.Rsvd1 = 0;\r
921 Aqa.Acqs = NVME_ACQ_SIZE;\r
922 Aqa.Rsvd2 = 0;\r
eb290d02
FT
923\r
924 //\r
925 // Address of admin submission queue.\r
926 //\r
c5921812 927 Asq = (UINT64)(UINTN)(Private->BufferPciAddr) & ~0xFFF;\r
eb290d02
FT
928\r
929 //\r
930 // Address of admin completion queue.\r
931 //\r
c5921812 932 Acq = (UINT64)(UINTN)(Private->BufferPciAddr + EFI_PAGE_SIZE) & ~0xFFF;\r
eb290d02
FT
933\r
934 //\r
935 // Address of I/O submission & completion queue.\r
936 //\r
758ea946 937 ZeroMem (Private->Buffer, EFI_PAGES_TO_SIZE (6));\r
eb290d02
FT
938 Private->SqBuffer[0] = (NVME_SQ *)(UINTN)(Private->Buffer);\r
939 Private->SqBufferPciAddr[0] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr);\r
940 Private->CqBuffer[0] = (NVME_CQ *)(UINTN)(Private->Buffer + 1 * EFI_PAGE_SIZE);\r
941 Private->CqBufferPciAddr[0] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 1 * EFI_PAGE_SIZE);\r
942 Private->SqBuffer[1] = (NVME_SQ *)(UINTN)(Private->Buffer + 2 * EFI_PAGE_SIZE);\r
943 Private->SqBufferPciAddr[1] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr + 2 * EFI_PAGE_SIZE);\r
944 Private->CqBuffer[1] = (NVME_CQ *)(UINTN)(Private->Buffer + 3 * EFI_PAGE_SIZE);\r
945 Private->CqBufferPciAddr[1] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 3 * EFI_PAGE_SIZE);\r
758ea946
HW
946 Private->SqBuffer[2] = (NVME_SQ *)(UINTN)(Private->Buffer + 4 * EFI_PAGE_SIZE);\r
947 Private->SqBufferPciAddr[2] = (NVME_SQ *)(UINTN)(Private->BufferPciAddr + 4 * EFI_PAGE_SIZE);\r
948 Private->CqBuffer[2] = (NVME_CQ *)(UINTN)(Private->Buffer + 5 * EFI_PAGE_SIZE);\r
949 Private->CqBufferPciAddr[2] = (NVME_CQ *)(UINTN)(Private->BufferPciAddr + 5 * EFI_PAGE_SIZE);\r
eb290d02
FT
950\r
951 DEBUG ((EFI_D_INFO, "Private->Buffer = [%016X]\n", (UINT64)(UINTN)Private->Buffer));\r
758ea946
HW
952 DEBUG ((EFI_D_INFO, "Admin Submission Queue size (Aqa.Asqs) = [%08X]\n", Aqa.Asqs));\r
953 DEBUG ((EFI_D_INFO, "Admin Completion Queue size (Aqa.Acqs) = [%08X]\n", Aqa.Acqs));\r
954 DEBUG ((EFI_D_INFO, "Admin Submission Queue (SqBuffer[0]) = [%016X]\n", Private->SqBuffer[0]));\r
955 DEBUG ((EFI_D_INFO, "Admin Completion Queue (CqBuffer[0]) = [%016X]\n", Private->CqBuffer[0]));\r
956 DEBUG ((EFI_D_INFO, "Sync I/O Submission Queue (SqBuffer[1]) = [%016X]\n", Private->SqBuffer[1]));\r
957 DEBUG ((EFI_D_INFO, "Sync I/O Completion Queue (CqBuffer[1]) = [%016X]\n", Private->CqBuffer[1]));\r
958 DEBUG ((EFI_D_INFO, "Async I/O Submission Queue (SqBuffer[2]) = [%016X]\n", Private->SqBuffer[2]));\r
959 DEBUG ((EFI_D_INFO, "Async I/O Completion Queue (CqBuffer[2]) = [%016X]\n", Private->CqBuffer[2]));\r
eb290d02
FT
960\r
961 //\r
962 // Program admin queue attributes.\r
963 //\r
964 Status = WriteNvmeAdminQueueAttributes (Private, &Aqa);\r
965\r
966 if (EFI_ERROR(Status)) {\r
967 return Status;\r
968 }\r
969\r
970 //\r
971 // Program admin submission queue address.\r
972 //\r
973 Status = WriteNvmeAdminSubmissionQueueBaseAddress (Private, &Asq);\r
974\r
975 if (EFI_ERROR(Status)) {\r
976 return Status;\r
977 }\r
978\r
979 //\r
980 // Program admin completion queue address.\r
981 //\r
982 Status = WriteNvmeAdminCompletionQueueBaseAddress (Private, &Acq);\r
983\r
984 if (EFI_ERROR(Status)) {\r
985 return Status;\r
986 }\r
987\r
988 Status = NvmeEnableController (Private);\r
989 if (EFI_ERROR(Status)) {\r
990 return Status;\r
991 }\r
992\r
eb290d02
FT
993 //\r
994 // Allocate buffer for Identify Controller data\r
995 //\r
eb290d02 996 if (Private->ControllerData == NULL) {\r
6523ae8b
TF
997 Private->ControllerData = (NVME_ADMIN_CONTROLLER_DATA *)AllocateZeroPool (sizeof(NVME_ADMIN_CONTROLLER_DATA));\r
998 \r
999 if (Private->ControllerData == NULL) {\r
1000 return EFI_OUT_OF_RESOURCES;\r
1001 }\r
eb290d02
FT
1002 }\r
1003\r
1004 //\r
1005 // Get current Identify Controller Data\r
1006 //\r
1007 Status = NvmeIdentifyController (Private, Private->ControllerData);\r
1008\r
1009 if (EFI_ERROR(Status)) {\r
1010 FreePool(Private->ControllerData);\r
1011 Private->ControllerData = NULL;\r
1012 return EFI_NOT_FOUND;\r
1013 }\r
7b8883c6
FT
1014\r
1015 //\r
1016 // Dump NvmExpress Identify Controller Data\r
1017 //\r
da7c7274
FT
1018 CopyMem (Sn, Private->ControllerData->Sn, sizeof (Private->ControllerData->Sn));\r
1019 Sn[20] = 0;\r
1020 CopyMem (Mn, Private->ControllerData->Mn, sizeof (Private->ControllerData->Mn));\r
1021 Mn[40] = 0;\r
7b8883c6
FT
1022 DEBUG ((EFI_D_INFO, " == NVME IDENTIFY CONTROLLER DATA ==\n"));\r
1023 DEBUG ((EFI_D_INFO, " PCI VID : 0x%x\n", Private->ControllerData->Vid));\r
1024 DEBUG ((EFI_D_INFO, " PCI SSVID : 0x%x\n", Private->ControllerData->Ssvid));\r
da7c7274
FT
1025 DEBUG ((EFI_D_INFO, " SN : %a\n", Sn));\r
1026 DEBUG ((EFI_D_INFO, " MN : %a\n", Mn));\r
7b8883c6
FT
1027 DEBUG ((EFI_D_INFO, " FR : 0x%x\n", *((UINT64*)Private->ControllerData->Fr)));\r
1028 DEBUG ((EFI_D_INFO, " RAB : 0x%x\n", Private->ControllerData->Rab));\r
4ab4497c 1029 DEBUG ((EFI_D_INFO, " IEEE : 0x%x\n", *(UINT32*)Private->ControllerData->Ieee_oui));\r
7b8883c6
FT
1030 DEBUG ((EFI_D_INFO, " AERL : 0x%x\n", Private->ControllerData->Aerl));\r
1031 DEBUG ((EFI_D_INFO, " SQES : 0x%x\n", Private->ControllerData->Sqes));\r
1032 DEBUG ((EFI_D_INFO, " CQES : 0x%x\n", Private->ControllerData->Cqes));\r
1033 DEBUG ((EFI_D_INFO, " NN : 0x%x\n", Private->ControllerData->Nn));\r
1034\r
d6c55989 1035 //\r
758ea946
HW
1036 // Create two I/O completion queues.\r
1037 // One for blocking I/O, one for non-blocking I/O.\r
d6c55989
FT
1038 //\r
1039 Status = NvmeCreateIoCompletionQueue (Private);\r
1040 if (EFI_ERROR(Status)) {\r
1041 return Status;\r
1042 }\r
1043\r
1044 //\r
758ea946
HW
1045 // Create two I/O Submission queues.\r
1046 // One for blocking I/O, one for non-blocking I/O.\r
d6c55989
FT
1047 //\r
1048 Status = NvmeCreateIoSubmissionQueue (Private);\r
1049 if (EFI_ERROR(Status)) {\r
1050 return Status;\r
1051 }\r
1052\r
eb290d02
FT
1053 return Status;\r
1054}\r
1055\r