[PATCH 3/4] usb: Add usb port system pm support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch is to add usb port system pm support. Add
usb port's system suspend/resume callbacks and call
usb_port_runtime_resume/suspend() to power off these
ports whose pm qos NO_POWER_OFF flag is not set, system
wakeup is disabled and persist is enalbed.

During system pm, usb port should be powered off after
dev being suspended and powered on before dev being
resumed. Since usb ports and devs enable async suspend,
call device_pm_wait_for_dev() in the usb_port_system_suspend()
and usb_port_resume() to keeping the order.

If usb port was already powered off by runtime pm with
port_dev->power_is_on being false, usb_port_system_suspend()
returns directly.

If usb port was not powered off during system suspend with
port_dev->power_is_on being true, usb_port_system_resume()
returns directly.

Signed-off-by: Lan Tianyu <tianyu.lan@xxxxxxxxx>
---
 drivers/usb/core/hub.c  |    4 ++++
 drivers/usb/core/port.c |   49 ++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index c228e69..8c2562e 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3166,6 +3166,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 	int		status;
 	u16		portchange, portstatus;
 
+	/* Wait for usb port system resume finishing */
+	if (!PMSG_IS_AUTO(msg))
+		device_pm_wait_for_dev(&udev->dev, &port_dev->dev);
+
 	if (port_dev->did_runtime_put) {
 		status = pm_runtime_get_sync(&port_dev->dev);
 		port_dev->did_runtime_put = false;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 797f9d5..f14fc72 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -71,7 +71,7 @@ static void usb_port_device_release(struct device *dev)
 	kfree(port_dev);
 }
 
-#ifdef CONFIG_USB_SUSPEND
+#ifdef CONFIG_PM
 static int usb_port_runtime_resume(struct device *dev)
 {
 	struct usb_port *port_dev = to_usb_port(dev);
@@ -131,25 +131,68 @@ static int usb_port_runtime_suspend(struct device *dev)
 	set_bit(port1, hub->busy_bits);
 	retval = usb_hub_set_port_power(hdev, port1, false);
 	usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
-	usb_clear_port_feature(hdev, port1,	USB_PORT_FEAT_C_ENABLE);
+	usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
 	clear_bit(port1, hub->busy_bits);
 	usb_autopm_put_interface(intf);
 	return retval;
 }
-#endif
+
+static int usb_port_system_suspend(struct device *dev)
+{
+	struct usb_port *port_dev = to_usb_port(dev);
+
+	if (!port_dev->power_is_on)
+		return 0;
+
+	if (port_dev->child) {
+		struct usb_device *udev = port_dev->child;
+
+		/*
+		 * usb port can't be powered off when dev's system
+		 * wakeup is enabled or persist is disabled.
+		 */
+		if (device_may_wakeup(&udev->dev)
+				|| !udev->persist_enabled)
+			return 0;
+
+		/*
+		 * usb port should be powered off after usb dev
+		 * being suspended.
+		 */
+		device_pm_wait_for_dev(dev, &port_dev->child->dev);
+	}
+
+	usb_port_runtime_suspend(dev);
+	return 0;
+}
+
+static int usb_port_system_resume(struct device *dev)
+{
+	struct usb_port *port_dev = to_usb_port(dev);
+
+	if (port_dev->power_is_on)
+		return 0;
+
+	return usb_port_runtime_resume(dev);
+}
 
 static const struct dev_pm_ops usb_port_pm_ops = {
+	.suspend =	usb_port_system_suspend,
+	.resume =	usb_port_system_resume,
 #ifdef CONFIG_USB_SUSPEND
 	.runtime_suspend =	usb_port_runtime_suspend,
 	.runtime_resume =	usb_port_runtime_resume,
 	.runtime_idle =		pm_generic_runtime_idle,
 #endif
 };
+#endif
 
 struct device_type usb_port_device_type = {
 	.name =		"usb_port",
 	.release =	usb_port_device_release,
+#if CONFIG_PM
 	.pm =		&usb_port_pm_ops,
+#endif
 };
 
 int usb_hub_create_port_device(struct usb_hub *hub, int port1)
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux