Commit 20f2eb9c4cf8d770dae0c6e85991809c2f93663a

Authored by Dom Cobley
Committed by Greg Kroah-Hartman
1 parent 8418108634

staging: dwc2: add microframe scheduler from downstream Pi kernel

The transfer scheduler in the dwc2 driver is pretty basic, not to
mention buggy. It works fairly well with just a couple of devices
plugged in, but if you add, say, multiple devices with periodic
endpoints, the scheduler breaks down and can't even enumerate all
the devices.

To improve this, import the "microframe scheduler" patch from the
driver in the downstream Raspberry Pi kernel, which is based on
the Synopsys vendor driver. The original patch came from Denx
(http://git.denx.de/?p=linux-denx.git) and was commited to the
raspberrypi.org git tree by "popcornmix" (Dom Cobley).

I have added a driver parameter for this, enabled by default, in
case anyone has problems with it and needs to disable it. I don't
think we should add a DT binding for that, though, since I plan
to remove the option once any bugs are fixed.

[raspberrypi.org patch from Dom Cobley]
Signed-off-by: Dom Cobley <popcornmix@gmail.com>
[adapted to dwc2 driver by Paul Zimmerman]
Signed-off-by: Paul Zimmerman <paulz@synopsys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 8 changed files with 281 additions and 42 deletions Side-by-side Diff

drivers/staging/dwc2/core.c
... ... @@ -2736,6 +2736,26 @@
2736 2736 return 0;
2737 2737 }
2738 2738  
  2739 +int dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
  2740 +{
  2741 + int retval = 0;
  2742 +
  2743 + if (DWC2_PARAM_TEST(val, 0, 1)) {
  2744 + if (val >= 0) {
  2745 + dev_err(hsotg->dev,
  2746 + "'%d' invalid for parameter uframe_sched\n",
  2747 + val);
  2748 + dev_err(hsotg->dev, "uframe_sched must be 0 or 1\n");
  2749 + }
  2750 + val = 1;
  2751 + dev_dbg(hsotg->dev, "Setting uframe_sched to %d\n", val);
  2752 + retval = -EINVAL;
  2753 + }
  2754 +
  2755 + hsotg->core_params->uframe_sched = val;
  2756 + return retval;
  2757 +}
  2758 +
2739 2759 /*
2740 2760 * This function is called during module intialization to pass module parameters
2741 2761 * for the DWC_otg core. It returns non-0 if any parameters are invalid.
... ... @@ -2782,6 +2802,7 @@
2782 2802 retval |= dwc2_set_param_reload_ctl(hsotg, params->reload_ctl);
2783 2803 retval |= dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
2784 2804 retval |= dwc2_set_param_otg_ver(hsotg, params->otg_ver);
  2805 + retval |= dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
2785 2806  
2786 2807 return retval;
2787 2808 }
drivers/staging/dwc2/core.h
... ... @@ -188,6 +188,7 @@
188 188 * bits defined by GAHBCFG_CTRL_MASK are controlled
189 189 * by the driver and are ignored in this
190 190 * configuration value.
  191 + * @uframe_sched: True to enable the microframe scheduler
191 192 *
192 193 * The following parameters may be specified when starting the module. These
193 194 * parameters define how the DWC_otg controller should be configured. A
... ... @@ -224,6 +225,7 @@
224 225 int ts_dline;
225 226 int reload_ctl;
226 227 int ahbcfg;
  228 + int uframe_sched;
227 229 };
228 230  
229 231 /**
... ... @@ -370,6 +372,7 @@
370 372 * This value is in microseconds per (micro)frame. The
371 373 * assumption is that all periodic transfers may occur in
372 374 * the same (micro)frame.
  375 + * @frame_usecs: Internal variable used by the microframe scheduler
373 376 * @frame_number: Frame number read from the core at SOF. The value ranges
374 377 * from 0 to HFNUM_MAX_FRNUM.
375 378 * @periodic_qh_count: Count of periodic QHs, if using several eps. Used for
... ... @@ -382,6 +385,8 @@
382 385 * host channel is available for non-periodic transactions.
383 386 * @non_periodic_channels: Number of host channels assigned to non-periodic
384 387 * transfers
  388 + * @available_host_channels Number of host channels available for the microframe
  389 + * scheduler to use
385 390 * @hc_ptr_array: Array of pointers to the host channel descriptors.
386 391 * Allows accessing a host channel descriptor given the
387 392 * host channel number. This is useful in interrupt
... ... @@ -436,6 +441,7 @@
436 441 struct list_head periodic_sched_assigned;
437 442 struct list_head periodic_sched_queued;
438 443 u16 periodic_usecs;
  444 + u16 frame_usecs[8];
439 445 u16 frame_number;
440 446 u16 periodic_qh_count;
441 447  
... ... @@ -451,6 +457,7 @@
451 457 struct list_head free_hc_list;
452 458 int periodic_channels;
453 459 int non_periodic_channels;
  460 + int available_host_channels;
454 461 struct dwc2_host_chan *hc_ptr_array[MAX_EPS_CHANNELS];
455 462 u8 *status_buf;
456 463 dma_addr_t status_buf_dma;
drivers/staging/dwc2/hcd.c
... ... @@ -537,11 +537,16 @@
537 537 int i;
538 538  
539 539 hsotg->flags.d32 = 0;
540   -
541 540 hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active;
542   - hsotg->non_periodic_channels = 0;
543   - hsotg->periodic_channels = 0;
544 541  
  542 + if (hsotg->core_params->uframe_sched > 0) {
  543 + hsotg->available_host_channels =
  544 + hsotg->core_params->host_channels;
  545 + } else {
  546 + hsotg->non_periodic_channels = 0;
  547 + hsotg->periodic_channels = 0;
  548 + }
  549 +
545 550 /*
546 551 * Put all channels in the free channel list and clean up channel
547 552 * states
... ... @@ -716,8 +721,7 @@
716 721 * @qh: Transactions from the first QTD for this QH are selected and assigned
717 722 * to a free host channel
718 723 */
719   -static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg,
720   - struct dwc2_qh *qh)
  724 +static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
721 725 {
722 726 struct dwc2_host_chan *chan;
723 727 struct dwc2_hcd_urb *urb;
724 728  
725 729  
... ... @@ -729,18 +733,18 @@
729 733  
730 734 if (list_empty(&qh->qtd_list)) {
731 735 dev_dbg(hsotg->dev, "No QTDs in QH list\n");
732   - return;
  736 + return -ENOMEM;
733 737 }
734 738  
735 739 if (list_empty(&hsotg->free_hc_list)) {
736 740 dev_dbg(hsotg->dev, "No free channel to assign\n");
737   - return;
  741 + return -ENOMEM;
738 742 }
739 743  
740 744 chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan,
741 745 hc_list_entry);
742 746  
743   - /* Remove the host channel from the free list */
  747 + /* Remove host channel from free list */
744 748 list_del_init(&chan->hc_list_entry);
745 749  
746 750 qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
... ... @@ -821,7 +825,7 @@
821 825 &hsotg->free_hc_list);
822 826 qtd->in_process = 0;
823 827 qh->channel = NULL;
824   - return;
  828 + return -ENOMEM;
825 829 }
826 830 } else {
827 831 chan->align_buf = 0;
... ... @@ -840,6 +844,8 @@
840 844  
841 845 dwc2_hc_init(hsotg, chan);
842 846 chan->qh = qh;
  847 +
  848 + return 0;
843 849 }
844 850  
845 851 /**
846 852  
... ... @@ -868,8 +874,14 @@
868 874 while (qh_ptr != &hsotg->periodic_sched_ready) {
869 875 if (list_empty(&hsotg->free_hc_list))
870 876 break;
  877 + if (hsotg->core_params->uframe_sched > 0) {
  878 + if (hsotg->available_host_channels <= 1)
  879 + break;
  880 + hsotg->available_host_channels--;
  881 + }
871 882 qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
872   - dwc2_assign_and_init_hc(hsotg, qh);
  883 + if (dwc2_assign_and_init_hc(hsotg, qh))
  884 + break;
873 885  
874 886 /*
875 887 * Move the QH from the periodic ready schedule to the
876 888  
877 889  
... ... @@ -888,14 +900,22 @@
888 900 num_channels = hsotg->core_params->host_channels;
889 901 qh_ptr = hsotg->non_periodic_sched_inactive.next;
890 902 while (qh_ptr != &hsotg->non_periodic_sched_inactive) {
891   - if (hsotg->non_periodic_channels >= num_channels -
  903 + if (hsotg->core_params->uframe_sched <= 0 &&
  904 + hsotg->non_periodic_channels >= num_channels -
892 905 hsotg->periodic_channels)
893 906 break;
894 907 if (list_empty(&hsotg->free_hc_list))
895 908 break;
896 909 qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry);
897   - dwc2_assign_and_init_hc(hsotg, qh);
  910 + if (hsotg->core_params->uframe_sched > 0) {
  911 + if (hsotg->available_host_channels < 1)
  912 + break;
  913 + hsotg->available_host_channels--;
  914 + }
898 915  
  916 + if (dwc2_assign_and_init_hc(hsotg, qh))
  917 + break;
  918 +
899 919 /*
900 920 * Move the QH from the non-periodic inactive schedule to the
901 921 * non-periodic active schedule
... ... @@ -909,7 +929,8 @@
909 929 else
910 930 ret_val = DWC2_TRANSACTION_ALL;
911 931  
912   - hsotg->non_periodic_channels++;
  932 + if (hsotg->core_params->uframe_sched <= 0)
  933 + hsotg->non_periodic_channels++;
913 934 }
914 935  
915 936 return ret_val;
... ... @@ -2851,6 +2872,9 @@
2851 2872 channel->hc_num = i;
2852 2873 hsotg->hc_ptr_array[i] = channel;
2853 2874 }
  2875 +
  2876 + if (hsotg->core_params->uframe_sched > 0)
  2877 + dwc2_hcd_init_usecs(hsotg);
2854 2878  
2855 2879 /* Initialize hsotg start work */
2856 2880 INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func);
drivers/staging/dwc2/hcd.h
... ... @@ -238,6 +238,7 @@
238 238 * @interval: Interval between transfers in (micro)frames
239 239 * @sched_frame: (Micro)frame to initialize a periodic transfer.
240 240 * The transfer executes in the following (micro)frame.
  241 + * @frame_usecs: Internal variable used by the microframe scheduler
241 242 * @start_split_frame: (Micro)frame at which last start split was initialized
242 243 * @ntd: Actual number of transfer descriptors in a list
243 244 * @dw_align_buf: Used instead of original buffer if its physical address
... ... @@ -271,6 +272,7 @@
271 272 u16 usecs;
272 273 u16 interval;
273 274 u16 sched_frame;
  275 + u16 frame_usecs[8];
274 276 u16 start_split_frame;
275 277 u16 ntd;
276 278 u8 *dw_align_buf;
... ... @@ -463,6 +465,7 @@
463 465  
464 466 /* Schedule Queue Functions */
465 467 /* Implemented in hcd_queue.c */
  468 +extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
466 469 extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
467 470 extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
468 471 extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
drivers/staging/dwc2/hcd_ddma.c
... ... @@ -271,10 +271,14 @@
271 271 {
272 272 struct dwc2_host_chan *chan = qh->channel;
273 273  
274   - if (dwc2_qh_is_non_per(qh))
275   - hsotg->non_periodic_channels--;
276   - else
  274 + if (dwc2_qh_is_non_per(qh)) {
  275 + if (hsotg->core_params->uframe_sched > 0)
  276 + hsotg->available_host_channels++;
  277 + else
  278 + hsotg->non_periodic_channels--;
  279 + } else {
277 280 dwc2_update_frame_list(hsotg, qh, 0);
  281 + }
278 282  
279 283 /*
280 284 * The condition is added to prevent double cleanup try in case of
... ... @@ -370,7 +374,8 @@
370 374  
371 375 if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC ||
372 376 qh->ep_type == USB_ENDPOINT_XFER_INT) &&
373   - !hsotg->periodic_channels && hsotg->frame_list) {
  377 + (hsotg->core_params->uframe_sched > 0 ||
  378 + !hsotg->periodic_channels) && hsotg->frame_list) {
374 379 dwc2_per_sched_disable(hsotg);
375 380 dwc2_frame_list_free(hsotg);
376 381 }
drivers/staging/dwc2/hcd_intr.c
... ... @@ -748,18 +748,23 @@
748 748 dwc2_hc_cleanup(hsotg, chan);
749 749 list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
750 750  
751   - switch (chan->ep_type) {
752   - case USB_ENDPOINT_XFER_CONTROL:
753   - case USB_ENDPOINT_XFER_BULK:
754   - hsotg->non_periodic_channels--;
755   - break;
756   - default:
757   - /*
758   - * Don't release reservations for periodic channels here.
759   - * That's done when a periodic transfer is descheduled (i.e.
760   - * when the QH is removed from the periodic schedule).
761   - */
762   - break;
  751 + if (hsotg->core_params->uframe_sched > 0) {
  752 + hsotg->available_host_channels++;
  753 + } else {
  754 + switch (chan->ep_type) {
  755 + case USB_ENDPOINT_XFER_CONTROL:
  756 + case USB_ENDPOINT_XFER_BULK:
  757 + hsotg->non_periodic_channels--;
  758 + break;
  759 + default:
  760 + /*
  761 + * Don't release reservations for periodic channels
  762 + * here. That's done when a periodic transfer is
  763 + * descheduled (i.e. when the QH is removed from the
  764 + * periodic schedule).
  765 + */
  766 + break;
  767 + }
763 768 }
764 769  
765 770 haintmsk = readl(hsotg->regs + HAINTMSK);
drivers/staging/dwc2/hcd_queue.c
... ... @@ -324,6 +324,146 @@
324 324 }
325 325  
326 326 /**
  327 + * Microframe scheduler
  328 + * track the total use in hsotg->frame_usecs
  329 + * keep each qh use in qh->frame_usecs
  330 + * when surrendering the qh then donate the time back
  331 + */
  332 +static const unsigned short max_uframe_usecs[] = {
  333 + 100, 100, 100, 100, 100, 100, 30, 0
  334 +};
  335 +
  336 +void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
  337 +{
  338 + int i;
  339 +
  340 + for (i = 0; i < 8; i++)
  341 + hsotg->frame_usecs[i] = max_uframe_usecs[i];
  342 +}
  343 +
  344 +static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  345 +{
  346 + unsigned short utime = qh->usecs;
  347 + int done = 0;
  348 + int i = 0;
  349 + int ret = -1;
  350 +
  351 + while (!done) {
  352 + /* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
  353 + if (utime <= hsotg->frame_usecs[i]) {
  354 + hsotg->frame_usecs[i] -= utime;
  355 + qh->frame_usecs[i] += utime;
  356 + ret = i;
  357 + done = 1;
  358 + } else {
  359 + i++;
  360 + if (i == 8)
  361 + done = 1;
  362 + }
  363 + }
  364 +
  365 + return ret;
  366 +}
  367 +
  368 +/*
  369 + * use this for FS apps that can span multiple uframes
  370 + */
  371 +static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  372 +{
  373 + unsigned short utime = qh->usecs;
  374 + unsigned short xtime;
  375 + int t_left = utime;
  376 + int done = 0;
  377 + int i = 0;
  378 + int j;
  379 + int ret = -1;
  380 +
  381 + while (!done) {
  382 + if (hsotg->frame_usecs[i] <= 0) {
  383 + i++;
  384 + if (i == 8) {
  385 + ret = -1;
  386 + done = 1;
  387 + }
  388 + continue;
  389 + }
  390 +
  391 + /*
  392 + * we need n consecutive slots so use j as a start slot
  393 + * j plus j+1 must be enough time (for now)
  394 + */
  395 + xtime = hsotg->frame_usecs[i];
  396 + for (j = i + 1; j < 8; j++) {
  397 + /*
  398 + * if we add this frame remaining time to xtime we may
  399 + * be OK, if not we need to test j for a complete frame
  400 + */
  401 + if (xtime + hsotg->frame_usecs[j] < utime) {
  402 + if (hsotg->frame_usecs[j] <
  403 + max_uframe_usecs[j]) {
  404 + ret = -1;
  405 + break;
  406 + }
  407 + }
  408 + if (xtime >= utime) {
  409 + ret = i;
  410 + break;
  411 + }
  412 + /* add the frame time to x time */
  413 + xtime += hsotg->frame_usecs[j];
  414 + /* we must have a fully available next frame or break */
  415 + if (xtime < utime &&
  416 + hsotg->frame_usecs[j] == max_uframe_usecs[j]) {
  417 + ret = -1;
  418 + break;
  419 + }
  420 + }
  421 + if (ret >= 0) {
  422 + t_left = utime;
  423 + for (j = i; t_left > 0 && j < 8; j++) {
  424 + t_left -= hsotg->frame_usecs[j];
  425 + if (t_left <= 0) {
  426 + qh->frame_usecs[j] +=
  427 + hsotg->frame_usecs[j] + t_left;
  428 + hsotg->frame_usecs[j] = -t_left;
  429 + ret = i;
  430 + done = 1;
  431 + } else {
  432 + qh->frame_usecs[j] +=
  433 + hsotg->frame_usecs[j];
  434 + hsotg->frame_usecs[j] = 0;
  435 + }
  436 + }
  437 + } else {
  438 + i++;
  439 + if (i == 8) {
  440 + ret = -1;
  441 + done = 1;
  442 + }
  443 + }
  444 + }
  445 +
  446 + return ret;
  447 +}
  448 +
  449 +static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  450 +{
  451 + int ret;
  452 +
  453 + if (qh->dev_speed == USB_SPEED_HIGH) {
  454 + /* if this is a hs transaction we need a full frame */
  455 + ret = dwc2_find_single_uframe(hsotg, qh);
  456 + } else {
  457 + /*
  458 + * if this is a fs transaction we may need a sequence
  459 + * of frames
  460 + */
  461 + ret = dwc2_find_multi_uframe(hsotg, qh);
  462 + }
  463 + return ret;
  464 +}
  465 +
  466 +/**
327 467 * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
328 468 * host channel is large enough to handle the maximum data transfer in a single
329 469 * (micro)frame for a periodic transfer
330 470  
... ... @@ -367,15 +507,35 @@
367 507 {
368 508 int status;
369 509  
370   - status = dwc2_periodic_channel_available(hsotg);
371   - if (status) {
372   - dev_dbg(hsotg->dev,
373   - "%s: No host channel available for periodic transfer\n",
374   - __func__);
375   - return status;
  510 + if (hsotg->core_params->uframe_sched > 0) {
  511 + int frame = -1;
  512 +
  513 + status = dwc2_find_uframe(hsotg, qh);
  514 + if (status == 0)
  515 + frame = 7;
  516 + else if (status > 0)
  517 + frame = status - 1;
  518 +
  519 + /* Set the new frame up */
  520 + if (frame > -1) {
  521 + qh->sched_frame &= ~0x7;
  522 + qh->sched_frame |= (frame & 7);
  523 + }
  524 +
  525 + if (status != -1)
  526 + status = 0;
  527 + } else {
  528 + status = dwc2_periodic_channel_available(hsotg);
  529 + if (status) {
  530 + dev_info(hsotg->dev,
  531 + "%s: No host channel available for periodic transfer\n",
  532 + __func__);
  533 + return status;
  534 + }
  535 +
  536 + status = dwc2_check_periodic_bandwidth(hsotg, qh);
376 537 }
377 538  
378   - status = dwc2_check_periodic_bandwidth(hsotg, qh);
379 539 if (status) {
380 540 dev_dbg(hsotg->dev,
381 541 "%s: Insufficient periodic bandwidth for periodic transfer\n",
... ... @@ -399,8 +559,9 @@
399 559 list_add_tail(&qh->qh_list_entry,
400 560 &hsotg->periodic_sched_inactive);
401 561  
402   - /* Reserve periodic channel */
403   - hsotg->periodic_channels++;
  562 + if (hsotg->core_params->uframe_sched <= 0)
  563 + /* Reserve periodic channel */
  564 + hsotg->periodic_channels++;
404 565  
405 566 /* Update claimed usecs per (micro)frame */
406 567 hsotg->periodic_usecs += qh->usecs;
407 568  
408 569  
... ... @@ -418,13 +579,22 @@
418 579 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
419 580 struct dwc2_qh *qh)
420 581 {
  582 + int i;
  583 +
421 584 list_del_init(&qh->qh_list_entry);
422 585  
423   - /* Release periodic channel reservation */
424   - hsotg->periodic_channels--;
425   -
426 586 /* Update claimed usecs per (micro)frame */
427 587 hsotg->periodic_usecs -= qh->usecs;
  588 +
  589 + if (hsotg->core_params->uframe_sched > 0) {
  590 + for (i = 0; i < 8; i++) {
  591 + hsotg->frame_usecs[i] += qh->frame_usecs[i];
  592 + qh->frame_usecs[i] = 0;
  593 + }
  594 + } else {
  595 + /* Release periodic channel reservation */
  596 + hsotg->periodic_channels--;
  597 + }
428 598 }
429 599  
430 600 /**
... ... @@ -581,7 +751,10 @@
581 751 * Remove from periodic_sched_queued and move to
582 752 * appropriate queue
583 753 */
584   - if (qh->sched_frame == frame_number)
  754 + if ((hsotg->core_params->uframe_sched > 0 &&
  755 + dwc2_frame_num_le(qh->sched_frame, frame_number))
  756 + || (hsotg->core_params->uframe_sched <= 0 &&
  757 + qh->sched_frame == frame_number))
585 758 list_move(&qh->qh_list_entry,
586 759 &hsotg->periodic_sched_ready);
587 760 else
drivers/staging/dwc2/pci.c
... ... @@ -84,6 +84,7 @@
84 84 .ts_dline = -1,
85 85 .reload_ctl = -1,
86 86 .ahbcfg = -1,
  87 + .uframe_sched = -1,
87 88 };
88 89  
89 90 /**