Branch data Line data Source code
1 : : /* ==========================================================================
2 : : * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $
3 : : * $Revision: #44 $
4 : : * $Date: 2011/10/26 $
5 : : * $Change: 1873028 $
6 : : *
7 : : * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
8 : : * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
9 : : * otherwise expressly agreed to in writing between Synopsys and you.
10 : : *
11 : : * The Software IS NOT an item of Licensed Software or Licensed Product under
12 : : * any End User Software License Agreement or Agreement for Licensed Product
13 : : * with Synopsys or any supplement thereto. You are permitted to use and
14 : : * redistribute this Software in source and binary forms, with or without
15 : : * modification, provided that redistributions of source code must retain this
16 : : * notice. You may not view, use, disclose, copy or distribute this file or
17 : : * any information contained herein except pursuant to this license grant from
18 : : * Synopsys. If you do not agree with this notice, including the disclaimer
19 : : * below, then you are not authorized to use the Software.
20 : : *
21 : : * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
22 : : * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 : : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 : : * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
25 : : * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 : : * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 : : * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 : : * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 : : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 : : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 : : * DAMAGE.
32 : : * ========================================================================== */
33 : : #ifndef DWC_DEVICE_ONLY
34 : :
35 : : /**
36 : : * @file
37 : : *
38 : : * This file contains the functions to manage Queue Heads and Queue
39 : : * Transfer Descriptors.
40 : : */
41 : :
42 : : #include "dwc_otg_hcd.h"
43 : : #include "dwc_otg_regs.h"
44 : :
45 : : extern bool microframe_schedule;
46 : : extern unsigned short int_ep_interval_min;
47 : :
48 : : /**
49 : : * Free each QTD in the QH's QTD-list then free the QH. QH should already be
50 : : * removed from a list. QTD list should already be empty if called from URB
51 : : * Dequeue.
52 : : *
53 : : * @param hcd HCD instance.
54 : : * @param qh The QH to free.
55 : : */
56 : 1616 : void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
57 : : {
58 : : dwc_otg_qtd_t *qtd, *qtd_tmp;
59 : : dwc_irqflags_t flags;
60 : : uint32_t buf_size = 0;
61 : : uint8_t *align_buf_virt = NULL;
62 : : dwc_dma_t align_buf_dma;
63 : : struct device *dev = dwc_otg_hcd_to_dev(hcd);
64 : :
65 : : /* Free each QTD in the QTD list */
66 : 1616 : DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags);
67 [ - + ]: 1616 : DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) {
68 [ # # # # ]: 0 : DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry);
69 : : dwc_otg_hcd_qtd_free(qtd);
70 : : }
71 : :
72 [ - + ]: 1616 : if (hcd->core_if->dma_desc_enable) {
73 : 0 : dwc_otg_hcd_qh_free_ddma(hcd, qh);
74 [ - + ]: 1616 : } else if (qh->dw_align_buf) {
75 [ # # ]: 0 : if (qh->ep_type == UE_ISOCHRONOUS) {
76 : : buf_size = 4096;
77 : : } else {
78 : 0 : buf_size = hcd->core_if->core_params->max_transfer_size;
79 : : }
80 : : align_buf_virt = qh->dw_align_buf;
81 : 0 : align_buf_dma = qh->dw_align_buf_dma;
82 : : }
83 : :
84 : 1616 : DWC_FREE(qh);
85 : 1616 : DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags);
86 [ - + ]: 1616 : if (align_buf_virt)
87 : 0 : DWC_DMA_FREE(dev, buf_size, align_buf_virt, align_buf_dma);
88 : 1616 : return;
89 : : }
90 : :
91 : : #define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6)
92 : : #define HS_HOST_DELAY 5 /* nanoseconds */
93 : : #define FS_LS_HOST_DELAY 1000 /* nanoseconds */
94 : : #define HUB_LS_SETUP 333 /* nanoseconds */
95 : : #define NS_TO_US(ns) ((ns + 500) / 1000)
96 : : /* convert & round nanoseconds to microseconds */
97 : :
98 : 1368 : static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, int bytecount)
99 : : {
100 : : unsigned long retval;
101 : :
102 [ - + - - ]: 1368 : switch (speed) {
103 : : case USB_SPEED_HIGH:
104 [ # # ]: 0 : if (is_isoc) {
105 : 0 : retval =
106 : 0 : ((38 * 8 * 2083) +
107 : 0 : (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
108 : : HS_HOST_DELAY;
109 : : } else {
110 : 0 : retval =
111 : 0 : ((55 * 8 * 2083) +
112 : 0 : (2083 * (3 + BitStuffTime(bytecount)))) / 1000 +
113 : : HS_HOST_DELAY;
114 : : }
115 : : break;
116 : : case USB_SPEED_FULL:
117 [ - + ]: 1368 : if (is_isoc) {
118 : 0 : retval =
119 : 0 : (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
120 [ # # ]: 0 : if (is_in) {
121 : 0 : retval = 7268 + FS_LS_HOST_DELAY + retval;
122 : : } else {
123 : 0 : retval = 6265 + FS_LS_HOST_DELAY + retval;
124 : : }
125 : : } else {
126 : 1368 : retval =
127 : 1368 : (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000;
128 : 1368 : retval = 9107 + FS_LS_HOST_DELAY + retval;
129 : : }
130 : : break;
131 : : case USB_SPEED_LOW:
132 [ # # ]: 0 : if (is_in) {
133 : 0 : retval =
134 : 0 : (67667 * (31 + 10 * BitStuffTime(bytecount))) /
135 : : 1000;
136 : 0 : retval =
137 : : 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
138 : : retval;
139 : : } else {
140 : 0 : retval =
141 : 0 : (66700 * (31 + 10 * BitStuffTime(bytecount))) /
142 : : 1000;
143 : 0 : retval =
144 : : 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY +
145 : : retval;
146 : : }
147 : : break;
148 : : default:
149 : 0 : DWC_WARN("Unknown device speed\n");
150 : : retval = -1;
151 : : }
152 : :
153 : 1368 : return NS_TO_US(retval);
154 : : }
155 : :
156 : : /**
157 : : * Initializes a QH structure.
158 : : *
159 : : * @param hcd The HCD state structure for the DWC OTG controller.
160 : : * @param qh The QH to init.
161 : : * @param urb Holds the information about the device/endpoint that we need
162 : : * to initialize the QH.
163 : : */
164 : : #define SCHEDULE_SLOP 10
165 : 5408 : void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb)
166 : : {
167 : : char *speed, *type;
168 : : int dev_speed;
169 : : uint32_t hub_addr, hub_port;
170 : : hprt0_data_t hprt;
171 : :
172 : 5408 : dwc_memset(qh, 0, sizeof(dwc_otg_qh_t));
173 : 5408 : hprt.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0);
174 : :
175 : : /* Initialize QH */
176 : 5408 : qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info);
177 : 5408 : qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
178 : :
179 : 5408 : qh->data_toggle = DWC_OTG_HC_PID_DATA0;
180 : 5408 : qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info);
181 : 5408 : DWC_CIRCLEQ_INIT(&qh->qtd_list);
182 : 5408 : DWC_LIST_INIT(&qh->qh_list_entry);
183 : 5408 : qh->channel = NULL;
184 : :
185 : : /* FS/LS Enpoint on HS Hub
186 : : * NOT virtual root hub */
187 : 5408 : dev_speed = hcd->fops->speed(hcd, urb->priv);
188 : :
189 : 5408 : hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port);
190 : 5408 : qh->do_split = 0;
191 [ + - ]: 5408 : if (microframe_schedule)
192 : 5408 : qh->speed = dev_speed;
193 : :
194 : 5408 : qh->nak_frame = 0xffff;
195 : :
196 [ - + # # ]: 5408 : if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED &&
197 : : dev_speed != USB_SPEED_HIGH) {
198 : : DWC_DEBUGPL(DBG_HCD,
199 : : "QH init: EP %d: TT found at hub addr %d, for port %d\n",
200 : : dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr,
201 : : hub_port);
202 : 0 : qh->do_split = 1;
203 : 0 : qh->skip_count = 0;
204 : : }
205 : :
206 [ + + ]: 5408 : if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) {
207 : : /* Compute scheduling parameters once and save them. */
208 : :
209 : : /** @todo Account for split transfers in the bus time. */
210 : 1368 : int bytecount =
211 : 1368 : dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp);
212 : :
213 : 1368 : qh->usecs =
214 [ + - ]: 2736 : calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed),
215 : 1368 : qh->ep_is_in, (qh->ep_type == UE_ISOCHRONOUS),
216 : : bytecount);
217 : : /* Start in a slightly future (micro)frame. */
218 : 2736 : qh->sched_frame = dwc_frame_num_inc(hcd->frame_number,
219 : : SCHEDULE_SLOP);
220 : 1368 : qh->interval = urb->interval;
221 : :
222 [ - + ]: 1368 : if (hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) {
223 [ # # ]: 0 : if (dev_speed == USB_SPEED_LOW ||
224 : : dev_speed == USB_SPEED_FULL) {
225 : 0 : qh->interval *= 8;
226 : 0 : qh->sched_frame |= 0x7;
227 : 0 : qh->start_split_frame = qh->sched_frame;
228 [ # # # # ]: 0 : } else if (int_ep_interval_min >= 2 &&
229 [ # # ]: 0 : qh->interval < int_ep_interval_min &&
230 : 0 : qh->ep_type == UE_INTERRUPT) {
231 : 0 : qh->interval = int_ep_interval_min;
232 : : }
233 : : }
234 : : }
235 : :
236 : : DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
237 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh);
238 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
239 : : dwc_otg_hcd_get_dev_addr(&urb->pipe_info));
240 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
241 : : dwc_otg_hcd_get_ep_num(&urb->pipe_info),
242 : : dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
243 [ - + - - ]: 5408 : switch (dev_speed) {
244 : : case USB_SPEED_LOW:
245 : 0 : qh->dev_speed = DWC_OTG_EP_SPEED_LOW;
246 : : speed = "low";
247 : 0 : break;
248 : : case USB_SPEED_FULL:
249 : 5408 : qh->dev_speed = DWC_OTG_EP_SPEED_FULL;
250 : : speed = "full";
251 : 5408 : break;
252 : : case USB_SPEED_HIGH:
253 : 0 : qh->dev_speed = DWC_OTG_EP_SPEED_HIGH;
254 : : speed = "high";
255 : 0 : break;
256 : : default:
257 : : speed = "?";
258 : : break;
259 : : }
260 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed);
261 : :
262 : : switch (qh->ep_type) {
263 : : case UE_ISOCHRONOUS:
264 : : type = "isochronous";
265 : : break;
266 : : case UE_INTERRUPT:
267 : : type = "interrupt";
268 : : break;
269 : : case UE_CONTROL:
270 : : type = "control";
271 : : break;
272 : : case UE_BULK:
273 : : type = "bulk";
274 : : break;
275 : : default:
276 : : type = "?";
277 : : break;
278 : : }
279 : :
280 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type);
281 : :
282 : : #ifdef DEBUG
283 : : if (qh->ep_type == UE_INTERRUPT) {
284 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
285 : : qh->usecs);
286 : : DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
287 : : qh->interval);
288 : : }
289 : : #endif
290 : :
291 : 5408 : }
292 : :
293 : : /**
294 : : * This function allocates and initializes a QH.
295 : : *
296 : : * @param hcd The HCD state structure for the DWC OTG controller.
297 : : * @param urb Holds the information about the device/endpoint that we need
298 : : * to initialize the QH.
299 : : * @param atomic_alloc Flag to do atomic allocation if needed
300 : : *
301 : : * @return Returns pointer to the newly allocated QH, or NULL on error. */
302 : 5408 : dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd,
303 : : dwc_otg_hcd_urb_t * urb, int atomic_alloc)
304 : : {
305 : : dwc_otg_qh_t *qh;
306 : :
307 : : /* Allocate memory */
308 : : /** @todo add memflags argument */
309 : 5408 : qh = dwc_otg_hcd_qh_alloc(atomic_alloc);
310 [ - + ]: 5408 : if (qh == NULL) {
311 : 0 : DWC_ERROR("qh allocation failed");
312 : 0 : return NULL;
313 : : }
314 : :
315 : 5408 : qh_init(hcd, qh, urb);
316 : :
317 [ - + ]: 5408 : if (hcd->core_if->dma_desc_enable
318 [ # # ]: 0 : && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) {
319 : 0 : dwc_otg_hcd_qh_free(hcd, qh);
320 : 0 : return NULL;
321 : : }
322 : :
323 : 5408 : return qh;
324 : : }
325 : :
326 : : /* microframe_schedule=0 start */
327 : :
328 : : /**
329 : : * Checks that a channel is available for a periodic transfer.
330 : : *
331 : : * @return 0 if successful, negative error code otherise.
332 : : */
333 : 0 : static int periodic_channel_available(dwc_otg_hcd_t * hcd)
334 : : {
335 : : /*
336 : : * Currently assuming that there is a dedicated host channnel for each
337 : : * periodic transaction plus at least one host channel for
338 : : * non-periodic transactions.
339 : : */
340 : : int status;
341 : : int num_channels;
342 : :
343 : 0 : num_channels = hcd->core_if->core_params->host_channels;
344 [ # # ]: 0 : if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels)
345 [ # # ]: 0 : && (hcd->periodic_channels < num_channels - 1)) {
346 : : status = 0;
347 : : } else {
348 : 0 : DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n",
349 : : __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels); //NOTICE
350 : : status = -DWC_E_NO_SPACE;
351 : : }
352 : :
353 : 0 : return status;
354 : : }
355 : :
356 : : /**
357 : : * Checks that there is sufficient bandwidth for the specified QH in the
358 : : * periodic schedule. For simplicity, this calculation assumes that all the
359 : : * transfers in the periodic schedule may occur in the same (micro)frame.
360 : : *
361 : : * @param hcd The HCD state structure for the DWC OTG controller.
362 : : * @param qh QH containing periodic bandwidth required.
363 : : *
364 : : * @return 0 if successful, negative error code otherwise.
365 : : */
366 : 0 : static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
367 : : {
368 : : int status;
369 : : int16_t max_claimed_usecs;
370 : :
371 : : status = 0;
372 : :
373 [ # # # # ]: 0 : if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) {
374 : : /*
375 : : * High speed mode.
376 : : * Max periodic usecs is 80% x 125 usec = 100 usec.
377 : : */
378 : :
379 : 0 : max_claimed_usecs = 100 - qh->usecs;
380 : : } else {
381 : : /*
382 : : * Full speed mode.
383 : : * Max periodic usecs is 90% x 1000 usec = 900 usec.
384 : : */
385 : 0 : max_claimed_usecs = 900 - qh->usecs;
386 : : }
387 : :
388 [ # # ]: 0 : if (hcd->periodic_usecs > max_claimed_usecs) {
389 : 0 : DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs); //NOTICE
390 : : status = -DWC_E_NO_SPACE;
391 : : }
392 : :
393 : 0 : return status;
394 : : }
395 : :
396 : : /* microframe_schedule=0 end */
397 : :
398 : : /**
399 : : * Microframe scheduler
400 : : * track the total use in hcd->frame_usecs
401 : : * keep each qh use in qh->frame_usecs
402 : : * when surrendering the qh then donate the time back
403 : : */
404 : : const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 };
405 : :
406 : : /*
407 : : * called from dwc_otg_hcd.c:dwc_otg_hcd_init
408 : : */
409 : 1212 : void init_hcd_usecs(dwc_otg_hcd_t *_hcd)
410 : : {
411 : : int i;
412 [ + + ]: 1212 : if (_hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
413 : 808 : _hcd->frame_usecs[0] = 900;
414 [ + + ]: 6464 : for (i = 1; i < 8; i++)
415 : 5656 : _hcd->frame_usecs[i] = 0;
416 : : } else {
417 [ + + ]: 3232 : for (i = 0; i < 8; i++)
418 : 3232 : _hcd->frame_usecs[i] = max_uframe_usecs[i];
419 : : }
420 : 1212 : }
421 : :
422 : : static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
423 : : {
424 : : int i;
425 : : unsigned short utime;
426 : : int t_left;
427 : : int ret;
428 : : int done;
429 : :
430 : : ret = -1;
431 : 106280 : utime = _qh->usecs;
432 : : t_left = utime;
433 : : i = 0;
434 : : done = 0;
435 [ + - ]: 106280 : while (done == 0) {
436 : : /* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */
437 [ + - ]: 106280 : if (utime <= _hcd->frame_usecs[i]) {
438 : 106280 : _hcd->frame_usecs[i] -= utime;
439 : 106280 : _qh->frame_usecs[i] += utime;
440 : : t_left -= utime;
441 : 106280 : ret = i;
442 : : done = 1;
443 : : return ret;
444 : : } else {
445 : 0 : i++;
446 [ # # ]: 0 : if (i == 8) {
447 : : done = 1;
448 : : ret = -1;
449 : : }
450 : : }
451 : : }
452 : : return ret;
453 : : }
454 : :
455 : : /*
456 : : * use this for FS apps that can span multiple uframes
457 : : */
458 : 0 : static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
459 : : {
460 : : int i;
461 : : int j;
462 : : unsigned short utime;
463 : : int t_left;
464 : : int ret;
465 : : int done;
466 : : unsigned short xtime;
467 : :
468 : : ret = -1;
469 : 0 : utime = _qh->usecs;
470 : : t_left = utime;
471 : : i = 0;
472 : : done = 0;
473 : : loop:
474 [ # # ]: 0 : while (done == 0) {
475 [ # # ]: 0 : if(_hcd->frame_usecs[i] <= 0) {
476 : 0 : i++;
477 [ # # ]: 0 : if (i == 8) {
478 : : done = 1;
479 : : ret = -1;
480 : : }
481 : : goto loop;
482 : : }
483 : :
484 : : /*
485 : : * we need n consecutive slots
486 : : * so use j as a start slot j plus j+1 must be enough time (for now)
487 : : */
488 : : xtime= _hcd->frame_usecs[i];
489 [ # # ]: 0 : for (j = i+1 ; j < 8 ; j++ ) {
490 : : /*
491 : : * if we add this frame remaining time to xtime we may
492 : : * be OK, if not we need to test j for a complete frame
493 : : */
494 [ # # ]: 0 : if ((xtime+_hcd->frame_usecs[j]) < utime) {
495 [ # # ]: 0 : if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) {
496 : : j = 8;
497 : : ret = -1;
498 : 0 : continue;
499 : : }
500 : : }
501 [ # # ]: 0 : if (xtime >= utime) {
502 : : ret = i;
503 : : j = 8; /* stop loop with a good value ret */
504 : 0 : continue;
505 : : }
506 : : /* add the frame time to x time */
507 : 0 : xtime += _hcd->frame_usecs[j];
508 : : /* we must have a fully available next frame or break */
509 [ # # ]: 0 : if ((xtime < utime)
510 [ # # ]: 0 : && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) {
511 : : ret = -1;
512 : : j = 8; /* stop loop with a bad value ret */
513 : 0 : continue;
514 : : }
515 : : }
516 [ # # ]: 0 : if (ret >= 0) {
517 : 0 : t_left = utime;
518 [ # # ]: 0 : for (j = i; (t_left>0) && (j < 8); j++ ) {
519 : 0 : t_left -= _hcd->frame_usecs[j];
520 [ # # ]: 0 : if ( t_left <= 0 ) {
521 : 0 : _qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left;
522 : 0 : _hcd->frame_usecs[j]= -t_left;
523 : : ret = i;
524 : : done = 1;
525 : : } else {
526 : 0 : _qh->frame_usecs[j] += _hcd->frame_usecs[j];
527 : 0 : _hcd->frame_usecs[j] = 0;
528 : : }
529 : : }
530 : : } else {
531 : : i++;
532 [ # # ]: 0 : if (i == 8) {
533 : : done = 1;
534 : : ret = -1;
535 : : }
536 : : }
537 : : }
538 : 0 : return ret;
539 : : }
540 : :
541 : 106280 : static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
542 : : {
543 : : int ret;
544 : : ret = -1;
545 : :
546 [ + - + - ]: 212560 : if (_qh->speed == USB_SPEED_HIGH ||
547 : 106280 : _hcd->flags.b.port_speed == DWC_HPRT0_PRTSPD_FULL_SPEED) {
548 : : /* if this is a hs transaction we need a full frame - or account for FS usecs */
549 : 106280 : ret = find_single_uframe(_hcd, _qh);
550 : : } else {
551 : : /* if this is a fs transaction we may need a sequence of frames */
552 : 0 : ret = find_multi_uframe(_hcd, _qh);
553 : : }
554 : 106280 : return ret;
555 : : }
556 : :
557 : : /**
558 : : * Checks that the max transfer size allowed in a host channel is large enough
559 : : * to handle the maximum data transfer in a single (micro)frame for a periodic
560 : : * transfer.
561 : : *
562 : : * @param hcd The HCD state structure for the DWC OTG controller.
563 : : * @param qh QH for a periodic endpoint.
564 : : *
565 : : * @return 0 if successful, negative error code otherwise.
566 : : */
567 : 106280 : static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
568 : : {
569 : : int status;
570 : : uint32_t max_xfer_size;
571 : : uint32_t max_channel_xfer_size;
572 : :
573 : : status = 0;
574 : :
575 : 106280 : max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp);
576 : 106280 : max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size;
577 : :
578 [ - + ]: 106280 : if (max_xfer_size > max_channel_xfer_size) {
579 : 0 : DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n",
580 : : __func__, max_xfer_size, max_channel_xfer_size); //NOTICE
581 : : status = -DWC_E_NO_SPACE;
582 : : }
583 : :
584 : 106280 : return status;
585 : : }
586 : :
587 : :
588 : :
589 : : /**
590 : : * Schedules an interrupt or isochronous transfer in the periodic schedule.
591 : : *
592 : : * @param hcd The HCD state structure for the DWC OTG controller.
593 : : * @param qh QH for the periodic transfer. The QH should already contain the
594 : : * scheduling information.
595 : : *
596 : : * @return 0 if successful, negative error code otherwise.
597 : : */
598 : 106280 : static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
599 : : {
600 : : int status = 0;
601 : :
602 [ + - ]: 106280 : if (microframe_schedule) {
603 : : int frame;
604 : 106280 : status = find_uframe(hcd, qh);
605 : : frame = -1;
606 [ - + ]: 106280 : if (status == 0) {
607 : : frame = 7;
608 : : } else {
609 [ # # ]: 0 : if (status > 0 )
610 : 0 : frame = status-1;
611 : : }
612 : :
613 : : /* Set the new frame up */
614 [ + - ]: 106280 : if (frame > -1) {
615 : 106280 : qh->sched_frame &= ~0x7;
616 : 106280 : qh->sched_frame |= (frame & 7);
617 : : }
618 : :
619 [ + - ]: 106280 : if (status != -1)
620 : : status = 0;
621 : : } else {
622 : 0 : status = periodic_channel_available(hcd);
623 [ # # ]: 0 : if (status) {
624 : 0 : DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE
625 : 0 : return status;
626 : : }
627 : :
628 : 0 : status = check_periodic_bandwidth(hcd, qh);
629 : : }
630 [ - + ]: 106280 : if (status) {
631 : 0 : DWC_INFO("%s: Insufficient periodic bandwidth for "
632 : : "periodic transfer.\n", __func__);
633 : 0 : return -DWC_E_NO_SPACE;
634 : : }
635 : 106280 : status = check_max_xfer_size(hcd, qh);
636 [ - + ]: 106280 : if (status) {
637 : 0 : DWC_INFO("%s: Channel max transfer size too small "
638 : : "for periodic transfer.\n", __func__);
639 : 0 : return status;
640 : : }
641 : :
642 [ - + ]: 106280 : if (hcd->core_if->dma_desc_enable) {
643 : : /* Don't rely on SOF and start in ready schedule */
644 : 0 : DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry);
645 : : }
646 : : else {
647 [ + - + + : 139804 : if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame)))
+ + ]
648 : : {
649 : 81974 : hcd->fiq_state->next_sched_frame = qh->sched_frame;
650 : :
651 : : }
652 : : /* Always start in the inactive schedule. */
653 : 106280 : DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry);
654 : : }
655 : :
656 [ - + ]: 106280 : if (!microframe_schedule) {
657 : : /* Reserve the periodic channel. */
658 : 0 : hcd->periodic_channels++;
659 : : }
660 : :
661 : : /* Update claimed usecs per (micro)frame. */
662 : 106280 : hcd->periodic_usecs += qh->usecs;
663 : :
664 : 106280 : return status;
665 : : }
666 : :
667 : :
668 : : /**
669 : : * This function adds a QH to either the non periodic or periodic schedule if
670 : : * it is not already in the schedule. If the QH is already in the schedule, no
671 : : * action is taken.
672 : : *
673 : : * @return 0 if successful, negative error code otherwise.
674 : : */
675 : 269534 : int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
676 : : {
677 : : int status = 0;
678 : 269534 : gintmsk_data_t intr_mask = {.d32 = 0 };
679 : :
680 [ + + ]: 269534 : if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) {
681 : : /* QH already in a schedule. */
682 : : return status;
683 : : }
684 : :
685 : : /* Add the new QH to the appropriate schedule */
686 [ + + ]: 257180 : if (dwc_qh_is_non_per(qh)) {
687 : : /* Always start in the inactive schedule. */
688 : 150900 : DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive,
689 : : &qh->qh_list_entry);
690 : : //hcd->fiq_state->kick_np_queues = 1;
691 : : } else {
692 : 106280 : status = schedule_periodic(hcd, qh);
693 [ + + ]: 106280 : if ( !hcd->periodic_qh_count ) {
694 : 72628 : intr_mask.b.sofintr = 1;
695 [ + - ]: 72628 : if (fiq_enable) {
696 : 72628 : local_fiq_disable();
697 : 72628 : fiq_fsm_spin_lock(&hcd->fiq_state->lock);
698 : 72628 : DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
699 : 72628 : fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
700 : 72628 : local_fiq_enable();
701 : : } else {
702 : 0 : DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32);
703 : : }
704 : : }
705 : 106280 : hcd->periodic_qh_count++;
706 : : }
707 : :
708 : 257180 : return status;
709 : : }
710 : :
711 : : /**
712 : : * Removes an interrupt or isochronous transfer from the periodic schedule.
713 : : *
714 : : * @param hcd The HCD state structure for the DWC OTG controller.
715 : : * @param qh QH for the periodic transfer.
716 : : */
717 : 104912 : static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
718 : : {
719 : : int i;
720 : 104912 : DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
721 : :
722 : : /* Update claimed usecs per (micro)frame. */
723 : 104912 : hcd->periodic_usecs -= qh->usecs;
724 : :
725 [ + - ]: 104912 : if (!microframe_schedule) {
726 : : /* Release the periodic channel reservation. */
727 : 0 : hcd->periodic_channels--;
728 : : } else {
729 [ + + ]: 839296 : for (i = 0; i < 8; i++) {
730 : 839296 : hcd->frame_usecs[i] += qh->frame_usecs[i];
731 : 839296 : qh->frame_usecs[i] = 0;
732 : : }
733 : : }
734 : 104912 : }
735 : :
736 : : /**
737 : : * Removes a QH from either the non-periodic or periodic schedule. Memory is
738 : : * not freed.
739 : : *
740 : : * @param hcd The HCD state structure.
741 : : * @param qh QH to remove from schedule. */
742 : 257024 : void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
743 : : {
744 : 257024 : gintmsk_data_t intr_mask = {.d32 = 0 };
745 : :
746 [ + + ]: 257024 : if (DWC_LIST_EMPTY(&qh->qh_list_entry)) {
747 : : /* QH is not in a schedule. */
748 : 1616 : return;
749 : : }
750 : :
751 [ + + ]: 255408 : if (dwc_qh_is_non_per(qh)) {
752 [ + + ]: 150496 : if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) {
753 : 127272 : hcd->non_periodic_qh_ptr =
754 : 127272 : hcd->non_periodic_qh_ptr->next;
755 : : }
756 : 150496 : DWC_LIST_REMOVE_INIT(&qh->qh_list_entry);
757 : : //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive))
758 : : // hcd->fiq_state->kick_np_queues = 1;
759 : : } else {
760 : 104912 : deschedule_periodic(hcd, qh);
761 : 104912 : hcd->periodic_qh_count--;
762 [ + + + - ]: 104912 : if( !hcd->periodic_qh_count && !fiq_fsm_enable ) {
763 : 72224 : intr_mask.b.sofintr = 1;
764 [ + - ]: 72224 : if (fiq_enable) {
765 : 72224 : local_fiq_disable();
766 : 72224 : fiq_fsm_spin_lock(&hcd->fiq_state->lock);
767 : 72224 : DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
768 : 72224 : fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
769 : 72224 : local_fiq_enable();
770 : : } else {
771 : 0 : DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0);
772 : : }
773 : : }
774 : : }
775 : : }
776 : :
777 : : /**
778 : : * Deactivates a QH. For non-periodic QHs, removes the QH from the active
779 : : * non-periodic schedule. The QH is added to the inactive non-periodic
780 : : * schedule if any QTDs are still attached to the QH.
781 : : *
782 : : * For periodic QHs, the QH is removed from the periodic queued schedule. If
783 : : * there are any QTDs still attached to the QH, the QH is added to either the
784 : : * periodic inactive schedule or the periodic ready schedule and its next
785 : : * scheduled frame is calculated. The QH is placed in the ready schedule if
786 : : * the scheduled frame has been reached already. Otherwise it's placed in the
787 : : * inactive schedule. If there are no QTDs attached to the QH, the QH is
788 : : * completely removed from the periodic schedule.
789 : : */
790 : 227978502 : void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh,
791 : : int sched_next_periodic_split)
792 : : {
793 [ + + ]: 227978502 : if (dwc_qh_is_non_per(qh)) {
794 : 150496 : dwc_otg_hcd_qh_remove(hcd, qh);
795 [ + + ]: 150496 : if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
796 : : /* Add back to inactive non-periodic schedule. */
797 : 83890 : dwc_otg_hcd_qh_add(hcd, qh);
798 : : //hcd->fiq_state->kick_np_queues = 1;
799 : : } else {
800 [ + - - + ]: 66606 : if(nak_holdoff && qh->do_split) {
801 : 0 : qh->nak_frame = 0xFFFF;
802 : : }
803 : : }
804 : : } else {
805 : 227828006 : uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd);
806 : :
807 [ - + ]: 227828006 : if (qh->do_split) {
808 : : /* Schedule the next continuing periodic split transfer */
809 [ # # ]: 0 : if (sched_next_periodic_split) {
810 : :
811 : 0 : qh->sched_frame = frame_number;
812 : :
813 [ # # ]: 0 : if (dwc_frame_num_le(frame_number,
814 : : dwc_frame_num_inc
815 : : (qh->start_split_frame,
816 : : 1))) {
817 : : /*
818 : : * Allow one frame to elapse after start
819 : : * split microframe before scheduling
820 : : * complete split, but DONT if we are
821 : : * doing the next start split in the
822 : : * same frame for an ISOC out.
823 : : */
824 [ # # ]: 0 : if ((qh->ep_type != UE_ISOCHRONOUS) ||
825 : : (qh->ep_is_in != 0)) {
826 : 0 : qh->sched_frame =
827 : : dwc_frame_num_inc(qh->sched_frame, 1);
828 : : }
829 : : }
830 : : } else {
831 : 0 : qh->sched_frame =
832 : 0 : dwc_frame_num_inc(qh->start_split_frame,
833 : : qh->interval);
834 [ # # ]: 0 : if (dwc_frame_num_le
835 : : (qh->sched_frame, frame_number)) {
836 : 0 : qh->sched_frame = frame_number;
837 : : }
838 : 0 : qh->sched_frame |= 0x7;
839 : 0 : qh->start_split_frame = qh->sched_frame;
840 : : }
841 : : } else {
842 : 227828006 : qh->sched_frame =
843 : 227828006 : dwc_frame_num_inc(qh->sched_frame, qh->interval);
844 [ + + ]: 227828006 : if (dwc_frame_num_le(qh->sched_frame, frame_number)) {
845 : 105964792 : qh->sched_frame = frame_number;
846 : : }
847 : : }
848 : :
849 [ + + ]: 227828006 : if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) {
850 : 104892 : dwc_otg_hcd_qh_remove(hcd, qh);
851 : : } else {
852 : : /*
853 : : * Remove from periodic_sched_queued and move to
854 : : * appropriate queue.
855 : : */
856 [ + - + + : 455446228 : if ((microframe_schedule && dwc_frame_num_le(qh->sched_frame, frame_number)) ||
- + ]
857 [ # # ]: 0 : (!microframe_schedule && qh->sched_frame == frame_number)) {
858 : 105930416 : DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready,
859 : : &qh->qh_list_entry);
860 : : } else {
861 [ + - + + ]: 243585396 : if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame))
862 : : {
863 : 52138210 : hcd->fiq_state->next_sched_frame = qh->sched_frame;
864 : : }
865 : :
866 : 121792698 : DWC_LIST_MOVE_HEAD
867 : : (&hcd->periodic_sched_inactive,
868 : : &qh->qh_list_entry);
869 : : }
870 : : }
871 : : }
872 : 227978502 : }
873 : :
874 : : /**
875 : : * This function allocates and initializes a QTD.
876 : : *
877 : : * @param urb The URB to create a QTD from. Each URB-QTD pair will end up
878 : : * pointing to each other so each pair should have a unique correlation.
879 : : * @param atomic_alloc Flag to do atomic alloc if needed
880 : : *
881 : : * @return Returns pointer to the newly allocated QTD, or NULL on error. */
882 : 185644 : dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb, int atomic_alloc)
883 : : {
884 : : dwc_otg_qtd_t *qtd;
885 : :
886 : 185644 : qtd = dwc_otg_hcd_qtd_alloc(atomic_alloc);
887 [ + - ]: 185644 : if (qtd == NULL) {
888 : : return NULL;
889 : : }
890 : :
891 : 185644 : dwc_otg_hcd_qtd_init(qtd, urb);
892 : 185644 : return qtd;
893 : : }
894 : :
895 : : /**
896 : : * Initializes a QTD structure.
897 : : *
898 : : * @param qtd The QTD to initialize.
899 : : * @param urb The URB to use for initialization. */
900 : 185642 : void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb)
901 : : {
902 : 185642 : dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t));
903 : 185642 : qtd->urb = urb;
904 [ + + ]: 185642 : if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) {
905 : : /*
906 : : * The only time the QTD data toggle is used is on the data
907 : : * phase of control transfers. This phase always starts with
908 : : * DATA1.
909 : : */
910 : 45706 : qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
911 : 45706 : qtd->control_phase = DWC_OTG_CONTROL_SETUP;
912 : : }
913 : :
914 : : /* start split */
915 : 185642 : qtd->complete_split = 0;
916 : 185642 : qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
917 : 185642 : qtd->isoc_split_offset = 0;
918 : 185642 : qtd->in_process = 0;
919 : :
920 : : /* Store the qtd ptr in the urb to reference what QTD. */
921 : 185642 : urb->qtd = qtd;
922 : 185642 : return;
923 : : }
924 : :
925 : : /**
926 : : * This function adds a QTD to the QTD-list of a QH. It will find the correct
927 : : * QH to place the QTD into. If it does not find a QH, then it will create a
928 : : * new QH. If the QH to which the QTD is added is not currently scheduled, it
929 : : * is placed into the proper schedule based on its EP type.
930 : : * HCD lock must be held and interrupts must be disabled on entry
931 : : *
932 : : * @param[in] qtd The QTD to add
933 : : * @param[in] hcd The DWC HCD structure
934 : : * @param[out] qh out parameter to return queue head
935 : : * @param atomic_alloc Flag to do atomic alloc if needed
936 : : *
937 : : * @return 0 if successful, negative error code otherwise.
938 : : */
939 : 185642 : int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd,
940 : : dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc)
941 : : {
942 : : int retval = 0;
943 : 185642 : dwc_otg_hcd_urb_t *urb = qtd->urb;
944 : :
945 : : /*
946 : : * Get the QH which holds the QTD-list to insert to. Create QH if it
947 : : * doesn't exist.
948 : : */
949 [ + + ]: 185642 : if (*qh == NULL) {
950 : 5408 : *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc);
951 [ + - ]: 5408 : if (*qh == NULL) {
952 : : retval = -DWC_E_NO_MEMORY;
953 : : goto done;
954 : : } else {
955 [ + - ]: 5408 : if (fiq_enable)
956 : 5408 : hcd->fiq_state->kick_np_queues = 1;
957 : : }
958 : : }
959 : 185642 : retval = dwc_otg_hcd_qh_add(hcd, *qh);
960 [ + - ]: 185642 : if (retval == 0) {
961 [ + + ]: 185642 : DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd,
962 : : qtd_list_entry);
963 : 185642 : qtd->qh = *qh;
964 : : }
965 : : done:
966 : :
967 : 185642 : return retval;
968 : : }
969 : :
970 : : #endif /* DWC_DEVICE_ONLY */
|