aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-02-12 06:21:11 -0500
committerRafael J. Wysocki <rjw@sisk.pl>2010-02-26 14:39:12 -0500
commit6d19c009cc780c63de25a046509ebc9473809fd6 (patch)
treed3a6186a5b3b2cd75a5778ba355ea9c3c7823a6c /drivers/usb
parenta1e4d72cd3024999bfb6703092ea271438805c89 (diff)
USB: implement non-tree resume ordering constraints for PCI host controllers
This patch (as1331) adds non-tree ordering constraints needed for proper resume of PCI USB host controllers from hibernation. The main issue is that non-high-speed devices must not be resumed before the high-speed root hub, because it is the ehci_bus_resume() routine which takes care of handing the device connection over to the companion controller. If the device resume is attempted before the handover then the device won't be found and it will be treated as though it had disconnected. The patch adds a new field to the usb_bus structure; for each full/low-speed bus this field will contain a pointer to the companion high-speed bus (if one exists). It is used during normal device resume; if the hs_companion pointer isn't NULL then we wait for the root-hub device on the hs_companion bus. A secondary issue is that an EHCI controlller shouldn't be resumed before any of its companions. On some machines I have observed handovers failing if the companion controller is reinitialized after the handover. Thus, the EHCI resume routine must wait for the companion controllers to be resumed. The patch also fixes a small bug in usb_hcd_pci_probe(); an error path jumps to the wrong label, causing a memory leak. [rjw: Fixed compilation for CONFIG_PM_SLEEP unset.] Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/driver.c8
-rw-r--r--drivers/usb/core/hcd-pci.c127
2 files changed, 134 insertions, 1 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 60a45f1e3a67..f2f055eb6831 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1022,6 +1022,14 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
1022 goto done; 1022 goto done;
1023 } 1023 }
1024 1024
1025 /* Non-root devices on a full/low-speed bus must wait for their
1026 * companion high-speed root hub, in case a handoff is needed.
1027 */
1028 if (!(msg.event & PM_EVENT_AUTO) && udev->parent &&
1029 udev->bus->hs_companion)
1030 device_pm_wait_for_dev(&udev->dev,
1031 &udev->bus->hs_companion->root_hub->dev);
1032
1025 if (udev->quirks & USB_QUIRK_RESET_RESUME) 1033 if (udev->quirks & USB_QUIRK_RESET_RESUME)
1026 udev->reset_resume = 1; 1034 udev->reset_resume = 1;
1027 1035
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 2dcf906df569..15286533c15a 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -19,6 +19,7 @@
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <linux/module.h> 20#include <linux/module.h>
21#include <linux/pci.h> 21#include <linux/pci.h>
22#include <linux/pm_runtime.h>
22#include <linux/usb.h> 23#include <linux/usb.h>
23 24
24#include <asm/io.h> 25#include <asm/io.h>
@@ -37,6 +38,122 @@
37 38
38/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ 39/* PCI-based HCs are common, but plenty of non-PCI HCs are used too */
39 40
41#ifdef CONFIG_PM_SLEEP
42
43/* Coordinate handoffs between EHCI and companion controllers
44 * during system resume
45 */
46
47static DEFINE_MUTEX(companions_mutex);
48
49#define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI
50#define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI
51#define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI
52
53enum companion_action {
54 SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS
55};
56
57static void companion_common(struct pci_dev *pdev, struct usb_hcd *hcd,
58 enum companion_action action)
59{
60 struct pci_dev *companion;
61 struct usb_hcd *companion_hcd;
62 unsigned int slot = PCI_SLOT(pdev->devfn);
63
64 /* Iterate through other PCI functions in the same slot.
65 * If pdev is OHCI or UHCI then we are looking for EHCI, and
66 * vice versa.
67 */
68 companion = NULL;
69 for (;;) {
70 companion = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, companion);
71 if (!companion)
72 break;
73 if (companion->bus != pdev->bus ||
74 PCI_SLOT(companion->devfn) != slot)
75 continue;
76
77 companion_hcd = pci_get_drvdata(companion);
78 if (!companion_hcd)
79 continue;
80
81 /* For SET_HS_COMPANION, store a pointer to the EHCI bus in
82 * the OHCI/UHCI companion bus structure.
83 * For CLEAR_HS_COMPANION, clear the pointer to the EHCI bus
84 * in the OHCI/UHCI companion bus structure.
85 * For WAIT_FOR_COMPANIONS, wait until the OHCI/UHCI
86 * companion controllers have fully resumed.
87 */
88
89 if ((pdev->class == CL_OHCI || pdev->class == CL_UHCI) &&
90 companion->class == CL_EHCI) {
91 /* action must be SET_HS_COMPANION */
92 dev_dbg(&companion->dev, "HS companion for %s\n",
93 dev_name(&pdev->dev));
94 hcd->self.hs_companion = &companion_hcd->self;
95
96 } else if (pdev->class == CL_EHCI &&
97 (companion->class == CL_OHCI ||
98 companion->class == CL_UHCI)) {
99 switch (action) {
100 case SET_HS_COMPANION:
101 dev_dbg(&pdev->dev, "HS companion for %s\n",
102 dev_name(&companion->dev));
103 companion_hcd->self.hs_companion = &hcd->self;
104 break;
105 case CLEAR_HS_COMPANION:
106 companion_hcd->self.hs_companion = NULL;
107 break;
108 case WAIT_FOR_COMPANIONS:
109 device_pm_wait_for_dev(&pdev->dev,
110 &companion->dev);
111 break;
112 }
113 }
114 }
115}
116
117static void set_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
118{
119 mutex_lock(&companions_mutex);
120 dev_set_drvdata(&pdev->dev, hcd);
121 companion_common(pdev, hcd, SET_HS_COMPANION);
122 mutex_unlock(&companions_mutex);
123}
124
125static void clear_hs_companion(struct pci_dev *pdev, struct usb_hcd *hcd)
126{
127 mutex_lock(&companions_mutex);
128 dev_set_drvdata(&pdev->dev, NULL);
129
130 /* If pdev is OHCI or UHCI, just clear its hs_companion pointer */
131 if (pdev->class == CL_OHCI || pdev->class == CL_UHCI)
132 hcd->self.hs_companion = NULL;
133
134 /* Otherwise search for companion buses and clear their pointers */
135 else
136 companion_common(pdev, hcd, CLEAR_HS_COMPANION);
137 mutex_unlock(&companions_mutex);
138}
139
140static void wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd)
141{
142 /* Only EHCI controllers need to wait.
143 * No locking is needed because a controller cannot be resumed
144 * while one of its companions is getting unbound.
145 */
146 if (pdev->class == CL_EHCI)
147 companion_common(pdev, hcd, WAIT_FOR_COMPANIONS);
148}
149
150#else /* !CONFIG_PM_SLEEP */
151
152static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
153static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {}
154static inline void wait_for_companions(struct pci_dev *d, struct usb_hcd *h) {}
155
156#endif /* !CONFIG_PM_SLEEP */
40 157
41/*-------------------------------------------------------------------------*/ 158/*-------------------------------------------------------------------------*/
42 159
@@ -123,7 +240,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
123 if (region == PCI_ROM_RESOURCE) { 240 if (region == PCI_ROM_RESOURCE) {
124 dev_dbg(&dev->dev, "no i/o regions available\n"); 241 dev_dbg(&dev->dev, "no i/o regions available\n");
125 retval = -EBUSY; 242 retval = -EBUSY;
126 goto err1; 243 goto err2;
127 } 244 }
128 } 245 }
129 246
@@ -132,6 +249,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
132 retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); 249 retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
133 if (retval != 0) 250 if (retval != 0)
134 goto err4; 251 goto err4;
252 set_hs_companion(dev, hcd);
135 return retval; 253 return retval;
136 254
137 err4: 255 err4:
@@ -142,6 +260,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
142 } else 260 } else
143 release_region(hcd->rsrc_start, hcd->rsrc_len); 261 release_region(hcd->rsrc_start, hcd->rsrc_len);
144 err2: 262 err2:
263 clear_hs_companion(dev, hcd);
145 usb_put_hcd(hcd); 264 usb_put_hcd(hcd);
146 err1: 265 err1:
147 pci_disable_device(dev); 266 pci_disable_device(dev);
@@ -180,6 +299,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
180 } else { 299 } else {
181 release_region(hcd->rsrc_start, hcd->rsrc_len); 300 release_region(hcd->rsrc_start, hcd->rsrc_len);
182 } 301 }
302 clear_hs_companion(dev, hcd);
183 usb_put_hcd(hcd); 303 usb_put_hcd(hcd);
184 pci_disable_device(dev); 304 pci_disable_device(dev);
185} 305}
@@ -344,6 +464,11 @@ static int resume_common(struct device *dev, bool hibernated)
344 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); 464 clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
345 465
346 if (hcd->driver->pci_resume) { 466 if (hcd->driver->pci_resume) {
467 /* This call should be made only during system resume,
468 * not during runtime resume.
469 */
470 wait_for_companions(pci_dev, hcd);
471
347 retval = hcd->driver->pci_resume(hcd, hibernated); 472 retval = hcd->driver->pci_resume(hcd, hibernated);
348 if (retval) { 473 if (retval) {
349 dev_err(dev, "PCI post-resume error %d!\n", retval); 474 dev_err(dev, "PCI post-resume error %d!\n", retval);