]> Pileus Git - ~andy/linux/blobdiff - drivers/staging/dwc2/hcd_queue.c
Merge tag 'spi-v3.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[~andy/linux] / drivers / staging / dwc2 / hcd_queue.c
index c09b2db2c89a042766d4b947d9e1590ff01be33e..f200f1f6e1c67ddf3d151552e3aeed0088b5b8f8 100644 (file)
@@ -323,6 +323,146 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
        return status;
 }
 
+/**
+ * Microframe scheduler
+ * track the total use in hsotg->frame_usecs
+ * keep each qh use in qh->frame_usecs
+ * when surrendering the qh then donate the time back
+ */
+static const unsigned short max_uframe_usecs[] = {
+       100, 100, 100, 100, 100, 100, 30, 0
+};
+
+void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
+{
+       int i;
+
+       for (i = 0; i < 8; i++)
+               hsotg->frame_usecs[i] = max_uframe_usecs[i];
+}
+
+static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       unsigned short utime = qh->usecs;
+       int done = 0;
+       int i = 0;
+       int ret = -1;
+
+       while (!done) {
+               /* At the start hsotg->frame_usecs[i] = max_uframe_usecs[i] */
+               if (utime <= hsotg->frame_usecs[i]) {
+                       hsotg->frame_usecs[i] -= utime;
+                       qh->frame_usecs[i] += utime;
+                       ret = i;
+                       done = 1;
+               } else {
+                       i++;
+                       if (i == 8)
+                               done = 1;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * use this for FS apps that can span multiple uframes
+ */
+static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       unsigned short utime = qh->usecs;
+       unsigned short xtime;
+       int t_left = utime;
+       int done = 0;
+       int i = 0;
+       int j;
+       int ret = -1;
+
+       while (!done) {
+               if (hsotg->frame_usecs[i] <= 0) {
+                       i++;
+                       if (i == 8) {
+                               ret = -1;
+                               done = 1;
+                       }
+                       continue;
+               }
+
+               /*
+                * we need n consecutive slots so use j as a start slot
+                * j plus j+1 must be enough time (for now)
+                */
+               xtime = hsotg->frame_usecs[i];
+               for (j = i + 1; j < 8; j++) {
+                       /*
+                        * if we add this frame remaining time to xtime we may
+                        * be OK, if not we need to test j for a complete frame
+                        */
+                       if (xtime + hsotg->frame_usecs[j] < utime) {
+                               if (hsotg->frame_usecs[j] <
+                                                       max_uframe_usecs[j]) {
+                                       ret = -1;
+                                       break;
+                               }
+                       }
+                       if (xtime >= utime) {
+                               ret = i;
+                               break;
+                       }
+                       /* add the frame time to x time */
+                       xtime += hsotg->frame_usecs[j];
+                       /* we must have a fully available next frame or break */
+                       if (xtime < utime &&
+                          hsotg->frame_usecs[j] == max_uframe_usecs[j]) {
+                               ret = -1;
+                               break;
+                       }
+               }
+               if (ret >= 0) {
+                       t_left = utime;
+                       for (j = i; t_left > 0 && j < 8; j++) {
+                               t_left -= hsotg->frame_usecs[j];
+                               if (t_left <= 0) {
+                                       qh->frame_usecs[j] +=
+                                               hsotg->frame_usecs[j] + t_left;
+                                       hsotg->frame_usecs[j] = -t_left;
+                                       ret = i;
+                                       done = 1;
+                               } else {
+                                       qh->frame_usecs[j] +=
+                                               hsotg->frame_usecs[j];
+                                       hsotg->frame_usecs[j] = 0;
+                               }
+                       }
+               } else {
+                       i++;
+                       if (i == 8) {
+                               ret = -1;
+                               done = 1;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       int ret;
+
+       if (qh->dev_speed == USB_SPEED_HIGH) {
+               /* if this is a hs transaction we need a full frame */
+               ret = dwc2_find_single_uframe(hsotg, qh);
+       } else {
+               /*
+                * if this is a fs transaction we may need a sequence
+                * of frames
+                */
+               ret = dwc2_find_multi_uframe(hsotg, qh);
+       }
+       return ret;
+}
+
 /**
  * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
  * host channel is large enough to handle the maximum data transfer in a single
@@ -367,15 +507,35 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
        int status;
 
-       status = dwc2_periodic_channel_available(hsotg);
-       if (status) {
-               dev_dbg(hsotg->dev,
-                       "%s: No host channel available for periodic transfer\n",
-                       __func__);
-               return status;
+       if (hsotg->core_params->uframe_sched > 0) {
+               int frame = -1;
+
+               status = dwc2_find_uframe(hsotg, qh);
+               if (status == 0)
+                       frame = 7;
+               else if (status > 0)
+                       frame = status - 1;
+
+               /* Set the new frame up */
+               if (frame > -1) {
+                       qh->sched_frame &= ~0x7;
+                       qh->sched_frame |= (frame & 7);
+               }
+
+               if (status != -1)
+                       status = 0;
+       } else {
+               status = dwc2_periodic_channel_available(hsotg);
+               if (status) {
+                       dev_info(hsotg->dev,
+                                "%s: No host channel available for periodic transfer\n",
+                                __func__);
+                       return status;
+               }
+
+               status = dwc2_check_periodic_bandwidth(hsotg, qh);
        }
 
-       status = dwc2_check_periodic_bandwidth(hsotg, qh);
        if (status) {
                dev_dbg(hsotg->dev,
                        "%s: Insufficient periodic bandwidth for periodic transfer\n",
@@ -399,8 +559,9 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                list_add_tail(&qh->qh_list_entry,
                              &hsotg->periodic_sched_inactive);
 
-       /* Reserve periodic channel */
-       hsotg->periodic_channels++;
+       if (hsotg->core_params->uframe_sched <= 0)
+               /* Reserve periodic channel */
+               hsotg->periodic_channels++;
 
        /* Update claimed usecs per (micro)frame */
        hsotg->periodic_usecs += qh->usecs;
@@ -418,13 +579,22 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
                                     struct dwc2_qh *qh)
 {
-       list_del_init(&qh->qh_list_entry);
+       int i;
 
-       /* Release periodic channel reservation */
-       hsotg->periodic_channels--;
+       list_del_init(&qh->qh_list_entry);
 
        /* Update claimed usecs per (micro)frame */
        hsotg->periodic_usecs -= qh->usecs;
+
+       if (hsotg->core_params->uframe_sched > 0) {
+               for (i = 0; i < 8; i++) {
+                       hsotg->frame_usecs[i] += qh->frame_usecs[i];
+                       qh->frame_usecs[i] = 0;
+               }
+       } else {
+               /* Release periodic channel reservation */
+               hsotg->periodic_channels--;
+       }
 }
 
 /**
@@ -581,7 +751,10 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                         * Remove from periodic_sched_queued and move to
                         * appropriate queue
                         */
-                       if (qh->sched_frame == frame_number)
+                       if ((hsotg->core_params->uframe_sched > 0 &&
+                            dwc2_frame_num_le(qh->sched_frame, frame_number))
+                        || (hsotg->core_params->uframe_sched <= 0 &&
+                            qh->sched_frame == frame_number))
                                list_move(&qh->qh_list_entry,
                                          &hsotg->periodic_sched_ready);
                        else