Linux-USB Archive on lore.kernel.org
 help / color / Atom feed
From: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
To: linux-bluetooth@vger.kernel.org, linux-usb@vger.kernel.org
Cc: dianders@chromium.org,
	Abhishek Pandit-Subedi <abhishekpandit@chromium.org>,
	Kai-Heng Feng <kai.heng.feng@canonical.com>,
	Alan Stern <stern@rowland.harvard.edu>,
	Hui Peng <benquike@gmail.com>,
	linux-pm@vger.kernel.org,
	Suzuki K Poulose <suzuki.poulose@arm.com>,
	Mark Brown <broonie@kernel.org>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Wolfram Sang <wsa@the-dreams.de>,
	linux-kernel@vger.kernel.org, Len Brown <len.brown@intel.com>,
	Mathias Payer <mathias.payer@nebelwelt.net>,
	Dmitry Torokhov <dtor@chromium.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Mans Rullgard <mans@mansr.com>, Pavel Machek <pavel@ucw.cz>,
	YueHaibing <yuehaibing@huawei.com>
Subject: [PATCH 1/2] usb: support suspend_noirq
Date: Tue, 17 Sep 2019 14:27:01 -0700
Message-ID: <20190917212702.35747-2-abhishekpandit@chromium.org> (raw)
In-Reply-To: <20190917212702.35747-1-abhishekpandit@chromium.org>

If we put a usb device into reset in the suspend callback, it will
disconnect and the resume will not be called. In order to support
turning off the device on suspend and restoring it on resume, we must do
the reset action in suspend_noirq.

e.g. Undesirable behavior: bluetooth driver asserts reset in suspend, it
disconnects, the resume is never called

e.g. Desirable new behavior: bluetooth driver asserts reset in
suspend_noirq, device doesn't disconnect, resume is called where power
is restored and usb disconnects and reconnects device

Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
---

 drivers/usb/core/driver.c | 56 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/usb.c    |  6 +++++
 include/linux/pm.h        |  8 ++++++
 include/linux/usb.h       |  3 +++
 4 files changed, 73 insertions(+)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ebcadaad89d1..a7657d4e3177 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1207,6 +1207,26 @@ static int usb_suspend_interface(struct usb_device *udev,
 	return status;
 }
 
+/* Ignore errors during system sleep transitions so no return code */
+static void usb_suspend_interface_noirq(struct usb_device *udev,
+				       struct usb_interface *intf,
+				       pm_message_t msg)
+{
+	struct usb_driver	*driver;
+	int			status = 0;
+
+	if (udev->state == USB_STATE_NOTATTACHED ||
+			intf->condition == USB_INTERFACE_UNBOUND)
+		return;
+	driver = to_usb_driver(intf->dev.driver);
+
+	if (driver->suspend_noirq)
+		status = driver->suspend_noirq(intf, msg);
+
+	if (status)
+		dev_err(&intf->dev, "suspend_noirq error %d\n", status);
+}
+
 static int usb_resume_interface(struct usb_device *udev,
 		struct usb_interface *intf, pm_message_t msg, int reset_resume)
 {
@@ -1369,6 +1389,39 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 	return status;
 }
 
+/**
+ * usb_suspend_noirq - additional suspend activity without interrupt handlers
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * As a final step in suspend, we allow interfaces to take action without the
+ * interrupt handler enabled. For example, a driver may want to power down
+ * a device during suspend (to save power) and re-enable it on resume. Doing it
+ * in the noirq callback will make sure the resume runs (device doesn't get
+ * removed on suspend path) and the device can complete a reset.
+ *
+ * This routine can run only in process context.
+ *
+ * Return: 0 once complete.
+ */
+
+static int usb_suspend_noirq(struct usb_device *udev, pm_message_t msg)
+{
+	int			i = 0, n = 0;
+	struct usb_interface	*intf;
+
+	/* Run noirq callbacks on all interfaces */
+	if (udev->state != USB_STATE_NOTATTACHED && udev->actconfig) {
+		n = udev->actconfig->desc.bNumInterfaces;
+		for (i = n - 1; i >= 0; --i) {
+			intf = udev->actconfig->interface[i];
+			usb_suspend_interface_noirq(udev, intf, msg);
+		}
+	}
+
+	return 0;
+}
+
 /**
  * usb_resume_both - resume a USB device and its interfaces
  * @udev: the usb_device to resume
@@ -1455,6 +1508,9 @@ int usb_suspend(struct device *dev, pm_message_t msg)
 	struct usb_device	*udev = to_usb_device(dev);
 	int r;
 
+	if (PMSG_IS_NOIRQ(msg))
+		return usb_suspend_noirq(udev, msg);
+
 	unbind_no_pm_drivers_interfaces(udev);
 
 	/* From now on we are sure all drivers support suspend/resume
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0ab8738047da..a236477de50a 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -465,6 +465,11 @@ static int usb_dev_suspend(struct device *dev)
 	return usb_suspend(dev, PMSG_SUSPEND);
 }
 
+static int usb_dev_suspend_noirq(struct device *dev)
+{
+	return usb_suspend(dev, PMSG_SUSPEND_NOIRQ);
+}
+
 static int usb_dev_resume(struct device *dev)
 {
 	return usb_resume(dev, PMSG_RESUME);
@@ -494,6 +499,7 @@ static const struct dev_pm_ops usb_device_pm_ops = {
 	.prepare =	usb_dev_prepare,
 	.complete =	usb_dev_complete,
 	.suspend =	usb_dev_suspend,
+	.suspend_noirq =	usb_dev_suspend_noirq,
 	.resume =	usb_dev_resume,
 	.freeze =	usb_dev_freeze,
 	.thaw =		usb_dev_thaw,
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 3619a870eaa4..9f5e7899f076 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -410,6 +410,9 @@ const struct dev_pm_ops name = { \
  *		memory contents from a hibernation image has failed, call
  *		->thaw() and ->complete() for all devices.
  *
+ * SUSPEND_NOIRQ	System is going to suspend without interrupt handlers
+ *			enabled, call ->suspend_noirq() for all devices.
+ *
  * The following PM_EVENT_ messages are defined for internal use by
  * kernel subsystems.  They are never issued by the PM core.
  *
@@ -439,6 +442,7 @@ const struct dev_pm_ops name = { \
 #define PM_EVENT_USER		0x0100
 #define PM_EVENT_REMOTE		0x0200
 #define PM_EVENT_AUTO		0x0400
+#define PM_EVENT_NOIRQ		0x0800
 
 #define PM_EVENT_SLEEP		(PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
 #define PM_EVENT_USER_SUSPEND	(PM_EVENT_USER | PM_EVENT_SUSPEND)
@@ -446,6 +450,7 @@ const struct dev_pm_ops name = { \
 #define PM_EVENT_REMOTE_RESUME	(PM_EVENT_REMOTE | PM_EVENT_RESUME)
 #define PM_EVENT_AUTO_SUSPEND	(PM_EVENT_AUTO | PM_EVENT_SUSPEND)
 #define PM_EVENT_AUTO_RESUME	(PM_EVENT_AUTO | PM_EVENT_RESUME)
+#define PM_EVENT_SUSPEND_NOIRQ	(PM_EVENT_NOIRQ | PM_EVENT_SUSPEND)
 
 #define PMSG_INVALID	((struct pm_message){ .event = PM_EVENT_INVALID, })
 #define PMSG_ON		((struct pm_message){ .event = PM_EVENT_ON, })
@@ -457,6 +462,8 @@ const struct dev_pm_ops name = { \
 #define PMSG_THAW	((struct pm_message){ .event = PM_EVENT_THAW, })
 #define PMSG_RESTORE	((struct pm_message){ .event = PM_EVENT_RESTORE, })
 #define PMSG_RECOVER	((struct pm_message){ .event = PM_EVENT_RECOVER, })
+#define PMSG_SUSPEND_NOIRQ	((struct pm_message){ \
+					.event = PM_EVENT_SUSPEND_NOIRQ, })
 #define PMSG_USER_SUSPEND	((struct pm_message) \
 					{ .event = PM_EVENT_USER_SUSPEND, })
 #define PMSG_USER_RESUME	((struct pm_message) \
@@ -469,6 +476,7 @@ const struct dev_pm_ops name = { \
 					{ .event = PM_EVENT_AUTO_RESUME, })
 
 #define PMSG_IS_AUTO(msg)	(((msg).event & PM_EVENT_AUTO) != 0)
+#define PMSG_IS_NOIRQ(msg)	(((msg).event & PM_EVENT_NOIRQ) != 0)
 
 /*
  * Device run-time power management status.
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e87826e23d59..0e2079661ae6 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1139,6 +1139,8 @@ struct usbdrv_wrap {
  *	try to continue using the device if suspend fails in this case.
  *	Instead, let the resume or reset-resume routine recover from
  *	the failure.
+ * @suspend_noirq: Similar to suspend() but called with the usb interrupt
+ *	handler disabled.
  * @resume: Called when the device is being resumed by the system.
  * @reset_resume: Called when the suspended device has been reset instead
  *	of being resumed.
@@ -1191,6 +1193,7 @@ struct usb_driver {
 			void *buf);
 
 	int (*suspend) (struct usb_interface *intf, pm_message_t message);
+	int (*suspend_noirq)(struct usb_interface *intf, pm_message_t message);
 	int (*resume) (struct usb_interface *intf);
 	int (*reset_resume)(struct usb_interface *intf);
 
-- 
2.23.0.237.gc6a4ce50a0-goog


  reply index

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-17 21:27 [PATCH 0/2] Reset realtek bluetooth devices during user suspend Abhishek Pandit-Subedi
2019-09-17 21:27 ` Abhishek Pandit-Subedi [this message]
2019-10-04 11:58   ` [PATCH 1/2] usb: support suspend_noirq Greg Kroah-Hartman
2019-09-17 21:27 ` [PATCH 2/2] Bluetooth: btusb: Reset realtek devices on user suspend Abhishek Pandit-Subedi
2019-10-04 11:59   ` Greg KH
2019-09-18 14:23 ` [PATCH 0/2] Reset realtek bluetooth devices during " Alan Stern
2019-09-18 17:19   ` Abhishek Pandit-Subedi
2019-09-18 18:51     ` Alan Stern
2019-09-26 20:51       ` Abhishek Pandit-Subedi
2019-09-18 14:41 ` Oliver Neukum

Reply instructions:

You may reply publically to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190917212702.35747-2-abhishekpandit@chromium.org \
    --to=abhishekpandit@chromium.org \
    --cc=benquike@gmail.com \
    --cc=broonie@kernel.org \
    --cc=dianders@chromium.org \
    --cc=dtor@chromium.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=kai.heng.feng@canonical.com \
    --cc=len.brown@intel.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mans@mansr.com \
    --cc=mathias.payer@nebelwelt.net \
    --cc=pavel@ucw.cz \
    --cc=rjw@rjwysocki.net \
    --cc=stern@rowland.harvard.edu \
    --cc=suzuki.poulose@arm.com \
    --cc=wsa@the-dreams.de \
    --cc=yuehaibing@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Linux-USB Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-usb/0 linux-usb/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-usb linux-usb/ https://lore.kernel.org/linux-usb \
		linux-usb@vger.kernel.org
	public-inbox-index linux-usb

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-usb


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git