From 6d88e6792574497bfac9a81403cc47712040636f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 9 Jun 2010 17:34:17 -0400 Subject: USB: don't stop root-hub status polls too soon This patch (as1390) fixes a problem that crops up when a UHCI host controller is unbound from uhci-hcd while there are still some active URBs. The URBs have to be unlinked when the root hub is unregistered, and uhci-hcd relies upon root-hub status polls as part of its unlinking procedure. But usb_hcd_poll_rh_status() won't make those status calls if hcd->rh_registered is clear, and the flag is cleared _before_ the unregistration takes place. Since hcd->rh_registered is used for other things and needs to be cleared early, the solution is to add a new flag (rh_pollable) and use it instead. It gets cleared _after_ the root hub is unregistered. Now that the status polls don't end too soon, we have to make sure they also don't occur too late -- after the root hub's usb_device structure or the HCD's private structures are deallocated. Therefore the patch adds usb_get_device() and usb_put_device() calls to protect the root hub structure, and it adds an extra del_timer_sync() to prevent the root-hub timer from causing an unexpected status poll. This additional complexity would not be needed if the HCD framework had provided separate stop() and release() callbacks instead of just stop(). This lack could be fixed at some future time (although it would require changes to every host controller driver); when that happens this patch won't be needed any more. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/hcd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/usb/hcd.h') diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2e3a4ea1a3da..11b638195901 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -95,6 +95,7 @@ struct usb_hcd { #define HCD_FLAG_SAW_IRQ 0x00000002 unsigned rh_registered:1;/* is root hub registered? */ + unsigned rh_pollable:1; /* may we poll the root hub? */ /* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */ -- cgit v1.2.3 From 48f24970144479c29b8cee6d2e1dbedf6dcf9cfb Mon Sep 17 00:00:00 2001 From: Alek Du Date: Fri, 4 Jun 2010 15:47:55 +0800 Subject: USB: EHCI: EHCI 1.1 addendum: Basic LPM feature support With this patch, the LPM capable EHCI host controller can put device into L1 sleep state which is a mode that can enter/exit quickly, and reduce power consumption. Signed-off-by: Jacob Pan Signed-off-by: Alek Du Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 4 ++- drivers/usb/host/ehci-hcd.c | 17 ++++++++++ drivers/usb/host/ehci-hub.c | 5 +++ drivers/usb/host/ehci-lpm.c | 83 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci-pci.c | 21 ++++++++++++ drivers/usb/host/ehci.h | 2 +- include/linux/usb/hcd.h | 4 +++ 7 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 drivers/usb/host/ehci-lpm.c (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 70cccc75a362..9cd77a2af821 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2880,7 +2880,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, } retval = 0; - + /* notify HCD that we have a device connected and addressed */ + if (hcd->driver->update_device) + hcd->driver->update_device(hcd, udev); fail: if (retval) { hub_port_disable(hub, port1, 0); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 20ca6a9ff213..baf9b648bb1f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -101,6 +101,11 @@ static int ignore_oc = 0; module_param (ignore_oc, bool, S_IRUGO); MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); +/* for link power management(LPM) feature */ +static unsigned int hird; +module_param(hird, int, S_IRUGO); +MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); + #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) /*-------------------------------------------------------------------------*/ @@ -305,6 +310,7 @@ static void end_unlink_async(struct ehci_hcd *ehci); static void ehci_work(struct ehci_hcd *ehci); #include "ehci-hub.c" +#include "ehci-lpm.c" #include "ehci-mem.c" #include "ehci-q.c" #include "ehci-sched.c" @@ -604,6 +610,17 @@ static int ehci_init(struct usb_hcd *hcd) default: BUG(); } } + if (HCC_LPM(hcc_params)) { + /* support link power management EHCI 1.1 addendum */ + ehci_dbg(ehci, "support lpm\n"); + ehci->has_lpm = 1; + if (hird > 0xf) { + ehci_dbg(ehci, "hird %d invalid, use default 0", + hird); + hird = 0; + } + temp |= hird << 24; + } ehci->command = temp; /* Accept arbitrarily long scatter-gather lists */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index e7d3d8def282..8a28dae8a375 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -790,6 +790,11 @@ static int ehci_hub_control ( status_reg); break; case USB_PORT_FEAT_C_CONNECTION: + if (ehci->has_lpm) { + /* clear PORTSC bits on disconnect */ + temp &= ~PORT_LPM; + temp &= ~PORT_DEV_ADDR; + } ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, status_reg); break; diff --git a/drivers/usb/host/ehci-lpm.c b/drivers/usb/host/ehci-lpm.c new file mode 100644 index 000000000000..b4d4d63c13ed --- /dev/null +++ b/drivers/usb/host/ehci-lpm.c @@ -0,0 +1,83 @@ +/* ehci-lpm.c EHCI HCD LPM support code + * Copyright (c) 2008 - 2010, Intel Corporation. + * Author: Jacob Pan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* this file is part of ehci-hcd.c */ +static int ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) +{ + u32 __iomem portsc; + + ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num); + if (port_num > HCS_N_PORTS(ehci->hcs_params)) { + ehci_dbg(ehci, "invalid port number %d\n", port_num); + return -ENODEV; + } + portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]); + portsc &= ~PORT_DEV_ADDR; + portsc |= dev_addr<<25; + ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); + return 0; +} + +/* + * this function is used to check if the device support LPM + * if yes, mark the PORTSC register with PORT_LPM bit + */ +static int ehci_lpm_check(struct ehci_hcd *ehci, int port) +{ + u32 __iomem *portsc ; + u32 val32; + int retval; + + portsc = &ehci->regs->port_status[port-1]; + val32 = ehci_readl(ehci, portsc); + if (!(val32 & PORT_DEV_ADDR)) { + ehci_dbg(ehci, "LPM: no device attached\n"); + return -ENODEV; + } + val32 |= PORT_LPM; + ehci_writel(ehci, val32, portsc); + msleep(5); + val32 |= PORT_SUSPEND; + ehci_dbg(ehci, "Sending LPM 0x%08x to port %d\n", val32, port); + ehci_writel(ehci, val32, portsc); + /* wait for ACK */ + msleep(10); + retval = handshake(ehci, &ehci->regs->port_status[port-1], PORT_SSTS, + PORTSC_SUSPEND_STS_ACK, 125); + dbg_port(ehci, "LPM", port, val32); + if (retval != -ETIMEDOUT) { + ehci_dbg(ehci, "LPM: device ACK for LPM\n"); + val32 |= PORT_LPM; + /* + * now device should be in L1 sleep, let's wake up the device + * so that we can complete enumeration. + */ + ehci_writel(ehci, val32, portsc); + msleep(10); + val32 |= PORT_RESUME; + ehci_writel(ehci, val32, portsc); + } else { + ehci_dbg(ehci, "LPM: device does not ACK, disable LPM %d\n", + retval); + val32 &= ~PORT_LPM; + retval = -ETIMEDOUT; + ehci_writel(ehci, val32, portsc); + } + + return retval; +} diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index d43d176161aa..a307d550bdaf 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -361,6 +361,22 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) } #endif +static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc = 0; + + if (!udev->parent) /* udev is root hub itself, impossible */ + rc = -1; + /* we only support lpm device connected to root hub yet */ + if (ehci->has_lpm && !udev->parent->parent) { + rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum); + if (!rc) + rc = ehci_lpm_check(ehci, udev->portnum); + } + return rc; +} + static const struct hc_driver ehci_pci_hc_driver = { .description = hcd_name, .product_desc = "EHCI Host Controller", @@ -407,6 +423,11 @@ static const struct hc_driver ehci_pci_hc_driver = { .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + /* + * call back when device connected and addressed + */ + .update_device = ehci_update_device, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bfaac1646365..21f30a0c3d2f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -140,7 +140,7 @@ struct ehci_hcd { /* one per controller */ #define OHCI_HCCTRL_LEN 0x4 __hc32 *ohci_hcctrl_reg; unsigned has_hostpc:1; - + unsigned has_lpm:1; /* support link power management */ u8 sbrn; /* packed release number */ /* irq statistics */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 11b638195901..9b867e64a0f4 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -300,6 +300,10 @@ struct hc_driver { int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int (*reset_device)(struct usb_hcd *, struct usb_device *); + /* Notifies the HCD after a device is connected and its + * address is set + */ + int (*update_device)(struct usb_hcd *, struct usb_device *); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); -- cgit v1.2.3 From 541c7d432f76771079e7c295d596ea47cc6a3030 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 22 Jun 2010 16:39:10 -0400 Subject: USB: convert usb_hcd bitfields into atomic flags This patch (as1393) converts several of the single-bit fields in struct usb_hcd to atomic flags. This is for safety's sake; not all CPUs can update bitfield values atomically, and these flags are used in multiple contexts. The flag fields that are set only during registration or removal can remain as they are, since non-atomic accesses at those times will not cause any problems. (Strictly speaking, the authorized_default flag should become atomic as well. I didn't bother with it because it gets changed only via sysfs. It can be done later, if anyone wants.) Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/vhci_hcd.c | 6 +++--- drivers/usb/c67x00/c67x00-hcd.c | 4 ++-- drivers/usb/core/hcd.c | 26 ++++++++++++-------------- drivers/usb/gadget/dummy_hcd.c | 6 +++--- drivers/usb/host/ehci-dbg.c | 2 +- drivers/usb/host/ehci-hcd.c | 1 - drivers/usb/host/ehci-hub.c | 2 +- drivers/usb/host/ehci-q.c | 3 +-- drivers/usb/host/ehci-sched.c | 9 +++------ drivers/usb/host/hwa-hc.c | 4 ++-- drivers/usb/host/isp1760-hcd.c | 3 +-- drivers/usb/host/ohci-dbg.c | 4 ++-- drivers/usb/host/ohci-hcd.c | 6 +++--- drivers/usb/host/ohci-hub.c | 16 ++++++++++------ drivers/usb/host/oxu210hp-hcd.c | 7 ++----- drivers/usb/host/uhci-hcd.c | 21 ++++++++++++--------- drivers/usb/host/uhci-hub.c | 4 ++-- drivers/usb/host/whci/hcd.c | 2 +- drivers/usb/host/xhci.c | 3 +-- drivers/usb/musb/musb_virthub.c | 2 +- include/linux/usb/hcd.h | 22 +++++++++++++++++----- 21 files changed, 80 insertions(+), 73 deletions(-) (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index be5d8db98165..0574d848b900 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -215,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) vhci = hcd_to_vhci(hcd); spin_lock_irqsave(&vhci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { usbip_dbg_vhci_rh("hw accessible flag in on?\n"); goto done; } @@ -269,7 +269,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u32 prev_port_status[VHCI_NPORTS]; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; /* @@ -1041,7 +1041,7 @@ static int vhci_bus_resume(struct usb_hcd *hcd) dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq(&vhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { /* vhci->rh_state = DUMMY_RH_RUNNING; diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c index a22b887f4e9e..d3e1356d091e 100644 --- a/drivers/usb/c67x00/c67x00-hcd.c +++ b/drivers/usb/c67x00/c67x00-hcd.c @@ -264,7 +264,7 @@ static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg) if (unlikely(hcd->state == HC_STATE_HALT)) return; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return; /* Handle Start of frame events */ @@ -282,7 +282,7 @@ static int c67x00_hcd_start(struct usb_hcd *hcd) { hcd->uses_new_polling = 1; hcd->state = HC_STATE_RUNNING; - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); return 0; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 53f14c82ff2e..f2fe7c8e991d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -679,7 +679,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) spin_lock_irqsave(&hcd_root_hub_lock, flags); urb = hcd->status_urb; if (urb) { - hcd->poll_pending = 0; + clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); hcd->status_urb = NULL; urb->actual_length = length; memcpy(urb->transfer_buffer, buffer, length); @@ -690,7 +690,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) spin_lock(&hcd_root_hub_lock); } else { length = 0; - hcd->poll_pending = 1; + set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); } spin_unlock_irqrestore(&hcd_root_hub_lock, flags); } @@ -699,7 +699,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) * exceed that limit if HZ is 100. The math is more clunky than * maybe expected, this is to make sure that all timers for USB devices * fire at the same time to give the CPU a break inbetween */ - if (hcd->uses_new_polling ? hcd->poll_rh : + if (hcd->uses_new_polling ? HCD_POLL_RH(hcd) : (length == 0 && hcd->status_urb != NULL)) mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); } @@ -736,7 +736,7 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) mod_timer(&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); /* If a status change has already occurred, report it ASAP */ - else if (hcd->poll_pending) + else if (HCD_POLL_PENDING(hcd)) mod_timer(&hcd->rh_timer, jiffies); retval = 0; done: @@ -1150,8 +1150,7 @@ int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) && - !is_root_hub(urb->dev)) { + if (!HCD_SAW_IRQ(hcd) && !is_root_hub(urb->dev)) { dev_warn(hcd->self.controller, "Unlink after no-IRQ? " "Controller is probably using the wrong IRQ.\n"); set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); @@ -2063,8 +2062,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || - !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; @@ -2098,7 +2096,7 @@ void usb_hc_died (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) { - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* make khubd clean up old urbs and devices */ usb_set_device_state (hcd->self.root_hub, @@ -2301,7 +2299,7 @@ int usb_add_hcd(struct usb_hcd *hcd, retval); goto error_create_attr_group; } - if (hcd->uses_new_polling && hcd->poll_rh) + if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); return retval; @@ -2320,11 +2318,11 @@ error_create_attr_group: mutex_unlock(&usb_bus_list_lock); err_register_root_hub: hcd->rh_pollable = 0; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); err_hcd_driver_start: if (hcd->irq >= 0) @@ -2380,14 +2378,14 @@ void usb_remove_hcd(struct usb_hcd *hcd) * the hub_status_data() callback. */ hcd->rh_pollable = 0; - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; /* In case the HCD restarted the timer, stop it again. */ - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); del_timer_sync(&hcd->rh_timer); if (hcd->irq >= 0) diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 4f9e578cde9d..dc6546248ed9 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -1542,7 +1542,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) goto done; if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { @@ -1588,7 +1588,7 @@ static int dummy_hub_control ( int retval = 0; unsigned long flags; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; dum = hcd_to_dummy (hcd); @@ -1739,7 +1739,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd) dev_dbg (&hcd->self.root_hub->dev, "%s\n", __func__); spin_lock_irq (&dum->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { rc = -ESHUTDOWN; } else { dum->rh_state = DUMMY_RH_RUNNING; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index df5546bb8367..4498efb49b95 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -712,7 +712,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) spin_lock_irqsave (&ehci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { size = scnprintf (next, size, "bus %s, device %s\n" "%s\n" diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8697ad19f313..2a19336c9824 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -642,7 +642,6 @@ static int ehci_run (struct usb_hcd *hcd) u32 hcc_params; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; /* EHCI spec section 4.1 */ if ((retval = ehci_reset(ehci)) != 0) { diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 84e792d71c22..0931f5a7dec4 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -316,7 +316,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { spin_unlock_irq(&ehci->lock); return -ESHUTDOWN; } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 11a79c4f4a9d..233c288e3f93 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1126,8 +1126,7 @@ submit_async ( #endif spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { rc = -ESHUTDOWN; goto done; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 805ec633a652..d640346f9b56 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -880,8 +880,7 @@ static int intr_submit ( spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } @@ -1815,8 +1814,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } @@ -2201,8 +2199,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, /* schedule ... need to lock */ spin_lock_irqsave (&ehci->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) { status = -ESHUTDOWN; goto done_not_linked; } diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index 35742f8c7cda..9bfac657572e 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -159,7 +159,7 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd) goto error_set_cluster_id; usb_hcd->uses_new_polling = 1; - usb_hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; result = 0; out: @@ -776,7 +776,7 @@ static int hwahc_probe(struct usb_interface *usb_iface, goto error_alloc; } usb_hcd->wireless = 1; - usb_hcd->flags |= HCD_FLAG_SAW_IRQ; + set_bit(HCD_FLAG_SAW_IRQ, &usb_hcd->flags); wusbhc = usb_hcd_to_wusbhc(usb_hcd); hwahc = container_of(wusbhc, struct hwahc, wusbhc); hwahc_init(hwahc); diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index dbcafa29c775..d1a3dfc9a408 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -482,7 +482,6 @@ static int isp1760_run(struct usb_hcd *hcd) u32 chipid; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; hcd->state = HC_STATE_RUNNING; isp1760_enable_interrupts(hcd); @@ -1450,7 +1449,7 @@ static int isp1760_prepare_enqueue(struct isp1760_hcd *priv, struct urb *urb, epnum = urb->ep->desc.bEndpointAddress; spin_lock_irqsave(&priv->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &priv_to_hcd(priv)->flags)) { + if (!HCD_HW_ACCESSIBLE(priv_to_hcd(priv))) { rc = -ESHUTDOWN; goto done; } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 8ad2441b0284..36abd2baa3ea 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -645,7 +645,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) hcd->product_desc, hcd_name); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { size -= scnprintf (next, size, "SUSPENDED (no register access)\n"); goto done; @@ -687,7 +687,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; temp = scnprintf (next, size, "hub poll timer %s\n", - ohci_to_hcd(ohci)->poll_rh ? "ON" : "off"); + HCD_POLL_RH(ohci_to_hcd(ohci)) ? "ON" : "off"); size -= temp; next += temp; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 02864a237a2c..c3b4ccc7337b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -212,7 +212,7 @@ static int ohci_urb_enqueue ( spin_lock_irqsave (&ohci->lock, flags); /* don't submit to a dead HC */ - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { retval = -ENODEV; goto fail; } @@ -685,7 +685,7 @@ retry: } /* use rhsc irqs after khubd is fully initialized */ - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); hcd->uses_new_polling = 1; /* start controller operations */ @@ -822,7 +822,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) else if (ints & OHCI_INTR_RD) { ohci_vdbg(ohci, "resume detect\n"); ohci_writel(ohci, OHCI_INTR_RD, ®s->intrstatus); - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); if (ohci->autostop) { spin_lock (&ohci->lock); ohci_rh_resume (ohci); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 65cac8cc8921..4dd39022c388 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -284,7 +284,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) rc = -ESHUTDOWN; else rc = ohci_rh_suspend (ohci, 0); @@ -302,7 +302,7 @@ static int ohci_bus_resume (struct usb_hcd *hcd) spin_lock_irq (&ohci->lock); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) rc = -ESHUTDOWN; else rc = ohci_rh_resume (ohci); @@ -489,7 +489,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) unsigned long flags; spin_lock_irqsave (&ohci->lock, flags); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) goto done; /* undocumented erratum seen on at least rev D */ @@ -533,8 +533,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) } } - hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, - any_connected, rhsc_status); + if (ohci_root_hub_state_changes(ohci, changed, + any_connected, rhsc_status)) + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + else + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + done: spin_unlock_irqrestore (&ohci->lock, flags); @@ -701,7 +705,7 @@ static int ohci_hub_control ( u32 temp; int retval = 0; - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) return -ESHUTDOWN; switch (typeReq) { diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index f608dfd09a8a..d9c85a292737 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1641,8 +1641,7 @@ static int submit_async(struct oxu_hcd *oxu, struct urb *urb, #endif spin_lock_irqsave(&oxu->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &oxu_to_hcd(oxu)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { rc = -ESHUTDOWN; goto done; } @@ -2209,8 +2208,7 @@ static int intr_submit(struct oxu_hcd *oxu, struct urb *urb, spin_lock_irqsave(&oxu->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &oxu_to_hcd(oxu)->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(oxu_to_hcd(oxu)))) { status = -ESHUTDOWN; goto done; } @@ -2715,7 +2713,6 @@ static int oxu_run(struct usb_hcd *hcd) u32 temp, hcc_params; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; /* EHCI spec section 4.1 */ retval = ehci_reset(oxu); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index d1dce2166eff..2743ec770f0c 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -140,7 +140,7 @@ static void finish_reset(struct uhci_hcd *uhci) uhci->rh_state = UHCI_RH_RESET; uhci->is_stopped = UHCI_IS_STOPPED; uhci_to_hcd(uhci)->state = HC_STATE_HALT; - uhci_to_hcd(uhci)->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); uhci->dead = 0; /* Full reset resurrects the controller */ } @@ -344,7 +344,10 @@ __acquires(uhci->lock) /* If interrupts don't work and remote wakeup is enabled then * the suspended root hub needs to be polled. */ - uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable); + if (!int_enable && wakeup_enable) + set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); + else + clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); uhci_scan_schedule(uhci); uhci_fsbr_off(uhci); @@ -363,7 +366,7 @@ static void start_rh(struct uhci_hcd *uhci) uhci->io_addr + USBINTR); mb(); uhci->rh_state = UHCI_RH_RUNNING; - uhci_to_hcd(uhci)->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); } static void wakeup_rh(struct uhci_hcd *uhci) @@ -733,7 +736,7 @@ static void uhci_stop(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); spin_lock_irq(&uhci->lock); - if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && !uhci->dead) + if (HCD_HW_ACCESSIBLE(hcd) && !uhci->dead) uhci_hc_died(uhci); uhci_scan_schedule(uhci); spin_unlock_irq(&uhci->lock); @@ -750,7 +753,7 @@ static int uhci_rh_suspend(struct usb_hcd *hcd) int rc = 0; spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else if (uhci->dead) ; /* Dead controllers tell no tales */ @@ -777,7 +780,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd) int rc = 0; spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) + if (!HCD_HW_ACCESSIBLE(hcd)) rc = -ESHUTDOWN; else if (!uhci->dead) wakeup_rh(uhci); @@ -793,7 +796,7 @@ static int uhci_pci_suspend(struct usb_hcd *hcd) dev_dbg(uhci_dev(uhci), "%s\n", __func__); spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done_okay; /* Already suspended or dead */ if (uhci->rh_state > UHCI_RH_SUSPENDED) { @@ -807,7 +810,7 @@ static int uhci_pci_suspend(struct usb_hcd *hcd) */ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); mb(); - hcd->poll_rh = 0; + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* FIXME: Enable non-PME# remote wakeup? */ @@ -860,7 +863,7 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) * the suspended root hub needs to be polled. */ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) { - hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); usb_hcd_poll_rh_status(hcd); } return 0; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 8270055848ca..f0c58116c0ad 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -190,7 +190,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) spin_lock_irqsave(&uhci->lock, flags); uhci_scan_schedule(uhci); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done; uhci_check_ports(uhci); @@ -246,7 +246,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wPortChange, wPortStatus; unsigned long flags; - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) return -ETIMEDOUT; spin_lock_irqsave(&uhci->lock, flags); diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index e0d3401285c8..72b6892fda67 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -68,7 +68,7 @@ static int whc_start(struct usb_hcd *usb_hcd) whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); usb_hcd->uses_new_polling = 1; - usb_hcd->poll_rh = 1; + set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 343f1047f5d0..5e73386b3899 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -427,7 +427,6 @@ int xhci_run(struct usb_hcd *hcd) void (*doorbell)(struct xhci_hcd *) = NULL; hcd->uses_new_polling = 1; - hcd->poll_rh = 0; xhci_dbg(xhci, "xhci_run\n"); #if 0 /* FIXME: MSI not setup yet */ @@ -733,7 +732,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ret = -EINVAL; goto exit; } - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + if (!HCD_HW_ACCESSIBLE(hcd)) { if (!in_interrupt()) xhci_dbg(xhci, "urb submitted during PCI suspend\n"); ret = -ESHUTDOWN; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 92e85e027cfb..43233c397b6e 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -244,7 +244,7 @@ int musb_hub_control( spin_lock_irqsave(&musb->lock, flags); - if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { + if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) { spin_unlock_irqrestore(&musb->lock, flags); return -ESHUTDOWN; } diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 9b867e64a0f4..f8f8fa7a56e8 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -89,19 +89,31 @@ struct usb_hcd { */ const struct hc_driver *driver; /* hw-specific hooks */ - /* Flags that need to be manipulated atomically */ + /* Flags that need to be manipulated atomically because they can + * change while the host controller is running. Always use + * set_bit() or clear_bit() to change their values. + */ unsigned long flags; -#define HCD_FLAG_HW_ACCESSIBLE 0x00000001 -#define HCD_FLAG_SAW_IRQ 0x00000002 +#define HCD_FLAG_HW_ACCESSIBLE 0 /* at full power */ +#define HCD_FLAG_SAW_IRQ 1 +#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ +#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ + + /* The flags can be tested using these macros; they are likely to + * be slightly faster than test_bit(). + */ +#define HCD_HW_ACCESSIBLE(hcd) ((hcd)->flags & (1U << HCD_FLAG_HW_ACCESSIBLE)) +#define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ)) +#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) +#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) + /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ unsigned rh_pollable:1; /* may we poll the root hub? */ /* The next flag is a stopgap, to be removed when all the HCDs * support the new root-hub polling mechanism. */ unsigned uses_new_polling:1; - unsigned poll_rh:1; /* poll for rh status? */ - unsigned poll_pending:1; /* status has changed? */ unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ -- cgit v1.2.3 From 4147200d25c423e627ab4487530b3d9f2ef829c8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:14 -0400 Subject: USB: add do_wakeup parameter for PCI HCD suspend This patch (as1385) adds a "do_wakeup" parameter to the pci_suspend method used by PCI-based host controller drivers. ehci-hcd in particular needs to know whether or not to enable wakeup when suspending a controller. Although that information is currently available through device_may_wakeup(), when support is added for runtime suspend this will no longer be true. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 4 +++- drivers/usb/host/ehci-au1xxx.c | 2 +- drivers/usb/host/ehci-fsl.c | 3 ++- drivers/usb/host/ehci-hub.c | 5 ++--- drivers/usb/host/ehci-pci.c | 4 ++-- drivers/usb/host/ehci.h | 8 ++++---- drivers/usb/host/ohci-pci.c | 2 +- drivers/usb/host/uhci-hcd.c | 2 +- include/linux/usb/hcd.h | 2 +- 9 files changed, 17 insertions(+), 15 deletions(-) (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f0156de8db67..e387e394f876 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -386,7 +386,9 @@ static int hcd_pci_suspend(struct device *dev) return retval; if (hcd->driver->pci_suspend) { - retval = hcd->driver->pci_suspend(hcd); + bool do_wakeup = device_may_wakeup(dev); + + retval = hcd->driver->pci_suspend(hcd, do_wakeup); suspend_report_result(hcd->driver->pci_suspend, retval); if (retval) return retval; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index faa61748db70..2baf8a849086 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -228,7 +228,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) * the root hub is either suspended or stopped. */ spin_lock_irqsave(&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci); + ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); ehci_writel(ehci, 0, &ehci->regs->intr_enable); (void)ehci_readl(ehci, &ehci->regs->intr_enable); diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5cd967d28938..a416421abfa2 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -313,7 +313,8 @@ static int ehci_fsl_drv_suspend(struct device *dev) struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); void __iomem *non_ehci = hcd->regs; - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd)); + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), + device_may_wakeup(dev)); if (!fsl_deep_sleep()) return 0; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 0931f5a7dec4..1292a5b2197a 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -107,7 +107,7 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) } static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, - bool suspending) + bool suspending, bool do_wakeup) { int port; u32 temp; @@ -117,8 +117,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, * when the controller is suspended or resumed. In all other * cases they don't need to be changed. */ - if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || - device_may_wakeup(ehci_to_hcd(ehci)->self.controller)) + if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup) return; /* clear phy low-power mode before changing wakeup flags */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a307d550bdaf..f555e4f35a04 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -277,7 +277,7 @@ done: * Also they depend on separate root hub suspend/resume. */ -static int ehci_pci_suspend(struct usb_hcd *hcd) +static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; @@ -291,7 +291,7 @@ static int ehci_pci_suspend(struct usb_hcd *hcd) * the root hub is either suspended or stopped. */ spin_lock_irqsave (&ehci->lock, flags); - ehci_prepare_ports_for_controller_suspend(ehci); + ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); ehci_writel(ehci, 0, &ehci->regs->intr_enable); (void)ehci_readl(ehci, &ehci->regs->intr_enable); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e6c57cc416f6..a4a63ce290e9 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -540,11 +540,11 @@ struct ehci_fstn { /* Prepare the PORTSC wakeup flags during controller suspend/resume */ -#define ehci_prepare_ports_for_controller_suspend(ehci) \ - ehci_adjust_port_wakeup_flags(ehci, true); +#define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup) \ + ehci_adjust_port_wakeup_flags(ehci, true, do_wakeup); -#define ehci_prepare_ports_for_controller_resume(ehci) \ - ehci_adjust_port_wakeup_flags(ehci, false); +#define ehci_prepare_ports_for_controller_resume(ehci) \ + ehci_adjust_port_wakeup_flags(ehci, false, false); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index b8a1148f248e..6bdc8b25a6a1 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -392,7 +392,7 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd) #ifdef CONFIG_PM -static int ohci_pci_suspend(struct usb_hcd *hcd) +static int ohci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); unsigned long flags; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 2743ec770f0c..a7850f51fdc5 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -788,7 +788,7 @@ static int uhci_rh_resume(struct usb_hcd *hcd) return rc; } -static int uhci_pci_suspend(struct usb_hcd *hcd) +static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int rc = 0; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index f8f8fa7a56e8..ae10020b4023 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -211,7 +211,7 @@ struct hc_driver { * a whole, not just the root hub; they're for PCI bus glue. */ /* called after suspending the hub, before entering D3 etc */ - int (*pci_suspend)(struct usb_hcd *hcd); + int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup); /* called after entering D0 (etc), before resuming the hub */ int (*pci_resume)(struct usb_hcd *hcd, bool hibernated); -- cgit v1.2.3 From ff2f07874362d34684296f2bd5547a099f33c6d4 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 25 Jun 2010 14:02:35 -0400 Subject: USB: fix race between root-hub wakeup & controller suspend This patch (as1395) adds code to hcd_pci_suspend() for handling wakeup races. This is another general race pattern, similar to the "open vs. unregister" race we're all familiar with. Here, the race is between suspending a device and receiving a wakeup request from one of the device's suspended children. In particular, if a root-hub wakeup is requested at about the same time as the corresponding USB controller is suspended, and if the controller is enabled for wakeup, then the controller should either fail to suspend or else wake right back up again. During system sleep this won't happen very much, especially since host controllers generally aren't enabled for wakeup during sleep. However it is definitely an issue for runtime PM. Something like this will be needed to prevent the controller from autosuspending while waiting for a root-hub resume to take place. (That is, in fact, the common case, for which there is an extra test.) Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 12 ++++++++++++ drivers/usb/core/hcd.c | 5 ++++- include/linux/usb/hcd.h | 2 ++ 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index e387e394f876..352577baa53d 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -388,8 +388,20 @@ static int hcd_pci_suspend(struct device *dev) if (hcd->driver->pci_suspend) { bool do_wakeup = device_may_wakeup(dev); + /* Optimization: Don't suspend if a root-hub wakeup is + * pending and it would cause the HCD to wake up anyway. + */ + if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) + return -EBUSY; retval = hcd->driver->pci_suspend(hcd, do_wakeup); suspend_report_result(hcd->driver->pci_suspend, retval); + + /* Check again in case wakeup raced with pci_suspend */ + if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) { + if (hcd->driver->pci_resume) + hcd->driver->pci_resume(hcd, false); + retval = -EBUSY; + } if (retval) return retval; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index f2fe7c8e991d..0358c05e6e8a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1940,6 +1940,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) @@ -1993,8 +1994,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) unsigned long flags; spin_lock_irqsave (&hcd_root_hub_lock, flags); - if (hcd->rh_registered) + if (hcd->rh_registered) { + set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); queue_work(pm_wq, &hcd->wakeup_work); + } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index ae10020b4023..3b571f1ffbb3 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -98,6 +98,7 @@ struct usb_hcd { #define HCD_FLAG_SAW_IRQ 1 #define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ #define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ +#define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -106,6 +107,7 @@ struct usb_hcd { #define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ)) #define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) #define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) +#define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ -- cgit v1.2.3 From 496dda704bca1208e08773ba39b29a69536f5381 Mon Sep 17 00:00:00 2001 From: Maulik Mankad Date: Fri, 24 Sep 2010 13:44:06 +0300 Subject: usb: musb: host: unmap the buffer for PIO data transfers The USB stack maps the buffer for DMA if the controller supports DMA. MUSB controller can perform DMA as well as PIO transfers. The buffer needs to be unmapped before CPU can perform PIO data transfers. Export unmap_urb_for_dma() so that drivers can perform the DMA unmapping in a sane way. Signed-off-by: Maulik Mankad Acked-by: Alan Stern Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 3 ++- drivers/usb/musb/musb_host.c | 5 +++++ include/linux/usb/hcd.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5cca00a6d09d..cb2d894321da 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,7 +1263,7 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { enum dma_data_direction dir; @@ -1307,6 +1307,7 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } +EXPORT_SYMBOL_GPL(unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 9e65c47cc98b..62e39fc57211 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "musb_core.h" #include "musb_host.h" @@ -1332,6 +1333,8 @@ void musb_host_tx(struct musb *musb, u8 epnum) */ if (length > qh->maxpacket) length = qh->maxpacket; + /* Unmap the buffer so that CPU can use it */ + unmap_urb_for_dma(musb_to_hcd(musb), urb); musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset); qh->segsize = length; @@ -1752,6 +1755,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) #endif /* Mentor DMA */ if (!dma) { + /* Unmap the buffer so that CPU can use it */ + unmap_urb_for_dma(musb_to_hcd(musb), urb); done = musb_host_packet_rx(musb, urb, epnum, iso_err); DBG(6, "read %spacket\n", done ? "last " : ""); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3b571f1ffbb3..fe89f7c298aa 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,6 +329,7 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern void unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_disable_endpoint(struct usb_device *udev, -- cgit v1.2.3 From 1dae423dd9b247b048eda00cb598c755e5933213 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Fri, 1 Oct 2010 00:21:55 +0200 Subject: USB: introduce unmap_urb_setup_for_dma() Split unmap_urb_for_dma() to allow just the setup buffer to be unmapped. This allows HCDs to use PIO for the setup buffer if it is not suitable for DMA. Signed-off-by: Martin Fuzzey Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 18 +++++++++++++----- include/linux/usb/hcd.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include/linux/usb/hcd.h') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index cb2d894321da..61800f77dac8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1263,10 +1263,8 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, *dma_handle = 0; } -void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +void unmap_urb_setup_for_dma(struct usb_hcd *hcd, struct urb *urb) { - enum dma_data_direction dir; - if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) dma_unmap_single(hcd->self.controller, urb->setup_dma, @@ -1279,6 +1277,17 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); + /* Make it safe to call this routine more than once */ + urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL); +} +EXPORT_SYMBOL_GPL(unmap_urb_setup_for_dma); + +void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + enum dma_data_direction dir; + + unmap_urb_setup_for_dma(hcd, urb); + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (urb->transfer_flags & URB_DMA_MAP_SG) dma_unmap_sg(hcd->self.controller, @@ -1303,8 +1312,7 @@ void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) dir); /* Make it safe to call this routine more than once */ - urb->transfer_flags &= ~(URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL | - URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | + urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } EXPORT_SYMBOL_GPL(unmap_urb_for_dma); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index fe89f7c298aa..0b6e751ea0b1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -329,6 +329,7 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern void unmap_urb_setup_for_dma(struct usb_hcd *, struct urb *); extern void unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); -- cgit v1.2.3