]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/EhciPei/EhciSched.c
ShellPkg: Update rm command to properly prepend file system identifiers to filenames.
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciPei / EhciSched.c
CommitLineData
4b1bf81c 1/** @file\r
2PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
3which is used to enable recovery function from USB Drivers.\r
4\r
7538d536 5Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>\r
4b1bf81c 6 \r
7This program and the accompanying materials\r
8are licensed and made available under the terms and conditions\r
9of the BSD License which accompanies this distribution. The\r
10full text of the license may be found at\r
11http://opensource.org/licenses/bsd-license.php\r
12\r
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include "EhcPeim.h"\r
19\r
20/**\r
21 Create helper QTD/QH for the EHCI device.\r
22 \r
23 @param Ehc The EHCI device.\r
24\r
25 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for helper QTD/QH.\r
26 @retval EFI_SUCCESS Helper QH/QTD are created.\r
27\r
28**/\r
29EFI_STATUS\r
30EhcCreateHelpQ (\r
31 IN PEI_USB2_HC_DEV *Ehc\r
32 )\r
33{\r
34 USB_ENDPOINT Ep;\r
35 PEI_EHC_QH *Qh;\r
36 QH_HW *QhHw;\r
37 PEI_EHC_QTD *Qtd;\r
38\r
39 //\r
40 // Create an inactive Qtd to terminate the short packet read.\r
41 //\r
42 Qtd = EhcCreateQtd (Ehc, NULL, 0, QTD_PID_INPUT, 0, 64);\r
43\r
44 if (Qtd == NULL) {\r
45 return EFI_OUT_OF_RESOURCES;\r
46 }\r
47\r
48 Qtd->QtdHw.Status = QTD_STAT_HALTED;\r
49 Ehc->ShortReadStop = Qtd;\r
50\r
51 //\r
52 // Create a QH to act as the EHC reclamation header.\r
53 // Set the header to loopback to itself.\r
54 //\r
55 Ep.DevAddr = 0;\r
56 Ep.EpAddr = 1;\r
57 Ep.Direction = EfiUsbDataIn;\r
58 Ep.DevSpeed = EFI_USB_SPEED_HIGH;\r
59 Ep.MaxPacket = 64;\r
60 Ep.HubAddr = 0;\r
61 Ep.HubPort = 0;\r
62 Ep.Toggle = 0;\r
63 Ep.Type = EHC_BULK_TRANSFER;\r
64 Ep.PollRate = 1;\r
65\r
66 Qh = EhcCreateQh (Ehc, &Ep);\r
67\r
68 if (Qh == NULL) {\r
69 return EFI_OUT_OF_RESOURCES;\r
70 }\r
71\r
72 QhHw = &Qh->QhHw;\r
73 QhHw->HorizonLink = QH_LINK (QhHw, EHC_TYPE_QH, FALSE);\r
74 QhHw->Status = QTD_STAT_HALTED;\r
75 QhHw->ReclaimHead = 1;\r
76 Ehc->ReclaimHead = Qh;\r
77\r
78 //\r
79 // Create a dummy QH to act as the terminator for periodical schedule\r
80 //\r
81 Ep.EpAddr = 2;\r
82 Ep.Type = EHC_INT_TRANSFER_SYNC;\r
83\r
84 Qh = EhcCreateQh (Ehc, &Ep);\r
85\r
86 if (Qh == NULL) {\r
87 return EFI_OUT_OF_RESOURCES;\r
88 }\r
89\r
90 Qh->QhHw.Status = QTD_STAT_HALTED;\r
91 Ehc->PeriodOne = Qh;\r
92\r
93 return EFI_SUCCESS;\r
94}\r
95\r
96/**\r
97 Initialize the schedule data structure such as frame list.\r
98 \r
99 @param Ehc The EHCI device to init schedule data for.\r
100\r
101 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to init schedule data.\r
102 @retval EFI_SUCCESS The schedule data is initialized.\r
103\r
104**/\r
105EFI_STATUS\r
106EhcInitSched (\r
107 IN PEI_USB2_HC_DEV *Ehc\r
108 )\r
109{\r
110 EFI_PHYSICAL_ADDRESS PhyAddr;\r
111 VOID *Map;\r
112 UINTN Index;\r
113 UINT32 *Desc;\r
114 EFI_STATUS Status;\r
115\r
116 //\r
117 // First initialize the periodical schedule data:\r
118 // 1. Allocate and map the memory for the frame list\r
119 // 2. Create the help QTD/QH\r
120 // 3. Initialize the frame entries\r
121 // 4. Set the frame list register\r
122 //\r
123 //\r
124 // The Frame List ocupies 4K bytes,\r
125 // and must be aligned on 4-Kbyte boundaries.\r
126 //\r
127 Status = PeiServicesAllocatePages (\r
128 EfiBootServicesCode,\r
129 1,\r
130 &PhyAddr\r
131 );\r
132\r
133 Map = NULL;\r
134 Ehc->PeriodFrameHost = (VOID *)(UINTN)PhyAddr;\r
135 Ehc->PeriodFrame = (VOID *)(UINTN)PhyAddr;\r
136 Ehc->PeriodFrameMap = Map;\r
137 Ehc->High32bitAddr = EHC_HIGH_32BIT (PhyAddr);\r
138\r
139 //\r
140 // Init memory pool management then create the helper\r
141 // QTD/QH. If failed, previously allocated resources\r
142 // will be freed by EhcFreeSched\r
143 //\r
144 Ehc->MemPool = UsbHcInitMemPool (\r
145 Ehc,\r
146 EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT),\r
147 Ehc->High32bitAddr\r
148 );\r
149\r
150 if (Ehc->MemPool == NULL) {\r
151 return EFI_OUT_OF_RESOURCES;\r
152 }\r
153\r
154 Status = EhcCreateHelpQ (Ehc);\r
155\r
156 if (EFI_ERROR (Status)) {\r
157 return Status;\r
158 }\r
159 \r
160 //\r
161 // Initialize the frame list entries then set the registers\r
162 //\r
163 Desc = (UINT32 *) Ehc->PeriodFrame;\r
164\r
165 for (Index = 0; Index < EHC_FRAME_LEN; Index++) {\r
166 Desc[Index] = QH_LINK (Ehc->PeriodOne, EHC_TYPE_QH, FALSE);\r
167 }\r
168\r
169 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, EHC_LOW_32BIT (Ehc->PeriodFrame));\r
170\r
171 //\r
172 // Second initialize the asynchronous schedule:\r
173 // Only need to set the AsynListAddr register to\r
174 // the reclamation header\r
175 //\r
176 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, EHC_LOW_32BIT (Ehc->ReclaimHead));\r
177 return EFI_SUCCESS;\r
178}\r
179\r
180/**\r
181 Free the schedule data. It may be partially initialized.\r
182 \r
183 @param Ehc The EHCI device. \r
184\r
185**/\r
186VOID\r
187EhcFreeSched (\r
188 IN PEI_USB2_HC_DEV *Ehc\r
189 )\r
190{\r
191 EhcWriteOpReg (Ehc, EHC_FRAME_BASE_OFFSET, 0);\r
192 EhcWriteOpReg (Ehc, EHC_ASYNC_HEAD_OFFSET, 0);\r
193\r
194 if (Ehc->PeriodOne != NULL) {\r
195 UsbHcFreeMem (Ehc->MemPool, Ehc->PeriodOne, sizeof (PEI_EHC_QH));\r
196 Ehc->PeriodOne = NULL;\r
197 }\r
198\r
199 if (Ehc->ReclaimHead != NULL) {\r
200 UsbHcFreeMem (Ehc->MemPool, Ehc->ReclaimHead, sizeof (PEI_EHC_QH));\r
201 Ehc->ReclaimHead = NULL;\r
202 }\r
203\r
204 if (Ehc->ShortReadStop != NULL) {\r
205 UsbHcFreeMem (Ehc->MemPool, Ehc->ShortReadStop, sizeof (PEI_EHC_QTD));\r
206 Ehc->ShortReadStop = NULL;\r
207 }\r
208\r
209 if (Ehc->MemPool != NULL) {\r
210 UsbHcFreeMemPool (Ehc->MemPool);\r
211 Ehc->MemPool = NULL;\r
212 }\r
213\r
214 if (Ehc->PeriodFrame != NULL) {\r
215 Ehc->PeriodFrame = NULL;\r
216 }\r
217}\r
218\r
219/**\r
220 Link the queue head to the asynchronous schedule list.\r
221 UEFI only supports one CTRL/BULK transfer at a time\r
222 due to its interfaces. This simplifies the AsynList\r
223 management: A reclamation header is always linked to\r
224 the AsyncListAddr, the only active QH is appended to it.\r
225 \r
226 @param Ehc The EHCI device.\r
227 @param Qh The queue head to link.\r
228\r
229**/\r
230VOID\r
231EhcLinkQhToAsync (\r
232 IN PEI_USB2_HC_DEV *Ehc,\r
233 IN PEI_EHC_QH *Qh\r
234 )\r
235{\r
236 PEI_EHC_QH *Head;\r
237\r
238 //\r
239 // Append the queue head after the reclaim header, then\r
240 // fix the hardware visiable parts (EHCI R1.0 page 72).\r
241 // ReclaimHead is always linked to the EHCI's AsynListAddr.\r
242 //\r
243 Head = Ehc->ReclaimHead;\r
244\r
245 Qh->NextQh = Head->NextQh;\r
246 Head->NextQh = Qh;\r
247\r
248 Qh->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);;\r
249 Head->QhHw.HorizonLink = QH_LINK (Qh, EHC_TYPE_QH, FALSE);\r
250}\r
251\r
252/**\r
253 Unlink a queue head from the asynchronous schedule list.\r
254 Need to synchronize with hardware.\r
255 \r
256 @param Ehc The EHCI device.\r
257 @param Qh The queue head to unlink.\r
258\r
259**/\r
260VOID\r
261EhcUnlinkQhFromAsync (\r
262 IN PEI_USB2_HC_DEV *Ehc,\r
263 IN PEI_EHC_QH *Qh\r
264 )\r
265{\r
266 PEI_EHC_QH *Head;\r
4b1bf81c 267\r
268 ASSERT (Ehc->ReclaimHead->NextQh == Qh);\r
269\r
270 //\r
271 // Remove the QH from reclamation head, then update the hardware\r
272 // visiable part: Only need to loopback the ReclaimHead. The Qh\r
273 // is pointing to ReclaimHead (which is staill in the list).\r
274 //\r
275 Head = Ehc->ReclaimHead;\r
276\r
277 Head->NextQh = Qh->NextQh;\r
278 Qh->NextQh = NULL;\r
279\r
280 Head->QhHw.HorizonLink = QH_LINK (Head, EHC_TYPE_QH, FALSE);\r
281\r
282 //\r
283 // Set and wait the door bell to synchronize with the hardware\r
284 //\r
7538d536 285 EhcSetAndWaitDoorBell (Ehc, EHC_GENERIC_TIMEOUT);\r
4b1bf81c 286 \r
287 return;\r
288}\r
289\r
4b1bf81c 290/**\r
291 Check the URB's execution result and update the URB's\r
292 result accordingly. \r
293\r
294 @param Ehc The EHCI device.\r
295 @param Urb The URB to check result.\r
296\r
297 @retval TRUE URB transfer is finialized.\r
298 @retval FALSE URB transfer is not finialized.\r
299\r
300**/\r
301BOOLEAN\r
302EhcCheckUrbResult (\r
303 IN PEI_USB2_HC_DEV *Ehc,\r
304 IN PEI_URB *Urb\r
305 )\r
306{\r
307 EFI_LIST_ENTRY *Entry;\r
308 PEI_EHC_QTD *Qtd;\r
309 QTD_HW *QtdHw;\r
310 UINT8 State;\r
311 BOOLEAN Finished;\r
312\r
313 ASSERT ((Ehc != NULL) && (Urb != NULL) && (Urb->Qh != NULL));\r
314\r
315 Finished = TRUE;\r
316 Urb->Completed = 0;\r
317\r
318 Urb->Result = EFI_USB_NOERROR;\r
319\r
320 if (EhcIsHalt (Ehc) || EhcIsSysError (Ehc)) {\r
321 Urb->Result |= EFI_USB_ERR_SYSTEM;\r
322 goto ON_EXIT;\r
323 }\r
324\r
325 EFI_LIST_FOR_EACH (Entry, &Urb->Qh->Qtds) {\r
326 Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);\r
327 QtdHw = &Qtd->QtdHw;\r
328 State = (UINT8) QtdHw->Status;\r
329\r
330 if (EHC_BIT_IS_SET (State, QTD_STAT_HALTED)) {\r
331 //\r
332 // EHCI will halt the queue head when met some error.\r
333 // If it is halted, the result of URB is finialized.\r
334 //\r
335 if ((State & QTD_STAT_ERR_MASK) == 0) {\r
336 Urb->Result |= EFI_USB_ERR_STALL;\r
337 }\r
338\r
339 if (EHC_BIT_IS_SET (State, QTD_STAT_BABBLE_ERR)) {\r
340 Urb->Result |= EFI_USB_ERR_BABBLE;\r
341 }\r
342\r
343 if (EHC_BIT_IS_SET (State, QTD_STAT_BUFF_ERR)) {\r
344 Urb->Result |= EFI_USB_ERR_BUFFER;\r
345 }\r
346\r
347 if (EHC_BIT_IS_SET (State, QTD_STAT_TRANS_ERR) && (QtdHw->ErrCnt == 0)) {\r
348 Urb->Result |= EFI_USB_ERR_TIMEOUT;\r
349 }\r
350\r
351 Finished = TRUE;\r
352 goto ON_EXIT;\r
353 \r
354 } else if (EHC_BIT_IS_SET (State, QTD_STAT_ACTIVE)) {\r
355 //\r
356 // The QTD is still active, no need to check furthur.\r
357 //\r
358 Urb->Result |= EFI_USB_ERR_NOTEXECUTE;\r
359 \r
360 Finished = FALSE;\r
361 goto ON_EXIT;\r
362\r
363 } else {\r
364 //\r
365 // This QTD is finished OK or met short packet read. Update the\r
366 // transfer length if it isn't a setup.\r
367 //\r
368 if (QtdHw->Pid != QTD_PID_SETUP) {\r
369 Urb->Completed += Qtd->DataLen - QtdHw->TotalBytes;\r
370 }\r
371\r
372 if ((QtdHw->TotalBytes != 0) && (QtdHw->Pid == QTD_PID_INPUT)) {\r
373 //EHC_DUMP_QH ((Urb->Qh, "Short packet read", FALSE));\r
374\r
375 //\r
376 // Short packet read condition. If it isn't a setup transfer,\r
377 // no need to check furthur: the queue head will halt at the\r
378 // ShortReadStop. If it is a setup transfer, need to check the\r
379 // Status Stage of the setup transfer to get the finial result\r
380 //\r
381 if (QtdHw->AltNext == QTD_LINK (Ehc->ShortReadStop, FALSE)) {\r
382 \r
383 Finished = TRUE;\r
384 goto ON_EXIT;\r
385 }\r
386 }\r
387 }\r
388 }\r
389\r
390ON_EXIT:\r
391 //\r
392 // Return the data toggle set by EHCI hardware, bulk and interrupt\r
393 // transfer will use this to initialize the next transaction. For\r
394 // Control transfer, it always start a new data toggle sequence for\r
395 // new transfer.\r
396 //\r
397 // NOTICE: don't move DT update before the loop, otherwise there is\r
398 // a race condition that DT is wrong.\r
399 //\r
400 Urb->DataToggle = (UINT8) Urb->Qh->QhHw.DataToggle;\r
401\r
402 return Finished;\r
403}\r
404\r
405/**\r
406 Execute the transfer by polling the URB. This is a synchronous operation.\r
407 \r
408 @param Ehc The EHCI device.\r
409 @param Urb The URB to execute.\r
410 @param TimeOut The time to wait before abort, in millisecond.\r
411\r
412 @retval EFI_DEVICE_ERROR The transfer failed due to transfer error.\r
413 @retval EFI_TIMEOUT The transfer failed due to time out.\r
414 @retval EFI_SUCCESS The transfer finished OK.\r
415\r
416**/\r
417EFI_STATUS\r
418EhcExecTransfer (\r
419 IN PEI_USB2_HC_DEV *Ehc,\r
420 IN PEI_URB *Urb,\r
421 IN UINTN TimeOut\r
422 )\r
423{\r
424 EFI_STATUS Status;\r
425 UINTN Index;\r
426 UINTN Loop;\r
427 BOOLEAN Finished;\r
428\r
429 Status = EFI_SUCCESS;\r
430 Loop = (TimeOut * EHC_1_MILLISECOND / EHC_SYNC_POLL_INTERVAL) + 1;\r
431 Finished = FALSE;\r
432\r
433 for (Index = 0; Index < Loop; Index++) {\r
434 Finished = EhcCheckUrbResult (Ehc, Urb);\r
435\r
436 if (Finished) {\r
437 break;\r
438 }\r
439\r
440 MicroSecondDelay (EHC_SYNC_POLL_INTERVAL);\r
441 }\r
442\r
443 if (!Finished) {\r
444 Status = EFI_TIMEOUT;\r
445 } else if (Urb->Result != EFI_USB_NOERROR) {\r
446 Status = EFI_DEVICE_ERROR;\r
447 }\r
448\r
449 return Status;\r
450}\r
451\r