Commit 971fcd492cebf544714f12d94549d2f0d2002645
Committed by
Greg Kroah-Hartman
1 parent
6802771bba
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
usb: add runtime pm support for usb port device
This patch is to add runtime pm callback for usb port device. Set/clear PORT_POWER feature in the resume/suspend callback. Add portnum for struct usb_port to record port number. Do pm_rumtime_get_sync/put(portdev) when a device is plugged/unplugged to prevent it from being powered off when it is active. Acked-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Lan Tianyu <tianyu.lan@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 3 changed files with 77 additions and 0 deletions Side-by-side Diff
drivers/usb/core/hub.c
... | ... | @@ -715,6 +715,27 @@ |
715 | 715 | } |
716 | 716 | |
717 | 717 | /** |
718 | + * usb_hub_set_port_power - control hub port's power state | |
719 | + * @hdev: target hub | |
720 | + * @port1: port index | |
721 | + * @set: expected status | |
722 | + * | |
723 | + * call this function to control port's power via setting or | |
724 | + * clearing the port's PORT_POWER feature. | |
725 | + */ | |
726 | +int usb_hub_set_port_power(struct usb_device *hdev, int port1, | |
727 | + bool set) | |
728 | +{ | |
729 | + int ret; | |
730 | + | |
731 | + if (set) | |
732 | + ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); | |
733 | + else | |
734 | + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER); | |
735 | + return ret; | |
736 | +} | |
737 | + | |
738 | +/** | |
718 | 739 | * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub |
719 | 740 | * @urb: an URB associated with the failed or incomplete split transaction |
720 | 741 | * |
... | ... | @@ -1569,6 +1590,7 @@ |
1569 | 1590 | kfree(hub->status); |
1570 | 1591 | kfree(hub->buffer); |
1571 | 1592 | |
1593 | + pm_suspend_ignore_children(&intf->dev, false); | |
1572 | 1594 | kref_put(&hub->kref, hub_release); |
1573 | 1595 | } |
1574 | 1596 | |
... | ... | @@ -1671,6 +1693,7 @@ |
1671 | 1693 | |
1672 | 1694 | usb_set_intfdata (intf, hub); |
1673 | 1695 | intf->needs_remote_wakeup = 1; |
1696 | + pm_suspend_ignore_children(&intf->dev, true); | |
1674 | 1697 | |
1675 | 1698 | if (hdev->speed == USB_SPEED_HIGH) |
1676 | 1699 | highspeed_hubs++; |
... | ... | @@ -1997,6 +2020,8 @@ |
1997 | 2020 | |
1998 | 2021 | sysfs_remove_link(&udev->dev.kobj, "port"); |
1999 | 2022 | sysfs_remove_link(&port_dev->dev.kobj, "device"); |
2023 | + | |
2024 | + pm_runtime_put(&port_dev->dev); | |
2000 | 2025 | } |
2001 | 2026 | |
2002 | 2027 | usb_remove_ep_devs(&udev->ep0); |
... | ... | @@ -2307,6 +2332,8 @@ |
2307 | 2332 | sysfs_remove_link(&udev->dev.kobj, "port"); |
2308 | 2333 | goto fail; |
2309 | 2334 | } |
2335 | + | |
2336 | + pm_runtime_get_sync(&port_dev->dev); | |
2310 | 2337 | } |
2311 | 2338 | |
2312 | 2339 | (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); |
drivers/usb/core/hub.h
... | ... | @@ -79,12 +79,14 @@ |
79 | 79 | * @dev: generic device interface |
80 | 80 | * @port_owner: port's owner |
81 | 81 | * @connect_type: port's connect type |
82 | + * @portnum: port index num based one | |
82 | 83 | */ |
83 | 84 | struct usb_port { |
84 | 85 | struct usb_device *child; |
85 | 86 | struct device dev; |
86 | 87 | struct dev_state *port_owner; |
87 | 88 | enum usb_port_connect_type connect_type; |
89 | + u8 portnum; | |
88 | 90 | }; |
89 | 91 | |
90 | 92 | #define to_usb_port(_dev) \ |
... | ... | @@ -94,4 +96,6 @@ |
94 | 96 | int port1); |
95 | 97 | extern void usb_hub_remove_port_device(struct usb_hub *hub, |
96 | 98 | int port1); |
99 | +extern int usb_hub_set_port_power(struct usb_device *hdev, | |
100 | + int port1, bool set); |
drivers/usb/core/port.c
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | */ |
18 | 18 | |
19 | 19 | #include <linux/slab.h> |
20 | +#include <linux/pm_qos.h> | |
20 | 21 | |
21 | 22 | #include "hub.h" |
22 | 23 | |
23 | 24 | |
... | ... | @@ -70,9 +71,50 @@ |
70 | 71 | kfree(port_dev); |
71 | 72 | } |
72 | 73 | |
74 | +#ifdef CONFIG_USB_SUSPEND | |
75 | +static int usb_port_runtime_resume(struct device *dev) | |
76 | +{ | |
77 | + struct usb_port *port_dev = to_usb_port(dev); | |
78 | + struct usb_device *hdev = to_usb_device(dev->parent->parent); | |
79 | + struct usb_interface *intf = to_usb_interface(dev->parent); | |
80 | + int retval; | |
81 | + | |
82 | + usb_autopm_get_interface(intf); | |
83 | + retval = usb_hub_set_port_power(hdev, port_dev->portnum, true); | |
84 | + usb_autopm_put_interface(intf); | |
85 | + return retval; | |
86 | +} | |
87 | + | |
88 | +static int usb_port_runtime_suspend(struct device *dev) | |
89 | +{ | |
90 | + struct usb_port *port_dev = to_usb_port(dev); | |
91 | + struct usb_device *hdev = to_usb_device(dev->parent->parent); | |
92 | + struct usb_interface *intf = to_usb_interface(dev->parent); | |
93 | + int retval; | |
94 | + | |
95 | + if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF) | |
96 | + == PM_QOS_FLAGS_ALL) | |
97 | + return -EAGAIN; | |
98 | + | |
99 | + usb_autopm_get_interface(intf); | |
100 | + retval = usb_hub_set_port_power(hdev, port_dev->portnum, false); | |
101 | + usb_autopm_put_interface(intf); | |
102 | + return retval; | |
103 | +} | |
104 | +#endif | |
105 | + | |
106 | +static const struct dev_pm_ops usb_port_pm_ops = { | |
107 | +#ifdef CONFIG_USB_SUSPEND | |
108 | + .runtime_suspend = usb_port_runtime_suspend, | |
109 | + .runtime_resume = usb_port_runtime_resume, | |
110 | + .runtime_idle = pm_generic_runtime_idle, | |
111 | +#endif | |
112 | +}; | |
113 | + | |
73 | 114 | struct device_type usb_port_device_type = { |
74 | 115 | .name = "usb_port", |
75 | 116 | .release = usb_port_device_release, |
117 | + .pm = &usb_port_pm_ops, | |
76 | 118 | }; |
77 | 119 | |
78 | 120 | int usb_hub_create_port_device(struct usb_hub *hub, int port1) |
... | ... | @@ -87,6 +129,7 @@ |
87 | 129 | } |
88 | 130 | |
89 | 131 | hub->ports[port1 - 1] = port_dev; |
132 | + port_dev->portnum = port1; | |
90 | 133 | port_dev->dev.parent = hub->intfdev; |
91 | 134 | port_dev->dev.groups = port_dev_group; |
92 | 135 | port_dev->dev.type = &usb_port_device_type; |
... | ... | @@ -95,6 +138,9 @@ |
95 | 138 | retval = device_register(&port_dev->dev); |
96 | 139 | if (retval) |
97 | 140 | goto error_register; |
141 | + | |
142 | + pm_runtime_set_active(&port_dev->dev); | |
143 | + pm_runtime_enable(&port_dev->dev); | |
98 | 144 | |
99 | 145 | retval = usb_acpi_register_power_resources(&port_dev->dev); |
100 | 146 | if (retval && retval != -ENODEV) |