Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * (C) Copyright David Brownell 2000-2002 | |
34bbe4c1 | 3 | * |
1da177e4 LT |
4 | * This program is free software; you can redistribute it and/or modify it |
5 | * under the terms of the GNU General Public License as published by the | |
6 | * Free Software Foundation; either version 2 of the License, or (at your | |
7 | * option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 | * for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software Foundation, | |
16 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | */ | |
18 | ||
1da177e4 LT |
19 | #include <linux/kernel.h> |
20 | #include <linux/module.h> | |
21 | #include <linux/pci.h> | |
21b1861f | 22 | #include <linux/usb.h> |
27729aad | 23 | #include <linux/usb/hcd.h> |
21b1861f | 24 | |
1da177e4 LT |
25 | #include <asm/io.h> |
26 | #include <asm/irq.h> | |
21b1861f DB |
27 | |
28 | #ifdef CONFIG_PPC_PMAC | |
29 | #include <asm/machdep.h> | |
30 | #include <asm/pmac_feature.h> | |
31 | #include <asm/pci-bridge.h> | |
32 | #include <asm/prom.h> | |
33 | #endif | |
5f827ea3 DB |
34 | |
35 | #include "usb.h" | |
1da177e4 LT |
36 | |
37 | ||
c6053ecf | 38 | /* PCI-based HCs are common, but plenty of non-PCI HCs are used too */ |
1da177e4 | 39 | |
05768918 AS |
40 | /* |
41 | * Coordinate handoffs between EHCI and companion controllers | |
42 | * during EHCI probing and system resume. | |
6d19c009 AS |
43 | */ |
44 | ||
05768918 | 45 | static DECLARE_RWSEM(companions_rwsem); |
6d19c009 AS |
46 | |
47 | #define CL_UHCI PCI_CLASS_SERIAL_USB_UHCI | |
48 | #define CL_OHCI PCI_CLASS_SERIAL_USB_OHCI | |
49 | #define CL_EHCI PCI_CLASS_SERIAL_USB_EHCI | |
50 | ||
05768918 AS |
51 | static inline int is_ohci_or_uhci(struct pci_dev *pdev) |
52 | { | |
53 | return pdev->class == CL_OHCI || pdev->class == CL_UHCI; | |
54 | } | |
55 | ||
56 | typedef void (*companion_fn)(struct pci_dev *pdev, struct usb_hcd *hcd, | |
57 | struct pci_dev *companion, struct usb_hcd *companion_hcd); | |
6d19c009 | 58 | |
05768918 AS |
59 | /* Iterate over PCI devices in the same slot as pdev and call fn for each */ |
60 | static void for_each_companion(struct pci_dev *pdev, struct usb_hcd *hcd, | |
61 | companion_fn fn) | |
6d19c009 AS |
62 | { |
63 | struct pci_dev *companion; | |
64 | struct usb_hcd *companion_hcd; | |
65 | unsigned int slot = PCI_SLOT(pdev->devfn); | |
66 | ||
05768918 AS |
67 | /* |
68 | * Iterate through other PCI functions in the same slot. | |
69 | * If the function's drvdata isn't set then it isn't bound to | |
70 | * a USB host controller driver, so skip it. | |
6d19c009 AS |
71 | */ |
72 | companion = NULL; | |
402e8dd6 | 73 | for_each_pci_dev(companion) { |
6d19c009 AS |
74 | if (companion->bus != pdev->bus || |
75 | PCI_SLOT(companion->devfn) != slot) | |
76 | continue; | |
6d19c009 | 77 | companion_hcd = pci_get_drvdata(companion); |
a2ff864b | 78 | if (!companion_hcd || !companion_hcd->self.root_hub) |
6d19c009 | 79 | continue; |
05768918 | 80 | fn(pdev, hcd, companion, companion_hcd); |
6d19c009 AS |
81 | } |
82 | } | |
83 | ||
05768918 AS |
84 | /* |
85 | * We're about to add an EHCI controller, which will unceremoniously grab | |
86 | * all the port connections away from its companions. To prevent annoying | |
87 | * error messages, lock the companion's root hub and gracefully unconfigure | |
88 | * it beforehand. Leave it locked until the EHCI controller is all set. | |
89 | */ | |
90 | static void ehci_pre_add(struct pci_dev *pdev, struct usb_hcd *hcd, | |
91 | struct pci_dev *companion, struct usb_hcd *companion_hcd) | |
6d19c009 | 92 | { |
05768918 AS |
93 | struct usb_device *udev; |
94 | ||
95 | if (is_ohci_or_uhci(companion)) { | |
96 | udev = companion_hcd->self.root_hub; | |
97 | usb_lock_device(udev); | |
98 | usb_set_configuration(udev, 0); | |
99 | } | |
6d19c009 AS |
100 | } |
101 | ||
05768918 AS |
102 | /* |
103 | * Adding the EHCI controller has either succeeded or failed. Set the | |
104 | * companion pointer accordingly, and in either case, reconfigure and | |
105 | * unlock the root hub. | |
106 | */ | |
107 | static void ehci_post_add(struct pci_dev *pdev, struct usb_hcd *hcd, | |
108 | struct pci_dev *companion, struct usb_hcd *companion_hcd) | |
6d19c009 | 109 | { |
05768918 | 110 | struct usb_device *udev; |
6d19c009 | 111 | |
05768918 AS |
112 | if (is_ohci_or_uhci(companion)) { |
113 | if (dev_get_drvdata(&pdev->dev)) { /* Succeeded */ | |
114 | dev_dbg(&pdev->dev, "HS companion for %s\n", | |
115 | dev_name(&companion->dev)); | |
116 | companion_hcd->self.hs_companion = &hcd->self; | |
117 | } | |
118 | udev = companion_hcd->self.root_hub; | |
119 | usb_set_configuration(udev, 1); | |
120 | usb_unlock_device(udev); | |
121 | } | |
122 | } | |
6d19c009 | 123 | |
05768918 AS |
124 | /* |
125 | * We just added a non-EHCI controller. Find the EHCI controller to | |
126 | * which it is a companion, and store a pointer to the bus structure. | |
127 | */ | |
128 | static void non_ehci_add(struct pci_dev *pdev, struct usb_hcd *hcd, | |
129 | struct pci_dev *companion, struct usb_hcd *companion_hcd) | |
130 | { | |
131 | if (is_ohci_or_uhci(pdev) && companion->class == CL_EHCI) { | |
132 | dev_dbg(&pdev->dev, "FS/LS companion for %s\n", | |
133 | dev_name(&companion->dev)); | |
134 | hcd->self.hs_companion = &companion_hcd->self; | |
135 | } | |
6d19c009 AS |
136 | } |
137 | ||
05768918 AS |
138 | /* We are removing an EHCI controller. Clear the companions' pointers. */ |
139 | static void ehci_remove(struct pci_dev *pdev, struct usb_hcd *hcd, | |
140 | struct pci_dev *companion, struct usb_hcd *companion_hcd) | |
6d19c009 | 141 | { |
05768918 AS |
142 | if (is_ohci_or_uhci(companion)) |
143 | companion_hcd->self.hs_companion = NULL; | |
6d19c009 AS |
144 | } |
145 | ||
05768918 | 146 | #ifdef CONFIG_PM |
6d19c009 | 147 | |
05768918 AS |
148 | /* An EHCI controller must wait for its companions before resuming. */ |
149 | static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, | |
150 | struct pci_dev *companion, struct usb_hcd *companion_hcd) | |
151 | { | |
152 | if (is_ohci_or_uhci(companion)) | |
153 | device_pm_wait_for_dev(&pdev->dev, &companion->dev); | |
154 | } | |
6d19c009 | 155 | |
05768918 | 156 | #endif /* CONFIG_PM */ |
1da177e4 LT |
157 | |
158 | /*-------------------------------------------------------------------------*/ | |
159 | ||
160 | /* configure so an HC device and id are always provided */ | |
161 | /* always called with process context; sleeping is OK */ | |
162 | ||
163 | /** | |
164 | * usb_hcd_pci_probe - initialize PCI-based HCDs | |
165 | * @dev: USB Host Controller being probed | |
166 | * @id: pci hotplug id connecting controller to HCD framework | |
167 | * Context: !in_interrupt() | |
168 | * | |
169 | * Allocates basic PCI resources for this USB host controller, and | |
170 | * then invokes the start() method for the HCD associated with it | |
171 | * through the hotplug entry's driver_data. | |
172 | * | |
173 | * Store this function in the HCD's struct pci_driver as probe(). | |
626f090c YB |
174 | * |
175 | * Return: 0 if successful. | |
1da177e4 | 176 | */ |
34bbe4c1 | 177 | int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) |
1da177e4 LT |
178 | { |
179 | struct hc_driver *driver; | |
180 | struct usb_hcd *hcd; | |
181 | int retval; | |
00eed9c8 | 182 | int hcd_irq = 0; |
1da177e4 LT |
183 | |
184 | if (usb_disabled()) | |
185 | return -ENODEV; | |
186 | ||
34bbe4c1 GKH |
187 | if (!id) |
188 | return -EINVAL; | |
189 | driver = (struct hc_driver *)id->driver_data; | |
190 | if (!driver) | |
1da177e4 LT |
191 | return -EINVAL; |
192 | ||
34bbe4c1 | 193 | if (pci_enable_device(dev) < 0) |
1da177e4 | 194 | return -ENODEV; |
34bbe4c1 | 195 | |
00eed9c8 HR |
196 | /* |
197 | * The xHCI driver has its own irq management | |
198 | * make sure irq setup is not touched for xhci in generic hcd code | |
68d07f64 | 199 | */ |
00eed9c8 HR |
200 | if ((driver->flags & HCD_MASK) != HCD_USB3) { |
201 | if (!dev->irq) { | |
202 | dev_err(&dev->dev, | |
203 | "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", | |
204 | pci_name(dev)); | |
205 | retval = -ENODEV; | |
206 | goto disable_pci; | |
207 | } | |
208 | hcd_irq = dev->irq; | |
34bbe4c1 | 209 | } |
1da177e4 | 210 | |
34bbe4c1 | 211 | hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); |
1da177e4 LT |
212 | if (!hcd) { |
213 | retval = -ENOMEM; | |
8766c815 | 214 | goto disable_pci; |
1da177e4 LT |
215 | } |
216 | ||
7868943d HR |
217 | hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) && |
218 | driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0; | |
219 | ||
34bbe4c1 GKH |
220 | if (driver->flags & HCD_MEMORY) { |
221 | /* EHCI, OHCI */ | |
222 | hcd->rsrc_start = pci_resource_start(dev, 0); | |
223 | hcd->rsrc_len = pci_resource_len(dev, 0); | |
224 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, | |
1da177e4 | 225 | driver->description)) { |
34bbe4c1 | 226 | dev_dbg(&dev->dev, "controller already in use\n"); |
1da177e4 | 227 | retval = -EBUSY; |
05768918 | 228 | goto put_hcd; |
1da177e4 | 229 | } |
34bbe4c1 | 230 | hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); |
1da177e4 | 231 | if (hcd->regs == NULL) { |
34bbe4c1 | 232 | dev_dbg(&dev->dev, "error mapping memory\n"); |
1da177e4 | 233 | retval = -EFAULT; |
8766c815 | 234 | goto release_mem_region; |
1da177e4 LT |
235 | } |
236 | ||
34bbe4c1 GKH |
237 | } else { |
238 | /* UHCI */ | |
1da177e4 LT |
239 | int region; |
240 | ||
241 | for (region = 0; region < PCI_ROM_RESOURCE; region++) { | |
34bbe4c1 | 242 | if (!(pci_resource_flags(dev, region) & |
1da177e4 LT |
243 | IORESOURCE_IO)) |
244 | continue; | |
245 | ||
34bbe4c1 GKH |
246 | hcd->rsrc_start = pci_resource_start(dev, region); |
247 | hcd->rsrc_len = pci_resource_len(dev, region); | |
248 | if (request_region(hcd->rsrc_start, hcd->rsrc_len, | |
1da177e4 LT |
249 | driver->description)) |
250 | break; | |
251 | } | |
252 | if (region == PCI_ROM_RESOURCE) { | |
34bbe4c1 | 253 | dev_dbg(&dev->dev, "no i/o regions available\n"); |
1da177e4 | 254 | retval = -EBUSY; |
05768918 | 255 | goto put_hcd; |
1da177e4 LT |
256 | } |
257 | } | |
258 | ||
34bbe4c1 | 259 | pci_set_master(dev); |
1da177e4 | 260 | |
05768918 AS |
261 | /* Note: dev_set_drvdata must be called while holding the rwsem */ |
262 | if (dev->class == CL_EHCI) { | |
263 | down_write(&companions_rwsem); | |
264 | dev_set_drvdata(&dev->dev, hcd); | |
265 | for_each_companion(dev, hcd, ehci_pre_add); | |
266 | retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); | |
267 | if (retval != 0) | |
268 | dev_set_drvdata(&dev->dev, NULL); | |
269 | for_each_companion(dev, hcd, ehci_post_add); | |
270 | up_write(&companions_rwsem); | |
271 | } else { | |
272 | down_read(&companions_rwsem); | |
273 | dev_set_drvdata(&dev->dev, hcd); | |
274 | retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); | |
275 | if (retval != 0) | |
276 | dev_set_drvdata(&dev->dev, NULL); | |
277 | else | |
278 | for_each_companion(dev, hcd, non_ehci_add); | |
279 | up_read(&companions_rwsem); | |
280 | } | |
281 | ||
1da177e4 | 282 | if (retval != 0) |
8766c815 | 283 | goto unmap_registers; |
3c9740a1 | 284 | device_wakeup_enable(hcd->self.controller); |
3da7cff4 AS |
285 | |
286 | if (pci_dev_run_wake(dev)) | |
287 | pm_runtime_put_noidle(&dev->dev); | |
1da177e4 LT |
288 | return retval; |
289 | ||
8766c815 | 290 | unmap_registers: |
1da177e4 | 291 | if (driver->flags & HCD_MEMORY) { |
34bbe4c1 | 292 | iounmap(hcd->regs); |
8766c815 | 293 | release_mem_region: |
34bbe4c1 | 294 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
1da177e4 | 295 | } else |
34bbe4c1 | 296 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
05768918 | 297 | put_hcd: |
34bbe4c1 | 298 | usb_put_hcd(hcd); |
8766c815 | 299 | disable_pci: |
34bbe4c1 GKH |
300 | pci_disable_device(dev); |
301 | dev_err(&dev->dev, "init %s fail, %d\n", pci_name(dev), retval); | |
1da177e4 | 302 | return retval; |
34bbe4c1 | 303 | } |
782e70c6 | 304 | EXPORT_SYMBOL_GPL(usb_hcd_pci_probe); |
1da177e4 LT |
305 | |
306 | ||
307 | /* may be called without controller electrically present */ | |
308 | /* may be called with controller, bus, and devices active */ | |
309 | ||
310 | /** | |
311 | * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs | |
312 | * @dev: USB Host Controller being removed | |
313 | * Context: !in_interrupt() | |
314 | * | |
315 | * Reverses the effect of usb_hcd_pci_probe(), first invoking | |
316 | * the HCD's stop() method. It is always called from a thread | |
317 | * context, normally "rmmod", "apmd", or something similar. | |
318 | * | |
319 | * Store this function in the HCD's struct pci_driver as remove(). | |
320 | */ | |
34bbe4c1 | 321 | void usb_hcd_pci_remove(struct pci_dev *dev) |
1da177e4 LT |
322 | { |
323 | struct usb_hcd *hcd; | |
324 | ||
325 | hcd = pci_get_drvdata(dev); | |
326 | if (!hcd) | |
327 | return; | |
328 | ||
3da7cff4 AS |
329 | if (pci_dev_run_wake(dev)) |
330 | pm_runtime_get_noresume(&dev->dev); | |
331 | ||
c548795a AS |
332 | /* Fake an interrupt request in order to give the driver a chance |
333 | * to test whether the controller hardware has been removed (e.g., | |
334 | * cardbus physical eject). | |
335 | */ | |
336 | local_irq_disable(); | |
337 | usb_hcd_irq(0, hcd); | |
338 | local_irq_enable(); | |
339 | ||
05768918 AS |
340 | /* Note: dev_set_drvdata must be called while holding the rwsem */ |
341 | if (dev->class == CL_EHCI) { | |
342 | down_write(&companions_rwsem); | |
343 | for_each_companion(dev, hcd, ehci_remove); | |
344 | usb_remove_hcd(hcd); | |
345 | dev_set_drvdata(&dev->dev, NULL); | |
346 | up_write(&companions_rwsem); | |
347 | } else { | |
348 | /* Not EHCI; just clear the companion pointer */ | |
349 | down_read(&companions_rwsem); | |
350 | hcd->self.hs_companion = NULL; | |
351 | usb_remove_hcd(hcd); | |
352 | dev_set_drvdata(&dev->dev, NULL); | |
353 | up_read(&companions_rwsem); | |
354 | } | |
355 | ||
1da177e4 | 356 | if (hcd->driver->flags & HCD_MEMORY) { |
34bbe4c1 GKH |
357 | iounmap(hcd->regs); |
358 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | |
1da177e4 | 359 | } else { |
34bbe4c1 | 360 | release_region(hcd->rsrc_start, hcd->rsrc_len); |
1da177e4 | 361 | } |
05768918 | 362 | |
34bbe4c1 | 363 | usb_put_hcd(hcd); |
1da177e4 LT |
364 | pci_disable_device(dev); |
365 | } | |
782e70c6 | 366 | EXPORT_SYMBOL_GPL(usb_hcd_pci_remove); |
1da177e4 | 367 | |
1da177e4 | 368 | /** |
abb30641 AS |
369 | * usb_hcd_pci_shutdown - shutdown host controller |
370 | * @dev: USB Host Controller being shutdown | |
1da177e4 | 371 | */ |
abb30641 AS |
372 | void usb_hcd_pci_shutdown(struct pci_dev *dev) |
373 | { | |
374 | struct usb_hcd *hcd; | |
375 | ||
376 | hcd = pci_get_drvdata(dev); | |
377 | if (!hcd) | |
378 | return; | |
379 | ||
3da7cff4 | 380 | if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && |
3df7169e | 381 | hcd->driver->shutdown) { |
abb30641 | 382 | hcd->driver->shutdown(hcd); |
c5946f9d JL |
383 | if (usb_hcd_is_primary_hcd(hcd) && hcd->irq > 0) |
384 | free_irq(hcd->irq, hcd); | |
3df7169e AS |
385 | pci_disable_device(dev); |
386 | } | |
abb30641 AS |
387 | } |
388 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); | |
389 | ||
aa338601 | 390 | #ifdef CONFIG_PM |
abb30641 | 391 | |
2138a1f1 AS |
392 | #ifdef CONFIG_PPC_PMAC |
393 | static void powermac_set_asic(struct pci_dev *pci_dev, int enable) | |
394 | { | |
395 | /* Enanble or disable ASIC clocks for USB */ | |
396 | if (machine_is(powermac)) { | |
397 | struct device_node *of_node; | |
398 | ||
399 | of_node = pci_device_to_OF_node(pci_dev); | |
400 | if (of_node) | |
401 | pmac_call_feature(PMAC_FTR_USB_ENABLE, | |
402 | of_node, 0, enable); | |
403 | } | |
404 | } | |
405 | ||
406 | #else | |
407 | ||
408 | static inline void powermac_set_asic(struct pci_dev *pci_dev, int enable) | |
409 | {} | |
410 | ||
411 | #endif /* CONFIG_PPC_PMAC */ | |
412 | ||
abb30641 AS |
413 | static int check_root_hub_suspended(struct device *dev) |
414 | { | |
415 | struct pci_dev *pci_dev = to_pci_dev(dev); | |
416 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | |
417 | ||
9b37596a | 418 | if (HCD_RH_RUNNING(hcd)) { |
abb30641 AS |
419 | dev_warn(dev, "Root hub is not suspended\n"); |
420 | return -EBUSY; | |
421 | } | |
ff9d78b3 SS |
422 | if (hcd->shared_hcd) { |
423 | hcd = hcd->shared_hcd; | |
424 | if (HCD_RH_RUNNING(hcd)) { | |
425 | dev_warn(dev, "Secondary root hub is not suspended\n"); | |
426 | return -EBUSY; | |
427 | } | |
428 | } | |
abb30641 AS |
429 | return 0; |
430 | } | |
431 | ||
3da7cff4 | 432 | static int suspend_common(struct device *dev, bool do_wakeup) |
1da177e4 | 433 | { |
abb30641 AS |
434 | struct pci_dev *pci_dev = to_pci_dev(dev); |
435 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | |
436 | int retval; | |
1da177e4 | 437 | |
5f827ea3 DB |
438 | /* Root hub suspend should have stopped all downstream traffic, |
439 | * and all bus master traffic. And done so for both the interface | |
440 | * and the stub usb_device (which we check here). But maybe it | |
441 | * didn't; writing sysfs power/state files ignores such rules... | |
5f827ea3 | 442 | */ |
abb30641 AS |
443 | retval = check_root_hub_suspended(dev); |
444 | if (retval) | |
445 | return retval; | |
a0d4922d | 446 | |
9b37596a | 447 | if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { |
ff2f0787 AS |
448 | /* Optimization: Don't suspend if a root-hub wakeup is |
449 | * pending and it would cause the HCD to wake up anyway. | |
450 | */ | |
451 | if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) | |
452 | return -EBUSY; | |
ff9d78b3 SS |
453 | if (do_wakeup && hcd->shared_hcd && |
454 | HCD_WAKEUP_PENDING(hcd->shared_hcd)) | |
455 | return -EBUSY; | |
4147200d | 456 | retval = hcd->driver->pci_suspend(hcd, do_wakeup); |
7be7d741 | 457 | suspend_report_result(hcd->driver->pci_suspend, retval); |
ff2f0787 AS |
458 | |
459 | /* Check again in case wakeup raced with pci_suspend */ | |
ff9d78b3 SS |
460 | if ((retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) || |
461 | (retval == 0 && do_wakeup && hcd->shared_hcd && | |
462 | HCD_WAKEUP_PENDING(hcd->shared_hcd))) { | |
ff2f0787 AS |
463 | if (hcd->driver->pci_resume) |
464 | hcd->driver->pci_resume(hcd, false); | |
465 | retval = -EBUSY; | |
466 | } | |
02669492 | 467 | if (retval) |
abb30641 | 468 | return retval; |
5f827ea3 DB |
469 | } |
470 | ||
0029227f AX |
471 | /* If MSI-X is enabled, the driver will have synchronized all vectors |
472 | * in pci_suspend(). If MSI or legacy PCI is enabled, that will be | |
473 | * synchronized here. | |
474 | */ | |
475 | if (!hcd->msix_enabled) | |
476 | synchronize_irq(pci_dev->irq); | |
c6053ecf | 477 | |
a15d95a0 RW |
478 | /* Downstream ports from this root hub should already be quiesced, so |
479 | * there will be no DMA activity. Now we can shut down the upstream | |
abb30641 AS |
480 | * link (except maybe for PME# resume signaling). We'll enter a |
481 | * low power state during suspend_noirq, if the hardware allows. | |
a15d95a0 | 482 | */ |
abb30641 AS |
483 | pci_disable_device(pci_dev); |
484 | return retval; | |
485 | } | |
486 | ||
057c58bf AS |
487 | static int resume_common(struct device *dev, int event) |
488 | { | |
489 | struct pci_dev *pci_dev = to_pci_dev(dev); | |
490 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | |
491 | int retval; | |
492 | ||
ff9d78b3 SS |
493 | if (HCD_RH_RUNNING(hcd) || |
494 | (hcd->shared_hcd && | |
495 | HCD_RH_RUNNING(hcd->shared_hcd))) { | |
057c58bf AS |
496 | dev_dbg(dev, "can't resume, not suspended!\n"); |
497 | return 0; | |
498 | } | |
499 | ||
500 | retval = pci_enable_device(pci_dev); | |
501 | if (retval < 0) { | |
502 | dev_err(dev, "can't re-enable after resume, %d!\n", retval); | |
503 | return retval; | |
504 | } | |
505 | ||
506 | pci_set_master(pci_dev); | |
507 | ||
9b37596a | 508 | if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { |
05768918 AS |
509 | |
510 | /* | |
511 | * Only EHCI controllers have to wait for their companions. | |
512 | * No locking is needed because PCI controller drivers do not | |
513 | * get unbound during system resume. | |
514 | */ | |
515 | if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) | |
516 | for_each_companion(pci_dev, hcd, | |
517 | ehci_wait_for_companions); | |
057c58bf AS |
518 | |
519 | retval = hcd->driver->pci_resume(hcd, | |
520 | event == PM_EVENT_RESTORE); | |
521 | if (retval) { | |
522 | dev_err(dev, "PCI post-resume error %d!\n", retval); | |
ff9d78b3 SS |
523 | if (hcd->shared_hcd) |
524 | usb_hc_died(hcd->shared_hcd); | |
057c58bf AS |
525 | usb_hc_died(hcd); |
526 | } | |
527 | } | |
528 | return retval; | |
529 | } | |
530 | ||
3da7cff4 AS |
531 | #ifdef CONFIG_PM_SLEEP |
532 | ||
533 | static int hcd_pci_suspend(struct device *dev) | |
534 | { | |
535 | return suspend_common(dev, device_may_wakeup(dev)); | |
536 | } | |
537 | ||
abb30641 AS |
538 | static int hcd_pci_suspend_noirq(struct device *dev) |
539 | { | |
540 | struct pci_dev *pci_dev = to_pci_dev(dev); | |
541 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | |
542 | int retval; | |
543 | ||
544 | retval = check_root_hub_suspended(dev); | |
545 | if (retval) | |
546 | return retval; | |
a15d95a0 | 547 | |
abb30641 | 548 | pci_save_state(pci_dev); |
a15d95a0 | 549 | |
ff9d78b3 SS |
550 | /* If the root hub is dead rather than suspended, disallow remote |
551 | * wakeup. usb_hc_died() should ensure that both hosts are marked as | |
552 | * dying, so we only need to check the primary roothub. | |
1da177e4 | 553 | */ |
9b37596a | 554 | if (HCD_DEAD(hcd)) |
abb30641 AS |
555 | device_set_wakeup_enable(dev, 0); |
556 | dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); | |
a0d4922d | 557 | |
abb30641 AS |
558 | /* Possibly enable remote wakeup, |
559 | * choose the appropriate low-power state, and go to that state. | |
560 | */ | |
561 | retval = pci_prepare_to_sleep(pci_dev); | |
562 | if (retval == -EIO) { /* Low-power not supported */ | |
563 | dev_dbg(dev, "--> PCI D0 legacy\n"); | |
564 | retval = 0; | |
565 | } else if (retval == 0) { | |
566 | dev_dbg(dev, "--> PCI %s\n", | |
567 | pci_power_name(pci_dev->current_state)); | |
a0d4922d | 568 | } else { |
abb30641 AS |
569 | suspend_report_result(pci_prepare_to_sleep, retval); |
570 | return retval; | |
1da177e4 LT |
571 | } |
572 | ||
2138a1f1 | 573 | powermac_set_asic(pci_dev, 0); |
1da177e4 LT |
574 | return retval; |
575 | } | |
1da177e4 | 576 | |
abb30641 | 577 | static int hcd_pci_resume_noirq(struct device *dev) |
a0d4922d | 578 | { |
abb30641 | 579 | struct pci_dev *pci_dev = to_pci_dev(dev); |
a0d4922d | 580 | |
2138a1f1 | 581 | powermac_set_asic(pci_dev, 1); |
a15d95a0 | 582 | |
abb30641 AS |
583 | /* Go back to D0 and disable remote wakeup */ |
584 | pci_back_from_sleep(pci_dev); | |
585 | return 0; | |
586 | } | |
587 | ||
abb30641 | 588 | static int hcd_pci_resume(struct device *dev) |
64a21d02 | 589 | { |
057c58bf | 590 | return resume_common(dev, PM_EVENT_RESUME); |
abb30641 | 591 | } |
64a21d02 | 592 | |
abb30641 AS |
593 | static int hcd_pci_restore(struct device *dev) |
594 | { | |
057c58bf | 595 | return resume_common(dev, PM_EVENT_RESTORE); |
64a21d02 | 596 | } |
1da177e4 | 597 | |
3da7cff4 AS |
598 | #else |
599 | ||
600 | #define hcd_pci_suspend NULL | |
601 | #define hcd_pci_suspend_noirq NULL | |
602 | #define hcd_pci_resume_noirq NULL | |
603 | #define hcd_pci_resume NULL | |
604 | #define hcd_pci_restore NULL | |
605 | ||
606 | #endif /* CONFIG_PM_SLEEP */ | |
607 | ||
3da7cff4 AS |
608 | static int hcd_pci_runtime_suspend(struct device *dev) |
609 | { | |
610 | int retval; | |
611 | ||
612 | retval = suspend_common(dev, true); | |
613 | if (retval == 0) | |
614 | powermac_set_asic(to_pci_dev(dev), 0); | |
615 | dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); | |
616 | return retval; | |
617 | } | |
618 | ||
619 | static int hcd_pci_runtime_resume(struct device *dev) | |
620 | { | |
621 | int retval; | |
622 | ||
623 | powermac_set_asic(to_pci_dev(dev), 1); | |
624 | retval = resume_common(dev, PM_EVENT_AUTO_RESUME); | |
625 | dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); | |
626 | return retval; | |
627 | } | |
628 | ||
47145210 | 629 | const struct dev_pm_ops usb_hcd_pci_pm_ops = { |
abb30641 AS |
630 | .suspend = hcd_pci_suspend, |
631 | .suspend_noirq = hcd_pci_suspend_noirq, | |
632 | .resume_noirq = hcd_pci_resume_noirq, | |
633 | .resume = hcd_pci_resume, | |
634 | .freeze = check_root_hub_suspended, | |
635 | .freeze_noirq = check_root_hub_suspended, | |
636 | .thaw_noirq = NULL, | |
637 | .thaw = NULL, | |
638 | .poweroff = hcd_pci_suspend, | |
639 | .poweroff_noirq = hcd_pci_suspend_noirq, | |
640 | .restore_noirq = hcd_pci_resume_noirq, | |
641 | .restore = hcd_pci_restore, | |
3da7cff4 AS |
642 | .runtime_suspend = hcd_pci_runtime_suspend, |
643 | .runtime_resume = hcd_pci_runtime_resume, | |
abb30641 AS |
644 | }; |
645 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); | |
646 | ||
aa338601 | 647 | #endif /* CONFIG_PM */ |