2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
5 Copyright (c) 2010 - 2013, 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
,
268 ASSERT (Ehc
->ReclaimHead
->NextQh
== Qh
);
271 // Remove the QH from reclamation head, then update the hardware
272 // visiable part: Only need to loopback the ReclaimHead. The Qh
273 // is pointing to ReclaimHead (which is staill in the list).
275 Head
= Ehc
->ReclaimHead
;
277 Head
->NextQh
= Qh
->NextQh
;
280 Head
->QhHw
.HorizonLink
= QH_LINK (Head
, EHC_TYPE_QH
, FALSE
);
283 // Set and wait the door bell to synchronize with the hardware
285 EhcSetAndWaitDoorBell (Ehc
, EHC_GENERIC_TIMEOUT
);
291 Check the URB's execution result and update the URB's
294 @param Ehc The EHCI device.
295 @param Urb The URB to check result.
297 @retval TRUE URB transfer is finialized.
298 @retval FALSE URB transfer is not finialized.
303 IN PEI_USB2_HC_DEV
*Ehc
,
307 EFI_LIST_ENTRY
*Entry
;
313 ASSERT ((Ehc
!= NULL
) && (Urb
!= NULL
) && (Urb
->Qh
!= NULL
));
318 Urb
->Result
= EFI_USB_NOERROR
;
320 if (EhcIsHalt (Ehc
) || EhcIsSysError (Ehc
)) {
321 Urb
->Result
|= EFI_USB_ERR_SYSTEM
;
325 EFI_LIST_FOR_EACH (Entry
, &Urb
->Qh
->Qtds
) {
326 Qtd
= EFI_LIST_CONTAINER (Entry
, PEI_EHC_QTD
, QtdList
);
328 State
= (UINT8
) QtdHw
->Status
;
330 if (EHC_BIT_IS_SET (State
, QTD_STAT_HALTED
)) {
332 // EHCI will halt the queue head when met some error.
333 // If it is halted, the result of URB is finialized.
335 if ((State
& QTD_STAT_ERR_MASK
) == 0) {
336 Urb
->Result
|= EFI_USB_ERR_STALL
;
339 if (EHC_BIT_IS_SET (State
, QTD_STAT_BABBLE_ERR
)) {
340 Urb
->Result
|= EFI_USB_ERR_BABBLE
;
343 if (EHC_BIT_IS_SET (State
, QTD_STAT_BUFF_ERR
)) {
344 Urb
->Result
|= EFI_USB_ERR_BUFFER
;
347 if (EHC_BIT_IS_SET (State
, QTD_STAT_TRANS_ERR
) && (QtdHw
->ErrCnt
== 0)) {
348 Urb
->Result
|= EFI_USB_ERR_TIMEOUT
;
354 } else if (EHC_BIT_IS_SET (State
, QTD_STAT_ACTIVE
)) {
356 // The QTD is still active, no need to check furthur.
358 Urb
->Result
|= EFI_USB_ERR_NOTEXECUTE
;
365 // This QTD is finished OK or met short packet read. Update the
366 // transfer length if it isn't a setup.
368 if (QtdHw
->Pid
!= QTD_PID_SETUP
) {
369 Urb
->Completed
+= Qtd
->DataLen
- QtdHw
->TotalBytes
;
372 if ((QtdHw
->TotalBytes
!= 0) && (QtdHw
->Pid
== QTD_PID_INPUT
)) {
373 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));
376 // Short packet read condition. If it isn't a setup transfer,
377 // no need to check furthur: the queue head will halt at the
378 // ShortReadStop. If it is a setup transfer, need to check the
379 // Status Stage of the setup transfer to get the finial result
381 if (QtdHw
->AltNext
== QTD_LINK (Ehc
->ShortReadStop
, FALSE
)) {
392 // Return the data toggle set by EHCI hardware, bulk and interrupt
393 // transfer will use this to initialize the next transaction. For
394 // Control transfer, it always start a new data toggle sequence for
397 // NOTICE: don't move DT update before the loop, otherwise there is
398 // a race condition that DT is wrong.
400 Urb
->DataToggle
= (UINT8
) Urb
->Qh
->QhHw
.DataToggle
;
406 Execute the transfer by polling the URB. This is a synchronous operation.
408 @param Ehc The EHCI device.
409 @param Urb The URB to execute.
410 @param TimeOut The time to wait before abort, in millisecond.
412 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.
413 @retval EFI_TIMEOUT The transfer failed due to time out.
414 @retval EFI_SUCCESS The transfer finished OK.
419 IN PEI_USB2_HC_DEV
*Ehc
,
428 BOOLEAN InfiniteLoop
;
430 Status
= EFI_SUCCESS
;
431 Loop
= TimeOut
* EHC_1_MILLISECOND
;
433 InfiniteLoop
= FALSE
;
436 // If Timeout is 0, then the caller must wait for the function to be completed
437 // until EFI_SUCCESS or EFI_DEVICE_ERROR is returned.
443 for (Index
= 0; InfiniteLoop
|| (Index
< Loop
); Index
++) {
444 Finished
= EhcCheckUrbResult (Ehc
, Urb
);
450 MicroSecondDelay (EHC_1_MICROSECOND
);
454 Status
= EFI_TIMEOUT
;
455 } else if (Urb
->Result
!= EFI_USB_NOERROR
) {
456 Status
= EFI_DEVICE_ERROR
;