linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] usb: roles: Add PM callbacks
@ 2019-07-08  2:25 Chen, Hu
  2019-07-08  8:30 ` Sergei Shtylyov
  2019-07-08 14:10 ` Hans de Goede
  0 siblings, 2 replies; 4+ messages in thread
From: Chen, Hu @ 2019-07-08  2:25 UTC (permalink / raw)
  To: hdegoede; +Cc: hu1.chen, Balaji, Greg Kroah-Hartman, linux-usb, linux-kernel

On some Broxton NUC, the usb role is lost after S3 (it becomes "none").
Add PM callbacks to address this issue: save the role during suspend and
restore usb to that role during resume.

Test:
Run Android on UC6CAY, a NUC powered by Broxton. Access this NUC via
"adb shell" from a host PC. After a suspend/resume cycle, the adb still
works well.

Signed-off-by: Chen, Hu <hu1.chen@intel.com>
Signed-off-by: Balaji <m.balaji@intel.com>

diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 277de96181f9..caa1cfab41cc 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -37,6 +37,7 @@
 struct intel_xhci_usb_data {
 	struct usb_role_switch *role_sw;
 	void __iomem *base;
+	enum usb_role role;
 };
 
 static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
@@ -167,6 +168,30 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int intel_xhci_usb_suspend(struct platform_device *pdev,
+				  pm_message_t state)
+{
+	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	data->role = intel_xhci_usb_get_role(dev);
+
+	return 0;
+}
+
+static int intel_xhci_usb_resume(struct platform_device *pdev)
+{
+	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	if (intel_xhci_usb_get_role(dev) != data->role) {
+		if (intel_xhci_usb_set_role(dev, data->role) != 0)
+			dev_warn(dev, "Failed to set role during resume\n");
+	}
+
+	return 0;
+}
+
 static const struct platform_device_id intel_xhci_usb_table[] = {
 	{ .name = DRV_NAME },
 	{}
@@ -180,6 +205,8 @@ static struct platform_driver intel_xhci_usb_driver = {
 	.id_table = intel_xhci_usb_table,
 	.probe = intel_xhci_usb_probe,
 	.remove = intel_xhci_usb_remove,
+	.suspend = intel_xhci_usb_suspend,
+	.resume = intel_xhci_usb_resume,
 };
 
 module_platform_driver(intel_xhci_usb_driver);
-- 
2.22.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] usb: roles: Add PM callbacks
  2019-07-08  2:25 [PATCH] usb: roles: Add PM callbacks Chen, Hu
@ 2019-07-08  8:30 ` Sergei Shtylyov
  2019-07-08 14:10 ` Hans de Goede
  1 sibling, 0 replies; 4+ messages in thread
From: Sergei Shtylyov @ 2019-07-08  8:30 UTC (permalink / raw)
  To: Chen, Hu, hdegoede; +Cc: Balaji, Greg Kroah-Hartman, linux-usb, linux-kernel

Hello!

On 08.07.2019 5:25, Chen, Hu wrote:

> On some Broxton NUC, the usb role is lost after S3 (it becomes "none").
> Add PM callbacks to address this issue: save the role during suspend and
> restore usb to that role during resume.
> 
> Test:
> Run Android on UC6CAY, a NUC powered by Broxton. Access this NUC via
> "adb shell" from a host PC. After a suspend/resume cycle, the adb still
> works well.
> 
> Signed-off-by: Chen, Hu <hu1.chen@intel.com>
> Signed-off-by: Balaji <m.balaji@intel.com>
> 
> diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
> index 277de96181f9..caa1cfab41cc 100644
> --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
> +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
[...]
> @@ -167,6 +168,30 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
>   	return 0;
>   }
>   
> +static int intel_xhci_usb_suspend(struct platform_device *pdev,
> +				  pm_message_t state)
> +{
> +	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
> +	struct device *dev = &pdev->dev;
> +
> +	data->role = intel_xhci_usb_get_role(dev);

    Why not just pass &pdev->dev here?

> +
> +	return 0;
> +}
> +
[...]

MBR, Sergei

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] usb: roles: Add PM callbacks
  2019-07-08  2:25 [PATCH] usb: roles: Add PM callbacks Chen, Hu
  2019-07-08  8:30 ` Sergei Shtylyov
@ 2019-07-08 14:10 ` Hans de Goede
  2019-07-09 10:30   ` [PATCH v2] " Chen, Hu
  1 sibling, 1 reply; 4+ messages in thread
From: Hans de Goede @ 2019-07-08 14:10 UTC (permalink / raw)
  To: Chen, Hu; +Cc: Balaji, Greg Kroah-Hartman, linux-usb, linux-kernel

Hi,

On 08-07-19 04:25, Chen, Hu wrote:
> On some Broxton NUC, the usb role is lost after S3 (it becomes "none").
> Add PM callbacks to address this issue: save the role during suspend and
> restore usb to that role during resume.
> 
> Test:
> Run Android on UC6CAY, a NUC powered by Broxton. Access this NUC via
> "adb shell" from a host PC. After a suspend/resume cycle, the adb still
> works well.
> 
> Signed-off-by: Chen, Hu <hu1.chen@intel.com>
> Signed-off-by: Balaji <m.balaji@intel.com>

Hmm, but what if the user has say unplugged a host-adapter with a usb-stick
in there and plugged in a cable to a computer *while suspended* because the
battery was getting low?

I see 2 scenarios here:

1) We may get an interrupt for the role change, followed by an
intel_xhci_usb_set_role. In this case the interrupt handling may happen
before the intel_xhci_usb_resume call and we would end up restoring the
wrong value

2) The role may be changed underneath us by the firmware / AML code
without us ever becoming aware of this.

For the specific problem discussed in the commit message it might be better
to do something like:

static int intel_xhci_usb_resume(struct platform_device *pdev)
{
	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
	struct device *dev = &pdev->dev;

	if (intel_xhci_usb_get_role(dev) == USB_ROLE_NONE &&
	    power_supply_is_system_supplied())
		intel_xhci_usb_set_role(dev, USB_ROLE_DEVICE);

	return 0;
}

But that is somewhat specific for solving the problem described in
the commit message, but it has the advantage of not being able to
cause any regressions (that I can think of).

Regards,

Hans





> 
> diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
> index 277de96181f9..caa1cfab41cc 100644
> --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
> +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
> @@ -37,6 +37,7 @@
>   struct intel_xhci_usb_data {
>   	struct usb_role_switch *role_sw;
>   	void __iomem *base;
> +	enum usb_role role;
>   };
>   
>   static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
> @@ -167,6 +168,30 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
>   	return 0;
>   }
>   
> +static int intel_xhci_usb_suspend(struct platform_device *pdev,
> +				  pm_message_t state)
> +{
> +	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
> +	struct device *dev = &pdev->dev;
> +
> +	data->role = intel_xhci_usb_get_role(dev);
> +
> +	return 0;
> +}
> +
> +static int intel_xhci_usb_resume(struct platform_device *pdev)
> +{
> +	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
> +	struct device *dev = &pdev->dev;
> +
> +	if (intel_xhci_usb_get_role(dev) != data->role) {
> +		if (intel_xhci_usb_set_role(dev, data->role) != 0)
> +			dev_warn(dev, "Failed to set role during resume\n");
> +	}
> +
> +	return 0;
> +}
> +
>   static const struct platform_device_id intel_xhci_usb_table[] = {
>   	{ .name = DRV_NAME },
>   	{}
> @@ -180,6 +205,8 @@ static struct platform_driver intel_xhci_usb_driver = {
>   	.id_table = intel_xhci_usb_table,
>   	.probe = intel_xhci_usb_probe,
>   	.remove = intel_xhci_usb_remove,
> +	.suspend = intel_xhci_usb_suspend,
> +	.resume = intel_xhci_usb_resume,
>   };
>   
>   module_platform_driver(intel_xhci_usb_driver);
> 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2] usb: roles: Add PM callbacks
  2019-07-08 14:10 ` Hans de Goede
@ 2019-07-09 10:30   ` Chen, Hu
  0 siblings, 0 replies; 4+ messages in thread
From: Chen, Hu @ 2019-07-09 10:30 UTC (permalink / raw)
  To: hdegoede; +Cc: hu1.chen, m.balaji, Greg Kroah-Hartman, linux-usb, linux-kernel

On some Broxton NUC, the usb role is lost after S3 (it becomes "none").
Add PM callbacks to address this. In .suspend, save the role. In
.resume, restore usb role to saved one if it's lost.

Tests:
Run Android on UC6CAY, a NUC powered by Broxton. The usb port near to
the power button is switchable.
- as device. Access the NUC via "adb shell" from a host PC. Adb works
after S3 cycle.
- as host. Mouse works on that usb port after S3 cycle.

Without this fix, both of tests fails.

Signed-off-by: Chen, Hu <hu1.chen@intel.com>
Signed-off-by: Balaji <m.balaji@intel.com>
---
v2: The role maybe changed by ISR or firmware etc. between .suspend and
.resume (Hans).
 .../usb/roles/intel-xhci-usb-role-switch.c    | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c
index 277de96181f9..e830299c401e 100644
--- a/drivers/usb/roles/intel-xhci-usb-role-switch.c
+++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/usb/role.h>
+#include <linux/power_supply.h>
 
 /* register definition */
 #define DUAL_ROLE_CFG0			0x68
@@ -37,6 +38,7 @@
 struct intel_xhci_usb_data {
 	struct usb_role_switch *role_sw;
 	void __iomem *base;
+	enum usb_role role;
 };
 
 static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
@@ -167,6 +169,29 @@ static int intel_xhci_usb_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int intel_xhci_usb_suspend(struct platform_device *pdev,
+				  pm_message_t state)
+{
+	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	data->role = intel_xhci_usb_get_role(dev);
+
+	return 0;
+}
+
+static int intel_xhci_usb_resume(struct platform_device *pdev)
+{
+	struct intel_xhci_usb_data *data = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+
+	if (intel_xhci_usb_get_role(dev) == USB_ROLE_NONE &&
+	    power_supply_is_system_supplied())
+		intel_xhci_usb_set_role(dev, data->role);
+
+	return 0;
+}
+
 static const struct platform_device_id intel_xhci_usb_table[] = {
 	{ .name = DRV_NAME },
 	{}
@@ -180,6 +205,8 @@ static struct platform_driver intel_xhci_usb_driver = {
 	.id_table = intel_xhci_usb_table,
 	.probe = intel_xhci_usb_probe,
 	.remove = intel_xhci_usb_remove,
+	.suspend = intel_xhci_usb_suspend,
+	.resume = intel_xhci_usb_resume,
 };
 
 module_platform_driver(intel_xhci_usb_driver);
-- 
2.22.0


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-07-09 10:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-08  2:25 [PATCH] usb: roles: Add PM callbacks Chen, Hu
2019-07-08  8:30 ` Sergei Shtylyov
2019-07-08 14:10 ` Hans de Goede
2019-07-09 10:30   ` [PATCH v2] " Chen, Hu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).