aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2013-01-22 15:26:29 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-25 13:12:19 -0500
commit971fcd492cebf544714f12d94549d2f0d2002645 (patch)
tree553a48486114b3b3b1507cf5dc391f24a464912f /drivers/usb/core
parent6802771bba0455a751d8f4ece7587585be3eaa2f (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.c27
-rw-r--r--drivers/usb/core/hub.h4
-rw-r--r--drivers/usb/core/port.c46
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 */
726int 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 */
83struct usb_port { 84struct 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);
95extern void usb_hub_remove_port_device(struct usb_hub *hub, 97extern void usb_hub_remove_port_device(struct usb_hub *hub,
96 int port1); 98 int port1);
99extern 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
75static 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
88static 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
106static 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
73struct device_type usb_port_device_type = { 114struct 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
78int usb_hub_create_port_device(struct usb_hub *hub, int port1) 120int 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");