diff options
-rw-r--r-- | drivers/usb/core/driver.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 127 | ||||
-rw-r--r-- | include/linux/usb.h | 1 |
3 files changed, 135 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 | |||
47 | static 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 | |||
53 | enum companion_action { | ||
54 | SET_HS_COMPANION, CLEAR_HS_COMPANION, WAIT_FOR_COMPANIONS | ||
55 | }; | ||
56 | |||
57 | static 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 | |||
117 | static 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 | |||
125 | static 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 | |||
140 | static 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 | |||
152 | static inline void set_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | ||
153 | static inline void clear_hs_companion(struct pci_dev *d, struct usb_hcd *h) {} | ||
154 | static 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); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index d7ace1b80f09..332eaea61021 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -339,6 +339,7 @@ struct usb_bus { | |||
339 | 339 | ||
340 | struct usb_devmap devmap; /* device address allocation map */ | 340 | struct usb_devmap devmap; /* device address allocation map */ |
341 | struct usb_device *root_hub; /* Root hub */ | 341 | struct usb_device *root_hub; /* Root hub */ |
342 | struct usb_bus *hs_companion; /* Companion EHCI bus, if any */ | ||
342 | struct list_head bus_list; /* list of busses */ | 343 | struct list_head bus_list; /* list of busses */ |
343 | 344 | ||
344 | int bandwidth_allocated; /* on this bus: how much of the time | 345 | int bandwidth_allocated; /* on this bus: how much of the time |