Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d413984a authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman
Browse files

USB: OHCI avoids root hub timer polling



This teaches OHCI to use the root hub status change (RHSC) IRQ, bypassing
root hub timers most of the time and switching over to the "new" root hub
polling scheme.  It's complicated by the fact that implementations of OHCI
trigger and ack that IRQ differently (the spec is vague there).

Avoiding root hub timers helps mechanisms like "dynamic tick" leave the
CPU in lowpower modes for longer intervals.

Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 06afff00
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -239,7 +239,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
	 */
	.hub_status_data =	ohci_hub_status_data,
	.hub_control =		ohci_hub_control,

	.hub_irq_enable =	ohci_rhsc_enable,
#ifdef CONFIG_PM
	.bus_suspend =		ohci_bus_suspend,
	.bus_resume =		ohci_bus_resume,
+1 −4
Original line number Diff line number Diff line
@@ -268,10 +268,6 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
	 * basic lifecycle operations
	 */
	.start =		ohci_au1xxx_start,
#ifdef	CONFIG_PM
	/* suspend:		ohci_au1xxx_suspend,  -- tbd */
	/* resume:		ohci_au1xxx_resume,   -- tbd */
#endif /*CONFIG_PM*/
	.stop =			ohci_stop,

	/*
@@ -291,6 +287,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
	 */
	.hub_status_data =	ohci_hub_status_data,
	.hub_control =		ohci_hub_control,
	.hub_irq_enable =	ohci_rhsc_enable,
#ifdef	CONFIG_PM
	.bus_suspend =		ohci_bus_suspend,
	.bus_resume =		ohci_bus_resume,
+5 −0
Original line number Diff line number Diff line
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
	size -= temp;
	next += temp;

	temp = scnprintf (next, size, "hub poll timer %s\n",
			ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
	size -= temp;
	next += temp;

	/* roothub */
	ohci_dump_roothub (ohci, 1, &next, &size);

+1 −0
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
	.get_frame_number	= ohci_get_frame,
	.hub_status_data	= ohci_hub_status_data,
	.hub_control		= ohci_hub_control,
	.hub_irq_enable		= ohci_rhsc_enable,
#ifdef CONFIG_PM
	.bus_suspend		= ohci_bus_suspend,
	.bus_resume		= ohci_bus_resume,
+32 −9
Original line number Diff line number Diff line
@@ -101,7 +101,7 @@

#include "../core/hcd.h"

#define DRIVER_VERSION "2005 April 22"
#define DRIVER_VERSION "2006 August 04"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"

@@ -112,7 +112,8 @@
/* For initializing controller (mask in an HCFS mode too) */
#define	OHCI_CONTROL_INIT	OHCI_CTRL_CBSR
#define	OHCI_INTR_INIT \
	(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
		(OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
		| OHCI_INTR_RD | OHCI_INTR_WDH)

#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,6 +129,8 @@

static const char	hcd_name [] = "ohci_hcd";

#define	STATECHANGE_DELAY	msecs_to_jiffies(300)

#include "ohci.h"

static void ohci_dump (struct ohci_hcd *ohci, int verbose);
@@ -446,7 +449,6 @@ static int ohci_init (struct ohci_hcd *ohci)

	disable (ohci);
	ohci->regs = hcd->regs;
	ohci->next_statechange = jiffies;

	/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
	 * was never needed for most non-PCI systems ... remove the code?
@@ -637,6 +639,10 @@ static int ohci_run (struct ohci_hcd *ohci)
		return -EOVERFLOW;
	}

	/* use rhsc irqs after khubd is fully initialized */
	hcd->poll_rh = 1;
	hcd->uses_new_polling = 1;

	/* start controller operations */
	ohci->hc_control &= OHCI_CTRL_RWC;
	ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
@@ -648,7 +654,7 @@ static int ohci_run (struct ohci_hcd *ohci)

	/* Choose the interrupts we care about now, others later on demand */
	mask = OHCI_INTR_INIT;
	ohci_writel (ohci, mask, &ohci->regs->intrstatus);
	ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
	ohci_writel (ohci, mask, &ohci->regs->intrenable);

	/* handle root hub init quirks ... */
@@ -672,6 +678,7 @@ static int ohci_run (struct ohci_hcd *ohci)
	// flush those writes
	(void) ohci_readl (ohci, &ohci->regs->control);

	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
	spin_unlock_irq (&ohci->lock);

	// POTPGT delay is bits 24-31, in 2 ms units.
@@ -711,6 +718,22 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
		return IRQ_NOTMINE;
	}

	/* NOTE:  vendors didn't always make the same implementation
	 * choices for RHSC.  Sometimes it triggers on an edge (like
	 * setting and maybe clearing a port status change bit); and
	 * it's level-triggered on other silicon, active until khubd
	 * clears all active port status change bits.  Poll by timer
	 * til it's fully debounced and the difference won't matter.
	 */
	if (ints & OHCI_INTR_RHSC) {
		ohci_vdbg (ohci, "rhsc\n");
		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
		hcd->poll_rh = 1;
		ohci->next_statechange = jiffies + STATECHANGE_DELAY;
		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
		usb_hcd_poll_rh_status(hcd);
	}

	if (ints & OHCI_INTR_UE) {
		disable (ohci);
		ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
Loading