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 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
111 EFI_PHYSICAL_ADDRESS PhyAddr
;
116 EFI_PHYSICAL_ADDRESS PciAddr
;
119 // First initialize the periodical schedule data:
120 // 1. Allocate and map the memory for the frame list
121 // 2. Create the help QTD/QH
122 // 3. Initialize the frame entries
123 // 4. Set the frame list register
126 // The Frame List ocupies 4K bytes,
127 // and must be aligned on 4-Kbyte boundaries.
129 Status
= IoMmuAllocateBuffer (
137 if (EFI_ERROR (Status
) || (Buf
== NULL
)) {
138 return EFI_OUT_OF_RESOURCES
;
141 Ehc
->PeriodFrame
= Buf
;
142 Ehc
->PeriodFrameMap
= Map
;
143 Ehc
->High32bitAddr
= EHC_HIGH_32BIT (PhyAddr
);
146 // Init memory pool management then create the helper
147 // QTD/QH. If failed, previously allocated resources
148 // will be freed by EhcFreeSched
150 Ehc
->MemPool
= UsbHcInitMemPool (
152 EHC_BIT_IS_SET (Ehc
->HcCapParams
, HCCP_64BIT
),
156 if (Ehc
->MemPool
== NULL
) {
157 return EFI_OUT_OF_RESOURCES
;
160 Status
= EhcCreateHelpQ (Ehc
);
162 if (EFI_ERROR (Status
)) {
167 // Initialize the frame list entries then set the registers
169 Desc
= (UINT32
*) Ehc
->PeriodFrame
;
170 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
171 for (Index
= 0; Index
< EHC_FRAME_LEN
; Index
++) {
172 Desc
[Index
] = QH_LINK (PciAddr
, EHC_TYPE_QH
, FALSE
);
175 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, EHC_LOW_32BIT (PhyAddr
));
178 // Second initialize the asynchronous schedule:
179 // Only need to set the AsynListAddr register to
180 // the reclamation header
182 PciAddr
= UsbHcGetPciAddressForHostMem (Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
183 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, EHC_LOW_32BIT (PciAddr
));
188 Free the schedule data. It may be partially initialized.
190 @param Ehc The EHCI device.
195 IN PEI_USB2_HC_DEV
*Ehc
198 EhcWriteOpReg (Ehc
, EHC_FRAME_BASE_OFFSET
, 0);
199 EhcWriteOpReg (Ehc
, EHC_ASYNC_HEAD_OFFSET
, 0);
201 if (Ehc
->PeriodOne
!= NULL
) {
202 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->PeriodOne
, sizeof (PEI_EHC_QH
));
203 Ehc
->PeriodOne
= NULL
;
206 if (Ehc
->ReclaimHead
!= NULL
) {
207 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ReclaimHead
, sizeof (PEI_EHC_QH
));
208 Ehc
->ReclaimHead
= NULL
;
211 if (Ehc
->ShortReadStop
!= NULL
) {
212 UsbHcFreeMem (Ehc
, Ehc
->MemPool
, Ehc
->ShortReadStop
, sizeof (PEI_EHC_QTD
));
213 Ehc
->ShortReadStop
= NULL
;
216 if (Ehc
->MemPool
!= NULL
) {
217 UsbHcFreeMemPool (Ehc
, Ehc
->MemPool
);
221 if (Ehc
->PeriodFrame
!= NULL
) {
222 IoMmuFreeBuffer (Ehc
->IoMmu
, 1, Ehc
->PeriodFrame
, Ehc
->PeriodFrameMap
);
223 Ehc
->PeriodFrame
= NULL
;
228 Link the queue head to the asynchronous schedule list.
229 UEFI only supports one CTRL/BULK transfer at a time
230 due to its interfaces. This simplifies the AsynList
231 management: A reclamation header is always linked to
232 the AsyncListAddr, the only active QH is appended to it.
234 @param Ehc The EHCI device.
235 @param Qh The queue head to link.
240 IN PEI_USB2_HC_DEV
*Ehc
,
247 // Append the queue head after the reclaim header, then
248 // fix the hardware visiable parts (EHCI R1.0 page 72).
249 // ReclaimHead is always linked to the EHCI's AsynListAddr.
251 Head
= Ehc
->ReclaimHead
;
253 Qh
->NextQh
= Head
->NextQh
;
256 Qh
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);;
257 Head
->QhHw
.HorizonLink
= QH_LINK (Qh
, EHC_TYPE_QH
, FALSE
);
261 Unlink a queue head from the asynchronous schedule list.
262 Need to synchronize with hardware.
264 @param Ehc The EHCI device.
265 @param Qh The queue head to unlink.
269 EhcUnlinkQhFromAsync (
270 IN PEI_USB2_HC_DEV
*Ehc
,
276 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
279 // Remove the QH from reclamation head, then update the hardware
280 // visiable part: Only need to loopback the ReclaimHead. The Qh
281 // is pointing to ReclaimHead (which is staill in the list).
283 Head
= Ehc
->ReclaimHead
;
285 Head
->NextQh
= Qh
->NextQh
;
288 Head
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);
291 // Set and wait the door bell to synchronize with the hardware
293 EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
299 Check the URB's execution result and update the URB's
302 @param Ehc The EHCI device.
303 @param Urb The URB to check result.
305 @retval TRUE URB transfer is finialized.
306 @retval FALSE URB transfer is not finialized.
311 IN PEI_USB2_HC_DEV
*Ehc
,
315 EFI_LIST_ENTRY
*Entry
;
321 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
326 Urb
->Result
= EFI_USB_NOERROR
;
328 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
329 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
333 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
334 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
336 State
= (UINT8
) QtdHw
->Status
;
338 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
340 // EHCI will halt the queue head when met some error.
341 // If it is halted, the result of URB is finialized.
343 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
344 Urb
->Result
|= EFI_USB_ERR_STALL
;
347 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
348 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
351 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
352 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
355 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
356 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
362 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
364 // The QTD is still active, no need to check furthur.
366 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
373 // This QTD is finished OK or met short packet read. Update the
374 // transfer length if it isn't a setup.
376 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
377 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
380 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
381 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
384 // Short packet read condition. If it isn't a setup transfer,
385 // no need to check furthur: the queue head will halt at the
386 // ShortReadStop. If it is a setup transfer, need to check the
387 // Status Stage of the setup transfer to get the finial result
389 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
400 // Return the data toggle set by EHCI hardware, bulk and interrupt
401 // transfer will use this to initialize the next transaction. For
402 // Control transfer, it always start a new data toggle sequence for
405 // NOTICE: don't move DT update before the loop, otherwise there is
406 // a race condition that DT is wrong.
408 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
414 Execute the transfer by polling the URB. This is a synchronous operation.
416 @param Ehc The EHCI device.
417 @param Urb The URB to execute.
418 @param TimeOut The time to wait before abort, in millisecond.
420 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
421 @retval EFI_TIMEOUT The transfer failed due to time out.
422 @retval EFI_SUCCESS The transfer finished OK.
427 IN PEI_USB2_HC_DEV
*Ehc
,
436 BOOLEAN InfiniteLoop
;
438 Status
= EFI_SUCCESS
;
439 Loop
= TimeOut
* EHC_1_MILLISECOND
;
441 InfiniteLoop
= FALSE
;
444 // If Timeout is 0, then the caller must wait for the function to be completed
445 // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
451 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
452 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
458 MicroSecondDelay (EHC_1_MICROSECOND
);
462 Status
= EFI_TIMEOUT
;
463 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
464 Status
= EFI_DEVICE_ERROR
;