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