summaryrefslogtreecommitdiff
path: root/drivers/usb/core/port.c
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2013-01-22 20:26:29 (GMT)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-01-25 18:12:19 (GMT)
commit971fcd492cebf544714f12d94549d2f0d2002645 (patch)
tree553a48486114b3b3b1507cf5dc391f24a464912f /drivers/usb/core/port.c
parent6802771bba0455a751d8f4ece7587585be3eaa2f (diff)
downloadlinux-971fcd492cebf544714f12d94549d2f0d2002645.tar.xz
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/port.c')
-rw-r--r--drivers/usb/core/port.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 153e799..d288dfe 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -17,6 +17,7 @@
*/
#include <linux/slab.h>
+#include <linux/pm_qos.h>
#include "hub.h"
@@ -70,9 +71,50 @@ static void usb_port_device_release(struct device *dev)
kfree(port_dev);
}
+#ifdef CONFIG_USB_SUSPEND
+static int usb_port_runtime_resume(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ int retval;
+
+ usb_autopm_get_interface(intf);
+ retval = usb_hub_set_port_power(hdev, port_dev->portnum, true);
+ usb_autopm_put_interface(intf);
+ return retval;
+}
+
+static int usb_port_runtime_suspend(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ int retval;
+
+ if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
+ == PM_QOS_FLAGS_ALL)
+ return -EAGAIN;
+
+ usb_autopm_get_interface(intf);
+ retval = usb_hub_set_port_power(hdev, port_dev->portnum, false);
+ usb_autopm_put_interface(intf);
+ return retval;
+}
+#endif
+
+static const struct dev_pm_ops usb_port_pm_ops = {
+#ifdef CONFIG_USB_SUSPEND
+ .runtime_suspend = usb_port_runtime_suspend,
+ .runtime_resume = usb_port_runtime_resume,
+ .runtime_idle = pm_generic_runtime_idle,
+#endif
+};
+
struct device_type usb_port_device_type = {
.name = "usb_port",
.release = usb_port_device_release,
+ .pm = &usb_port_pm_ops,
};
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)
}
hub->ports[port1 - 1] = port_dev;
+ port_dev->portnum = port1;
port_dev->dev.parent = hub->intfdev;
port_dev->dev.groups = port_dev_group;
port_dev->dev.type = &usb_port_device_type;
@@ -96,6 +139,9 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
if (retval)
goto error_register;
+ pm_runtime_set_active(&port_dev->dev);
+ pm_runtime_enable(&port_dev->dev);
+
retval = usb_acpi_register_power_resources(&port_dev->dev);
if (retval && retval != -ENODEV)
dev_warn(&port_dev->dev, "the port can't register its ACPI power resource.\n");