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