RE: USB port power-off and system suspend

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

 



On 2013/3/23 1:35, Alan Stern wrote:> Tianyu:
> 
> Did you ever test the port power-off mechanism with system suspend?
> Right now it doesn't seem like it would work, because it relies on
> runtime PM to turn off the port power, and runtime PM doesn't operate
> normally during a system suspend.
> 
> Alan Stern
> 

Hi Alan:
	You are right. Current the port power-off can't work during
system suspend. I have already worked on a patch of adding usb
port system pm support. But there is a still problem that how to
control power off mechanism from kernel side. Before, we have a solution
for runtime pm that increase/decrease port dev's usage count.
	http://marc.info/?l=linux-usb&m=135772122031446&w=2
But now, we have another user, System pm. So I think we should find
a solution to cover these two cases. Maybe add a new pm qos request
for NO_POWER_OFF flag? I'd like to see your opinion. 

I have a patch that can work and it still needs to add some condition
checks in the usb_port_system_suspend(). Dev's system wakeup disable
and persist enable.
 
>From c3abd373707910672d6fd2e96da78879b3e22417 Mon Sep 17 00:00:00 2001
From: Lan Tianyu <tianyu.lan@xxxxxxxxx>
Date: Tue, 26 Feb 2013 11:12:09 +0800
Subject: [PATCH] usb: Add usb port system pm support

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 without pm qos NO_POWER_OFF flag setting.

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

Usb_port_system_suspend() ignores EAGAIN from usb_port_runtime_suspend().
EAGAIN is caused by pm qos NO_POWER_OFF setting. This is
not an error for usb port system pm.
---
 drivers/usb/core/hub.c  |    4 ++++
 drivers/usb/core/port.c |   31 +++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 53c1555..8868f15 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3162,6 +3162,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..e5c6ec5 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -84,6 +84,9 @@ static int usb_port_runtime_resume(struct device *dev)
 	if (!hub)
 		return -EINVAL;
 
+	if (port_dev->power_is_on)
+		return 0;
+
 	usb_autopm_get_interface(intf);
 	set_bit(port1, hub->busy_bits);
 
@@ -127,6 +130,9 @@ static int usb_port_runtime_suspend(struct device *dev)
 			== PM_QOS_FLAGS_ALL)
 		return -EAGAIN;
 
+	if (!port_dev->power_is_on)
+		return 0;
+
 	usb_autopm_get_interface(intf);
 	set_bit(port1, hub->busy_bits);
 	retval = usb_hub_set_port_power(hdev, port1, false);
@@ -136,9 +142,34 @@ static int usb_port_runtime_suspend(struct device *dev)
 	usb_autopm_put_interface(intf);
 	return retval;
 }
+#else
+static int usb_port_runtime_suspend(struct device *dev) { return 0; }
+static int usb_port_runtime_resume(struct device *dev) { return 0; }
 #endif
 
+static int usb_port_system_suspend(struct device *dev)
+{
+	struct usb_port *port_dev = to_usb_port(dev);
+	int retval;
+
+	if (port_dev->child)
+		device_pm_wait_for_dev(dev, &port_dev->child->dev);
+
+	retval = usb_port_runtime_suspend(dev);
+	if (retval < 0 && retval != -EAGAIN)
+		return retval;
+
+	return 0;
+}
+
+static int usb_port_system_resume(struct device *dev)
+{
+	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,
-- 
1.7.10.4



-- 
Best Regards
Tianyu Lan
linux kernel enabling team
--
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