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>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
14 Create helper QTD/QH for the EHCI device.
16 @param Ehc The EHCI device.
18 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.
19 @retval EFI_SUCCESS Helper QH/QTD are created.
24 IN PEI_USB2_HC_DEV
*Ehc
33 // Create an inactive Qtd to terminate the short packet read.
35 Qtd
= EhcCreateQtd (Ehc
, NULL
, 0, QTD_PID_INPUT
, 0, 64);
38 return EFI_OUT_OF_RESOURCES
;
41 Qtd
->QtdHw
.Status
= QTD_STAT_HALTED
;
42 Ehc
->ShortReadStop
= Qtd
;
45 // Create a QH to act as the EHC reclamation header.
46 // Set the header to loopback to itself.
50 Ep
.Direction
= EfiUsbDataIn
;
51 Ep
.DevSpeed
= EFI_USB_SPEED_HIGH
;
56 Ep
.Type
= EHC_BULK_TRANSFER
;
59 Qh
= EhcCreateQh (Ehc
, &Ep
);
62 return EFI_OUT_OF_RESOURCES
;
66 QhHw
->HorizonLink
= QH_LINK (QhHw
, EHC_TYPE_QH
, FALSE
);
67 QhHw
->Status
= QTD_STAT_HALTED
;
68 QhHw
->ReclaimHead
= 1;
69 Ehc
->ReclaimHead
= Qh
;
72 // Create a dummy QH to act as the terminator for periodical schedule
75 Ep
.Type
= EHC_INT_TRANSFER_SYNC
;
77 Qh
= EhcCreateQh (Ehc
, &Ep
);
80 return EFI_OUT_OF_RESOURCES
;
83 Qh
->QhHw
.Status
= QTD_STAT_HALTED
;
90 Initialize the schedule data structure such as frame list.
92 @param Ehc The EHCI device to init schedule data for.
94 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.
95 @retval EFI_SUCCESS The schedule data is initialized.
100 IN PEI_USB2_HC_DEV
*Ehc
104 EFI_PHYSICAL_ADDRESS PhyAddr
;
109 EFI_PHYSICAL_ADDRESS PciAddr
;
112 // First initialize the periodical schedule data:
113 // 1. Allocate and map the memory for the frame list
114 // 2. Create the help QTD/QH
115 // 3. Initialize the frame entries
116 // 4. Set the frame list register
119 // The Frame List ocupies 4K bytes,
120 // and must be aligned on 4-Kbyte boundaries.
122 Status
= IoMmuAllocateBuffer (
130 if (EFI_ERROR (Status
) || (Buf
== NULL
)) {
131 return EFI_OUT_OF_RESOURCES
;
134 Ehc
->PeriodFrame
= Buf
;
135 Ehc
->PeriodFrameMap
= Map
;
136 Ehc
->High32bitAddr
= EHC_HIGH_32BIT (PhyAddr
);
139 // Init memory pool management then create the helper
140 // QTD/QH. If failed, previously allocated resources
141 // will be freed by EhcFreeSched
143 Ehc
->MemPool
= UsbHcInitMemPool (
145 EHC_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
149 if (Ehc
->MemPool
== NULL
) {
150 return EFI_OUT_OF_RESOURCES
;
153 Status
= EhcCreateHelpQ (Ehc
);
155 if (EFI_ERROR (Status
)) {
160 // Initialize the frame list entries then set the registers
162 Desc
= (UINT32
*) Ehc
->PeriodFrame
;
163 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
164 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
165 Desc
[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
168 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
171 // Second initialize the asynchronous schedule:
172 // Only need to set the AsynListAddr register to
173 // the reclamation header
175 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
176 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
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
, Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
196 Ehc
->PeriodOne
= NULL
;
199 if (Ehc
->ReclaimHead
!= NULL
) {
200 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
201 Ehc
->ReclaimHead
= NULL
;
204 if (Ehc
->ShortReadStop
!= NULL
) {
205 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (PEI_EHC_QTD
));
206 Ehc
->ShortReadStop
= NULL
;
209 if (Ehc
->MemPool
!= NULL
) {
210 UsbHcFreeMemPool (Ehc
, Ehc
->MemPool
);
214 if (Ehc
->PeriodFrame
!= NULL
) {
215 IoMmuFreeBuffer (Ehc
->IoMmu
, 1, Ehc
->PeriodFrame
, Ehc
->PeriodFrameMap
);
216 Ehc
->PeriodFrame
= NULL
;
221 Link the queue head to the asynchronous schedule list.
222 UEFI only supports one CTRL/BULK transfer at a time
223 due to its interfaces. This simplifies the AsynList
224 management: A reclamation header is always linked to
225 the AsyncListAddr, the only active QH is appended to it.
227 @param Ehc The EHCI device.
228 @param Qh The queue head to link.
233 IN PEI_USB2_HC_DEV
*Ehc
,
240 // Append the queue head after the reclaim header, then
241 // fix the hardware visiable parts (EHCI R1.0 page 72).
242 // ReclaimHead is always linked to the EHCI's AsynListAddr.
244 Head
= Ehc
->ReclaimHead
;
246 Qh
->NextQh
= Head
->NextQh
;
249 Qh
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);;
250 Head
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
254 Unlink a queue head from the asynchronous schedule list.
255 Need to synchronize with hardware.
257 @param Ehc The EHCI device.
258 @param Qh The queue head to unlink.
262 EhcUnlinkQhFromAsync (
263 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 EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
292 Check the URB's execution result and update the URB's
295 @param Ehc The EHCI device.
296 @param Urb The URB to check result.
298 @retval TRUE URB transfer is finialized.
299 @retval FALSE URB transfer is not finialized.
304 IN PEI_USB2_HC_DEV
*Ehc
,
308 EFI_LIST_ENTRY
*Entry
;
314 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
319 Urb
->Result
= EFI_USB_NOERROR
;
321 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
322 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
326 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
327 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
329 State
= (UINT8
) QtdHw
->Status
;
331 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
333 // EHCI will halt the queue head when met some error.
334 // If it is halted, the result of URB is finialized.
336 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
337 Urb
->Result
|= EFI_USB_ERR_STALL
;
340 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
341 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
344 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
345 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
348 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
349 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
355 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
357 // The QTD is still active, no need to check furthur.
359 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
366 // This QTD is finished OK or met short packet read. Update the
367 // transfer length if it isn't a setup.
369 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
370 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
373 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
374 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
377 // Short packet read condition. If it isn't a setup transfer,
378 // no need to check furthur: the queue head will halt at the
379 // ShortReadStop. If it is a setup transfer, need to check the
380 // Status Stage of the setup transfer to get the finial result
382 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
393 // Return the data toggle set by EHCI hardware, bulk and interrupt
394 // transfer will use this to initialize the next transaction. For
395 // Control transfer, it always start a new data toggle sequence for
398 // NOTICE: don't move DT update before the loop, otherwise there is
399 // a race condition that DT is wrong.
401 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
407 Execute the transfer by polling the URB. This is a synchronous operation.
409 @param Ehc The EHCI device.
410 @param Urb The URB to execute.
411 @param TimeOut The time to wait before abort, in millisecond.
413 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
414 @retval EFI_TIMEOUT The transfer failed due to time out.
415 @retval EFI_SUCCESS The transfer finished OK.
420 IN PEI_USB2_HC_DEV
*Ehc
,
429 BOOLEAN InfiniteLoop
;
431 Status
= EFI_SUCCESS
;
432 Loop
= TimeOut
* EHC_1_MILLISECOND
;
434 InfiniteLoop
= FALSE
;
437 // If Timeout is 0, then the caller must wait for the function to be completed
438 // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
444 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
445 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
451 MicroSecondDelay (EHC_1_MICROSECOND
);
455 Status
= EFI_TIMEOUT
;
456 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
457 Status
= EFI_DEVICE_ERROR
;