Hi, The root hub status timer is problematic for tickless systems.
-bash-3.1# echo 1 > timer_stats ; sleep 5s ; cat timer_stats Timer Stats Version: v0.1 Sample period: 5.014 s 2, 0 swapper hrtimer_restart_sched_tick (hrtimer_sched_tick) 25, 1 swapper fbcon_add_cursor_timer (cursor_timer_handler) 84, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick) 24, 935 dhcdbd schedule_timeout (process_timeout) 19, 1 swapper usb_hcd_submit_urb (rh_timer_func) 5, 1 swapper init_tsc_clocksource (verify_tsc_freq) 1, 1 init schedule_timeout (process_timeout) 2, 1 swapper schedule_delayed_work_on (delayed_work_timer_fn) 1, 1 swapper neigh_table_init_no_netlink (neigh_periodic_timer) 1, 0 swapper page_writeback_init (wb_timer_fn) 1, 1128 sleep do_nanosleep (hrtimer_wakeup) 165 total events, 33.32 events/sec As can be seen by the timer_stats code, 19 timeouts were triggered in a 5s period (the rearming frequency for rh_timer is 250ms, or 4 timeouts per second). Greg added code to make the interrupt handler of the host controller inform status change events, making polling unnecessary during normal operation (new_polling flag). OHCI and UHCI have been converted to the new scheme, but EHCI hasnt. From what I understand from the EHCI spec, the STS_PCD flag (status change) of USBINTR register covers the following events: 4.15.2.1 Port Change Events Port registers contain status and status change bits. When the status change bits are set to a one, the host controller sets the Port Change Detect bit in the USBSTS register to a one. If the Port Change Interrupt Enable bit in the USBINTR register is a one, then the host controller will issue a hardware interrupt. The port status change bits include: • Connect Status Change • Port Enable/Disable Change • Over-current Change • Force Port Resume Which includes all events reported by ehci_hub_status_data. So I've hacked up a patch to switch ehci-hcd to new_polling. Unfortunately ehci_irq holds ehci->lock, so calling usb_hcd_poll_rh_status() directly from IRQ context is not possible. Due to that, I've moved the call to a workqueue instance. Insertion/removal of cards works as expected, but I'm afraid that there are corner cases which this simplistic code is not handling? Is it necessary to poll status during certain situations? diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 025d333..2e98f67 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -405,6 +405,13 @@ #endif dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); } +static void run_rh_status_poll(struct work_struct *work) +{ + struct ehci_hcd *ehci; + ehci = container_of(work, struct ehci_hcd, rh_status_worker); + usb_hcd_poll_rh_status(ehci_to_hcd(ehci)); +} + /* one-time init, only for memory state */ static int ehci_init(struct usb_hcd *hcd) { @@ -419,6 +426,8 @@ static int ehci_init(struct usb_hcd *hcd ehci->watchdog.function = ehci_watchdog; ehci->watchdog.data = (unsigned long) ehci; + INIT_WORK(&ehci->rh_status_worker, run_rh_status_poll); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -496,6 +505,8 @@ static int ehci_run (struct usb_hcd *hcd u32 temp; u32 hcc_params; + hcd->uses_new_polling = 1; + /* EHCI spec section 4.1 */ if ((retval = ehci_reset(ehci)) != 0) { ehci_mem_cleanup(ehci); @@ -622,6 +633,9 @@ #endif if (!(readl(&ehci->regs->command) & CMD_RUN)) usb_hcd_resume_root_hub(hcd); + /* Report port status change later on workqueue ctx */ + schedule_work(&ehci->rh_status_worker); + while (i--) { int pstatus = readl (&ehci->regs->port_status [i]); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index bfe5f30..5bf0786 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -291,6 +291,7 @@ ehci_hub_status_data (struct usb_hcd *hc status = STS_PCD; } } + hcd->poll_rh = 0; /* FIXME autosuspend idle root hubs */ spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 74dbc6c..1ae114c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -87,6 +87,7 @@ #define DEFAULT_I_TDPS 1024 /* some HC unsigned stamp; unsigned long next_statechange; u32 command; + struct work_struct rh_status_worker; /* SILICON QUIRKS */ unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel