Skip to content

Commit 85b7821

Browse files
committed
Add mechanism to reduce the number of SOF interrupts in dwc_otg USB driver. Enable through /proc/dwc_sof/SOF_reduction
1 parent c47ee9d commit 85b7821

File tree

7 files changed

+338
-7
lines changed

7 files changed

+338
-7
lines changed

drivers/usb/host/dwc_common_port/dwc_common_linux.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,7 @@ static void timer_callback(unsigned long data)
10211021
{
10221022
dwc_timer_t *timer = (dwc_timer_t *)data;
10231023
set_scheduled(timer, 0);
1024-
DWC_DEBUG("Timer %s callback", timer->name);
1024+
/*DWC_DEBUG("Timer %s callback", timer->name);*/
10251025
timer->cb(timer->data);
10261026
}
10271027

drivers/usb/host/dwc_otg/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ endif
1515

1616
# Use one of the following flags to compile the software in host-only or
1717
# device-only mode.
18-
#CPPFLAGS += -DDWC_HOST_ONLY
18+
ifeq ($(CONFIG_USB_GADGET_DWCOTG),)
19+
CPPFLAGS += -DDWC_HOST_ONLY
20+
endif
1921
#CPPFLAGS += -DDWC_DEVICE_ONLY
2022

23+
# Use this flag to reduce SOF interrupt service overhead
24+
CPPFLAGS += -DSOF_FIX
25+
2126
CPPFLAGS += -Dlinux -DDWC_HS_ELECT_TST
2227
#CGG: CPPFLAGS += -DDWC_EN_ISOC
2328
CPPFLAGS += -I$(obj)/../dwc_common_port

drivers/usb/host/dwc_otg/dwc_otg_driver.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@
8282

8383
# include <linux/irq.h>
8484

85+
#ifdef SOF_FIX
86+
#include <asm/uaccess.h>
87+
#include <linux/proc_fs.h>
88+
#endif
89+
8590
#include <asm/io.h>
8691

8792

@@ -178,6 +183,8 @@ struct dwc_otg_driver_module_params {
178183
int32_t lpm_enable;
179184
int32_t ic_usb_cap;
180185
int32_t ahb_thr_ratio;
186+
int32_t sof_setting; // 0=off, 1=on
187+
int32_t proc_init_done; // 0=not done, 1=done
181188
};
182189

183190
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 = {
254261
.lpm_enable = -1,
255262
.ic_usb_cap = -1,
256263
.ahb_thr_ratio = -1,
264+
.sof_setting = 0,
265+
.proc_init_done = 0,
257266
};
258267

268+
/**
269+
* PROC_FS SUPPORT
270+
* proc_fs support for setting the Start-of-Frame (SOF) interrupt processing
271+
* fix (reducing SOF interrupts by an order of magnitude). When set
272+
* to "on" the SOF interrupt will only be turned on once per tick, for
273+
* 3 micro-frame times. When set to "off" it will not turn off the
274+
* SOF interrupt, and process all 8000 per second.
275+
*/
276+
277+
#ifdef SOF_FIX
278+
279+
static struct proc_dir_entry *proc_dir, *proc_file;
280+
281+
int sof_setting(void)
282+
{
283+
return dwc_otg_module_params.sof_setting;
284+
}
285+
286+
static int sof_read_data (char *page,
287+
char **start,
288+
off_t off,
289+
int count,
290+
int *eof,
291+
void *data)
292+
{
293+
294+
if (dwc_otg_module_params.sof_setting == 1)
295+
{
296+
sprintf(page, "on\n");
297+
return 4;
298+
}
299+
else
300+
{
301+
sprintf(page, "off\n");
302+
return 5;
303+
}
304+
return 0;
305+
}
306+
307+
#define PROC_FS_MAX_SIZE 1024
308+
#define PROC_FS_NAME "SOF_reduction"
309+
310+
static char proc_fs_buffer[PROC_FS_MAX_SIZE];
311+
312+
static int sof_write_data (struct file *file,
313+
const char __user *buffer,
314+
unsigned long count,
315+
void *data)
316+
{
317+
unsigned long buffer_size = count;
318+
319+
if (buffer_size > PROC_FS_MAX_SIZE)
320+
buffer_size = PROC_FS_MAX_SIZE;
321+
322+
memset(proc_fs_buffer, 0, sizeof(proc_fs_buffer));
323+
324+
if (copy_from_user(proc_fs_buffer, buffer, buffer_size))
325+
{
326+
printk(KERN_ERR "\nSOF_write_data: copy_from_user failure\n");
327+
return -EFAULT;
328+
}
329+
330+
if ((strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE) == 3) &&
331+
(strncmp(proc_fs_buffer, "on", 2) == 0))
332+
{
333+
printk(KERN_ERR "\n%s: Setting SOF (reduction) ON.\n", PROC_FS_NAME);
334+
dwc_otg_module_params.sof_setting = 1;
335+
}
336+
else if ((strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE) == 4) &&
337+
(strncmp(proc_fs_buffer, "off", 3) == 0))
338+
{
339+
printk(KERN_ERR "\n%s: Setting SOF reduction OFF.\n",PROC_FS_NAME);
340+
dwc_otg_module_params.sof_setting = 0;
341+
}
342+
else
343+
printk(KERN_ERR "\n%s: input not \'on\' or \'off\', ignored.\n", PROC_FS_NAME);
344+
#ifdef DEBUG_SOF_FIX
345+
printk(KERN_ERR "\n%s:buffer %s, len = %d.\n",__func__,
346+
proc_fs_buffer, strnlen(proc_fs_buffer, PROC_FS_MAX_SIZE));
347+
#endif
348+
349+
return buffer_size;
350+
}
351+
352+
/**
353+
* Initialize proc_fs entry for SOF setting.
354+
*/
355+
static int init_proc_fs(void)
356+
{
357+
int retval = 0;
358+
359+
if (dwc_otg_module_params.proc_init_done)
360+
return 0;
361+
362+
proc_dir = proc_mkdir_mode("dwc_sof", 0755, NULL);
363+
364+
if(proc_dir == NULL)
365+
{
366+
retval = -ENOMEM;
367+
printk("Error creating dir\n");
368+
return retval;
369+
}
370+
371+
proc_file = create_proc_entry(PROC_FS_NAME, 0666, proc_dir);
372+
373+
if (proc_file != NULL)
374+
{
375+
dwc_otg_module_params.proc_init_done = 1;
376+
proc_file->read_proc = sof_read_data;
377+
proc_file->write_proc = sof_write_data;
378+
proc_file->mode = S_IFREG | S_IRUGO;
379+
proc_file->uid = 0;
380+
proc_file->gid = 0;
381+
proc_file->gid = PROC_FS_MAX_SIZE;
382+
}
383+
else
384+
{
385+
retval = -ENOMEM;
386+
printk("Error creating file\n");
387+
remove_proc_entry(PROC_FS_NAME, NULL);
388+
}
389+
390+
return retval;
391+
}
392+
393+
#endif
394+
395+
259396
/**
260397
* This function shows the Driver Version.
261398
*/
@@ -845,6 +982,12 @@ struct platform_device *_dev
845982
dev_dbg(&_dev->dev, "Calling attr_create\n");
846983
dwc_otg_attr_create(_dev);
847984

985+
#ifdef SOF_FIX
986+
retval = init_proc_fs();
987+
if (retval)
988+
goto fail;
989+
#endif
990+
848991
/*
849992
* Disable the global interrupt until all the interrupt
850993
* handlers are installed.
@@ -1015,6 +1158,7 @@ static struct platform_driver dwc_otg_driver = {
10151158
*
10161159
* @return
10171160
*/
1161+
10181162
static int __init dwc_otg_driver_init(void)
10191163
{
10201164
int retval = 0;
@@ -1049,6 +1193,11 @@ static int __init dwc_otg_driver_init(void)
10491193
error = driver_create_file(&dwc_otg_driver.driver,
10501194
&driver_attr_debuglevel);
10511195
#endif
1196+
1197+
#ifdef SOF_FIX
1198+
retval = init_proc_fs();
1199+
#endif
1200+
10521201
return retval;
10531202
}
10541203

drivers/usb/host/dwc_otg/dwc_otg_hcd.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,37 @@ void dwc_otg_hcd_connect_timeout(void *ptr)
6464
__DWC_ERROR("Device Not Connected/Responding\n");
6565
}
6666

67+
/**
68+
* SOF_FIX: Reduce the SOF overhead by disabling the SOF interrupt except
69+
* when there are USB transfers pending. Re-enable the interrupt
70+
* every tick for periodic transaction handling. MSO 5/31/12
71+
* SOF (Start of Frame) timeout function. Kick the driver by re-enabling
72+
* the SOF interrupt
73+
*/
74+
#ifdef SOF_FIX
75+
void dwc_otg_hcd_sof_timeout(void *ptr)
76+
{
77+
dwc_otg_hcd_t * hcd = (dwc_otg_hcd_t *)ptr;
78+
dwc_otg_core_if_t *core_if = hcd->core_if;
79+
gintmsk_data_t gintmsk = {.d32 = 0};
80+
dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
81+
unsigned int intmsk;
82+
83+
// turn on Start-of-Frame interrupt
84+
gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk);
85+
intmsk = gintmsk.d32;
86+
gintmsk.b.sofintr |= 1;
87+
dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32);
88+
DWC_TIMER_SCHEDULE(hcd->sof_timer, 1); /* 1ms */
89+
#ifdef DEBUG_SOF_FIX
90+
if ((++sof_timeout_count % 10000) == 0)
91+
printk(KERN_ERR "%s: %d timeouts handled, read 0x%x wrote 0x%x.",
92+
__FUNCTION__, sof_timeout_count, intmsk, gintmsk.d32);
93+
#endif
94+
95+
}
96+
#endif
97+
6798
#ifdef DEBUG
6899
static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
69100
{
@@ -792,6 +823,13 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if)
792823
hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer",
793824
dwc_otg_hcd_connect_timeout, 0);
794825

826+
#ifdef SOF_FIX
827+
/* Initialize the Start of Frame interrupt timeout timer. */
828+
hcd->sof_timer = DWC_TIMER_ALLOC("SOF timer",
829+
dwc_otg_hcd_sof_timeout, hcd);
830+
DWC_TIMER_SCHEDULE(hcd->sof_timer, 1); /* 1ms */
831+
#endif
832+
795833
/* Initialize reset tasklet. */
796834
hcd->reset_tasklet = DWC_TASK_ALLOC(reset_tasklet_func, hcd);
797835

@@ -1307,6 +1345,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd)
13071345
dwc_otg_qh_t *qh;
13081346
int num_channels;
13091347
dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE;
1348+
#ifdef SOF_FIX
1349+
dwc_otg_core_if_t *core_if = hcd->core_if;
1350+
gintmsk_data_t gintmsk = {.d32 = 0};
1351+
dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs;
1352+
#endif
13101353

13111354
#ifdef DEBUG_SOF
13121355
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)
13461389
}
13471390
#endif
13481391
}
1392+
#ifdef SOF_FIX
1393+
/*
1394+
* If there are transactions queued then enable the SOF interrupt to send them to
1395+
* the controller.
1396+
*/
1397+
if (ret_val != DWC_OTG_TRANSACTION_NONE)
1398+
{
1399+
// turn on Start-of-Frame interrupt
1400+
gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk);
1401+
gintmsk.b.sofintr |= 1;
1402+
dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32);
1403+
}
1404+
#endif
13491405

13501406
/*
13511407
* Process entries in the inactive portion of the non-periodic

drivers/usb/host/dwc_otg/dwc_otg_hcd.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,15 @@ struct dwc_otg_hcd {
569569
uint32_t hfnum_other_samples_b;
570570
uint64_t hfnum_other_frrem_accum_b;
571571
#endif
572+
#ifdef SOF_FIX
573+
/**
574+
* SOF wakeup timer. We disable the SOF interrupt if there is nothing
575+
* to do. However, that eventually gets us into trouble. So, re-enable
576+
* the SOF interrupt every tick so we can handle any backlog that does
577+
* not trigger any other interrupt.
578+
*/
579+
dwc_timer_t *sof_timer;
580+
#endif
572581
};
573582

574583
/** @name Transaction Execution Functions */

0 commit comments

Comments
 (0)