Permalink
Browse files

Merged in microframe scheduler, currently disabled. Enable with dwc_o…

…tg.microframe_schedule=1
  • Loading branch information...
1 parent f599001 commit 5cc98c1e75700e51e4a31d8a05b4a55a7d29648c @popcornmix popcornmix committed Aug 19, 2012
@@ -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"
*
@@ -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;
}
@@ -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
@@ -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)
@@ -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,26 +857,34 @@ 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. */
tr_type = dwc_otg_hcd_select_transactions(hcd);
if (tr_type != DWC_OTG_TRANSACTION_NONE) {
dwc_otg_hcd_queue_transactions(hcd, tr_type);
}
+ DWC_SPINLOCK_FREE(channel_lock);
}
/**
Oops, something went wrong.

0 comments on commit 5cc98c1

Please sign in to comment.