Permalink
Browse files

Add mechanism to reduce the number of SOF interrupts in dwc_otg USB d…

…river. Enable through /proc/dwc_sof/SOF_reduction
  • Loading branch information...
1 parent c47ee9d commit 85b7821857dd0b9cabab59d47f08eabed74679a3 @popcornmix popcornmix committed Jul 6, 2012
@@ -1021,7 +1021,7 @@ static void timer_callback(unsigned long data)
{
dwc_timer_t *timer = (dwc_timer_t *)data;
set_scheduled(timer, 0);
- DWC_DEBUG("Timer %s callback", timer->name);
+ /*DWC_DEBUG("Timer %s callback", timer->name);*/
timer->cb(timer->data);
}
@@ -15,9 +15,14 @@ endif
# Use one of the following flags to compile the software in host-only or
# device-only mode.
-#CPPFLAGS += -DDWC_HOST_ONLY
+ifeq ($(CONFIG_USB_GADGET_DWCOTG),)
+CPPFLAGS += -DDWC_HOST_ONLY
+endif
#CPPFLAGS += -DDWC_DEVICE_ONLY
+# Use this flag to reduce SOF interrupt service overhead
+CPPFLAGS += -DSOF_FIX
+
CPPFLAGS += -Dlinux -DDWC_HS_ELECT_TST
#CGG: CPPFLAGS += -DDWC_EN_ISOC
CPPFLAGS += -I$(obj)/../dwc_common_port
@@ -82,6 +82,11 @@
# include <linux/irq.h>
+#ifdef SOF_FIX
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#endif
+
#include <asm/io.h>
@@ -178,6 +183,8 @@ struct dwc_otg_driver_module_params {
int32_t lpm_enable;
int32_t ic_usb_cap;
int32_t ahb_thr_ratio;
+ int32_t sof_setting; // 0=off, 1=on
+ int32_t proc_init_done; // 0=not done, 1=done
};
static struct dwc_otg_driver_module_params dwc_otg_module_params = {
@@ -254,8 +261,138 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = {
.lpm_enable = -1,
.ic_usb_cap = -1,
.ahb_thr_ratio = -1,
+ .sof_setting = 0,
+ .proc_init_done = 0,
};
+/**
+ * PROC_FS SUPPORT
+ * proc_fs support for setting the Start-of-Frame (SOF) interrupt processing
+ * fix (reducing SOF interrupts by an order of magnitude). When set
+ * to "on" the SOF interrupt will only be turned on once per tick, for
+ * 3 micro-frame times. When set to "off" it will not turn off the
+ * SOF interrupt, and process all 8000 per second.
+ */
+
+#ifdef SOF_FIX
+
+static struct proc_dir_entry *proc_dir, *proc_file;
+
+int sof_setting(void)
+{
+ return dwc_otg_module_params.sof_setting;
+}
+
+static int sof_read_data (char *page,
+ char **start,
+ off_t off,
+ int count,
+ int *eof,
+ void *data)
+{
+
+ if (dwc_otg_module_params.sof_setting == 1)
+ {
+ sprintf(page, "on\n");
+ return 4;
+ }
+ else
+ {
+ sprintf(page, "off\n");
+ return 5;
+ }
+ return 0;
+}
+
+#define PROC_FS_MAX_SIZE 1024
+#define PROC_FS_NAME "SOF_reduction"
+
+static char proc_fs_buffer[PROC_FS_MAX_SIZE];
+
+static int sof_write_data (struct file *file,
+ const char __user *buffer,
+ unsigned long count,
+ void *data)
+{
+ unsigned long buffer_size = count;
+
+ if (buffer_size > PROC_FS_MAX_SIZE)
+ buffer_size = PROC_FS_MAX_SIZE;
+
+ memset(proc_fs_buffer, 0, sizeof(proc_fs_buffer));
+
+ if (copy_from_user(proc_fs_buffer, buffer, buffer_size))
+ {
+ printk(KERN_ERR "\nSOF_write_data: copy_from_user failure\n");
+ return -EFAULT;
+ }
+
+ if ((strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE) == 3) &&
+ (strncmp(proc_fs_buffer, "on", 2) == 0))
+ {
+ printk(KERN_ERR "\n%s: Setting SOF (reduction) ON.\n", PROC_FS_NAME);
+ dwc_otg_module_params.sof_setting = 1;
+ }
+ else if ((strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE) == 4) &&
+ (strncmp(proc_fs_buffer, "off", 3) == 0))
+ {
+ printk(KERN_ERR "\n%s: Setting SOF reduction OFF.\n",PROC_FS_NAME);
+ dwc_otg_module_params.sof_setting = 0;
+ }
+ else
+ printk(KERN_ERR "\n%s: input not \'on\' or \'off\', ignored.\n", PROC_FS_NAME);
+#ifdef DEBUG_SOF_FIX
+ printk(KERN_ERR "\n%s:buffer %s, len = %d.\n",__func__,
+ proc_fs_buffer, strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE));
+#endif
+
+ return buffer_size;
+}
+
+/**
+ * Initialize proc_fs entry for SOF setting.
+ */
+static int init_proc_fs(void)
+{
+ int retval = 0;
+
+ if (dwc_otg_module_params.proc_init_done)
+ return 0;
+
+ proc_dir = proc_mkdir_mode("dwc_sof", 0755, NULL);
+
+ if(proc_dir == NULL)
+ {
+ retval = -ENOMEM;
+ printk("Error creating dir\n");
+ return retval;
+ }
+
+ proc_file = create_proc_entry(PROC_FS_NAME, 0666, proc_dir);
+
+ if (proc_file != NULL)
+ {
+ dwc_otg_module_params.proc_init_done = 1;
+ proc_file->read_proc = sof_read_data;
+ proc_file->write_proc = sof_write_data;
+ proc_file->mode = S_IFREG | S_IRUGO;
+ proc_file->uid = 0;
+ proc_file->gid = 0;
+ proc_file->gid = PROC_FS_MAX_SIZE;
+ }
+ else
+ {
+ retval = -ENOMEM;
+ printk("Error creating file\n");
+ remove_proc_entry(PROC_FS_NAME, NULL);
+ }
+
+ return retval;
+}
+
+#endif
+
+
/**
* This function shows the Driver Version.
*/
@@ -845,6 +982,12 @@ struct platform_device *_dev
dev_dbg(&_dev->dev, "Calling attr_create\n");
dwc_otg_attr_create(_dev);
+#ifdef SOF_FIX
+ retval = init_proc_fs();
+ if (retval)
+ goto fail;
+#endif
+
/*
* Disable the global interrupt until all the interrupt
* handlers are installed.
@@ -1015,6 +1158,7 @@ static struct platform_driver dwc_otg_driver = {
*
* @return
*/
+
static int __init dwc_otg_driver_init(void)
{
int retval = 0;
@@ -1049,6 +1193,11 @@ static int __init dwc_otg_driver_init(void)
error = driver_create_file(&dwc_otg_driver.driver,
&driver_attr_debuglevel);
#endif
+
+#ifdef SOF_FIX
+ retval = init_proc_fs();
+#endif
+
return retval;
}
@@ -64,6 +64,37 @@ void dwc_otg_hcd_connect_timeout(void *ptr)
__DWC_ERROR("Device Not Connected/Responding\n");
}
+/**
+ * SOF_FIX: Reduce the SOF overhead by disabling the SOF interrupt except
+ * when there are USB transfers pending. Re-enable the interrupt
+ * every tick for periodic transaction handling. MSO 5/31/12
+ * SOF (Start of Frame) timeout function. Kick the driver by re-enabling
+ * the SOF interrupt
+ */
+#ifdef SOF_FIX
+void dwc_otg_hcd_sof_timeout(void *ptr)
+{
+ dwc_otg_hcd_t * hcd = (dwc_otg_hcd_t *)ptr;
+ dwc_otg_core_if_t *core_if = hcd->core_if;
+ gintmsk_data_t gintmsk = {.d32 = 0};
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+ unsigned int intmsk;
+
+ // turn on Start-of-Frame interrupt
+ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk);
+ intmsk = gintmsk.d32;
+ gintmsk.b.sofintr |= 1;
+ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32);
+ DWC_TIMER_SCHEDULE(hcd->sof_timer, 1); /* 1ms */
+#ifdef DEBUG_SOF_FIX
+ if ((++sof_timeout_count % 10000) == 0)
+ printk(KERN_ERR "%s: %d timeouts handled, read 0x%x wrote 0x%x.",
+ __FUNCTION__, sof_timeout_count, intmsk, gintmsk.d32);
+#endif
+
+}
+#endif
+
#ifdef DEBUG
static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
{
@@ -792,6 +823,13 @@ 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);
+#ifdef SOF_FIX
+ /* Initialize the Start of Frame interrupt timeout timer. */
+ hcd->sof_timer = DWC_TIMER_ALLOC("SOF timer",
+ dwc_otg_hcd_sof_timeout, hcd);
+ DWC_TIMER_SCHEDULE(hcd->sof_timer, 1); /* 1ms */
+#endif
+
/* Initialize reset tasklet. */
hcd->reset_tasklet = DWC_TASK_ALLOC(reset_tasklet_func, hcd);
@@ -1307,6 +1345,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
dwc_otg_qh_t *qh;
int num_channels;
dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
+#ifdef SOF_FIX
+ dwc_otg_core_if_t *core_if = hcd->core_if;
+ gintmsk_data_t gintmsk = {.d32 = 0};
+ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
+#endif
#ifdef DEBUG_SOF
DWC_DEBUGPL(DBG_HCD, " Select Transactions\n");
@@ -1346,6 +1389,19 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
}
#endif
}
+#ifdef SOF_FIX
+ /*
+ * If there are transactions queued then enable the SOF interrupt to send them to
+ * the controller.
+ */
+ if (ret_val != DWC_OTG_TRANSACTION_NONE)
+ {
+ // turn on Start-of-Frame interrupt
+ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk);
+ gintmsk.b.sofintr |= 1;
+ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32);
+ }
+#endif
/*
* Process entries in the inactive portion of the non-periodic
@@ -569,6 +569,15 @@ struct dwc_otg_hcd {
uint32_t hfnum_other_samples_b;
uint64_t hfnum_other_frrem_accum_b;
#endif
+#ifdef SOF_FIX
+ /**
+ * SOF wakeup timer. We disable the SOF interrupt if there is nothing
+ * to do. However, that eventually gets us into trouble. So, re-enable
+ * the SOF interrupt every tick so we can handle any backlog that does
+ * not trigger any other interrupt.
+ */
+ dwc_timer_t *sof_timer;
+#endif
};
/** @name Transaction Execution Functions */
Oops, something went wrong.

0 comments on commit 85b7821

Please sign in to comment.