From 9b790915450e2e2eb9a8df7fe32f41e895de9da1 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Fri, 17 May 2013 12:08:51 -0700 Subject: usb: ehci: Only sleep for post-resume handover if devices use persist The current EHCI code sleeps a flat 110ms in the resume path if there was a USB 1.1 device connected to its companion controller during suspend, waiting for the device to reappear and reset so that it can be handed back to the companion. This is necessary if the device uses persist, so that the companion controller can actually see it during its own resume path. However, if the device doesn't use persist, this is entirely unnecessary. We might just as well ignore it and have the normal device detection/reset/handoff code handle it asynchronously when it eventually shows up. As USB 1.1 devices are almost exclusively HIDs these days (for which persist has no value), this can allow distros to shave another tenth of a second off their resume time. In order to enable this optimization, the patch also adds a new usb_for_each_dev() iterator that is exported by the USB core and wraps bus_for_each_dev() with the logic to differentiate between struct usb_device and struct usb_interface on the usb_bus_type bus. Signed-off-by: Julius Werner Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/usb/host/ehci-hub.c') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 9ab4a4d9768..b2f64506840 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -42,6 +42,12 @@ static int ehci_hub_control( u16 wLength ); +static int persist_enabled_on_companion(struct usb_device *udev, void *unused) +{ + return !udev->maxchild && udev->persist_enabled && + udev->bus->root_hub->speed < USB_SPEED_HIGH; +} + /* After a power loss, ports that were owned by the companion must be * reset so that the companion can still own them. */ @@ -56,6 +62,16 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) if (!ehci->owned_ports) return; + /* + * USB 1.1 devices are mostly HIDs, which don't need to persist across + * suspends. If we ensure that none of our companion's devices have + * persist_enabled (by looking through all USB 1.1 buses in the system), + * we can skip this and avoid slowing resume down. Devices without + * persist will just get reenumerated shortly after resume anyway. + */ + if (!usb_for_each_dev(NULL, persist_enabled_on_companion)) + return; + /* Make sure the ports are powered */ port = HCS_N_PORTS(ehci->hcs_params); while (port--) { -- cgit v1.2.3-70-g09d2 From 2f3a6b86528994027c8241f9666f0fbcf3d3130b Mon Sep 17 00:00:00 2001 From: Manjunath Goudar Date: Thu, 13 Jun 2013 11:24:09 -0600 Subject: USB: EHCI: export ehci_handshake for ehci-hcd sub-drivers In order to split ehci-hcd.c into separate modules, handshake() must be exported. Rename the symbol to add an ehci_ prefix, to avoid any naming clashes. Signed-off-by: Manjunath Goudar [swarren, split Manjunath's patches more logically, limit this change to export just handshake()] Signed-off-by: Stephen Warren Acked-by: Alan Stern Acked-by: Arnd Bergmann Tested-by: Thierry Reding Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 17 ++++++++++------- drivers/usb/host/ehci-hub.c | 4 ++-- drivers/usb/host/ehci-tegra.c | 12 ++++++------ drivers/usb/host/ehci.h | 2 ++ 4 files changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers/usb/host/ehci-hub.c') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 246e124e6ac..e8a6f3dd735 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -139,7 +139,7 @@ static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) /*-------------------------------------------------------------------------*/ /* - * handshake - spin reading hc until handshake completes or fails + * ehci_handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read * @mask: bits to look at in result of read * @done: value of those bits when handshake succeeds @@ -155,8 +155,8 @@ static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) * before driver shutdown. But it also seems to be caused by bugs in cardbus * bridge shutdown: shutting down the bridge before the devices using it. */ -static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, - u32 mask, u32 done, int usec) +int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, + u32 mask, u32 done, int usec) { u32 result; @@ -172,6 +172,7 @@ static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, } while (usec > 0); return -ETIMEDOUT; } +EXPORT_SYMBOL_GPL(ehci_handshake); /* check TDI/ARC silicon is in host mode */ static int tdi_in_host_mode (struct ehci_hcd *ehci) @@ -212,7 +213,7 @@ static int ehci_halt (struct ehci_hcd *ehci) spin_unlock_irq(&ehci->lock); synchronize_irq(ehci_to_hcd(ehci)->irq); - return handshake(ehci, &ehci->regs->status, + return ehci_handshake(ehci, &ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); } @@ -251,7 +252,7 @@ static int ehci_reset (struct ehci_hcd *ehci) ehci_writel(ehci, command, &ehci->regs->command); ehci->rh_state = EHCI_RH_HALTED; ehci->next_statechange = jiffies; - retval = handshake (ehci, &ehci->regs->command, + retval = ehci_handshake(ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); if (ehci->has_hostpc) { @@ -286,7 +287,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) /* wait for any schedule enables/disables to take effect */ temp = (ehci->command << 10) & (STS_ASS | STS_PSS); - handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); + ehci_handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, + 16 * 125); /* then disable anything that's still active */ spin_lock_irq(&ehci->lock); @@ -295,7 +297,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci) spin_unlock_irq(&ehci->lock); /* hardware can take 16 microframes to turn off ... */ - handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); + ehci_handshake(ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, + 16 * 125); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index b2f64506840..2b702772d04 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -892,7 +892,7 @@ static int ehci_hub_control ( PORT_SUSPEND | PORT_RESUME); ehci_writel(ehci, temp, status_reg); clear_bit(wIndex, &ehci->resuming_ports); - retval = handshake(ehci, status_reg, + retval = ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 2000 /* 2msec */); if (retval != 0) { ehci_err(ehci, @@ -918,7 +918,7 @@ static int ehci_hub_control ( /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ - retval = handshake(ehci, status_reg, + retval = ehci_handshake(ehci, status_reg, PORT_RESET, 0, 1000); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 8390c870299..289b9b83f6b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -179,7 +179,7 @@ static int tegra_ehci_hub_control( * If a transaction is in progress, there may be a delay in * suspending the port. Poll until the port is suspended. */ - if (handshake(ehci, status_reg, PORT_SUSPEND, + if (ehci_handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); @@ -227,9 +227,9 @@ static int tegra_ehci_hub_control( spin_lock_irqsave(&ehci->lock, flags); /* Poll until the controller clears RESUME and SUSPEND */ - if (handshake(ehci, status_reg, PORT_RESUME, 0, 2000)) + if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 2000)) pr_err("%s: timeout waiting for RESUME\n", __func__); - if (handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) + if (ehci_handshake(ehci, status_reg, PORT_SUSPEND, 0, 2000)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); ehci->reset_done[wIndex-1] = 0; @@ -511,14 +511,14 @@ static int controller_resume(struct device *dev) } /* Poll until CCS is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_CONNECT, + if (ehci_handshake(ehci, &hw->port_status[0], PORT_CONNECT, PORT_CONNECT, 2000)) { pr_err("%s: timeout waiting for PORT_CONNECT\n", __func__); goto restart; } /* Poll until PE is enabled */ - if (handshake(ehci, &hw->port_status[0], PORT_PE, + if (ehci_handshake(ehci, &hw->port_status[0], PORT_PE, PORT_PE, 2000)) { pr_err("%s: timeout waiting for USB_PORTSC1_PE\n", __func__); goto restart; @@ -536,7 +536,7 @@ static int controller_resume(struct device *dev) writel(val, &hw->port_status[0]); /* Wait until port suspend completes */ - if (handshake(ehci, &hw->port_status[0], PORT_SUSPEND, + if (ehci_handshake(ehci, &hw->port_status[0], PORT_SUSPEND, PORT_SUSPEND, 1000)) { pr_err("%s: timeout waiting for PORT_SUSPEND\n", __func__); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 7c978b23520..64f9a08e959 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -800,6 +800,8 @@ struct ehci_driver_overrides { extern void ehci_init_driver(struct hc_driver *drv, const struct ehci_driver_overrides *over); extern int ehci_setup(struct usb_hcd *hcd); +extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, + u32 mask, u32 done, int usec); #ifdef CONFIG_PM extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); -- cgit v1.2.3-70-g09d2 From 47a64a13d54f6c669b00542848d5550be3d3310e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 9 Jul 2013 17:03:50 +0300 Subject: USB: EHCI: Fix resume signalling on remote wakeup Set the ehci->resuming flag for the port we receive a remote wakeup on so that resume signalling can be completed. Without this, the root hub timer will not fire again to check if the resume was completed and there will be a never-ending wait on on the port. This effect is only observed if the HUB IRQ IN does not come after we have initiated the port resume. Signed-off-by: Roger Quadros Cc: stable Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/usb/host/ehci-hub.c') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2b702772d04..6dce37555c4 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -874,6 +874,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); usb_hcd_start_port_resume(&hcd->self, wIndex); + set_bit(wIndex, &ehci->resuming_ports); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); -- cgit v1.2.3-70-g09d2