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