From 5cc98c1e75700e51e4a31d8a05b4a55a7d29648c Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 19 Aug 2012 11:58:01 +0100 Subject: [PATCH] Merged in microframe scheduler, currently disabled. Enable with dwc_otg.microframe_schedule=1 --- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 4 + drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 69 +++++- drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 25 +- drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 21 +- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 36 ++- drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 232 +++++++++++++++++-- 6 files changed, 346 insertions(+), 41 deletions(-) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c index 8a6e207f2256c2..d7be7efa825fd9 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c @@ -60,6 +60,8 @@ #define DWC_DRIVER_VERSION "2.94b 27-OCT-2011 (rev 01-DEC-2011)" #define DWC_DRIVER_DESC "HS OTG USB Controller driver" +bool microframe_schedule; + static const char dwc_driver_name[] = "dwc_otg"; extern int pcd_init( @@ -1337,6 +1339,8 @@ module_param_named(adp_enable, dwc_otg_module_params.adp_enable, int, 0444); MODULE_PARM_DESC(adp_enable, "ADP Enable 0=ADP Disabled 1=ADP Enabled"); module_param_named(otg_ver, dwc_otg_module_params.otg_ver, int, 0444); MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); +module_param(microframe_schedule, bool, 0444); +MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); /** @page "Module Parameters" * diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c index abe7093943d09c..174ba7ee5a62fd 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c @@ -43,6 +43,16 @@ #include "dwc_otg_hcd.h" #include "dwc_otg_regs.h" +extern bool microframe_schedule; + +//#define DEBUG_HOST_CHANNELS +#ifdef DEBUG_HOST_CHANNELS +static int last_sel_trans_num_per_scheduled = 0; +static int last_sel_trans_num_nonper_scheduled = 0; +static int last_sel_trans_num_avail_hc_at_start = 0; +static int last_sel_trans_num_avail_hc_at_end = 0; +#endif /* DEBUG_HOST_CHANNELS */ + dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) { return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); @@ -825,6 +835,8 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) DWC_FREE(dwc_otg_hcd); } +int init_hcd_usecs(dwc_otg_hcd_t *_hcd); + int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) { int retval = 0; @@ -888,6 +900,10 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", dwc_otg_hcd_connect_timeout, 0); + printk(KERN_DEBUG "dwc_otg: Microframe scheduler %s\n", microframe_schedule ? "enabled":"disabled"); + if (microframe_schedule) + init_hcd_usecs(hcd); + /* Initialize reset tasklet. */ hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); #ifdef DWC_DEV_SRPCAP @@ -947,9 +963,12 @@ static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd) hcd->flags.d32 = 0; hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active; - hcd->non_periodic_channels = 0; - hcd->periodic_channels = 0; - + if (!microframe_schedule) { + hcd->non_periodic_channels = 0; + hcd->periodic_channels = 0; + } else { + hcd->available_host_channels = hcd->core_if->core_params->host_channels; + } /* * Put all channels in the free channel list and clean up channel * states. @@ -1225,17 +1244,38 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) dwc_list_link_t *qh_ptr; dwc_otg_qh_t *qh; int num_channels; + dwc_irqflags_t flags; + dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; #ifdef DEBUG_SOF DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); #endif +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_per_scheduled = 0; + last_sel_trans_num_nonper_scheduled = 0; + last_sel_trans_num_avail_hc_at_start = hcd->available_host_channels; +#endif /* DEBUG_HOST_CHANNELS */ + /* Process entries in the periodic ready list. */ qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready); while (qh_ptr != &hcd->periodic_sched_ready && !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { + if (microframe_schedule) { + // Make sure we leave one channel for non periodic transactions. + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + if (hcd->available_host_channels <= 1) { + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + break; + } + hcd->available_host_channels--; + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_per_scheduled++; +#endif /* DEBUG_HOST_CHANNELS */ + } qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); assign_and_init_hc(hcd, qh); @@ -1244,8 +1284,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) * periodic assigned schedule. */ qh_ptr = DWC_LIST_NEXT(qh_ptr); + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); ret_val = DWC_OTG_TRANSACTION_PERIODIC; } @@ -1258,10 +1300,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) qh_ptr = hcd->non_periodic_sched_inactive.next; num_channels = hcd->core_if->core_params->host_channels; while (qh_ptr != &hcd->non_periodic_sched_inactive && - (hcd->non_periodic_channels < + (microframe_schedule || hcd->non_periodic_channels < num_channels - hcd->periodic_channels) && !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { + if (microframe_schedule) { + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + if (hcd->available_host_channels < 1) { + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + break; + } + hcd->available_host_channels--; + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_nonper_scheduled++; +#endif /* DEBUG_HOST_CHANNELS */ + } qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); assign_and_init_hc(hcd, qh); @@ -1271,8 +1325,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) * non-periodic active schedule. */ qh_ptr = DWC_LIST_NEXT(qh_ptr); + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active, &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); if (ret_val == DWC_OTG_TRANSACTION_NONE) { ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; @@ -1283,6 +1339,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) hcd->non_periodic_channels++; } +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels; +#endif /* DEBUG_HOST_CHANNELS */ + + DWC_SPINLOCK_FREE(channel_lock); return ret_val; } diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h index c87cb5bfec15b6..d2220cba9293a2 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h @@ -362,6 +362,9 @@ typedef struct dwc_otg_qh { /** @} */ + + uint16_t speed; + uint16_t frame_usecs[8]; } dwc_otg_qh_t; DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); @@ -476,6 +479,19 @@ struct dwc_otg_hcd { */ uint16_t periodic_usecs; + /** + * Total bandwidth claimed so far for all periodic transfers + * in a frame. + * This will include a mixture of HS and FS transfers. + * Units are microseconds per (micro)frame. + * We have a budget per frame and have to schedule + * transactions accordingly. + * Watch out for the fact that things are actually scheduled for the + * "next frame". + */ + uint16_t frame_usecs[8]; + + /** * Frame number read from the core at SOF. The value ranges from 0 to * DWC_HFNUM_MAX_FRNUM. @@ -498,12 +514,17 @@ struct dwc_otg_hcd { * transaction and at least one host channel available for * non-periodic transactions. */ - int periodic_channels; + int periodic_channels; /* microframe_schedule==0 */ + + /** + * Number of host channels assigned to non-periodic transfers. + */ + int non_periodic_channels; /* microframe_schedule==0 */ /** * Number of host channels assigned to non-periodic transfers. */ - int non_periodic_channels; + int available_host_channels; /** * Array of pointers to the host channel descriptors. Allows accessing diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c index e5dfa4c600c10d..d0d5fa155ea603 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c @@ -39,6 +39,8 @@ #include "dwc_otg_hcd.h" #include "dwc_otg_regs.h" +extern bool microframe_schedule; + static inline uint8_t frame_list_idx(uint16_t frame) { return (frame & (MAX_FRLIST_EN_NUM - 1)); @@ -273,10 +275,18 @@ void dump_frame_list(dwc_otg_hcd_t * hcd) static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) { + dwc_irqflags_t flags; + dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); + dwc_hc_t *hc = qh->channel; - if (dwc_qh_is_non_per(qh)) - hcd->non_periodic_channels--; - else + if (dwc_qh_is_non_per(qh)) { + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + if (!microframe_schedule) + hcd->non_periodic_channels--; + else + hcd->available_host_channels++; + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + } else update_frame_list(hcd, qh, 0); /* @@ -296,6 +306,7 @@ static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) dwc_memset(qh->desc_list, 0x00, sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); } + DWC_SPINLOCK_FREE(channel_lock); } /** @@ -358,7 +369,7 @@ void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) release_channel_ddma(hcd, qh); if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT) - && !hcd->periodic_channels && hcd->frame_list) { + && (microframe_schedule || !hcd->periodic_channels) && hcd->frame_list) { per_sched_disable(hcd); frame_list_free(hcd); @@ -665,7 +676,7 @@ static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) qtd->in_process = 1; - if (qh->ep_type == UE_CONTROL) + if (qh->ep_type == UE_CONTROL) break; if (n_desc == MAX_DMA_DESC_NUM_GENERIC) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c index 130946c9fe5ed2..63c1b5511d2c08 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c @@ -35,6 +35,8 @@ #include "dwc_otg_hcd.h" #include "dwc_otg_regs.h" +extern bool microframe_schedule; + /** @file * This file contains the implementation of the HCD Interrupt handlers. */ @@ -794,6 +796,8 @@ static void release_channel(dwc_otg_hcd_t * hcd, { dwc_otg_transaction_type_e tr_type; int free_qtd; + dwc_irqflags_t flags; + dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", __func__, hc->hc_num, halt_status, hc->xfer_len); @@ -853,19 +857,26 @@ static void release_channel(dwc_otg_hcd_t * hcd, dwc_otg_hc_cleanup(hcd->core_if, hc); DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); - switch (hc->ep_type) { - case DWC_OTG_EP_TYPE_CONTROL: - case DWC_OTG_EP_TYPE_BULK: - hcd->non_periodic_channels--; - break; + if (!microframe_schedule) { + switch (hc->ep_type) { + case DWC_OTG_EP_TYPE_CONTROL: + case DWC_OTG_EP_TYPE_BULK: + hcd->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; + 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; + } + } else { + + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + hcd->available_host_channels++; + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); } /* Try to queue more transfers now that there's a free channel. */ @@ -873,6 +884,7 @@ static void release_channel(dwc_otg_hcd_t * hcd, if (tr_type != DWC_OTG_TRANSACTION_NONE) { dwc_otg_hcd_queue_transactions(hcd, tr_type); } + DWC_SPINLOCK_FREE(channel_lock); } /** diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c index 950de1e1094728..2d3d5c356822e8 100755 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c @@ -42,6 +42,8 @@ #include "dwc_otg_hcd.h" #include "dwc_otg_regs.h" +extern bool microframe_schedule; + /** * Free each QTD in the QH's QTD-list then free the QH. QH should already be * removed from a list. QTD list should already be empty if called from URB @@ -177,6 +179,9 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port); qh->do_split = 0; + if (microframe_schedule) + qh->speed = dev_speed; + if (((dev_speed == USB_SPEED_LOW) || (dev_speed == USB_SPEED_FULL)) && @@ -312,6 +317,8 @@ dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd, return qh; } +/* microframe_schedule=0 start */ + /** * Checks that a channel is available for a periodic transfer. * @@ -380,6 +387,162 @@ static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) return status; } +/* microframe_schedule=0 end */ + +/** + * Microframe scheduler + * track the total use in hcd->frame_usecs + * keep each qh use in qh->frame_usecs + * when surrendering the qh then donate the time back + */ +const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 }; + +/* + * called from dwc_otg_hcd.c:dwc_otg_hcd_init + */ +int init_hcd_usecs(dwc_otg_hcd_t *_hcd) +{ + int i; + for (i=0; i<8; i++) { + _hcd->frame_usecs[i] = max_uframe_usecs[i]; + } + return 0; +} + +static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) +{ + int i; + unsigned short utime; + int t_left; + int ret; + int done; + + ret = -1; + utime = _qh->usecs; + t_left = utime; + i = 0; + done = 0; + while (done == 0) { + /* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */ + if (utime <= _hcd->frame_usecs[i]) { + _hcd->frame_usecs[i] -= utime; + _qh->frame_usecs[i] += utime; + t_left -= utime; + ret = i; + done = 1; + return ret; + } else { + i++; + if (i == 8) { + done = 1; + ret = -1; + } + } + } + return ret; + } + +/* + * use this for FS apps that can span multiple uframes + */ +static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) +{ + int i; + int j; + unsigned short utime; + int t_left; + int ret; + int done; + unsigned short xtime; + + ret = -1; + utime = _qh->usecs; + t_left = utime; + i = 0; + done = 0; +loop: + while (done == 0) { + if(_hcd->frame_usecs[i] <= 0) { + i++; + if (i == 8) { + done = 1; + ret = -1; + } + goto loop; + } + + /* + * we need n consecutive slots + * so use j as a start slot j plus j+1 must be enough time (for now) + */ + xtime= _hcd->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+_hcd->frame_usecs[j]) < utime) { + if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) { + j = 8; + ret = -1; + continue; + } + } + if (xtime >= utime) { + ret = i; + j = 8; /* stop loop with a good value ret */ + continue; + } + /* add the frame time to x time */ + xtime += _hcd->frame_usecs[j]; + /* we must have a fully available next frame or break */ + if ((xtime < utime) + && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) { + ret = -1; + j = 8; /* stop loop with a bad value ret */ + continue; + } + } + if (ret >= 0) { + t_left = utime; + for (j = i; (t_left>0) && (j < 8); j++ ) { + t_left -= _hcd->frame_usecs[j]; + if ( t_left <= 0 ) { + _qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left; + _hcd->frame_usecs[j]= -t_left; + ret = i; + done = 1; + } else { + _qh->frame_usecs[j] += _hcd->frame_usecs[j]; + _hcd->frame_usecs[j] = 0; + } + } + } else { + i++; + if (i == 8) { + done = 1; + ret = -1; + } + } + } + return ret; +} + +static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) +{ + int ret; + ret = -1; + + if (_qh->speed == USB_SPEED_HIGH) { + /* if this is a hs transaction we need a full frame */ + ret = find_single_uframe(_hcd, _qh); + } else { + /* if this is a fs transaction we may need a sequence of frames */ + ret = find_multi_uframe(_hcd, _qh); + } + return ret; +} + /** * 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 @@ -423,19 +586,41 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) { int status = 0; - status = periodic_channel_available(hcd); - if (status) { - DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE - return status; - } + if (microframe_schedule) { + int frame; + status = find_uframe(hcd, qh); + frame = -1; + if (status == 0) { + frame = 7; + } else { + if (status > 0 ) + frame = status-1; + } - status = check_periodic_bandwidth(hcd, qh); - if (status) { - DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE - return status; - } + /* Set the new frame up */ + if (frame > -1) { + qh->sched_frame &= ~0x7; + qh->sched_frame |= (frame & 7); + } + + if (status != -1) + status = 0; + } else { + status = periodic_channel_available(hcd); + if (status) { + DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE + return status; + } - status = check_max_xfer_size(hcd, qh); + status = check_periodic_bandwidth(hcd, qh); + + if (status) { + DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE + return status; + } + + status = check_max_xfer_size(hcd, qh); + } if (status) { DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); //NOTICE return status; @@ -450,8 +635,10 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); } - /* Reserve the periodic channel. */ - hcd->periodic_channels++; + if (!microframe_schedule) { + /* Reserve the periodic channel. */ + hcd->periodic_channels++; + } /* Update claimed usecs per (micro)frame. */ hcd->periodic_usecs += qh->usecs; @@ -502,13 +689,21 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) */ static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) { + int i; DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); - /* Release the periodic channel reservation. */ - hcd->periodic_channels--; + if (!microframe_schedule) { + /* Release the periodic channel reservation. */ + hcd->periodic_channels--; - /* Update claimed usecs per (micro)frame. */ - hcd->periodic_usecs -= qh->usecs; + /* Update claimed usecs per (micro)frame. */ + hcd->periodic_usecs -= qh->usecs; + } else { + for (i = 0; i < 8; i++) { + hcd->frame_usecs[i] += qh->frame_usecs[i]; + qh->frame_usecs[i] = 0; + } + } } /** @@ -616,7 +811,8 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, * Remove from periodic_sched_queued and move to * appropriate queue. */ - if (qh->sched_frame == frame_number) { + if ((microframe_schedule && dwc_frame_num_le(qh->sched_frame, frame_number)) || + (!microframe_schedule && qh->sched_frame == frame_number)) { DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, &qh->qh_list_entry); } else {