2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
5 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
21 Create helper QTD/QH for the EHCI device.
23 @param Ehc The EHCI device.
25 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
26 @retval EFI_SUCCESS Helper QH/QTD are created.
31 IN PEI_USB2_HC_DEV
*Ehc
40 // Create an inactive Qtd to terminate the short packet read.
42 Qtd
= EhcCreateQtd (Ehc
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
45 return EFI_OUT_OF_RESOURCES
;
48 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
49 Ehc
->ShortReadStop
= Qtd
;
52 // Create a QH to act as the EHC reclamation header.
53 // Set the header to loopback to itself.
57 Ep
.Direction
= EfiUsbDataIn
;
58 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
63 Ep
.Type
= EHC_BULK_TRANSFER
;
66 Qh
= EhcCreateQh (Ehc
, &Ep
);
69 return EFI_OUT_OF_RESOURCES
;
73 QhHw
->HorizonLink
= QH_LINK (QhHw
, EHC_TYPE_QH
, FALSE
);
74 QhHw
->Status
= QTD_STAT_HALTED
;
75 QhHw
->ReclaimHead
= 1;
76 Ehc
->ReclaimHead
= Qh
;
79 // Create a dummy QH to act as the terminator for periodical schedule
82 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
84 Qh
= EhcCreateQh (Ehc
, &Ep
);
87 return EFI_OUT_OF_RESOURCES
;
90 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
97 Initialize the schedule data structure such as frame list.
99 @param Ehc The EHCI device to init schedule data for.
101 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
102 @retval EFI_SUCCESS The schedule data is initialized.
107 IN PEI_USB2_HC_DEV
*Ehc
110 EFI_PHYSICAL_ADDRESS PhyAddr
;
117 // First initialize the periodical schedule data:
118 // 1. Allocate and map the memory for the frame list
119 // 2. Create the help QTD/QH
120 // 3. Initialize the frame entries
121 // 4. Set the frame list register
124 // The Frame List ocupies 4K bytes,
125 // and must be aligned on 4-Kbyte boundaries.
127 Status
= PeiServicesAllocatePages (
134 Ehc
->PeriodFrameHost
= (VOID
*)(UINTN
)PhyAddr
;
135 Ehc
->PeriodFrame
= (VOID
*)(UINTN
)PhyAddr
;
136 Ehc
->PeriodFrameMap
= Map
;
137 Ehc
->High32bitAddr
= EHC_HIGH_32BIT (PhyAddr
);
140 // Init memory pool management then create the helper
141 // QTD/QH. If failed, previously allocated resources
142 // will be freed by EhcFreeSched
144 Ehc
->MemPool
= UsbHcInitMemPool (
146 EHC_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
150 if (Ehc
->MemPool
== NULL
) {
151 return EFI_OUT_OF_RESOURCES
;
154 Status
= EhcCreateHelpQ (Ehc
);
156 if (EFI_ERROR (Status
)) {
161 // Initialize the frame list entries then set the registers
163 Desc
= (UINT32
*) Ehc
->PeriodFrame
;
165 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
166 Desc
[Index
] = QH_LINK (Ehc
->PeriodOne
, EHC_TYPE_QH
, FALSE
);
169 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (Ehc
->PeriodFrame
));
172 // Second initialize the asynchronous schedule:
173 // Only need to set the AsynListAddr register to
174 // the reclamation header
176 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (Ehc
->ReclaimHead
));
181 Free the schedule data. It may be partially initialized.
183 @param Ehc The EHCI device.
188 IN PEI_USB2_HC_DEV
*Ehc
191 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
192 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
194 if (Ehc
->PeriodOne
!= NULL
) {
195 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
196 Ehc
->PeriodOne
= NULL
;
199 if (Ehc
->ReclaimHead
!= NULL
) {
200 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
201 Ehc
->ReclaimHead
= NULL
;
204 if (Ehc
->ShortReadStop
!= NULL
) {
205 UsbHcFreeMem (Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (PEI_EHC_QTD
));
206 Ehc
->ShortReadStop
= NULL
;
209 if (Ehc
->MemPool
!= NULL
) {
210 UsbHcFreeMemPool (Ehc
->MemPool
);
214 if (Ehc
->PeriodFrame
!= NULL
) {
215 Ehc
->PeriodFrame
= NULL
;
220 Link the queue head to the asynchronous schedule list.
221 UEFI only supports one CTRL/BULK transfer at a time
222 due to its interfaces. This simplifies the AsynList
223 management: A reclamation header is always linked to
224 the AsyncListAddr, the only active QH is appended to it.
226 @param Ehc The EHCI device.
227 @param Qh The queue head to link.
232 IN PEI_USB2_HC_DEV
*Ehc
,
239 // Append the queue head after the reclaim header, then
240 // fix the hardware visiable parts (EHCI R1.0 page 72).
241 // ReclaimHead is always linked to the EHCI's AsynListAddr.
243 Head
= Ehc
->ReclaimHead
;
245 Qh
->NextQh
= Head
->NextQh
;
248 Qh
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);;
249 Head
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
253 Unlink a queue head from the asynchronous schedule list.
254 Need to synchronize with hardware.
256 @param Ehc The EHCI device.
257 @param Qh The queue head to unlink.
261 EhcUnlinkQhFromAsync (
262 IN PEI_USB2_HC_DEV
*Ehc
,
269 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
272 // Remove the QH from reclamation head, then update the hardware
273 // visiable part: Only need to loopback the ReclaimHead. The Qh
274 // is pointing to ReclaimHead (which is staill in the list).
276 Head
= Ehc
->ReclaimHead
;
278 Head
->NextQh
= Qh
->NextQh
;
281 Head
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);
284 // Set and wait the door bell to synchronize with the hardware
286 Status
= EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
292 Link a queue head for interrupt transfer to the periodic
293 schedule frame list. This code is very much the same as
296 @param Ehc The EHCI device.
297 @param Qh The queue head to link.
302 IN PEI_USB2_HC_DEV
*Ehc
,
311 Frames
= Ehc
->PeriodFrame
;
313 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
315 // First QH can't be NULL because we always keep PeriodOne
316 // heads on the frame list
318 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
319 Next
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
323 // Now, insert the queue head (Qh) into this frame:
324 // 1. Find a queue head with the same poll interval, just insert
325 // Qh after this queue head, then we are done.
327 // 2. Find the position to insert the queue head into:
328 // Previous head's interval is bigger than Qh's
329 // Next head's interval is less than Qh's
330 // Then, insert the Qh between then
332 while (Next
->Interval
> Qh
->Interval
) {
337 ASSERT (Next
!= NULL
);
340 // The entry may have been linked into the frame by early insertation.
341 // For example: if insert a Qh with Qh.Interval == 4, and there is a Qh
342 // with Qh.Interval == 8 on the frame. If so, we are done with this frame.
343 // It isn't necessary to compare all the QH with the same interval to
344 // Qh. This is because if there is other QH with the same interval, Qh
345 // should has been inserted after that at Frames[0] and at Frames[0] it is
346 // impossible for (Next == Qh)
352 if (Next
->Interval
== Qh
->Interval
) {
354 // If there is a QH with the same interval, it locates at
355 // Frames[0], and we can simply insert it after this QH. We
358 ASSERT ((Index
== 0) && (Qh
->NextQh
== NULL
));
366 Qh
->QhHw
.HorizonLink
= Prev
->QhHw
.HorizonLink
;
367 Prev
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
372 // OK, find the right position, insert it in. If Qh's next
373 // link has already been set, it is in position. This is
374 // guarranted by 2^n polling interval.
376 if (Qh
->NextQh
== NULL
) {
378 Qh
->QhHw
.HorizonLink
= QH_LINK (Next
, EHC_TYPE_QH
, FALSE
);
382 Frames
[Index
] = QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
385 Prev
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
391 Unlink an interrupt queue head from the periodic
394 @param Ehc The EHCI device.
395 @param Qh The queue head to unlink.
399 EhcUnlinkQhFromPeriod (
400 IN PEI_USB2_HC_DEV
*Ehc
,
409 Frames
= Ehc
->PeriodFrame
;
411 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
+= Qh
->Interval
) {
413 // Frame link can't be NULL because we always keep PeroidOne
416 ASSERT (!EHC_LINK_TERMINATED (Frames
[Index
]));
417 This
= EHC_ADDR (Ehc
->High32bitAddr
, Frames
[Index
]);
421 // Walk through the frame's QH list to find the
422 // queue head to remove
424 while ((This
!= NULL
) && (This
!= Qh
)) {
430 // Qh may have already been unlinked from this frame
431 // by early action. See the comments in EhcLinkQhToPeriod.
439 // Qh is the first entry in the frame
441 Frames
[Index
] = Qh
->QhHw
.HorizonLink
;
443 Prev
->NextQh
= Qh
->NextQh
;
444 Prev
->QhHw
.HorizonLink
= Qh
->QhHw
.HorizonLink
;
450 Check the URB's execution result and update the URB's
453 @param Ehc The EHCI device.
454 @param Urb The URB to check result.
456 @retval TRUE URB transfer is finialized.
457 @retval FALSE URB transfer is not finialized.
462 IN PEI_USB2_HC_DEV
*Ehc
,
466 EFI_LIST_ENTRY
*Entry
;
472 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
477 Urb
->Result
= EFI_USB_NOERROR
;
479 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
480 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
484 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
485 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
487 State
= (UINT8
) QtdHw
->Status
;
489 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
491 // EHCI will halt the queue head when met some error.
492 // If it is halted, the result of URB is finialized.
494 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
495 Urb
->Result
|= EFI_USB_ERR_STALL
;
498 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
499 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
502 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
503 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
506 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
507 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
513 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
515 // The QTD is still active, no need to check furthur.
517 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
524 // This QTD is finished OK or met short packet read. Update the
525 // transfer length if it isn't a setup.
527 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
528 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
531 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
532 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
535 // Short packet read condition. If it isn't a setup transfer,
536 // no need to check furthur: the queue head will halt at the
537 // ShortReadStop. If it is a setup transfer, need to check the
538 // Status Stage of the setup transfer to get the finial result
540 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
551 // Return the data toggle set by EHCI hardware, bulk and interrupt
552 // transfer will use this to initialize the next transaction. For
553 // Control transfer, it always start a new data toggle sequence for
556 // NOTICE: don't move DT update before the loop, otherwise there is
557 // a race condition that DT is wrong.
559 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
565 Execute the transfer by polling the URB. This is a synchronous operation.
567 @param Ehc The EHCI device.
568 @param Urb The URB to execute.
569 @param TimeOut The time to wait before abort, in millisecond.
571 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
572 @retval EFI_TIMEOUT The transfer failed due to time out.
573 @retval EFI_SUCCESS The transfer finished OK.
578 IN PEI_USB2_HC_DEV
*Ehc
,
588 Status
= EFI_SUCCESS
;
589 Loop
= (TimeOut
* EHC_1_MILLISECOND
/ EHC_SYNC_POLL_INTERVAL
) + 1;
592 for (Index
= 0; Index
< Loop
; Index
++) {
593 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
599 MicroSecondDelay (EHC_SYNC_POLL_INTERVAL
);
603 Status
= EFI_TIMEOUT
;
604 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
605 Status
= EFI_DEVICE_ERROR
;
612 Delete a single asynchronous interrupt transfer for
613 the device and endpoint.
615 @param Ehc The EHCI device.
616 @param DevAddr The address of the target device.
617 @param EpNum The endpoint of the target.
618 @param DataToggle Return the next data toggle to use.
620 @retval EFI_NOT_FOUND No transfer for the device is found.
621 @retval EFI_SUCCESS An asynchronous transfer is removed.
625 EhciDelAsyncIntTransfer (
626 IN PEI_USB2_HC_DEV
*Ehc
,
629 OUT UINT8
*DataToggle
632 EFI_LIST_ENTRY
*Entry
;
633 EFI_LIST_ENTRY
*Next
;
635 EFI_USB_DATA_DIRECTION Direction
;
637 Direction
= (((EpNum
& 0x80) != 0) ? EfiUsbDataIn
: EfiUsbDataOut
);
640 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
641 Urb
= EFI_LIST_CONTAINER (Entry
, PEI_URB
, UrbList
);
643 if ((Urb
->Ep
.DevAddr
== DevAddr
) && (Urb
->Ep
.EpAddr
== EpNum
) &&
644 (Urb
->Ep
.Direction
== Direction
)) {
646 // Check the URB status to retrieve the next data toggle
647 // from the associated queue head.
649 EhcCheckUrbResult (Ehc
, Urb
);
650 *DataToggle
= Urb
->DataToggle
;
652 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
653 RemoveEntryList (&Urb
->UrbList
);
655 EhcFreeUrb (Ehc
, Urb
);
660 return EFI_NOT_FOUND
;
664 Remove all the asynchronous interrutp transfers.
666 @param Ehc The EHCI device.
670 EhciDelAllAsyncIntTransfers (
671 IN PEI_USB2_HC_DEV
*Ehc
674 EFI_LIST_ENTRY
*Entry
;
675 EFI_LIST_ENTRY
*Next
;
678 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
679 Urb
= EFI_LIST_CONTAINER (Entry
, PEI_URB
, UrbList
);
681 EhcUnlinkQhFromPeriod (Ehc
, Urb
->Qh
);
682 RemoveEntryList (&Urb
->UrbList
);
684 EhcFreeUrb (Ehc
, Urb
);
689 Flush data from PCI controller specific address to mapped system
692 @param Ehc The EHCI device.
693 @param Urb The URB to unmap.
695 @retval EFI_DEVICE_ERROR Fail to flush data to mapped system memory.
696 @retval EFI_SUCCESS Success to flush data to mapped system memory.
700 EhcFlushAsyncIntMap (
701 IN PEI_USB2_HC_DEV
*Ehc
,
705 EFI_PHYSICAL_ADDRESS PhyAddr
;
708 PhyAddr
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Urb
->Data
;
709 Urb
->DataPhy
= (VOID
*) ((UINTN
) PhyAddr
);
714 Update the queue head for next round of asynchronous transfer.
716 @param Urb The URB to update.
720 EhcUpdateAsyncRequest (
724 EFI_LIST_ENTRY
*Entry
;
725 PEI_EHC_QTD
*FirstQtd
;
733 if (Urb
->Result
== EFI_USB_NOERROR
) {
736 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
737 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
739 if (FirstQtd
== NULL
) {
744 // Update the QTD for next round of transfer. Host control
745 // may change dt/Total Bytes to Transfer/C_Page/Cerr/Status/
746 // Current Offset. These fields need to be updated. DT isn't
747 // used by interrupt transfer. It uses DT in queue head.
748 // Current Offset is in Page[0], only need to reset Page[0]
749 // to initial data buffer.
752 QtdHw
->Status
= QTD_STAT_ACTIVE
;
753 QtdHw
->ErrCnt
= QTD_MAX_ERR
;
755 QtdHw
->TotalBytes
= (UINT32
) Qtd
->DataLen
;
756 QtdHw
->Page
[0] = EHC_LOW_32BIT (Qtd
->Data
);
760 // Update QH for next round of transfer. Host control only
761 // touch the fields in transfer overlay area. Only need to
762 // zero out the overlay area and set NextQtd to the first
763 // QTD. DateToggle bit is left untouched.
765 QhHw
= &Urb
->Qh
->QhHw
;
766 QhHw
->CurQtd
= QTD_LINK (0, TRUE
);
774 QhHw
->TotalBytes
= 0;
776 for (Index
= 0; Index
< 5; Index
++) {
777 QhHw
->Page
[Index
] = 0;
778 QhHw
->PageHigh
[Index
] = 0;
781 QhHw
->NextQtd
= QTD_LINK (FirstQtd
, FALSE
);
788 Remove all the asynchronous interrutp transfers.
790 @param Event Interrupt event.
791 @param Context Pointer to PEI_USB2_HC_DEV.
796 EhcMoniteAsyncRequests (
801 PEI_USB2_HC_DEV
*Ehc
;
802 EFI_LIST_ENTRY
*Entry
;
803 EFI_LIST_ENTRY
*Next
;
810 Ehc
= (PEI_USB2_HC_DEV
*) Context
;
812 EFI_LIST_FOR_EACH_SAFE (Entry
, Next
, &Ehc
->AsyncIntTransfers
) {
813 Urb
= EFI_LIST_CONTAINER (Entry
, PEI_URB
, UrbList
);
816 // Check the result of URB execution. If it is still
817 // active, check the next one.
819 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
826 // Flush any PCI posted write transactions from a PCI host
827 // bridge to system memory.
829 Status
= EhcFlushAsyncIntMap (Ehc
, Urb
);
832 // Allocate a buffer then copy the transferred data for user.
833 // If failed to allocate the buffer, update the URB for next
834 // round of transfer. Ignore the data of this round.
838 if (Urb
->Result
== EFI_USB_NOERROR
) {
839 ASSERT (Urb
->Completed
<= Urb
->DataLen
);
840 PageNumber
= Urb
->Completed
/PAGESIZE
+1;
841 Status
= PeiServicesAllocatePages (
844 (EFI_PHYSICAL_ADDRESS
*)ProcBuf
846 if (ProcBuf
== NULL
) {
847 EhcUpdateAsyncRequest (Urb
);
851 CopyMem (ProcBuf
, Urb
->Data
, Urb
->Completed
);
854 EhcUpdateAsyncRequest (Urb
);
857 // Leave error recovery to its related device driver. A
858 // common case of the error recovery is to re-submit the
859 // interrupt transfer which is linked to the head of the
860 // list. This function scans from head to tail. So the
861 // re-submitted interrupt transfer's callback function
862 // will not be called again in this round. Don't touch this
863 // URB after the callback, it may have been removed by the
866 if (Urb
->Callback
!= NULL
) {
867 (Urb
->Callback
) (ProcBuf
, Urb
->Completed
, Urb
->Context
, Urb
->Result
);
870 if (ProcBuf
!= NULL
) {