]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Pci/EhciDxe/EhciReg.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciDxe / EhciReg.c
... / ...
CommitLineData
1/** @file\r
2\r
3 The EHCI register operation routines.\r
4\r
5Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>\r
6SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10\r
11#include "Ehci.h"\r
12\r
13\r
14/**\r
15 Read EHCI capability register.\r
16\r
17 @param Ehc The EHCI device.\r
18 @param Offset Capability register address.\r
19\r
20 @return The register content read.\r
21 @retval If err, return 0xffff.\r
22\r
23**/\r
24UINT32\r
25EhcReadCapRegister (\r
26 IN USB2_HC_DEV *Ehc,\r
27 IN UINT32 Offset\r
28 )\r
29{\r
30 UINT32 Data;\r
31 EFI_STATUS Status;\r
32\r
33 Status = Ehc->PciIo->Mem.Read (\r
34 Ehc->PciIo,\r
35 EfiPciIoWidthUint32,\r
36 EHC_BAR_INDEX,\r
37 (UINT64) Offset,\r
38 1,\r
39 &Data\r
40 );\r
41\r
42 if (EFI_ERROR (Status)) {\r
43 DEBUG ((EFI_D_ERROR, "EhcReadCapRegister: Pci Io read error - %r at %d\n", Status, Offset));\r
44 Data = 0xFFFF;\r
45 }\r
46\r
47 return Data;\r
48}\r
49\r
50/**\r
51 Read EHCI debug port register.\r
52\r
53 @param Ehc The EHCI device.\r
54 @param Offset Debug port register offset.\r
55\r
56 @return The register content read.\r
57 @retval If err, return 0xffff.\r
58\r
59**/\r
60UINT32\r
61EhcReadDbgRegister (\r
62 IN CONST USB2_HC_DEV *Ehc,\r
63 IN UINT32 Offset\r
64 )\r
65{\r
66 UINT32 Data;\r
67 EFI_STATUS Status;\r
68\r
69 Status = Ehc->PciIo->Mem.Read (\r
70 Ehc->PciIo,\r
71 EfiPciIoWidthUint32,\r
72 Ehc->DebugPortBarNum,\r
73 Ehc->DebugPortOffset + Offset,\r
74 1,\r
75 &Data\r
76 );\r
77\r
78 if (EFI_ERROR (Status)) {\r
79 DEBUG ((EFI_D_ERROR, "EhcReadDbgRegister: Pci Io read error - %r at %d\n", Status, Offset));\r
80 Data = 0xFFFF;\r
81 }\r
82\r
83 return Data;\r
84}\r
85\r
86\r
87/**\r
88 Check whether the host controller has an in-use debug port.\r
89\r
90 @param[in] Ehc The Enhanced Host Controller to query.\r
91\r
92 @param[in] PortNumber If PortNumber is not NULL, then query whether\r
93 PortNumber is an in-use debug port on Ehc. (PortNumber\r
94 is taken in UEFI notation, i.e., zero-based.)\r
95 Otherwise, query whether Ehc has any in-use debug\r
96 port.\r
97\r
98 @retval TRUE PortNumber is an in-use debug port on Ehc (if PortNumber is\r
99 not NULL), or some port on Ehc is an in-use debug port\r
100 (otherwise).\r
101\r
102 @retval FALSE PortNumber is not an in-use debug port on Ehc (if PortNumber\r
103 is not NULL), or no port on Ehc is an in-use debug port\r
104 (otherwise).\r
105**/\r
106BOOLEAN\r
107EhcIsDebugPortInUse (\r
108 IN CONST USB2_HC_DEV *Ehc,\r
109 IN CONST UINT8 *PortNumber OPTIONAL\r
110 )\r
111{\r
112 UINT32 State;\r
113\r
114 if (Ehc->DebugPortNum == 0) {\r
115 //\r
116 // The host controller has no debug port.\r
117 //\r
118 return FALSE;\r
119 }\r
120\r
121 //\r
122 // The Debug Port Number field in HCSPARAMS is one-based.\r
123 //\r
124 if (PortNumber != NULL && *PortNumber != Ehc->DebugPortNum - 1) {\r
125 //\r
126 // The caller specified a port, but it's not the debug port of the host\r
127 // controller.\r
128 //\r
129 return FALSE;\r
130 }\r
131\r
132 //\r
133 // Deduce usage from the Control Register.\r
134 //\r
135 State = EhcReadDbgRegister(Ehc, 0);\r
136 return (State & USB_DEBUG_PORT_IN_USE_MASK) == USB_DEBUG_PORT_IN_USE_MASK;\r
137}\r
138\r
139\r
140/**\r
141 Read EHCI Operation register.\r
142\r
143 @param Ehc The EHCI device.\r
144 @param Offset The operation register offset.\r
145\r
146 @return The register content read.\r
147 @retval If err, return 0xffff.\r
148\r
149**/\r
150UINT32\r
151EhcReadOpReg (\r
152 IN USB2_HC_DEV *Ehc,\r
153 IN UINT32 Offset\r
154 )\r
155{\r
156 UINT32 Data;\r
157 EFI_STATUS Status;\r
158\r
159 ASSERT (Ehc->CapLen != 0);\r
160\r
161 Status = Ehc->PciIo->Mem.Read (\r
162 Ehc->PciIo,\r
163 EfiPciIoWidthUint32,\r
164 EHC_BAR_INDEX,\r
165 Ehc->CapLen + Offset,\r
166 1,\r
167 &Data\r
168 );\r
169\r
170 if (EFI_ERROR (Status)) {\r
171 DEBUG ((EFI_D_ERROR, "EhcReadOpReg: Pci Io Read error - %r at %d\n", Status, Offset));\r
172 Data = 0xFFFF;\r
173 }\r
174\r
175 return Data;\r
176}\r
177\r
178\r
179/**\r
180 Write the data to the EHCI operation register.\r
181\r
182 @param Ehc The EHCI device.\r
183 @param Offset EHCI operation register offset.\r
184 @param Data The data to write.\r
185\r
186**/\r
187VOID\r
188EhcWriteOpReg (\r
189 IN USB2_HC_DEV *Ehc,\r
190 IN UINT32 Offset,\r
191 IN UINT32 Data\r
192 )\r
193{\r
194 EFI_STATUS Status;\r
195\r
196 ASSERT (Ehc->CapLen != 0);\r
197\r
198 Status = Ehc->PciIo->Mem.Write (\r
199 Ehc->PciIo,\r
200 EfiPciIoWidthUint32,\r
201 EHC_BAR_INDEX,\r
202 Ehc->CapLen + Offset,\r
203 1,\r
204 &Data\r
205 );\r
206\r
207 if (EFI_ERROR (Status)) {\r
208 DEBUG ((EFI_D_ERROR, "EhcWriteOpReg: Pci Io Write error: %r at %d\n", Status, Offset));\r
209 }\r
210}\r
211\r
212\r
213/**\r
214 Set one bit of the operational register while keeping other bits.\r
215\r
216 @param Ehc The EHCI device.\r
217 @param Offset The offset of the operational register.\r
218 @param Bit The bit mask of the register to set.\r
219\r
220**/\r
221VOID\r
222EhcSetOpRegBit (\r
223 IN USB2_HC_DEV *Ehc,\r
224 IN UINT32 Offset,\r
225 IN UINT32 Bit\r
226 )\r
227{\r
228 UINT32 Data;\r
229\r
230 Data = EhcReadOpReg (Ehc, Offset);\r
231 Data |= Bit;\r
232 EhcWriteOpReg (Ehc, Offset, Data);\r
233}\r
234\r
235\r
236/**\r
237 Clear one bit of the operational register while keeping other bits.\r
238\r
239 @param Ehc The EHCI device.\r
240 @param Offset The offset of the operational register.\r
241 @param Bit The bit mask of the register to clear.\r
242\r
243**/\r
244VOID\r
245EhcClearOpRegBit (\r
246 IN USB2_HC_DEV *Ehc,\r
247 IN UINT32 Offset,\r
248 IN UINT32 Bit\r
249 )\r
250{\r
251 UINT32 Data;\r
252\r
253 Data = EhcReadOpReg (Ehc, Offset);\r
254 Data &= ~Bit;\r
255 EhcWriteOpReg (Ehc, Offset, Data);\r
256}\r
257\r
258\r
259/**\r
260 Wait the operation register's bit as specified by Bit\r
261 to become set (or clear).\r
262\r
263 @param Ehc The EHCI device.\r
264 @param Offset The offset of the operation register.\r
265 @param Bit The bit of the register to wait for.\r
266 @param WaitToSet Wait the bit to set or clear.\r
267 @param Timeout The time to wait before abort (in millisecond).\r
268\r
269 @retval EFI_SUCCESS The bit successfully changed by host controller.\r
270 @retval EFI_TIMEOUT The time out occurred.\r
271\r
272**/\r
273EFI_STATUS\r
274EhcWaitOpRegBit (\r
275 IN USB2_HC_DEV *Ehc,\r
276 IN UINT32 Offset,\r
277 IN UINT32 Bit,\r
278 IN BOOLEAN WaitToSet,\r
279 IN UINT32 Timeout\r
280 )\r
281{\r
282 UINT32 Index;\r
283\r
284 for (Index = 0; Index < Timeout / EHC_SYNC_POLL_INTERVAL + 1; Index++) {\r
285 if (EHC_REG_BIT_IS_SET (Ehc, Offset, Bit) == WaitToSet) {\r
286 return EFI_SUCCESS;\r
287 }\r
288\r
289 gBS->Stall (EHC_SYNC_POLL_INTERVAL);\r
290 }\r
291\r
292 return EFI_TIMEOUT;\r
293}\r
294\r
295\r
296/**\r
297 Add support for UEFI Over Legacy (UoL) feature, stop\r
298 the legacy USB SMI support.\r
299\r
300 @param Ehc The EHCI device.\r
301\r
302**/\r
303VOID\r
304EhcClearLegacySupport (\r
305 IN USB2_HC_DEV *Ehc\r
306 )\r
307{\r
308 UINT32 ExtendCap;\r
309 EFI_PCI_IO_PROTOCOL *PciIo;\r
310 UINT32 Value;\r
311 UINT32 TimeOut;\r
312\r
313 DEBUG ((EFI_D_INFO, "EhcClearLegacySupport: called to clear legacy support\n"));\r
314\r
315 PciIo = Ehc->PciIo;\r
316 ExtendCap = (Ehc->HcCapParams >> 8) & 0xFF;\r
317\r
318 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);\r
319 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);\r
320\r
321 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);\r
322 Value |= (0x1 << 24);\r
323 PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);\r
324\r
325 TimeOut = 40;\r
326 while (TimeOut-- != 0) {\r
327 gBS->Stall (500);\r
328\r
329 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);\r
330\r
331 if ((Value & 0x01010000) == 0x01000000) {\r
332 break;\r
333 }\r
334 }\r
335\r
336 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);\r
337 PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &Value);\r
338}\r
339\r
340\r
341\r
342/**\r
343 Set door bell and wait it to be ACKed by host controller.\r
344 This function is used to synchronize with the hardware.\r
345\r
346 @param Ehc The EHCI device.\r
347 @param Timeout The time to wait before abort (in millisecond, ms).\r
348\r
349 @retval EFI_SUCCESS Synchronized with the hardware.\r
350 @retval EFI_TIMEOUT Time out happened while waiting door bell to set.\r
351\r
352**/\r
353EFI_STATUS\r
354EhcSetAndWaitDoorBell (\r
355 IN USB2_HC_DEV *Ehc,\r
356 IN UINT32 Timeout\r
357 )\r
358{\r
359 EFI_STATUS Status;\r
360 UINT32 Data;\r
361\r
362 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_IAAD);\r
363\r
364 Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_IAA, TRUE, Timeout);\r
365\r
366 //\r
367 // ACK the IAA bit in USBSTS register. Make sure other\r
368 // interrupt bits are not ACKed. These bits are WC (Write Clean).\r
369 //\r
370 Data = EhcReadOpReg (Ehc, EHC_USBSTS_OFFSET);\r
371 Data &= ~USBSTS_INTACK_MASK;\r
372 Data |= USBSTS_IAA;\r
373\r
374 EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, Data);\r
375\r
376 return Status;\r
377}\r
378\r
379\r
380/**\r
381 Clear all the interrutp status bits, these bits\r
382 are Write-Clean.\r
383\r
384 @param Ehc The EHCI device.\r
385\r
386**/\r
387VOID\r
388EhcAckAllInterrupt (\r
389 IN USB2_HC_DEV *Ehc\r
390 )\r
391{\r
392 EhcWriteOpReg (Ehc, EHC_USBSTS_OFFSET, USBSTS_INTACK_MASK);\r
393}\r
394\r
395\r
396/**\r
397 Enable the periodic schedule then wait EHC to\r
398 actually enable it.\r
399\r
400 @param Ehc The EHCI device.\r
401 @param Timeout The time to wait before abort (in millisecond, ms).\r
402\r
403 @retval EFI_SUCCESS The periodical schedule is enabled.\r
404 @retval EFI_TIMEOUT Time out happened while enabling periodic schedule.\r
405\r
406**/\r
407EFI_STATUS\r
408EhcEnablePeriodSchd (\r
409 IN USB2_HC_DEV *Ehc,\r
410 IN UINT32 Timeout\r
411 )\r
412{\r
413 EFI_STATUS Status;\r
414\r
415 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_PERIOD);\r
416\r
417 Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_PERIOD_ENABLED, TRUE, Timeout);\r
418 return Status;\r
419}\r
420\r
421\r
422\r
423\r
424\r
425\r
426/**\r
427 Enable asynchrounous schedule.\r
428\r
429 @param Ehc The EHCI device.\r
430 @param Timeout Time to wait before abort.\r
431\r
432 @retval EFI_SUCCESS The EHCI asynchronous schedule is enabled.\r
433 @return Others Failed to enable the asynchronous scheudle.\r
434\r
435**/\r
436EFI_STATUS\r
437EhcEnableAsyncSchd (\r
438 IN USB2_HC_DEV *Ehc,\r
439 IN UINT32 Timeout\r
440 )\r
441{\r
442 EFI_STATUS Status;\r
443\r
444 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_ENABLE_ASYNC);\r
445\r
446 Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_ASYNC_ENABLED, TRUE, Timeout);\r
447 return Status;\r
448}\r
449\r
450\r
451\r
452\r
453\r
454\r
455\r
456/**\r
457 Whether Ehc is halted.\r
458\r
459 @param Ehc The EHCI device.\r
460\r
461 @retval TRUE The controller is halted.\r
462 @retval FALSE It isn't halted.\r
463\r
464**/\r
465BOOLEAN\r
466EhcIsHalt (\r
467 IN USB2_HC_DEV *Ehc\r
468 )\r
469{\r
470 return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT);\r
471}\r
472\r
473\r
474/**\r
475 Whether system error occurred.\r
476\r
477 @param Ehc The EHCI device.\r
478\r
479 @return TRUE System error happened.\r
480 @return FALSE No system error.\r
481\r
482**/\r
483BOOLEAN\r
484EhcIsSysError (\r
485 IN USB2_HC_DEV *Ehc\r
486 )\r
487{\r
488 return EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_SYS_ERROR);\r
489}\r
490\r
491\r
492/**\r
493 Reset the host controller.\r
494\r
495 @param Ehc The EHCI device.\r
496 @param Timeout Time to wait before abort (in millisecond, ms).\r
497\r
498 @retval EFI_SUCCESS The host controller is reset.\r
499 @return Others Failed to reset the host.\r
500\r
501**/\r
502EFI_STATUS\r
503EhcResetHC (\r
504 IN USB2_HC_DEV *Ehc,\r
505 IN UINT32 Timeout\r
506 )\r
507{\r
508 EFI_STATUS Status;\r
509\r
510 //\r
511 // Host can only be reset when it is halt. If not so, halt it\r
512 //\r
513 if (!EHC_REG_BIT_IS_SET (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT)) {\r
514 Status = EhcHaltHC (Ehc, Timeout);\r
515\r
516 if (EFI_ERROR (Status)) {\r
517 return Status;\r
518 }\r
519 }\r
520\r
521 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET);\r
522 Status = EhcWaitOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RESET, FALSE, Timeout);\r
523 return Status;\r
524}\r
525\r
526\r
527/**\r
528 Halt the host controller.\r
529\r
530 @param Ehc The EHCI device.\r
531 @param Timeout Time to wait before abort.\r
532\r
533 @retval EFI_SUCCESS The EHCI is halt.\r
534 @retval EFI_TIMEOUT Failed to halt the controller before Timeout.\r
535\r
536**/\r
537EFI_STATUS\r
538EhcHaltHC (\r
539 IN USB2_HC_DEV *Ehc,\r
540 IN UINT32 Timeout\r
541 )\r
542{\r
543 EFI_STATUS Status;\r
544\r
545 EhcClearOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);\r
546 Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, TRUE, Timeout);\r
547 return Status;\r
548}\r
549\r
550\r
551/**\r
552 Set the EHCI to run.\r
553\r
554 @param Ehc The EHCI device.\r
555 @param Timeout Time to wait before abort.\r
556\r
557 @retval EFI_SUCCESS The EHCI is running.\r
558 @return Others Failed to set the EHCI to run.\r
559\r
560**/\r
561EFI_STATUS\r
562EhcRunHC (\r
563 IN USB2_HC_DEV *Ehc,\r
564 IN UINT32 Timeout\r
565 )\r
566{\r
567 EFI_STATUS Status;\r
568\r
569 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);\r
570 Status = EhcWaitOpRegBit (Ehc, EHC_USBSTS_OFFSET, USBSTS_HALT, FALSE, Timeout);\r
571 return Status;\r
572}\r
573\r
574\r
575/**\r
576 Initialize the HC hardware.\r
577 EHCI spec lists the five things to do to initialize the hardware:\r
578 1. Program CTRLDSSEGMENT\r
579 2. Set USBINTR to enable interrupts\r
580 3. Set periodic list base\r
581 4. Set USBCMD, interrupt threshold, frame list size etc\r
582 5. Write 1 to CONFIGFLAG to route all ports to EHCI\r
583\r
584 @param Ehc The EHCI device.\r
585\r
586 @return EFI_SUCCESS The EHCI has come out of halt state.\r
587 @return EFI_TIMEOUT Time out happened.\r
588\r
589**/\r
590EFI_STATUS\r
591EhcInitHC (\r
592 IN USB2_HC_DEV *Ehc\r
593 )\r
594{\r
595 EFI_STATUS Status;\r
596 UINT32 Index;\r
597 UINT32 RegVal;\r
598\r
599 // This ASSERT crashes the BeagleBoard. There is some issue in the USB stack.\r
600 // This ASSERT needs to be removed so the BeagleBoard will boot. When we fix\r
601 // the USB stack we can put this ASSERT back in\r
602 // ASSERT (EhcIsHalt (Ehc));\r
603\r
604 //\r
605 // Allocate the periodic frame and associated memeory\r
606 // management facilities if not already done.\r
607 //\r
608 if (Ehc->PeriodFrame != NULL) {\r
609 EhcFreeSched (Ehc);\r
610 }\r
611\r
612 Status = EhcInitSched (Ehc);\r
613\r
614 if (EFI_ERROR (Status)) {\r
615 return Status;\r
616 }\r
617\r
618 //\r
619 // 1. Clear USBINTR to disable all the interrupt. UEFI works by polling\r
620 //\r
621 EhcWriteOpReg (Ehc, EHC_USBINTR_OFFSET, 0);\r
622\r
623 //\r
624 // 2. Start the Host Controller\r
625 //\r
626 EhcSetOpRegBit (Ehc, EHC_USBCMD_OFFSET, USBCMD_RUN);\r
627\r
628 //\r
629 // 3. Power up all ports if EHCI has Port Power Control (PPC) support\r
630 //\r
631 if (Ehc->HcStructParams & HCSP_PPC) {\r
632 for (Index = 0; Index < (UINT8) (Ehc->HcStructParams & HCSP_NPORTS); Index++) {\r
633 //\r
634 // Do not clear port status bits on initialization. Otherwise devices will\r
635 // not enumerate properly at startup.\r
636 //\r
637 RegVal = EhcReadOpReg(Ehc, (UINT32)(EHC_PORT_STAT_OFFSET + (4 * Index)));\r
638 RegVal &= ~PORTSC_CHANGE_MASK;\r
639 RegVal |= PORTSC_POWER;\r
640 EhcWriteOpReg (Ehc, (UINT32) (EHC_PORT_STAT_OFFSET + (4 * Index)), RegVal);\r
641 }\r
642 }\r
643\r
644 //\r
645 // Wait roothub port power stable\r
646 //\r
647 gBS->Stall (EHC_ROOT_PORT_RECOVERY_STALL);\r
648\r
649 //\r
650 // 4. Set all ports routing to EHC\r
651 //\r
652 EhcSetOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);\r
653\r
654 Status = EhcEnablePeriodSchd (Ehc, EHC_GENERIC_TIMEOUT);\r
655\r
656 if (EFI_ERROR (Status)) {\r
657 DEBUG ((EFI_D_ERROR, "EhcInitHC: failed to enable period schedule\n"));\r
658 return Status;\r
659 }\r
660\r
661 Status = EhcEnableAsyncSchd (Ehc, EHC_GENERIC_TIMEOUT);\r
662\r
663 if (EFI_ERROR (Status)) {\r
664 DEBUG ((EFI_D_ERROR, "EhcInitHC: failed to enable async schedule\n"));\r
665 return Status;\r
666 }\r
667\r
668 return EFI_SUCCESS;\r
669}\r