2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
5 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
15 Create helper QTD/QH for the EHCI device.
17 @param Ehc The EHCI device.
19 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
20 @retval EFI_SUCCESS Helper QH/QTD are created.
25 IN PEI_USB2_HC_DEV
*Ehc
34 // Create an inactive Qtd to terminate the short packet read.
36 Qtd
= EhcCreateQtd (Ehc
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
39 return EFI_OUT_OF_RESOURCES
;
42 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
43 Ehc
->ShortReadStop
= Qtd
;
46 // Create a QH to act as the EHC reclamation header.
47 // Set the header to loopback to itself.
51 Ep
.Direction
= EfiUsbDataIn
;
52 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
57 Ep
.Type
= EHC_BULK_TRANSFER
;
60 Qh
= EhcCreateQh (Ehc
, &Ep
);
63 return EFI_OUT_OF_RESOURCES
;
67 QhHw
->HorizonLink
= QH_LINK (QhHw
, EHC_TYPE_QH
, FALSE
);
68 QhHw
->Status
= QTD_STAT_HALTED
;
69 QhHw
->ReclaimHead
= 1;
70 Ehc
->ReclaimHead
= Qh
;
73 // Create a dummy QH to act as the terminator for periodical schedule
76 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
78 Qh
= EhcCreateQh (Ehc
, &Ep
);
81 return EFI_OUT_OF_RESOURCES
;
84 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
91 Initialize the schedule data structure such as frame list.
93 @param Ehc The EHCI device to init schedule data for.
95 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
96 @retval EFI_SUCCESS The schedule data is initialized.
101 IN PEI_USB2_HC_DEV
*Ehc
105 EFI_PHYSICAL_ADDRESS PhyAddr
;
110 EFI_PHYSICAL_ADDRESS PciAddr
;
113 // First initialize the periodical schedule data:
114 // 1. Allocate and map the memory for the frame list
115 // 2. Create the help QTD/QH
116 // 3. Initialize the frame entries
117 // 4. Set the frame list register
120 // The Frame List ocupies 4K bytes,
121 // and must be aligned on 4-Kbyte boundaries.
123 Status
= IoMmuAllocateBuffer (
131 if (EFI_ERROR (Status
) || (Buf
== NULL
)) {
132 return EFI_OUT_OF_RESOURCES
;
135 Ehc
->PeriodFrame
= Buf
;
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
;
164 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
165 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
166 Desc
[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
169 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
172 // Second initialize the asynchronous schedule:
173 // Only need to set the AsynListAddr register to
174 // the reclamation header
176 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
177 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
182 Free the schedule data. It may be partially initialized.
184 @param Ehc The EHCI device.
189 IN PEI_USB2_HC_DEV
*Ehc
192 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
193 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
195 if (Ehc
->PeriodOne
!= NULL
) {
196 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
197 Ehc
->PeriodOne
= NULL
;
200 if (Ehc
->ReclaimHead
!= NULL
) {
201 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
202 Ehc
->ReclaimHead
= NULL
;
205 if (Ehc
->ShortReadStop
!= NULL
) {
206 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (PEI_EHC_QTD
));
207 Ehc
->ShortReadStop
= NULL
;
210 if (Ehc
->MemPool
!= NULL
) {
211 UsbHcFreeMemPool (Ehc
, Ehc
->MemPool
);
215 if (Ehc
->PeriodFrame
!= NULL
) {
216 IoMmuFreeBuffer (Ehc
->IoMmu
, 1, Ehc
->PeriodFrame
, Ehc
->PeriodFrameMap
);
217 Ehc
->PeriodFrame
= NULL
;
222 Link the queue head to the asynchronous schedule list.
223 UEFI only supports one CTRL/BULK transfer at a time
224 due to its interfaces. This simplifies the AsynList
225 management: A reclamation header is always linked to
226 the AsyncListAddr, the only active QH is appended to it.
228 @param Ehc The EHCI device.
229 @param Qh The queue head to link.
234 IN PEI_USB2_HC_DEV
*Ehc
,
241 // Append the queue head after the reclaim header, then
242 // fix the hardware visiable parts (EHCI R1.0 page 72).
243 // ReclaimHead is always linked to the EHCI's AsynListAddr.
245 Head
= Ehc
->ReclaimHead
;
247 Qh
->NextQh
= Head
->NextQh
;
250 Qh
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);;
251 Head
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
255 Unlink a queue head from the asynchronous schedule list.
256 Need to synchronize with hardware.
258 @param Ehc The EHCI device.
259 @param Qh The queue head to unlink.
263 EhcUnlinkQhFromAsync (
264 IN PEI_USB2_HC_DEV
*Ehc
,
270 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
273 // Remove the QH from reclamation head, then update the hardware
274 // visiable part: Only need to loopback the ReclaimHead. The Qh
275 // is pointing to ReclaimHead (which is staill in the list).
277 Head
= Ehc
->ReclaimHead
;
279 Head
->NextQh
= Qh
->NextQh
;
282 Head
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);
285 // Set and wait the door bell to synchronize with the hardware
287 EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
293 Check the URB's execution result and update the URB's
296 @param Ehc The EHCI device.
297 @param Urb The URB to check result.
299 @retval TRUE URB transfer is finialized.
300 @retval FALSE URB transfer is not finialized.
305 IN PEI_USB2_HC_DEV
*Ehc
,
309 EFI_LIST_ENTRY
*Entry
;
315 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
320 Urb
->Result
= EFI_USB_NOERROR
;
322 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
323 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
327 BASE_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
328 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
330 State
= (UINT8
) QtdHw
->Status
;
332 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
334 // EHCI will halt the queue head when met some error.
335 // If it is halted, the result of URB is finialized.
337 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
338 Urb
->Result
|= EFI_USB_ERR_STALL
;
341 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
342 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
345 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
346 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
349 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
350 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
356 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
358 // The QTD is still active, no need to check furthur.
360 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
367 // This QTD is finished OK or met short packet read. Update the
368 // transfer length if it isn't a setup.
370 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
371 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
374 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
375 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
378 // Short packet read condition. If it isn't a setup transfer,
379 // no need to check furthur: the queue head will halt at the
380 // ShortReadStop. If it is a setup transfer, need to check the
381 // Status Stage of the setup transfer to get the finial result
383 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
394 // Return the data toggle set by EHCI hardware, bulk and interrupt
395 // transfer will use this to initialize the next transaction. For
396 // Control transfer, it always start a new data toggle sequence for
399 // NOTICE: don't move DT update before the loop, otherwise there is
400 // a race condition that DT is wrong.
402 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
408 Execute the transfer by polling the URB. This is a synchronous operation.
410 @param Ehc The EHCI device.
411 @param Urb The URB to execute.
412 @param TimeOut The time to wait before abort, in millisecond.
414 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
415 @retval EFI_TIMEOUT The transfer failed due to time out.
416 @retval EFI_SUCCESS The transfer finished OK.
421 IN PEI_USB2_HC_DEV
*Ehc
,
430 BOOLEAN InfiniteLoop
;
432 Status
= EFI_SUCCESS
;
433 Loop
= TimeOut
* EHC_1_MILLISECOND
;
435 InfiniteLoop
= FALSE
;
438 // If Timeout is 0, then the caller must wait for the function to be completed
439 // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
445 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
446 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
452 MicroSecondDelay (EHC_1_MICROSECOND
);
456 Status
= EFI_TIMEOUT
;
457 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
458 Status
= EFI_DEVICE_ERROR
;