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