diff options
author | Lan Tianyu <tianyu.lan@intel.com> | 2013-01-22 15:26:29 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-25 13:12:19 -0500 |
commit | 971fcd492cebf544714f12d94549d2f0d2002645 (patch) | |
tree | 553a48486114b3b3b1507cf5dc391f24a464912f /drivers/usb/core | |
parent | 6802771bba0455a751d8f4ece7587585be3eaa2f (diff) |
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>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/hub.c | 27 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 4 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 46 |
3 files changed, 77 insertions, 0 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 29ca6ed3bea8..7fb163365d02 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -715,6 +715,27 @@ static void hub_tt_work(struct work_struct *work) | |||
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 | * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub | 739 | * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub |
719 | * @urb: an URB associated with the failed or incomplete split transaction | 740 | * @urb: an URB associated with the failed or incomplete split transaction |
720 | * | 741 | * |
@@ -1569,6 +1590,7 @@ static void hub_disconnect(struct usb_interface *intf) | |||
1569 | kfree(hub->status); | 1590 | kfree(hub->status); |
1570 | kfree(hub->buffer); | 1591 | kfree(hub->buffer); |
1571 | 1592 | ||
1593 | pm_suspend_ignore_children(&intf->dev, false); | ||
1572 | kref_put(&hub->kref, hub_release); | 1594 | kref_put(&hub->kref, hub_release); |
1573 | } | 1595 | } |
1574 | 1596 | ||
@@ -1671,6 +1693,7 @@ descriptor_error: | |||
1671 | 1693 | ||
1672 | usb_set_intfdata (intf, hub); | 1694 | usb_set_intfdata (intf, hub); |
1673 | intf->needs_remote_wakeup = 1; | 1695 | intf->needs_remote_wakeup = 1; |
1696 | pm_suspend_ignore_children(&intf->dev, true); | ||
1674 | 1697 | ||
1675 | if (hdev->speed == USB_SPEED_HIGH) | 1698 | if (hdev->speed == USB_SPEED_HIGH) |
1676 | highspeed_hubs++; | 1699 | highspeed_hubs++; |
@@ -1997,6 +2020,8 @@ void usb_disconnect(struct usb_device **pdev) | |||
1997 | 2020 | ||
1998 | sysfs_remove_link(&udev->dev.kobj, "port"); | 2021 | sysfs_remove_link(&udev->dev.kobj, "port"); |
1999 | sysfs_remove_link(&port_dev->dev.kobj, "device"); | 2022 | sysfs_remove_link(&port_dev->dev.kobj, "device"); |
2023 | |||
2024 | pm_runtime_put(&port_dev->dev); | ||
2000 | } | 2025 | } |
2001 | 2026 | ||
2002 | usb_remove_ep_devs(&udev->ep0); | 2027 | usb_remove_ep_devs(&udev->ep0); |
@@ -2307,6 +2332,8 @@ int usb_new_device(struct usb_device *udev) | |||
2307 | sysfs_remove_link(&udev->dev.kobj, "port"); | 2332 | sysfs_remove_link(&udev->dev.kobj, "port"); |
2308 | goto fail; | 2333 | goto fail; |
2309 | } | 2334 | } |
2335 | |||
2336 | pm_runtime_get_sync(&port_dev->dev); | ||
2310 | } | 2337 | } |
2311 | 2338 | ||
2312 | (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); | 2339 | (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev); |
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index c472058f8f27..452e5cd7b249 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h | |||
@@ -79,12 +79,14 @@ struct usb_hub { | |||
79 | * @dev: generic device interface | 79 | * @dev: generic device interface |
80 | * @port_owner: port's owner | 80 | * @port_owner: port's owner |
81 | * @connect_type: port's connect type | 81 | * @connect_type: port's connect type |
82 | * @portnum: port index num based one | ||
82 | */ | 83 | */ |
83 | struct usb_port { | 84 | struct usb_port { |
84 | struct usb_device *child; | 85 | struct usb_device *child; |
85 | struct device dev; | 86 | struct device dev; |
86 | struct dev_state *port_owner; | 87 | struct dev_state *port_owner; |
87 | enum usb_port_connect_type connect_type; | 88 | enum usb_port_connect_type connect_type; |
89 | u8 portnum; | ||
88 | }; | 90 | }; |
89 | 91 | ||
90 | #define to_usb_port(_dev) \ | 92 | #define to_usb_port(_dev) \ |
@@ -94,4 +96,6 @@ extern int usb_hub_create_port_device(struct usb_hub *hub, | |||
94 | int port1); | 96 | int port1); |
95 | extern void usb_hub_remove_port_device(struct usb_hub *hub, | 97 | extern void usb_hub_remove_port_device(struct usb_hub *hub, |
96 | int port1); | 98 | int port1); |
99 | extern int usb_hub_set_port_power(struct usb_device *hdev, | ||
100 | int port1, bool set); | ||
97 | 101 | ||
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 153e799e7320..d288dfed6ccf 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c | |||
@@ -17,6 +17,7 @@ | |||
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/pm_qos.h> | ||
20 | 21 | ||
21 | #include "hub.h" | 22 | #include "hub.h" |
22 | 23 | ||
@@ -70,9 +71,50 @@ static void usb_port_device_release(struct device *dev) | |||
70 | kfree(port_dev); | 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 | struct device_type usb_port_device_type = { | 114 | struct device_type usb_port_device_type = { |
74 | .name = "usb_port", | 115 | .name = "usb_port", |
75 | .release = usb_port_device_release, | 116 | .release = usb_port_device_release, |
117 | .pm = &usb_port_pm_ops, | ||
76 | }; | 118 | }; |
77 | 119 | ||
78 | int usb_hub_create_port_device(struct usb_hub *hub, int port1) | 120 | int usb_hub_create_port_device(struct usb_hub *hub, int port1) |
@@ -87,6 +129,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) | |||
87 | } | 129 | } |
88 | 130 | ||
89 | hub->ports[port1 - 1] = port_dev; | 131 | hub->ports[port1 - 1] = port_dev; |
132 | port_dev->portnum = port1; | ||
90 | port_dev->dev.parent = hub->intfdev; | 133 | port_dev->dev.parent = hub->intfdev; |
91 | port_dev->dev.groups = port_dev_group; | 134 | port_dev->dev.groups = port_dev_group; |
92 | port_dev->dev.type = &usb_port_device_type; | 135 | port_dev->dev.type = &usb_port_device_type; |
@@ -96,6 +139,9 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) | |||
96 | if (retval) | 139 | if (retval) |
97 | goto error_register; | 140 | goto error_register; |
98 | 141 | ||
142 | pm_runtime_set_active(&port_dev->dev); | ||
143 | pm_runtime_enable(&port_dev->dev); | ||
144 | |||
99 | retval = usb_acpi_register_power_resources(&port_dev->dev); | 145 | retval = usb_acpi_register_power_resources(&port_dev->dev); |
100 | if (retval && retval != -ENODEV) | 146 | if (retval && retval != -ENODEV) |
101 | dev_warn(&port_dev->dev, "the port can't register its ACPI power resource.\n"); | 147 | dev_warn(&port_dev->dev, "the port can't register its ACPI power resource.\n"); |