]> git.proxmox.com Git - mirror_edk2.git/blame - QuarkSocPkg/QuarkSouthCluster/Usb/Ohci/Dxe/OhciSched.c
QuarkSocPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / QuarkSocPkg / QuarkSouthCluster / Usb / Ohci / Dxe / OhciSched.c
CommitLineData
9b6bbcdb
MK
1/** @file\r
2OHCI transfer scheduling routines.\r
3\r
4Copyright (c) 2013-2015 Intel Corporation.\r
5\r
c9f231d0 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
9b6bbcdb
MK
7\r
8**/\r
9\r
10\r
11#include "Ohci.h"\r
12\r
13/**\r
14\r
15 Add an item of interrupt context\r
16\r
17 @param Ohc UHC private data\r
18 @param NewEntry New entry to add\r
19\r
20 @retval EFI_SUCCESS Item successfully added\r
21\r
22**/\r
23EFI_STATUS\r
24OhciAddInterruptContextEntry (\r
25 IN USB_OHCI_HC_DEV *Ohc,\r
26 IN INTERRUPT_CONTEXT_ENTRY *NewEntry\r
27 )\r
28{\r
29 INTERRUPT_CONTEXT_ENTRY *Entry;\r
30 EFI_TPL OriginalTPL;\r
31\r
32 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);\r
33\r
34 if (Ohc->InterruptContextList == NULL) {\r
35 Ohc->InterruptContextList = NewEntry;\r
36 } else {\r
37 Entry = Ohc->InterruptContextList;\r
38 while (Entry->NextEntry != NULL) {\r
39 Entry = Entry->NextEntry;\r
40 }\r
41 Entry->NextEntry = NewEntry;\r
42 }\r
43\r
44 gBS->RestoreTPL (OriginalTPL);\r
45\r
46 return EFI_SUCCESS;\r
47}\r
48\r
49\r
50/**\r
51\r
52 Free a interrupt context entry\r
53\r
54 @param Ohc UHC private data\r
55 @param Entry Pointer to an interrupt context entry\r
56\r
57 @retval EFI_SUCCESS Entry freed\r
58 @retval EFI_INVALID_PARAMETER Entry is NULL\r
59\r
60**/\r
61EFI_STATUS\r
62OhciFreeInterruptContextEntry (\r
63 IN USB_OHCI_HC_DEV *Ohc,\r
64 IN INTERRUPT_CONTEXT_ENTRY *Entry\r
65 )\r
66{\r
67 TD_DESCRIPTOR *Td;\r
68 if (Entry == NULL) {\r
69 return EFI_INVALID_PARAMETER;\r
70 }\r
71 if (Entry->UCBufferMapping != NULL) {\r
72 Ohc->PciIo->Unmap(Ohc->PciIo, Entry->UCBufferMapping);\r
73 }\r
74 if (Entry->UCBuffer != NULL) {\r
75 FreePool(Entry->UCBuffer);\r
76 }\r
77 while (Entry->DataTd) {\r
78 Td = Entry->DataTd;\r
79 Entry->DataTd = (TD_DESCRIPTOR *)(UINTN)(Entry->DataTd->NextTDPointer);\r
80 UsbHcFreeMem(Ohc->MemPool, Td, sizeof(TD_DESCRIPTOR));\r
81 }\r
82 FreePool(Entry);\r
83 return EFI_SUCCESS;\r
84}\r
85\r
86\r
87/**\r
88\r
89 Free entries match the device address and endpoint address\r
90\r
91 @Param Ohc UHC private date\r
92 @Param DeviceAddress Item to free must match this device address\r
93 @Param EndPointAddress Item to free must match this end point address\r
94 @Param DataToggle DataToggle for output\r
95\r
96 @retval EFI_SUCCESS Items match the requirement removed\r
97\r
98**/\r
99EFI_STATUS\r
100OhciFreeInterruptContext(\r
101 IN USB_OHCI_HC_DEV *Ohc,\r
102 IN UINT8 DeviceAddress,\r
103 IN UINT8 EndPointAddress,\r
104 OUT UINT8 *DataToggle\r
105 )\r
106{\r
107 INTERRUPT_CONTEXT_ENTRY *Entry;\r
108 INTERRUPT_CONTEXT_ENTRY *TempEntry;\r
109 EFI_TPL OriginalTPL;\r
110\r
111\r
112 OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);\r
113\r
114 while (Ohc->InterruptContextList != NULL &&\r
115 Ohc->InterruptContextList->DeviceAddress == DeviceAddress &&\r
116 Ohc->InterruptContextList->EndPointAddress == EndPointAddress) {\r
117 TempEntry = Ohc->InterruptContextList;\r
118 Ohc->InterruptContextList = Ohc->InterruptContextList->NextEntry;\r
119 if (DataToggle != NULL) {\r
120 *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);\r
121 }\r
122 OhciFreeInterruptContextEntry (Ohc, TempEntry);\r
123 }\r
124\r
125 Entry = Ohc->InterruptContextList;\r
126 if (Entry == NULL) {\r
127 gBS->RestoreTPL (OriginalTPL);\r
128 return EFI_SUCCESS;\r
129 }\r
130 while (Entry->NextEntry != NULL) {\r
131 if (Entry->NextEntry->DeviceAddress == DeviceAddress &&\r
132 Entry->NextEntry->EndPointAddress == EndPointAddress) {\r
133 TempEntry = Entry->NextEntry;\r
134 Entry->NextEntry = Entry->NextEntry->NextEntry;\r
135 if (DataToggle != NULL) {\r
136 *DataToggle = (UINT8) (TempEntry->DataTd->Word0.DataToggle & 0x1);\r
137 }\r
138 OhciFreeInterruptContextEntry (Ohc, TempEntry);\r
139 } else {\r
140 Entry = Entry->NextEntry;\r
141 }\r
142 }\r
143\r
144 gBS->RestoreTPL (OriginalTPL);\r
145\r
146 return EFI_SUCCESS;\r
147}\r
148\r
149\r
150/**\r
151\r
152 Convert Error code from OHCI format to EFI format\r
153\r
154 @Param ErrorCode ErrorCode in OHCI format\r
155\r
156 @retval ErrorCode in EFI format\r
157\r
158**/\r
159UINT32\r
160ConvertErrorCode (\r
161 IN UINT32 ErrorCode\r
162 )\r
163{\r
164 UINT32 TransferResult;\r
165\r
166 switch (ErrorCode) {\r
167 case TD_NO_ERROR:\r
168 TransferResult = EFI_USB_NOERROR;\r
169 break;\r
170\r
171 case TD_TOBE_PROCESSED:\r
172 case TD_TOBE_PROCESSED_2:\r
173 TransferResult = EFI_USB_ERR_NOTEXECUTE;\r
174 break;\r
175\r
176 case TD_DEVICE_STALL:\r
177 TransferResult = EFI_USB_ERR_STALL;\r
178 break;\r
179\r
180 case TD_BUFFER_OVERRUN:\r
181 case TD_BUFFER_UNDERRUN:\r
182 TransferResult = EFI_USB_ERR_BUFFER;\r
183 break;\r
184\r
185 case TD_CRC_ERROR:\r
186 TransferResult = EFI_USB_ERR_CRC;\r
187 break;\r
188\r
189 case TD_NO_RESPONSE:\r
190 TransferResult = EFI_USB_ERR_TIMEOUT;\r
191 break;\r
192\r
193 case TD_BITSTUFFING_ERROR:\r
194 TransferResult = EFI_USB_ERR_BITSTUFF;\r
195 break;\r
196\r
197 default:\r
198 TransferResult = EFI_USB_ERR_SYSTEM;\r
199 }\r
200\r
201 return TransferResult;\r
202}\r
203\r
204\r
205/**\r
206\r
207 Check TDs Results\r
208\r
209 @Param Ohc UHC private data\r
210 @Param Td TD_DESCRIPTOR\r
211 @Param Result Result to return\r
212\r
213 @retval TRUE means OK\r
214 @retval FLASE means Error or Short packet\r
215\r
216**/\r
217BOOLEAN\r
218OhciCheckTDsResults (\r
219 IN USB_OHCI_HC_DEV *Ohc,\r
220 IN TD_DESCRIPTOR *Td,\r
221 OUT UINT32 *Result\r
222 )\r
223{\r
224 UINT32 TdCompletionCode;\r
225\r
226 *Result = EFI_USB_NOERROR;\r
227\r
228 while (Td) {\r
229 TdCompletionCode = Td->Word0.ConditionCode;\r
230\r
231 *Result |= ConvertErrorCode(TdCompletionCode);\r
232 //\r
233 // if any error encountered, stop processing the left TDs.\r
234 //\r
235 if (*Result) {\r
236 return FALSE;\r
237 }\r
238\r
239 Td = (TD_DESCRIPTOR *)(UINTN)(Td->NextTDPointer);\r
240 }\r
241 return TRUE;\r
242\r
243}\r
244\r
245\r
246/**\r
247\r
248 Check the task status on an ED\r
249\r
250 @Param Ed Pointer to the ED task that TD hooked on\r
251 @Param HeadTd TD header for current transaction\r
252\r
253 @retval Task Status Code\r
254\r
255**/\r
256\r
257UINT32\r
258CheckEDStatus (\r
259 IN ED_DESCRIPTOR *Ed,\r
260 IN TD_DESCRIPTOR *HeadTd,\r
261 OUT OHCI_ED_RESULT *EdResult\r
262 )\r
263{\r
264 while(HeadTd != NULL) {\r
265 if (HeadTd->NextTDPointer == 0) {\r
266 return TD_NO_ERROR;\r
267 }\r
268 if (HeadTd->Word0.ConditionCode != 0) {\r
269 return HeadTd->Word0.ConditionCode;\r
270 }\r
271 EdResult->NextToggle = ((UINT8)(HeadTd->Word0.DataToggle) & BIT0) ^ BIT0;\r
272 HeadTd = (TD_DESCRIPTOR *)(UINTN)(HeadTd->NextTDPointer);\r
273 }\r
274 if (OhciGetEDField (Ed, ED_TDHEAD_PTR) != OhciGetEDField (Ed, ED_TDTAIL_PTR)) {\r
275 return TD_TOBE_PROCESSED;\r
276 }\r
277 return TD_NO_ERROR;\r
278}\r
279\r
280/**\r
281\r
282 Check the task status\r
283\r
284 @Param Ohc UHC private data\r
285 @Param ListType Pipe type\r
286 @Param Ed Pointer to the ED task hooked on\r
287 @Param HeadTd Head of TD corresponding to the task\r
288 @Param ErrorCode return the ErrorCode\r
289\r
290 @retval EFI_SUCCESS Task done\r
291 @retval EFI_NOT_READY Task on processing\r
292 @retval EFI_DEVICE_ERROR Some error occured\r
293\r
294**/\r
295EFI_STATUS\r
296CheckIfDone (\r
297 IN USB_OHCI_HC_DEV *Ohc,\r
298 IN DESCRIPTOR_LIST_TYPE ListType,\r
299 IN ED_DESCRIPTOR *Ed,\r
300 IN TD_DESCRIPTOR *HeadTd,\r
301 OUT OHCI_ED_RESULT *EdResult\r
302 )\r
303{\r
304 EdResult->ErrorCode = TD_TOBE_PROCESSED;\r
305\r
306 switch (ListType) {\r
307 case CONTROL_LIST:\r
308 if (OhciGetHcCommandStatus (Ohc, CONTROL_LIST_FILLED) != 0) {\r
309 return EFI_NOT_READY;\r
310 }\r
311 break;\r
312 case BULK_LIST:\r
313 if (OhciGetHcCommandStatus (Ohc, BULK_LIST_FILLED) != 0) {\r
314 return EFI_NOT_READY;\r
315 }\r
316 break;\r
317 default:\r
318 break;\r
319 }\r
320\r
321 EdResult->ErrorCode = CheckEDStatus (Ed, HeadTd, EdResult);\r
322\r
323 if (EdResult->ErrorCode == TD_NO_ERROR) {\r
324 return EFI_SUCCESS;\r
325 } else if (EdResult->ErrorCode == TD_TOBE_PROCESSED) {\r
326 return EFI_NOT_READY;\r
327 } else {\r
328 return EFI_DEVICE_ERROR;\r
329 }\r
330}\r
331\r
332\r
333/**\r
334\r
335 Convert TD condition code to Efi Status\r
336\r
337 @Param ConditionCode Condition code to convert\r
338\r
339 @retval EFI_SUCCESS No error occured\r
340 @retval EFI_NOT_READY TD still on processing\r
341 @retval EFI_DEVICE_ERROR Error occured in processing TD\r
342\r
343**/\r
344\r
345EFI_STATUS\r
346OhciTDConditionCodeToStatus (\r
347 IN UINT32 ConditionCode\r
348 )\r
349{\r
350 if (ConditionCode == TD_NO_ERROR) {\r
351 return EFI_SUCCESS;\r
352 }\r
353\r
354 if (ConditionCode == TD_TOBE_PROCESSED) {\r
355 return EFI_NOT_READY;\r
356 }\r
357\r
358 return EFI_DEVICE_ERROR;\r
359}\r
360\r
361/**\r
362\r
363 Invoke callbacks hooked on done TDs\r
364\r
365 @Param Entry Interrupt transfer transaction information data structure\r
366 @Param Context Ohc private data\r
367\r
368**/\r
369\r
370VOID\r
371OhciInvokeInterruptCallBack(\r
372 IN INTERRUPT_CONTEXT_ENTRY *Entry,\r
373 IN UINT32 Result\r
374)\r
375{\r
376 //Generally speaking, Keyboard driver should not\r
377 //check the Keyboard buffer if an error happens, it will be robust\r
378 //if we NULLed the buffer once error happens\r
379 if (Result) {\r
380 Entry->CallBackFunction (\r
381 NULL,\r
382 0,\r
383 Entry->Context,\r
384 Result\r
385 );\r
386 }else{\r
387 Entry->CallBackFunction (\r
388 (VOID *)(UINTN)(Entry->DataTd->DataBuffer),\r
389 Entry->DataTd->ActualSendLength,\r
390 Entry->Context,\r
391 Result\r
392 );\r
393 }\r
394}\r
395\r
396\r
397/**\r
398\r
399 Timer to submit periodic interrupt transfer, and invoke callbacks hooked on done TDs\r
400\r
401 @param Event Event handle\r
402 @param Context Device private data\r
403\r
404**/\r
405\r
406VOID\r
407EFIAPI\r
408OhciHouseKeeper (\r
409 IN EFI_EVENT Event,\r
410 IN VOID *Context\r
411 )\r
412{\r
413\r
414 USB_OHCI_HC_DEV *Ohc;\r
415 INTERRUPT_CONTEXT_ENTRY *Entry;\r
416 INTERRUPT_CONTEXT_ENTRY *PreEntry;\r
417 ED_DESCRIPTOR *Ed;\r
418 TD_DESCRIPTOR *DataTd;\r
419 TD_DESCRIPTOR *HeadTd;\r
420\r
421 UINT8 Toggle;\r
422 EFI_TPL OriginalTPL;\r
423 UINT32 Result;\r
424\r
425 Ohc = (USB_OHCI_HC_DEV *) Context;\r
426 OriginalTPL = gBS->RaiseTPL(TPL_NOTIFY);\r
427\r
428 Entry = Ohc->InterruptContextList;\r
429 PreEntry = NULL;\r
430\r
431 while(Entry != NULL) {\r
432\r
433 OhciCheckTDsResults(Ohc, Entry->DataTd, &Result );\r
434 if (((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) ||\r
435 ((Result & EFI_USB_ERR_NOTEXECUTE) == EFI_USB_ERR_NOTEXECUTE)) {\r
436 PreEntry = Entry;\r
437 Entry = Entry->NextEntry;\r
438 continue;\r
439 }\r
440\r
441 if (Entry->CallBackFunction != NULL) {\r
442 OhciInvokeInterruptCallBack (Entry, Result);\r
443 if (Ohc->InterruptContextList == NULL) {\r
444 gBS->RestoreTPL (OriginalTPL);\r
445 return;\r
446 }\r
447 }\r
448 if (Entry->IsPeriodic) {\r
449\r
450 Ed = Entry->Ed;\r
451 HeadTd = Entry->DataTd;\r
452 DataTd = HeadTd;\r
453 Toggle = 0;\r
454 if (Result == EFI_USB_NOERROR) {\r
455 //\r
456 // Update toggle if there is no error, and re-submit the interrupt Ed&Tds\r
457 //\r
458 if ((Ed != NULL) && (DataTd != NULL)) {\r
459 Ed->Word0.Skip = 1;\r
460 }\r
461 //\r
462 // From hcir1_0a.pdf 4.2.2\r
463 // ToggleCarry:This bit is the data toggle carry bit,\r
464 // Whenever a TD is retired, this bit is written to\r
465 // contain the last data toggle value(LSb of data Toggel\r
466 // file) from the retired TD.\r
467 // This field is not used for Isochronous Endpoints\r
468 //\r
469 if (Ed == NULL) {\r
470 return;\r
471 }\r
472 Toggle = (UINT8) OhciGetEDField (Ed, ED_DTTOGGLE);\r
473 while(DataTd != NULL) {\r
474 if (DataTd->NextTDPointer == 0) {\r
475 DataTd->Word0.DataToggle = 0;\r
476 break;\r
477 } else {\r
478 OhciSetTDField (DataTd, TD_DT_TOGGLE, Toggle);\r
479 }\r
480 DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);\r
481 Toggle ^= 1;\r
482 }\r
483 //\r
484 // HC will only update DataToggle, ErrorCount, ConditionCode\r
485 // CurrentBufferPointer & NextTD, so we only need to update\r
486 // them once we want to active them again\r
487 //\r
488 DataTd = HeadTd;\r
489 while (DataTd != NULL) {\r
490 if (DataTd->NextTDPointer == 0) {\r
491 OhciSetTDField (DataTd, TD_ERROR_CNT | TD_COND_CODE | TD_CURR_BUFFER_PTR | TD_NEXT_PTR, 0);\r
492 break;\r
493 }\r
494 OhciSetTDField (DataTd, TD_ERROR_CNT, 0);\r
495 OhciSetTDField (DataTd, TD_COND_CODE, TD_TOBE_PROCESSED);\r
496 DataTd->NextTD = DataTd->NextTDPointer;\r
497 DataTd->CurrBufferPointer = DataTd->DataBuffer;\r
498 DataTd = (TD_DESCRIPTOR *)(UINTN)(DataTd->NextTDPointer);\r
499 }\r
500 //\r
501 // Active current Ed,Td\r
502 //\r
503 // HC will only update Halted, ToggleCarry & TDQueueHeadPointer,\r
504 // So we only need to update them once we want to active them again.\r
505 //\r
506 if ((Ed != NULL) && (DataTd != NULL)) {\r
507 Ed->Word2.TdHeadPointer = (UINT32)((UINTN)HeadTd>>4);\r
508 OhciSetEDField (Ed, ED_HALTED | ED_DTTOGGLE, 0);\r
509 Ed->Word0.Skip = 0;\r
510 }\r
511 }\r
512 } else {\r
513 if (PreEntry == NULL) {\r
514 Ohc->InterruptContextList = Entry->NextEntry;\r
515 } else {\r
516 PreEntry = Entry;\r
517 PreEntry->NextEntry = Entry->NextEntry;\r
518 }\r
519 OhciFreeInterruptContextEntry (Ohc, PreEntry);\r
520 gBS->RestoreTPL (OriginalTPL);\r
521 return;\r
522 }\r
523 PreEntry = Entry;\r
524 Entry = Entry->NextEntry;\r
525 }\r
526 gBS->RestoreTPL (OriginalTPL);\r
527}\r
528\r