diff options
Diffstat (limited to 'drivers/staging/dwc2')
-rw-r--r-- | drivers/staging/dwc2/TODO | 33 | ||||
-rw-r--r-- | drivers/staging/dwc2/core.c | 27 | ||||
-rw-r--r-- | drivers/staging/dwc2/core.h | 9 | ||||
-rw-r--r-- | drivers/staging/dwc2/hcd.c | 54 | ||||
-rw-r--r-- | drivers/staging/dwc2/hcd.h | 3 | ||||
-rw-r--r-- | drivers/staging/dwc2/hcd_ddma.c | 13 | ||||
-rw-r--r-- | drivers/staging/dwc2/hcd_intr.c | 46 | ||||
-rw-r--r-- | drivers/staging/dwc2/hcd_queue.c | 203 | ||||
-rw-r--r-- | drivers/staging/dwc2/pci.c | 1 | ||||
-rw-r--r-- | drivers/staging/dwc2/platform.c | 7 |
10 files changed, 336 insertions, 60 deletions
diff --git a/drivers/staging/dwc2/TODO b/drivers/staging/dwc2/TODO new file mode 100644 index 000000000000..282470d55315 --- /dev/null +++ b/drivers/staging/dwc2/TODO @@ -0,0 +1,33 @@ +TODO: + - Dan Carpenter would like to see some cleanups to the microframe + scheduler code: + http://www.mail-archive.com/linux-usb@vger.kernel.org/msg26650.html + + - Should merge the NAK holdoff patch from Raspberry Pi + (http://marc.info/?l=linux-usb&m=137625067103833). But as it stands + that patch is incomplete, it needs more investigation to see if it + can be made to work for non-Raspberry Pi platforms that lack the + special FIQ interrupt that the Pi has. Without this patch, the driver + has a high interrupt rate (8K/sec). + + - The Raspberry Pi platform needs to have support for its FIQ interrupt + added, to get the same level of functionality as the downstream + driver. The raspberrypi.org developers have indicated they are + willing to help with that. + + - Some of the default driver parameters (see 'struct dwc2_core_params' + in core.h) won't work for many platforms. So DT attributes will need + to be added for some of these. But that can be done as-needed as new + platforms are added. + + - Eventually the driver should be merged with the s3c-hsotg peripheral + mode driver, so that both modes of operation can be supported with a + single driver. But I think that can wait till after the driver has + been moved to mainline. + + - After that, OTG support can be added. I'm not sure how much demand + there is for that, though, so I have that as a low priority. + +Please send any patches for this driver to Paul Zimmerman <paulz@synopsys.com> +and Greg Kroah-Hartman <gregkh@linuxfoundation.org>. And please CC linux-usb +<linux-usb@vger.kernel.org> too. diff --git a/drivers/staging/dwc2/core.c b/drivers/staging/dwc2/core.c index 06dae67a9d62..6d001b52f652 100644 --- a/drivers/staging/dwc2/core.c +++ b/drivers/staging/dwc2/core.c @@ -564,7 +564,7 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* * This bit allows dynamic reloading of the HFIR register during - * runtime. This bit needs to be programmed during inital configuration + * runtime. This bit needs to be programmed during initial configuration * and its value must not be changed during runtime. */ if (hsotg->core_params->reload_ctl > 0) { @@ -2205,7 +2205,7 @@ int dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg, int val) { #ifndef NO_FS_PHY_HW_CHECKS int valid = 0; - u32 hs_phy_type, fs_phy_type; + u32 hs_phy_type, fs_phy_type; #endif int retval = 0; @@ -2553,7 +2553,7 @@ int dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val) hsotg->core_params->ahbcfg = val; else hsotg->core_params->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << - GAHBCFG_HBSTLEN_SHIFT; + GAHBCFG_HBSTLEN_SHIFT; return 0; } @@ -2736,6 +2736,26 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +int dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) +{ + int retval = 0; + + if (DWC2_PARAM_TEST(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter uframe_sched\n", + val); + dev_err(hsotg->dev, "uframe_sched must be 0 or 1\n"); + } + val = 1; + dev_dbg(hsotg->dev, "Setting uframe_sched to %d\n", val); + retval = -EINVAL; + } + + hsotg->core_params->uframe_sched = val; + return retval; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. It returns non-0 if any parameters are invalid. @@ -2782,6 +2802,7 @@ int dwc2_set_parameters(struct dwc2_hsotg *hsotg, retval |= dwc2_set_param_reload_ctl(hsotg, params->reload_ctl); retval |= dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); retval |= dwc2_set_param_otg_ver(hsotg, params->otg_ver); + retval |= dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); return retval; } diff --git a/drivers/staging/dwc2/core.h b/drivers/staging/dwc2/core.h index 9102f66d0111..fab718d9b326 100644 --- a/drivers/staging/dwc2/core.h +++ b/drivers/staging/dwc2/core.h @@ -188,6 +188,7 @@ enum dwc2_lx_state { * bits defined by GAHBCFG_CTRL_MASK are controlled * by the driver and are ignored in this * configuration value. + * @uframe_sched: True to enable the microframe scheduler * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -224,6 +225,7 @@ struct dwc2_core_params { int ts_dline; int reload_ctl; int ahbcfg; + int uframe_sched; }; /** @@ -292,7 +294,7 @@ struct dwc2_hw_params { unsigned dev_token_q_depth:5; unsigned max_transfer_size:26; unsigned max_packet_count:11; - unsigned host_channels:4; + unsigned host_channels:5; unsigned hs_phy_type:2; unsigned fs_phy_type:2; unsigned i2c_enable:1; @@ -370,6 +372,7 @@ struct dwc2_hw_params { * This value is in microseconds per (micro)frame. The * assumption is that all periodic transfers may occur in * the same (micro)frame. + * @frame_usecs: Internal variable used by the microframe scheduler * @frame_number: Frame number read from the core at SOF. The value ranges * from 0 to HFNUM_MAX_FRNUM. * @periodic_qh_count: Count of periodic QHs, if using several eps. Used for @@ -382,6 +385,8 @@ struct dwc2_hw_params { * host channel is available for non-periodic transactions. * @non_periodic_channels: Number of host channels assigned to non-periodic * transfers + * @available_host_channels Number of host channels available for the microframe + * scheduler to use * @hc_ptr_array: Array of pointers to the host channel descriptors. * Allows accessing a host channel descriptor given the * host channel number. This is useful in interrupt @@ -436,6 +441,7 @@ struct dwc2_hsotg { struct list_head periodic_sched_assigned; struct list_head periodic_sched_queued; u16 periodic_usecs; + u16 frame_usecs[8]; u16 frame_number; u16 periodic_qh_count; @@ -451,6 +457,7 @@ struct dwc2_hsotg { struct list_head free_hc_list; int periodic_channels; int non_periodic_channels; + int available_host_channels; struct dwc2_host_chan *hc_ptr_array[MAX_EPS_CHANNELS]; u8 *status_buf; dma_addr_t status_buf_dma; diff --git a/drivers/staging/dwc2/hcd.c b/drivers/staging/dwc2/hcd.c index da0d35cc33ce..3cfd2d5152c9 100644 --- a/drivers/staging/dwc2/hcd.c +++ b/drivers/staging/dwc2/hcd.c @@ -537,10 +537,15 @@ static void dwc2_hcd_reinit(struct dwc2_hsotg *hsotg) int i; hsotg->flags.d32 = 0; - hsotg->non_periodic_qh_ptr = &hsotg->non_periodic_sched_active; - hsotg->non_periodic_channels = 0; - hsotg->periodic_channels = 0; + + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels = + hsotg->core_params->host_channels; + } else { + hsotg->non_periodic_channels = 0; + hsotg->periodic_channels = 0; + } /* * Put all channels in the free channel list and clean up channel @@ -716,8 +721,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, * @qh: Transactions from the first QTD for this QH are selected and assigned * to a free host channel */ -static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, - struct dwc2_qh *qh) +static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { struct dwc2_host_chan *chan; struct dwc2_hcd_urb *urb; @@ -729,18 +733,18 @@ static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, if (list_empty(&qh->qtd_list)) { dev_dbg(hsotg->dev, "No QTDs in QH list\n"); - return; + return -ENOMEM; } if (list_empty(&hsotg->free_hc_list)) { dev_dbg(hsotg->dev, "No free channel to assign\n"); - return; + return -ENOMEM; } chan = list_first_entry(&hsotg->free_hc_list, struct dwc2_host_chan, hc_list_entry); - /* Remove the host channel from the free list */ + /* Remove host channel from free list */ list_del_init(&chan->hc_list_entry); qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry); @@ -780,6 +784,10 @@ static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, chan->data_pid_start = qh->data_toggle; chan->multi_count = 1; + if (urb->actual_length > urb->length && + !dwc2_hcd_is_pipe_in(&urb->pipe_info)) + urb->actual_length = urb->length; + if (hsotg->core_params->dma_enable > 0) { chan->xfer_dma = urb->dma + urb->actual_length; @@ -817,7 +825,7 @@ static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, &hsotg->free_hc_list); qtd->in_process = 0; qh->channel = NULL; - return; + return -ENOMEM; } } else { chan->align_buf = 0; @@ -836,6 +844,8 @@ static void dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, dwc2_hc_init(hsotg, chan); chan->qh = qh; + + return 0; } /** @@ -864,8 +874,14 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( while (qh_ptr != &hsotg->periodic_sched_ready) { if (list_empty(&hsotg->free_hc_list)) break; + if (hsotg->core_params->uframe_sched > 0) { + if (hsotg->available_host_channels <= 1) + break; + hsotg->available_host_channels--; + } qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); - dwc2_assign_and_init_hc(hsotg, qh); + if (dwc2_assign_and_init_hc(hsotg, qh)) + break; /* * Move the QH from the periodic ready schedule to the @@ -884,13 +900,21 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( num_channels = hsotg->core_params->host_channels; qh_ptr = hsotg->non_periodic_sched_inactive.next; while (qh_ptr != &hsotg->non_periodic_sched_inactive) { - if (hsotg->non_periodic_channels >= num_channels - + if (hsotg->core_params->uframe_sched <= 0 && + hsotg->non_periodic_channels >= num_channels - hsotg->periodic_channels) break; if (list_empty(&hsotg->free_hc_list)) break; qh = list_entry(qh_ptr, struct dwc2_qh, qh_list_entry); - dwc2_assign_and_init_hc(hsotg, qh); + if (hsotg->core_params->uframe_sched > 0) { + if (hsotg->available_host_channels < 1) + break; + hsotg->available_host_channels--; + } + + if (dwc2_assign_and_init_hc(hsotg, qh)) + break; /* * Move the QH from the non-periodic inactive schedule to the @@ -905,7 +929,8 @@ enum dwc2_transaction_type dwc2_hcd_select_transactions( else ret_val = DWC2_TRANSACTION_ALL; - hsotg->non_periodic_channels++; + if (hsotg->core_params->uframe_sched <= 0) + hsotg->non_periodic_channels++; } return ret_val; @@ -2848,6 +2873,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->hc_ptr_array[i] = channel; } + if (hsotg->core_params->uframe_sched > 0) + dwc2_hcd_init_usecs(hsotg); + /* Initialize hsotg start work */ INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func); diff --git a/drivers/staging/dwc2/hcd.h b/drivers/staging/dwc2/hcd.h index cc0a11708319..89a5484f5b74 100644 --- a/drivers/staging/dwc2/hcd.h +++ b/drivers/staging/dwc2/hcd.h @@ -238,6 +238,7 @@ enum dwc2_transaction_type { * @interval: Interval between transfers in (micro)frames * @sched_frame: (Micro)frame to initialize a periodic transfer. * The transfer executes in the following (micro)frame. + * @frame_usecs: Internal variable used by the microframe scheduler * @start_split_frame: (Micro)frame at which last start split was initialized * @ntd: Actual number of transfer descriptors in a list * @dw_align_buf: Used instead of original buffer if its physical address @@ -271,6 +272,7 @@ struct dwc2_qh { u16 usecs; u16 interval; u16 sched_frame; + u16 frame_usecs[8]; u16 start_split_frame; u16 ntd; u8 *dw_align_buf; @@ -463,6 +465,7 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, /* Schedule Queue Functions */ /* Implemented in hcd_queue.c */ +extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); diff --git a/drivers/staging/dwc2/hcd_ddma.c b/drivers/staging/dwc2/hcd_ddma.c index 69070f4442a8..c7d434519776 100644 --- a/drivers/staging/dwc2/hcd_ddma.c +++ b/drivers/staging/dwc2/hcd_ddma.c @@ -271,10 +271,14 @@ static void dwc2_release_channel_ddma(struct dwc2_hsotg *hsotg, { struct dwc2_host_chan *chan = qh->channel; - if (dwc2_qh_is_non_per(qh)) - hsotg->non_periodic_channels--; - else + if (dwc2_qh_is_non_per(qh)) { + if (hsotg->core_params->uframe_sched > 0) + hsotg->available_host_channels++; + else + hsotg->non_periodic_channels--; + } else { dwc2_update_frame_list(hsotg, qh, 0); + } /* * The condition is added to prevent double cleanup try in case of @@ -370,7 +374,8 @@ void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) if ((qh->ep_type == USB_ENDPOINT_XFER_ISOC || qh->ep_type == USB_ENDPOINT_XFER_INT) && - !hsotg->periodic_channels && hsotg->frame_list) { + (hsotg->core_params->uframe_sched > 0 || + !hsotg->periodic_channels) && hsotg->frame_list) { dwc2_per_sched_disable(hsotg); dwc2_frame_list_free(hsotg); } diff --git a/drivers/staging/dwc2/hcd_intr.c b/drivers/staging/dwc2/hcd_intr.c index e143f69939f5..dda18540f5a7 100644 --- a/drivers/staging/dwc2/hcd_intr.c +++ b/drivers/staging/dwc2/hcd_intr.c @@ -748,18 +748,23 @@ cleanup: dwc2_hc_cleanup(hsotg, chan); list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list); - switch (chan->ep_type) { - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - hsotg->non_periodic_channels--; - break; - default: - /* - * Don't release reservations for periodic channels here. - * That's done when a periodic transfer is descheduled (i.e. - * when the QH is removed from the periodic schedule). - */ - break; + if (hsotg->core_params->uframe_sched > 0) { + hsotg->available_host_channels++; + } else { + switch (chan->ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + hsotg->non_periodic_channels--; + break; + default: + /* + * Don't release reservations for periodic channels + * here. That's done when a periodic transfer is + * descheduled (i.e. when the QH is removed from the + * periodic schedule). + */ + break; + } } haintmsk = readl(hsotg->regs + HAINTMSK); @@ -1927,23 +1932,22 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) chan = hsotg->hc_ptr_array[chnum]; - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, "--Host Channel Interrupt--, Channel %d\n", - chnum); - hcint = readl(hsotg->regs + HCINT(chnum)); hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); - if (dbg_hc(chan)) - dev_vdbg(hsotg->dev, - " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", - hcint, hcintmsk, hcint & hcintmsk); - if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); writel(hcint, hsotg->regs + HCINT(chnum)); return; } + if (dbg_hc(chan)) { + dev_vdbg(hsotg->dev, "--Host Channel Interrupt--, Channel %d\n", + chnum); + dev_vdbg(hsotg->dev, + " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", + hcint, hcintmsk, hcint & hcintmsk); + } + writel(hcint, hsotg->regs + HCINT(chnum)); chan->hcint = hcint; hcint &= hcintmsk; diff --git a/drivers/staging/dwc2/hcd_queue.c b/drivers/staging/dwc2/hcd_queue.c index b1980ef28fa3..f200f1f6e1c6 100644 --- a/drivers/staging/dwc2/hcd_queue.c +++ b/drivers/staging/dwc2/hcd_queue.c @@ -251,12 +251,12 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) * * @hsotg: The HCD state structure for the DWC OTG controller * - * Return: 0 if successful, negative error code otherise + * Return: 0 if successful, negative error code otherwise */ static int dwc2_periodic_channel_available(struct dwc2_hsotg *hsotg) { /* - * Currently assuming that there is a dedicated host channnel for + * Currently assuming that there is a dedicated host channel for * each periodic transaction plus at least one host channel for * non-periodic transactions */ @@ -324,6 +324,146 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg, } /** + * 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 * (micro)frame for a periodic transfer @@ -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 diff --git a/drivers/staging/dwc2/pci.c b/drivers/staging/dwc2/pci.c index 9020260d5df8..3d14c8870fca 100644 --- a/drivers/staging/dwc2/pci.c +++ b/drivers/staging/dwc2/pci.c @@ -84,6 +84,7 @@ static const struct dwc2_core_params dwc2_module_params = { .ts_dline = -1, .reload_ctl = -1, .ahbcfg = -1, + .uframe_sched = -1, }; /** diff --git a/drivers/staging/dwc2/platform.c b/drivers/staging/dwc2/platform.c index 44cce2fa6361..83ca1053bb1d 100644 --- a/drivers/staging/dwc2/platform.c +++ b/drivers/staging/dwc2/platform.c @@ -100,13 +100,14 @@ static int dwc2_driver_probe(struct platform_device *dev) */ if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; - if (!dev->dev.coherent_dma_mask) - dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32)); + if (retval) + return retval; irq = platform_get_irq(dev, 0); if (irq < 0) { dev_err(&dev->dev, "missing IRQ resource\n"); - return -EINVAL; + return irq; } res = platform_get_resource(dev, IORESOURCE_MEM, 0); |