[marcelo at kvack.org: [PATCH][RFC] switch ehci-hcd to new polling scheme]
Marcelo Tosatti
marcelo at kvack.org
Thu Dec 28 12:35:34 EST 2006
----- Forwarded message from Marcelo Tosatti <marcelo at kvack.org> -----
From: Marcelo Tosatti <marcelo at kvack.org>
Date: Thu, 28 Dec 2006 15:33:42 -0200
To: linux-usb-devel at lists.sourceforge.net, Greg KH <greg at kroah.com>
Cc: olpc-devel at laptop.org
Subject: [PATCH][RFC] switch ehci-hcd to new polling scheme
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 */
----- End forwarded message -----
More information about the Devel
mailing list