[PATCH v2 4/4] usb: move hub power management routines to hub_pm.c

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

 



A rather asymmetric response to the compile breakage reported by -next
and the kbuild robot:

     drivers/usb/core/hub.c: In function 'port_event':
     drivers/usb/core/hub.c:4853:2: error: implicit declaration of function
     'hub_handle_remote_wakeup' [-Werror=implicit-function-declaration]

     Caused by commits af376a461cf0 ("usb: refactor port handling in
     hub_events()") and 7e73be227b15 ("usb: hub_handle_remote_wakeup()
     depends on CONFIG_PM_RUNTIME=y") This build has CONFIG_PM not set ...

Move all code in hub.c that is #ifdef CONFIG_PM guarded to hub_pm.c.  It
ends up needing to mark 11 routines in hub.c as global so that hub_pm.c
can consume them.  It's not perfect as it still requires #ifdef
CONFIG_PM_RUNTIME in hub_pm.c itself, but certainly gets us closer to
the "no ifdef in .c files" ideal.

Alan: "The general idea seems okay."
http://marc.info/?l=linux-usb&m=140146126207527&w=2

Reported-by: kbuild test robot <fengguang.wu@xxxxxxxxx>
Reported-by: Stephen Rothwell <sfr@xxxxxxxxxxxxxxxx>
Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 drivers/usb/core/Makefile |    1 
 drivers/usb/core/hub.c    | 1284 ++-------------------------------------------
 drivers/usb/core/hub.h    |   48 ++
 drivers/usb/core/hub_pm.c | 1130 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/usb.h    |   41 +
 include/linux/usb.h       |    9 
 6 files changed, 1273 insertions(+), 1240 deletions(-)
 create mode 100644 drivers/usb/core/hub_pm.c

diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 2f6f93220046..46c0d1f6c4bb 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -9,5 +9,6 @@ usbcore-y += port.o
 
 usbcore-$(CONFIG_PCI)		+= hcd-pci.o
 usbcore-$(CONFIG_ACPI)		+= usb-acpi.o
+usbcore-$(CONFIG_PM)		+= hub_pm.o
 
 obj-$(CONFIG_USB)		+= usbcore.o
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 63b1963620a3..afc5328c4df1 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -26,7 +26,6 @@
 #include <linux/mutex.h>
 #include <linux/freezer.h>
 #include <linux/random.h>
-#include <linux/pm_qos.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -104,8 +103,6 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 #define HUB_DEBOUNCE_STEP	  25
 #define HUB_DEBOUNCE_STABLE	 100
 
-static int usb_reset_and_verify_device(struct usb_device *udev);
-
 static inline char *portspeed(struct usb_hub *hub, int portstatus)
 {
 	if (hub_is_superspeed(hub->hdev))
@@ -403,7 +400,7 @@ int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature)
 /*
  * USB 2.0 spec Section 11.24.2.13
  */
-static int set_port_feature(struct usb_device *hdev, int port1, int feature)
+int usb_set_port_feature(struct usb_device *hdev, int port1, int feature)
 {
 	return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
 		USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1,
@@ -435,7 +432,7 @@ static void set_port_led(struct usb_hub *hub, int port1, int selector)
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	int status;
 
-	status = set_port_feature(hub->hdev, (selector << 8) | port1,
+	status = usb_set_port_feature(hub->hdev, (selector << 8) | port1,
 			USB_PORT_FEAT_INDICATOR);
 	dev_dbg(&port_dev->dev, "indicator %s status %d\n",
 		to_led_name(selector), status);
@@ -552,7 +549,7 @@ static int get_port_status(struct usb_device *hdev, int port1,
 	return status;
 }
 
-static int hub_port_status(struct usb_hub *hub, int port1,
+int usb_hub_port_status(struct usb_hub *hub, int port1,
 		u16 *status, u16 *change)
 {
 	int ret;
@@ -748,7 +745,7 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub,
 	int ret;
 
 	if (set)
-		ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+		ret = usb_set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 	else
 		ret = usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
@@ -834,7 +831,7 @@ static void hub_power_on(struct usb_hub *hub, bool do_delay)
 				"non-switchable hub\n");
 	for (port1 = 1; port1 <= hub->hdev->maxchild; port1++)
 		if (test_bit(port1, hub->power_bits))
-			set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
+			usb_set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
 		else
 			usb_clear_port_feature(hub->hdev, port1,
 						USB_PORT_FEAT_POWER);
@@ -862,10 +859,10 @@ static int hub_hub_status(struct usb_hub *hub,
 	return ret;
 }
 
-static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+int usb_hub_set_port_link_state(struct usb_hub *hub, int port1,
 			unsigned int link_status)
 {
-	return set_port_feature(hub->hdev,
+	return usb_set_port_feature(hub->hdev,
 			port1 | (link_status << 3),
 			USB_PORT_FEAT_LINK_STATE);
 }
@@ -889,13 +886,13 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
 	if (!hub_is_superspeed(hub->hdev))
 		return -EINVAL;
 
-	ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+	ret = usb_hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
 	if (ret)
 		return ret;
 
 	/* Wait for the link to enter the disabled state. */
 	for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
-		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);
 		if (ret < 0)
 			return ret;
 
@@ -910,10 +907,10 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
 		dev_warn(&hub->ports[port1 - 1]->dev,
 				"Could not disable after %d ms\n", total_time);
 
-	return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+	return usb_hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
 }
 
-static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
+int usb_hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_device *hdev = hub->hdev;
@@ -938,10 +935,10 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
  * time later khubd will disconnect() any existing usb_device on the port
  * and will re-enumerate if there actually is a device attached.
  */
-static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
+void usb_hub_port_logical_disconnect(struct usb_hub *hub, int port1)
 {
 	dev_dbg(&hub->ports[port1 - 1]->dev, "logical disconnect\n");
-	hub_port_disable(hub, port1, 1);
+	usb_hub_port_disable(hub, port1, 1);
 
 	/* FIXME let caller ask to power down the port:
 	 *  - some devices won't enumerate without a VBUS power cycle
@@ -980,20 +977,15 @@ int usb_remove_device(struct usb_device *udev)
 
 	usb_autopm_get_interface(intf);
 	set_bit(udev->portnum, hub->removed_bits);
-	hub_port_logical_disconnect(hub, udev->portnum);
+	usb_hub_port_logical_disconnect(hub, udev->portnum);
 	usb_autopm_put_interface(intf);
 	return 0;
 }
 
-enum hub_activation_type {
-	HUB_INIT, HUB_INIT2, HUB_INIT3,		/* INITs must come first */
-	HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
-};
-
 static void hub_init_func2(struct work_struct *ws);
 static void hub_init_func3(struct work_struct *ws);
 
-static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
+void usb_hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 {
 	struct usb_device *hdev = hub->hdev;
 	struct usb_hcd *hcd;
@@ -1090,7 +1082,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 		u16 portstatus, portchange;
 
 		portstatus = portchange = 0;
-		status = hub_port_status(hub, port1, &portstatus, &portchange);
+		status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
 		if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
 			dev_dbg(&port_dev->dev, "status %04x change %04x\n",
 					portstatus, portchange);
@@ -1232,21 +1224,17 @@ static void hub_init_func2(struct work_struct *ws)
 {
 	struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
 
-	hub_activate(hub, HUB_INIT2);
+	usb_hub_activate(hub, HUB_INIT2);
 }
 
 static void hub_init_func3(struct work_struct *ws)
 {
 	struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
 
-	hub_activate(hub, HUB_INIT3);
+	usb_hub_activate(hub, HUB_INIT3);
 }
 
-enum hub_quiescing_type {
-	HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
-};
-
-static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
+void usb_hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
 {
 	struct usb_device *hdev = hub->hdev;
 	int i;
@@ -1285,7 +1273,7 @@ static int hub_pre_reset(struct usb_interface *intf)
 {
 	struct usb_hub *hub = usb_get_intfdata(intf);
 
-	hub_quiesce(hub, HUB_PRE_RESET);
+	usb_hub_quiesce(hub, HUB_PRE_RESET);
 	hub->in_reset = 1;
 	hub_pm_barrier_for_all_ports(hub);
 	return 0;
@@ -1298,7 +1286,7 @@ static int hub_post_reset(struct usb_interface *intf)
 
 	hub->in_reset = 0;
 	hub_pm_barrier_for_all_ports(hub);
-	hub_activate(hub, HUB_POST_RESET);
+	usb_hub_activate(hub, HUB_POST_RESET);
 	return 0;
 }
 
@@ -1594,7 +1582,7 @@ static int hub_configure(struct usb_hub *hub,
 
 	usb_hub_adjust_deviceremovable(hdev, hub->descriptor);
 
-	hub_activate(hub, HUB_INIT);
+	usb_hub_activate(hub, HUB_INIT);
 	return 0;
 
 fail:
@@ -1631,7 +1619,7 @@ static void hub_disconnect(struct usb_interface *intf)
 
 	/* Disconnect all children and quiesce the hub */
 	hub->error = 0;
-	hub_quiesce(hub, HUB_DISCONNECT);
+	usb_hub_quiesce(hub, HUB_DISCONNECT);
 
 	mutex_lock(&usb_port_peer_mutex);
 
@@ -2569,7 +2557,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 /* Is a USB 3.0 port in the Inactive or Compliance Mode state?
  * Port worm reset is required to recover
  */
-static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
+bool usb_hub_port_warm_reset_required(struct usb_hub *hub, int port1,
 		u16 portstatus)
 {
 	u16 link_state;
@@ -2599,7 +2587,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 		msleep(delay);
 
 		/* read and decode port status */
-		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);
 		if (ret < 0)
 			return ret;
 
@@ -2619,7 +2607,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 	if ((portstatus & USB_PORT_STAT_RESET))
 		return -EBUSY;
 
-	if (hub_port_warm_reset_required(hub, port1, portstatus))
+	if (usb_hub_port_warm_reset_required(hub, port1, portstatus))
 		return -ENOTCONN;
 
 	/* Device went away? */
@@ -2714,19 +2702,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 		 * If the caller hasn't explicitly requested a warm reset,
 		 * double check and see if one is needed.
 		 */
-		status = hub_port_status(hub, port1,
+		status = usb_hub_port_status(hub, port1,
 					&portstatus, &portchange);
 		if (status < 0)
 			goto done;
 
-		if (hub_port_warm_reset_required(hub, port1, portstatus))
+		if (usb_hub_port_warm_reset_required(hub, port1, portstatus))
 			warm = true;
 	}
 	clear_bit(port1, hub->warm_reset_bits);
 
 	/* Reset the port */
 	for (i = 0; i < PORT_RESET_TRIES; i++) {
-		status = set_port_feature(hub->hdev, port1, (warm ?
+		status = usb_set_port_feature(hub->hdev, port1, (warm ?
 					USB_PORT_FEAT_BH_PORT_RESET :
 					USB_PORT_FEAT_RESET));
 		if (status == -ENODEV) {
@@ -2755,11 +2743,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 			 * If a USB 3.0 device migrates from reset to an error
 			 * state, re-issue the warm reset.
 			 */
-			if (hub_port_status(hub, port1,
+			if (usb_hub_port_status(hub, port1,
 					&portstatus, &portchange) < 0)
 				goto done;
 
-			if (!hub_port_warm_reset_required(hub, port1,
+			if (!usb_hub_port_warm_reset_required(hub, port1,
 					portstatus))
 				goto done;
 
@@ -2790,7 +2778,7 @@ done:
 }
 
 /* Check if a port is power on */
-static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
+int usb_port_is_power_on(struct usb_hub *hub, unsigned portstatus)
 {
 	int ret = 0;
 
@@ -2805,1183 +2793,20 @@ static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
 	return ret;
 }
 
-static void usb_lock_port(struct usb_port *port_dev)
+void usb_lock_port(struct usb_port *port_dev)
 		__acquires(&port_dev->status_lock)
 {
 	mutex_lock(&port_dev->status_lock);
 	__acquire(&port_dev->status_lock);
 }
 
-static void usb_unlock_port(struct usb_port *port_dev)
+void usb_unlock_port(struct usb_port *port_dev)
 		__releases(&port_dev->status_lock)
 {
 	mutex_unlock(&port_dev->status_lock);
 	__release(&port_dev->status_lock);
 }
 
-#ifdef	CONFIG_PM
-
-/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
-static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
-{
-	int ret = 0;
-
-	if (hub_is_superspeed(hub->hdev)) {
-		if ((portstatus & USB_PORT_STAT_LINK_STATE)
-				== USB_SS_PORT_LS_U3)
-			ret = 1;
-	} else {
-		if (portstatus & USB_PORT_STAT_SUSPEND)
-			ret = 1;
-	}
-
-	return ret;
-}
-
-/* Determine whether the device on a port is ready for a normal resume,
- * is ready for a reset-resume, or should be disconnected.
- */
-static int check_port_resume_type(struct usb_device *udev,
-		struct usb_hub *hub, int port1,
-		int status, unsigned portchange, unsigned portstatus)
-{
-	struct usb_port *port_dev = hub->ports[port1 - 1];
-
-	/* Is a warm reset needed to recover the connection? */
-	if (status == 0 && udev->reset_resume
-		&& hub_port_warm_reset_required(hub, port1, portstatus)) {
-		/* pass */;
-	}
-	/* Is the device still present? */
-	else if (status || port_is_suspended(hub, portstatus) ||
-			!port_is_power_on(hub, portstatus) ||
-			!(portstatus & USB_PORT_STAT_CONNECTION)) {
-		if (status >= 0)
-			status = -ENODEV;
-	}
-
-	/* Can't do a normal resume if the port isn't enabled,
-	 * so try a reset-resume instead.
-	 */
-	else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
-		if (udev->persist_enabled)
-			udev->reset_resume = 1;
-		else
-			status = -ENODEV;
-	}
-
-	if (status) {
-		dev_dbg(&port_dev->dev, "status %04x.%04x after resume, %d\n",
-				portchange, portstatus, status);
-	} else if (udev->reset_resume) {
-
-		/* Late port handoff can set status-change bits */
-		if (portchange & USB_PORT_STAT_C_CONNECTION)
-			usb_clear_port_feature(hub->hdev, port1,
-					USB_PORT_FEAT_C_CONNECTION);
-		if (portchange & USB_PORT_STAT_C_ENABLE)
-			usb_clear_port_feature(hub->hdev, port1,
-					USB_PORT_FEAT_C_ENABLE);
-	}
-
-	return status;
-}
-
-int usb_disable_ltm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
-	/* Check if the roothub and device supports LTM. */
-	if (!usb_device_supports_ltm(hcd->self.root_hub) ||
-			!usb_device_supports_ltm(udev))
-		return 0;
-
-	/* Clear Feature LTM Enable can only be sent if the device is
-	 * configured.
-	 */
-	if (!udev->actconfig)
-		return 0;
-
-	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
-			USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
-			USB_CTRL_SET_TIMEOUT);
-}
-EXPORT_SYMBOL_GPL(usb_disable_ltm);
-
-void usb_enable_ltm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
-	/* Check if the roothub and device supports LTM. */
-	if (!usb_device_supports_ltm(hcd->self.root_hub) ||
-			!usb_device_supports_ltm(udev))
-		return;
-
-	/* Set Feature LTM Enable can only be sent if the device is
-	 * configured.
-	 */
-	if (!udev->actconfig)
-		return;
-
-	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
-			USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
-			USB_CTRL_SET_TIMEOUT);
-}
-EXPORT_SYMBOL_GPL(usb_enable_ltm);
-
-/*
- * usb_enable_remote_wakeup - enable remote wakeup for a device
- * @udev: target device
- *
- * For USB-2 devices: Set the device's remote wakeup feature.
- *
- * For USB-3 devices: Assume there's only one function on the device and
- * enable remote wake for the first interface.  FIXME if the interface
- * association descriptor shows there's more than one function.
- */
-static int usb_enable_remote_wakeup(struct usb_device *udev)
-{
-	if (udev->speed < USB_SPEED_SUPER)
-		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
-				USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
-				USB_CTRL_SET_TIMEOUT);
-	else
-		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE,
-				USB_INTRF_FUNC_SUSPEND,
-				USB_INTRF_FUNC_SUSPEND_RW |
-					USB_INTRF_FUNC_SUSPEND_LP,
-				NULL, 0, USB_CTRL_SET_TIMEOUT);
-}
-
-/*
- * usb_disable_remote_wakeup - disable remote wakeup for a device
- * @udev: target device
- *
- * For USB-2 devices: Clear the device's remote wakeup feature.
- *
- * For USB-3 devices: Assume there's only one function on the device and
- * disable remote wake for the first interface.  FIXME if the interface
- * association descriptor shows there's more than one function.
- */
-static int usb_disable_remote_wakeup(struct usb_device *udev)
-{
-	if (udev->speed < USB_SPEED_SUPER)
-		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
-				USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
-				USB_CTRL_SET_TIMEOUT);
-	else
-		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
-				USB_INTRF_FUNC_SUSPEND,	0, NULL, 0,
-				USB_CTRL_SET_TIMEOUT);
-}
-
-/* Count of wakeup-enabled devices at or below udev */
-static unsigned wakeup_enabled_descendants(struct usb_device *udev)
-{
-	struct usb_hub *hub = usb_hub_to_struct_hub(udev);
-
-	return udev->do_remote_wakeup +
-			(hub ? hub->wakeup_enabled_descendants : 0);
-}
-
-/*
- * usb_port_suspend - suspend a usb device's upstream port
- * @udev: device that's no longer in active use, not a root hub
- * Context: must be able to sleep; device not locked; pm locks held
- *
- * Suspends a USB device that isn't in active use, conserving power.
- * Devices may wake out of a suspend, if anything important happens,
- * using the remote wakeup mechanism.  They may also be taken out of
- * suspend by the host, using usb_port_resume().  It's also routine
- * to disconnect devices while they are suspended.
- *
- * This only affects the USB hardware for a device; its interfaces
- * (and, for hubs, child devices) must already have been suspended.
- *
- * Selective port suspend reduces power; most suspended devices draw
- * less than 500 uA.  It's also used in OTG, along with remote wakeup.
- * All devices below the suspended port are also suspended.
- *
- * Devices leave suspend state when the host wakes them up.  Some devices
- * also support "remote wakeup", where the device can activate the USB
- * tree above them to deliver data, such as a keypress or packet.  In
- * some cases, this wakes the USB host.
- *
- * Suspending OTG devices may trigger HNP, if that's been enabled
- * between a pair of dual-role devices.  That will change roles, such
- * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
- *
- * Devices on USB hub ports have only one "suspend" state, corresponding
- * to ACPI D2, "may cause the device to lose some context".
- * State transitions include:
- *
- *   - suspend, resume ... when the VBUS power link stays live
- *   - suspend, disconnect ... VBUS lost
- *
- * Once VBUS drop breaks the circuit, the port it's using has to go through
- * normal re-enumeration procedures, starting with enabling VBUS power.
- * Other than re-initializing the hub (plug/unplug, except for root hubs),
- * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
- * timer, no SRP, no requests through sysfs.
- *
- * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
- * suspended until their bus goes into global suspend (i.e., the root
- * hub is suspended).  Nevertheless, we change @udev->state to
- * USB_STATE_SUSPENDED as this is the device's "logical" state.  The actual
- * upstream port setting is stored in @udev->port_is_suspended.
- *
- * Returns 0 on success, else negative errno.
- */
-int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
-{
-	struct usb_hub	*hub = usb_hub_to_struct_hub(udev->parent);
-	struct usb_port *port_dev = hub->ports[udev->portnum - 1];
-	int		port1 = udev->portnum;
-	int		status;
-	bool		really_suspend = true;
-
-	usb_lock_port(port_dev);
-
-	/* enable remote wakeup when appropriate; this lets the device
-	 * wake up the upstream hub (including maybe the root hub).
-	 *
-	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
-	 * we don't explicitly enable it here.
-	 */
-	if (udev->do_remote_wakeup) {
-		status = usb_enable_remote_wakeup(udev);
-		if (status) {
-			dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
-					status);
-			/* bail if autosuspend is requested */
-			if (PMSG_IS_AUTO(msg))
-				goto err_wakeup;
-		}
-	}
-
-	/* disable USB2 hardware LPM */
-	if (udev->usb2_hw_lpm_enabled == 1)
-		usb_set_usb2_hardware_lpm(udev, 0);
-
-	if (usb_disable_ltm(udev)) {
-		dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
-		status = -ENOMEM;
-		if (PMSG_IS_AUTO(msg))
-			goto err_ltm;
-	}
-	if (usb_unlocked_disable_lpm(udev)) {
-		dev_err(&udev->dev, "Failed to disable LPM before suspend\n.");
-		status = -ENOMEM;
-		if (PMSG_IS_AUTO(msg))
-			goto err_lpm3;
-	}
-
-	/* see 7.1.7.6 */
-	if (hub_is_superspeed(hub->hdev))
-		status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
-
-	/*
-	 * For system suspend, we do not need to enable the suspend feature
-	 * on individual USB-2 ports.  The devices will automatically go
-	 * into suspend a few ms after the root hub stops sending packets.
-	 * The USB 2.0 spec calls this "global suspend".
-	 *
-	 * However, many USB hubs have a bug: They don't relay wakeup requests
-	 * from a downstream port if the port's suspend feature isn't on.
-	 * Therefore we will turn on the suspend feature if udev or any of its
-	 * descendants is enabled for remote wakeup.
-	 */
-	else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
-		status = set_port_feature(hub->hdev, port1,
-				USB_PORT_FEAT_SUSPEND);
-	else {
-		really_suspend = false;
-		status = 0;
-	}
-	if (status) {
-		dev_dbg(&port_dev->dev, "can't suspend, status %d\n", status);
-
-		/* Try to enable USB3 LPM and LTM again */
-		usb_unlocked_enable_lpm(udev);
- err_lpm3:
-		usb_enable_ltm(udev);
- err_ltm:
-		/* Try to enable USB2 hardware LPM again */
-		if (udev->usb2_hw_lpm_capable == 1)
-			usb_set_usb2_hardware_lpm(udev, 1);
-
-		if (udev->do_remote_wakeup)
-			(void) usb_disable_remote_wakeup(udev);
- err_wakeup:
-
-		/* System sleep transitions should never fail */
-		if (!PMSG_IS_AUTO(msg))
-			status = 0;
-	} else {
-		dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
-				(PMSG_IS_AUTO(msg) ? "auto-" : ""),
-				udev->do_remote_wakeup);
-		if (really_suspend) {
-			udev->port_is_suspended = 1;
-
-			/* device has up to 10 msec to fully suspend */
-			msleep(10);
-		}
-		usb_set_device_state(udev, USB_STATE_SUSPENDED);
-	}
-
-	if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled
-			&& test_and_clear_bit(port1, hub->child_usage_bits))
-		pm_runtime_put_sync(&port_dev->dev);
-
-	usb_mark_last_busy(hub->hdev);
-
-	usb_unlock_port(port_dev);
-	return status;
-}
-
-/*
- * If the USB "suspend" state is in use (rather than "global suspend"),
- * many devices will be individually taken out of suspend state using
- * special "resume" signaling.  This routine kicks in shortly after
- * hardware resume signaling is finished, either because of selective
- * resume (by host) or remote wakeup (by device) ... now see what changed
- * in the tree that's rooted at this device.
- *
- * If @udev->reset_resume is set then the device is reset before the
- * status check is done.
- */
-static int finish_port_resume(struct usb_device *udev)
-{
-	int	status = 0;
-	u16	devstatus = 0;
-
-	/* caller owns the udev device lock */
-	dev_dbg(&udev->dev, "%s\n",
-		udev->reset_resume ? "finish reset-resume" : "finish resume");
-
-	/* usb ch9 identifies four variants of SUSPENDED, based on what
-	 * state the device resumes to.  Linux currently won't see the
-	 * first two on the host side; they'd be inside hub_port_init()
-	 * during many timeouts, but khubd can't suspend until later.
-	 */
-	usb_set_device_state(udev, udev->actconfig
-			? USB_STATE_CONFIGURED
-			: USB_STATE_ADDRESS);
-
-	/* 10.5.4.5 says not to reset a suspended port if the attached
-	 * device is enabled for remote wakeup.  Hence the reset
-	 * operation is carried out here, after the port has been
-	 * resumed.
-	 */
-	if (udev->reset_resume) {
-		/*
-		 * If the device morphs or switches modes when it is reset,
-		 * we don't want to perform a reset-resume.  We'll fail the
-		 * resume, which will cause a logical disconnect, and then
-		 * the device will be rediscovered.
-		 */
- retry_reset_resume:
-		if (udev->quirks & USB_QUIRK_RESET)
-			status = -ENODEV;
-		else
-			status = usb_reset_and_verify_device(udev);
-	}
-
-	/* 10.5.4.5 says be sure devices in the tree are still there.
-	 * For now let's assume the device didn't go crazy on resume,
-	 * and device drivers will know about any resume quirks.
-	 */
-	if (status == 0) {
-		devstatus = 0;
-		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
-
-		/* If a normal resume failed, try doing a reset-resume */
-		if (status && !udev->reset_resume && udev->persist_enabled) {
-			dev_dbg(&udev->dev, "retry with reset-resume\n");
-			udev->reset_resume = 1;
-			goto retry_reset_resume;
-		}
-	}
-
-	if (status) {
-		dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
-				status);
-	/*
-	 * There are a few quirky devices which violate the standard
-	 * by claiming to have remote wakeup enabled after a reset,
-	 * which crash if the feature is cleared, hence check for
-	 * udev->reset_resume
-	 */
-	} else if (udev->actconfig && !udev->reset_resume) {
-		if (udev->speed < USB_SPEED_SUPER) {
-			if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
-				status = usb_disable_remote_wakeup(udev);
-		} else {
-			status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
-					&devstatus);
-			if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
-					| USB_INTRF_STAT_FUNC_RW))
-				status = usb_disable_remote_wakeup(udev);
-		}
-
-		if (status)
-			dev_dbg(&udev->dev,
-				"disable remote wakeup, status %d\n",
-				status);
-		status = 0;
-	}
-	return status;
-}
-
-/*
- * usb_port_resume - re-activate a suspended usb device's upstream port
- * @udev: device to re-activate, not a root hub
- * Context: must be able to sleep; device not locked; pm locks held
- *
- * This will re-activate the suspended device, increasing power usage
- * while letting drivers communicate again with its endpoints.
- * USB resume explicitly guarantees that the power session between
- * the host and the device is the same as it was when the device
- * suspended.
- *
- * If @udev->reset_resume is set then this routine won't check that the
- * port is still enabled.  Furthermore, finish_port_resume() above will
- * reset @udev.  The end result is that a broken power session can be
- * recovered and @udev will appear to persist across a loss of VBUS power.
- *
- * For example, if a host controller doesn't maintain VBUS suspend current
- * during a system sleep or is reset when the system wakes up, all the USB
- * power sessions below it will be broken.  This is especially troublesome
- * for mass-storage devices containing mounted filesystems, since the
- * device will appear to have disconnected and all the memory mappings
- * to it will be lost.  Using the USB_PERSIST facility, the device can be
- * made to appear as if it had not disconnected.
- *
- * This facility can be dangerous.  Although usb_reset_and_verify_device() makes
- * every effort to insure that the same device is present after the
- * reset as before, it cannot provide a 100% guarantee.  Furthermore it's
- * quite possible for a device to remain unaltered but its media to be
- * changed.  If the user replaces a flash memory card while the system is
- * asleep, he will have only himself to blame when the filesystem on the
- * new card is corrupted and the system crashes.
- *
- * Returns 0 on success, else negative errno.
- */
-int usb_port_resume(struct usb_device *udev, pm_message_t msg)
-{
-	struct usb_hub	*hub = usb_hub_to_struct_hub(udev->parent);
-	struct usb_port *port_dev = hub->ports[udev->portnum  - 1];
-	int		port1 = udev->portnum;
-	int		status;
-	u16		portchange, portstatus;
-
-	if (!test_and_set_bit(port1, hub->child_usage_bits)) {
-		status = pm_runtime_get_sync(&port_dev->dev);
-		if (status < 0) {
-			dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
-					status);
-			return status;
-		}
-	}
-
-	usb_lock_port(port_dev);
-
-	/* Skip the initial Clear-Suspend step for a remote wakeup */
-	status = hub_port_status(hub, port1, &portstatus, &portchange);
-	if (status == 0 && !port_is_suspended(hub, portstatus))
-		goto SuspendCleared;
-
-	/* see 7.1.7.7; affects power usage, but not budgeting */
-	if (hub_is_superspeed(hub->hdev))
-		status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
-	else
-		status = usb_clear_port_feature(hub->hdev,
-				port1, USB_PORT_FEAT_SUSPEND);
-	if (status) {
-		dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
-	} else {
-		/* drive resume for at least 20 msec */
-		dev_dbg(&udev->dev, "usb %sresume\n",
-				(PMSG_IS_AUTO(msg) ? "auto-" : ""));
-		msleep(25);
-
-		/* Virtual root hubs can trigger on GET_PORT_STATUS to
-		 * stop resume signaling.  Then finish the resume
-		 * sequence.
-		 */
-		status = hub_port_status(hub, port1, &portstatus, &portchange);
-
-		/* TRSMRCY = 10 msec */
-		msleep(10);
-	}
-
- SuspendCleared:
-	if (status == 0) {
-		udev->port_is_suspended = 0;
-		if (hub_is_superspeed(hub->hdev)) {
-			if (portchange & USB_PORT_STAT_C_LINK_STATE)
-				usb_clear_port_feature(hub->hdev, port1,
-					USB_PORT_FEAT_C_PORT_LINK_STATE);
-		} else {
-			if (portchange & USB_PORT_STAT_C_SUSPEND)
-				usb_clear_port_feature(hub->hdev, port1,
-						USB_PORT_FEAT_C_SUSPEND);
-		}
-	}
-
-	status = check_port_resume_type(udev,
-			hub, port1, status, portchange, portstatus);
-	if (status == 0)
-		status = finish_port_resume(udev);
-	if (status < 0) {
-		dev_dbg(&udev->dev, "can't resume, status %d\n", status);
-		hub_port_logical_disconnect(hub, port1);
-	} else  {
-		/* Try to enable USB2 hardware LPM */
-		if (udev->usb2_hw_lpm_capable == 1)
-			usb_set_usb2_hardware_lpm(udev, 1);
-
-		/* Try to enable USB3 LTM and LPM */
-		usb_enable_ltm(udev);
-		usb_unlocked_enable_lpm(udev);
-	}
-
-	usb_unlock_port(port_dev);
-
-	return status;
-}
-
-#ifdef	CONFIG_PM_RUNTIME
-
-int usb_remote_wakeup(struct usb_device *udev)
-{
-	int	status = 0;
-
-	usb_lock_device(udev);
-	if (udev->state == USB_STATE_SUSPENDED) {
-		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
-		status = usb_autoresume_device(udev);
-		if (status == 0) {
-			/* Let the drivers do their thing, then... */
-			usb_autosuspend_device(udev);
-		}
-	}
-	usb_unlock_device(udev);
-	return status;
-}
-
-/* Returns 1 if there was a remote wakeup and a connect status change. */
-static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
-		u16 portstatus, u16 portchange)
-		__must_hold(&port_dev->status_lock)
-{
-	struct usb_port *port_dev = hub->ports[port - 1];
-	struct usb_device *hdev;
-	struct usb_device *udev;
-	int connect_change = 0;
-	int ret;
-
-	hdev = hub->hdev;
-	udev = port_dev->child;
-	if (!hub_is_superspeed(hdev)) {
-		if (!(portchange & USB_PORT_STAT_C_SUSPEND))
-			return 0;
-		usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
-	} else {
-		if (!udev || udev->state != USB_STATE_SUSPENDED ||
-				 (portstatus & USB_PORT_STAT_LINK_STATE) !=
-				 USB_SS_PORT_LS_U0)
-			return 0;
-	}
-
-	if (udev) {
-		/* TRSMRCY = 10 msec */
-		msleep(10);
-
-		usb_unlock_port(port_dev);
-		ret = usb_remote_wakeup(udev);
-		usb_lock_port(port_dev);
-		if (ret < 0)
-			connect_change = 1;
-	} else {
-		ret = -ENODEV;
-		hub_port_disable(hub, port, 1);
-	}
-	dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
-	return connect_change;
-}
-
-#else
-
-static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
-		u16 portstatus, u16 portchange)
-{
-	return 0;
-}
-
-#endif
-
-static int check_ports_changed(struct usb_hub *hub)
-{
-	int port1;
-
-	for (port1 = 1; port1 <= hub->hdev->maxchild; ++port1) {
-		u16 portstatus, portchange;
-		int status;
-
-		status = hub_port_status(hub, port1, &portstatus, &portchange);
-		if (!status && portchange)
-			return 1;
-	}
-	return 0;
-}
-
-static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
-{
-	struct usb_hub		*hub = usb_get_intfdata (intf);
-	struct usb_device	*hdev = hub->hdev;
-	unsigned		port1;
-	int			status;
-
-	/*
-	 * Warn if children aren't already suspended.
-	 * Also, add up the number of wakeup-enabled descendants.
-	 */
-	hub->wakeup_enabled_descendants = 0;
-	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-		struct usb_port *port_dev = hub->ports[port1 - 1];
-		struct usb_device *udev = port_dev->child;
-
-		if (udev && udev->can_submit) {
-			dev_warn(&port_dev->dev, "not suspended yet\n");
-			if (PMSG_IS_AUTO(msg))
-				return -EBUSY;
-		}
-		if (udev)
-			hub->wakeup_enabled_descendants +=
-					wakeup_enabled_descendants(udev);
-	}
-
-	if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
-		/* check if there are changes pending on hub ports */
-		if (check_ports_changed(hub)) {
-			if (PMSG_IS_AUTO(msg))
-				return -EBUSY;
-			pm_wakeup_event(&hdev->dev, 2000);
-		}
-	}
-
-	if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
-		/* Enable hub to send remote wakeup for all ports. */
-		for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-			status = set_port_feature(hdev,
-					port1 |
-					USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
-					USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
-					USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
-					USB_PORT_FEAT_REMOTE_WAKE_MASK);
-		}
-	}
-
-	dev_dbg(&intf->dev, "%s\n", __func__);
-
-	/* stop khubd and related activity */
-	hub_quiesce(hub, HUB_SUSPEND);
-	return 0;
-}
-
-static int hub_resume(struct usb_interface *intf)
-{
-	struct usb_hub *hub = usb_get_intfdata(intf);
-
-	dev_dbg(&intf->dev, "%s\n", __func__);
-	hub_activate(hub, HUB_RESUME);
-	return 0;
-}
-
-static int hub_reset_resume(struct usb_interface *intf)
-{
-	struct usb_hub *hub = usb_get_intfdata(intf);
-
-	dev_dbg(&intf->dev, "%s\n", __func__);
-	hub_activate(hub, HUB_RESET_RESUME);
-	return 0;
-}
-
-/**
- * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
- * @rhdev: struct usb_device for the root hub
- *
- * The USB host controller driver calls this function when its root hub
- * is resumed and Vbus power has been interrupted or the controller
- * has been reset.  The routine marks @rhdev as having lost power.
- * When the hub driver is resumed it will take notice and carry out
- * power-session recovery for all the "USB-PERSIST"-enabled child devices;
- * the others will be disconnected.
- */
-void usb_root_hub_lost_power(struct usb_device *rhdev)
-{
-	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
-	rhdev->reset_resume = 1;
-}
-EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
-
-static const char * const usb3_lpm_names[]  = {
-	"U0",
-	"U1",
-	"U2",
-	"U3",
-};
-
-/*
- * Send a Set SEL control transfer to the device, prior to enabling
- * device-initiated U1 or U2.  This lets the device know the exit latencies from
- * the time the device initiates a U1 or U2 exit, to the time it will receive a
- * packet from the host.
- *
- * This function will fail if the SEL or PEL values for udev are greater than
- * the maximum allowed values for the link state to be enabled.
- */
-static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
-{
-	struct usb_set_sel_req *sel_values;
-	unsigned long long u1_sel;
-	unsigned long long u1_pel;
-	unsigned long long u2_sel;
-	unsigned long long u2_pel;
-	int ret;
-
-	if (udev->state != USB_STATE_CONFIGURED)
-		return 0;
-
-	/* Convert SEL and PEL stored in ns to us */
-	u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
-	u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
-	u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
-	u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
-
-	/*
-	 * Make sure that the calculated SEL and PEL values for the link
-	 * state we're enabling aren't bigger than the max SEL/PEL
-	 * value that will fit in the SET SEL control transfer.
-	 * Otherwise the device would get an incorrect idea of the exit
-	 * latency for the link state, and could start a device-initiated
-	 * U1/U2 when the exit latencies are too high.
-	 */
-	if ((state == USB3_LPM_U1 &&
-				(u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
-				 u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
-			(state == USB3_LPM_U2 &&
-			 (u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
-			  u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
-		dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n",
-				usb3_lpm_names[state], u1_sel, u1_pel);
-		return -EINVAL;
-	}
-
-	/*
-	 * If we're enabling device-initiated LPM for one link state,
-	 * but the other link state has a too high SEL or PEL value,
-	 * just set those values to the max in the Set SEL request.
-	 */
-	if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
-		u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
-
-	if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
-		u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
-
-	if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
-		u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
-
-	if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
-		u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
-
-	/*
-	 * usb_enable_lpm() can be called as part of a failed device reset,
-	 * which may be initiated by an error path of a mass storage driver.
-	 * Therefore, use GFP_NOIO.
-	 */
-	sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
-	if (!sel_values)
-		return -ENOMEM;
-
-	sel_values->u1_sel = u1_sel;
-	sel_values->u1_pel = u1_pel;
-	sel_values->u2_sel = cpu_to_le16(u2_sel);
-	sel_values->u2_pel = cpu_to_le16(u2_pel);
-
-	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			USB_REQ_SET_SEL,
-			USB_RECIP_DEVICE,
-			0, 0,
-			sel_values, sizeof *(sel_values),
-			USB_CTRL_SET_TIMEOUT);
-	kfree(sel_values);
-	return ret;
-}
-
-/*
- * Enable or disable device-initiated U1 or U2 transitions.
- */
-static int usb_set_device_initiated_lpm(struct usb_device *udev,
-		enum usb3_link_state state, bool enable)
-{
-	int ret;
-	int feature;
-
-	switch (state) {
-	case USB3_LPM_U1:
-		feature = USB_DEVICE_U1_ENABLE;
-		break;
-	case USB3_LPM_U2:
-		feature = USB_DEVICE_U2_ENABLE;
-		break;
-	default:
-		dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
-				__func__, enable ? "enable" : "disable");
-		return -EINVAL;
-	}
-
-	if (udev->state != USB_STATE_CONFIGURED) {
-		dev_dbg(&udev->dev, "%s: Can't %s %s state "
-				"for unconfigured device.\n",
-				__func__, enable ? "enable" : "disable",
-				usb3_lpm_names[state]);
-		return 0;
-	}
-
-	if (enable) {
-		/*
-		 * Now send the control transfer to enable device-initiated LPM
-		 * for either U1 or U2.
-		 */
-		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_SET_FEATURE,
-				USB_RECIP_DEVICE,
-				feature,
-				0, NULL, 0,
-				USB_CTRL_SET_TIMEOUT);
-	} else {
-		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-				USB_REQ_CLEAR_FEATURE,
-				USB_RECIP_DEVICE,
-				feature,
-				0, NULL, 0,
-				USB_CTRL_SET_TIMEOUT);
-	}
-	if (ret < 0) {
-		dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
-				enable ? "Enable" : "Disable",
-				usb3_lpm_names[state]);
-		return -EBUSY;
-	}
-	return 0;
-}
-
-static int usb_set_lpm_timeout(struct usb_device *udev,
-		enum usb3_link_state state, int timeout)
-{
-	int ret;
-	int feature;
-
-	switch (state) {
-	case USB3_LPM_U1:
-		feature = USB_PORT_FEAT_U1_TIMEOUT;
-		break;
-	case USB3_LPM_U2:
-		feature = USB_PORT_FEAT_U2_TIMEOUT;
-		break;
-	default:
-		dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
-				__func__);
-		return -EINVAL;
-	}
-
-	if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
-			timeout != USB3_LPM_DEVICE_INITIATED) {
-		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, "
-				"which is a reserved value.\n",
-				usb3_lpm_names[state], timeout);
-		return -EINVAL;
-	}
-
-	ret = set_port_feature(udev->parent,
-			USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
-			feature);
-	if (ret < 0) {
-		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x,"
-				"error code %i\n", usb3_lpm_names[state],
-				timeout, ret);
-		return -EBUSY;
-	}
-	if (state == USB3_LPM_U1)
-		udev->u1_params.timeout = timeout;
-	else
-		udev->u2_params.timeout = timeout;
-	return 0;
-}
-
-/*
- * Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
- * U1/U2 entry.
- *
- * We will attempt to enable U1 or U2, but there are no guarantees that the
- * control transfers to set the hub timeout or enable device-initiated U1/U2
- * will be successful.
- *
- * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
- * driver know about it.  If that call fails, it should be harmless, and just
- * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
- */
-static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
-		enum usb3_link_state state)
-{
-	int timeout, ret;
-	__u8 u1_mel = udev->bos->ss_cap->bU1devExitLat;
-	__le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat;
-
-	/* If the device says it doesn't have *any* exit latency to come out of
-	 * U1 or U2, it's probably lying.  Assume it doesn't implement that link
-	 * state.
-	 */
-	if ((state == USB3_LPM_U1 && u1_mel == 0) ||
-			(state == USB3_LPM_U2 && u2_mel == 0))
-		return;
-
-	/*
-	 * First, let the device know about the exit latencies
-	 * associated with the link state we're about to enable.
-	 */
-	ret = usb_req_set_sel(udev, state);
-	if (ret < 0) {
-		dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n",
-				usb3_lpm_names[state]);
-		return;
-	}
-
-	/* We allow the host controller to set the U1/U2 timeout internally
-	 * first, so that it can change its schedule to account for the
-	 * additional latency to send data to a device in a lower power
-	 * link state.
-	 */
-	timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
-
-	/* xHCI host controller doesn't want to enable this LPM state. */
-	if (timeout == 0)
-		return;
-
-	if (timeout < 0) {
-		dev_warn(&udev->dev, "Could not enable %s link state, "
-				"xHCI error %i.\n", usb3_lpm_names[state],
-				timeout);
-		return;
-	}
-
-	if (usb_set_lpm_timeout(udev, state, timeout))
-		/* If we can't set the parent hub U1/U2 timeout,
-		 * device-initiated LPM won't be allowed either, so let the xHCI
-		 * host know that this link state won't be enabled.
-		 */
-		hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
-
-	/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
-	else if (udev->actconfig)
-		usb_set_device_initiated_lpm(udev, state, true);
-
-}
-
-/*
- * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
- * U1/U2 entry.
- *
- * If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
- * If zero is returned, the parent will not allow the link to go into U1/U2.
- *
- * If zero is returned, device-initiated U1/U2 entry may still be enabled, but
- * it won't have an effect on the bus link state because the parent hub will
- * still disallow device-initiated U1/U2 entry.
- *
- * If zero is returned, the xHCI host controller may still think U1/U2 entry is
- * possible.  The result will be slightly more bus bandwidth will be taken up
- * (to account for U1/U2 exit latency), but it should be harmless.
- */
-static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
-		enum usb3_link_state state)
-{
-	int feature;
-
-	switch (state) {
-	case USB3_LPM_U1:
-		feature = USB_PORT_FEAT_U1_TIMEOUT;
-		break;
-	case USB3_LPM_U2:
-		feature = USB_PORT_FEAT_U2_TIMEOUT;
-		break;
-	default:
-		dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
-				__func__);
-		return -EINVAL;
-	}
-
-	if (usb_set_lpm_timeout(udev, state, 0))
-		return -EBUSY;
-
-	usb_set_device_initiated_lpm(udev, state, false);
-
-	if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
-		dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
-				"bus schedule bandwidth may be impacted.\n",
-				usb3_lpm_names[state]);
-	return 0;
-}
-
-/*
- * Disable hub-initiated and device-initiated U1 and U2 entry.
- * Caller must own the bandwidth_mutex.
- *
- * This will call usb_enable_lpm() on failure, which will decrement
- * lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero.
- */
-int usb_disable_lpm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd;
-
-	if (!udev || !udev->parent ||
-			udev->speed != USB_SPEED_SUPER ||
-			!udev->lpm_capable)
-		return 0;
-
-	hcd = bus_to_hcd(udev->bus);
-	if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
-		return 0;
-
-	udev->lpm_disable_count++;
-	if ((udev->u1_params.timeout == 0 && udev->u2_params.timeout == 0))
-		return 0;
-
-	/* If LPM is enabled, attempt to disable it. */
-	if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
-		goto enable_lpm;
-	if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
-		goto enable_lpm;
-
-	return 0;
-
-enable_lpm:
-	usb_enable_lpm(udev);
-	return -EBUSY;
-}
-EXPORT_SYMBOL_GPL(usb_disable_lpm);
-
-/* Grab the bandwidth_mutex before calling usb_disable_lpm() */
-int usb_unlocked_disable_lpm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-	int ret;
-
-	if (!hcd)
-		return -EINVAL;
-
-	mutex_lock(hcd->bandwidth_mutex);
-	ret = usb_disable_lpm(udev);
-	mutex_unlock(hcd->bandwidth_mutex);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
-
-/*
- * Attempt to enable device-initiated and hub-initiated U1 and U2 entry.  The
- * xHCI host policy may prevent U1 or U2 from being enabled.
- *
- * Other callers may have disabled link PM, so U1 and U2 entry will be disabled
- * until the lpm_disable_count drops to zero.  Caller must own the
- * bandwidth_mutex.
- */
-void usb_enable_lpm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd;
-
-	if (!udev || !udev->parent ||
-			udev->speed != USB_SPEED_SUPER ||
-			!udev->lpm_capable)
-		return;
-
-	udev->lpm_disable_count--;
-	hcd = bus_to_hcd(udev->bus);
-	/* Double check that we can both enable and disable LPM.
-	 * Device must be configured to accept set feature U1/U2 timeout.
-	 */
-	if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
-			!hcd->driver->disable_usb3_lpm_timeout)
-		return;
-
-	if (udev->lpm_disable_count > 0)
-		return;
-
-	usb_enable_link_state(hcd, udev, USB3_LPM_U1);
-	usb_enable_link_state(hcd, udev, USB3_LPM_U2);
-}
-EXPORT_SYMBOL_GPL(usb_enable_lpm);
-
-/* Grab the bandwidth_mutex before calling usb_enable_lpm() */
-void usb_unlocked_enable_lpm(struct usb_device *udev)
-{
-	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
-	if (!hcd)
-		return;
-
-	mutex_lock(hcd->bandwidth_mutex);
-	usb_enable_lpm(udev);
-	mutex_unlock(hcd->bandwidth_mutex);
-}
-EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
-
-
-#else	/* CONFIG_PM */
-
-#define hub_suspend		NULL
-#define hub_resume		NULL
-#define hub_reset_resume	NULL
-
-int usb_disable_lpm(struct usb_device *udev)
-{
-	return 0;
-}
-EXPORT_SYMBOL_GPL(usb_disable_lpm);
-
-void usb_enable_lpm(struct usb_device *udev) { }
-EXPORT_SYMBOL_GPL(usb_enable_lpm);
-
-int usb_unlocked_disable_lpm(struct usb_device *udev)
-{
-	return 0;
-}
-EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
-
-void usb_unlocked_enable_lpm(struct usb_device *udev) { }
-EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
-
-int usb_disable_ltm(struct usb_device *udev)
-{
-	return 0;
-}
-EXPORT_SYMBOL_GPL(usb_disable_ltm);
-
-void usb_enable_ltm(struct usb_device *udev) { }
-EXPORT_SYMBOL_GPL(usb_enable_ltm);
-
-#endif	/* CONFIG_PM */
-
-
 /* USB 2.0 spec, 7.1.7.3 / fig 7-29:
  *
  * Between connect detection and reset signaling there must be a delay
@@ -4006,7 +2831,7 @@ int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 
 	for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
-		ret = hub_port_status(hub, port1, &portstatus, &portchange);
+		ret = usb_hub_port_status(hub, port1, &portstatus, &portchange);
 		if (ret < 0)
 			return ret;
 
@@ -4432,7 +3257,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 	hub_set_initial_usb2_lpm_policy(udev);
 fail:
 	if (retval) {
-		hub_port_disable(hub, port1, 0);
+		usb_hub_port_disable(hub, port1, 0);
 		update_devnum(udev, devnum);	/* for disconnect processing */
 	}
 	mutex_unlock(&hdev->bus->usb_address0_mutex);
@@ -4557,8 +3382,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 
 		/* maybe switch power back on (e.g. root hub was reset) */
 		if (hub_is_port_power_switchable(hub)
-				&& !port_is_power_on(hub, portstatus))
-			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+				&& !usb_port_is_power_on(hub, portstatus))
+			usb_set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
 		if (portstatus & USB_PORT_STAT_ENABLE)
 			goto done;
@@ -4690,7 +3515,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		return;
 
 loop_disable:
-		hub_port_disable(hub, port1, 1);
+		usb_hub_port_disable(hub, port1, 1);
 loop:
 		usb_ep0_reinit(udev);
 		release_devnum(udev);
@@ -4708,7 +3533,7 @@ loop:
 	}
 
 done:
-	hub_port_disable(hub, port1, 1);
+	usb_hub_port_disable(hub, port1, 1);
 	if (hcd->driver->relinquish_port && !hub->hdev->parent)
 		hcd->driver->relinquish_port(hcd, port1);
 
@@ -4750,16 +3575,15 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 			udev->state != USB_STATE_NOTATTACHED) {
 		if (portstatus & USB_PORT_STAT_ENABLE) {
 			status = 0;		/* Nothing to do */
-#ifdef CONFIG_PM_RUNTIME
-		} else if (udev->state == USB_STATE_SUSPENDED &&
-				udev->persist_enabled) {
+		} else if (IS_ENABLED(PM_RUNTIME)
+				&& udev->state == USB_STATE_SUSPENDED
+				&& udev->persist_enabled) {
 			/* For a suspended device, treat this as a
 			 * remote wakeup event.
 			 */
 			usb_unlock_port(port_dev);
 			status = usb_remote_wakeup(udev);
 			usb_lock_port(port_dev);
-#endif
 		} else {
 			/* Don't resuscitate */;
 		}
@@ -4788,7 +3612,7 @@ static void port_event(struct usb_hub *hub, int port1)
 	clear_bit(port1, hub->event_bits);
 	clear_bit(port1, hub->wakeup_bits);
 
-	if (hub_port_status(hub, port1, &portstatus, &portchange) < 0)
+	if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0)
 		return;
 
 	if (portchange & USB_PORT_STAT_C_CONNECTION) {
@@ -4822,7 +3646,7 @@ static void port_event(struct usb_hub *hub, int port1)
 				USB_PORT_FEAT_C_OVER_CURRENT);
 		msleep(100);	/* Cool down */
 		hub_power_on(hub, true);
-		hub_port_status(hub, port1, &status, &unused);
+		usb_hub_port_status(hub, port1, &status, &unused);
 		if (status & USB_PORT_STAT_OVERCURRENT)
 			dev_err(&port_dev->dev, "over-current condition\n");
 	}
@@ -4852,20 +3676,20 @@ static void port_event(struct usb_hub *hub, int port1)
 	if (!pm_runtime_active(&port_dev->dev))
 		return;
 
-	if (hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
+	if (usb_hub_handle_remote_wakeup(hub, port1, portstatus, portchange))
 		connect_change = 1;
 
 	/*
 	 * Warm reset a USB3 protocol port if it's in
 	 * SS.Inactive state.
 	 */
-	if (hub_port_warm_reset_required(hub, port1, portstatus)) {
+	if (usb_hub_port_warm_reset_required(hub, port1, portstatus)) {
 		dev_dbg(&port_dev->dev, "do warm reset\n");
 		if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
 				|| udev->state == USB_STATE_NOTATTACHED) {
 			if (hub_port_reset(hub, port1, NULL,
 					HUB_BH_RESET_TIME, true) < 0)
-				hub_port_disable(hub, port1, 1);
+				usb_hub_port_disable(hub, port1, 1);
 		} else
 			reset_device = 1;
 	}
@@ -4948,7 +3772,7 @@ static void hub_events(void)
 		/* If the hub has died, clean up after it */
 		if (hdev->state == USB_STATE_NOTATTACHED) {
 			hub->error = -ENODEV;
-			hub_quiesce(hub, HUB_DISCONNECT);
+			usb_hub_quiesce(hub, HUB_DISCONNECT);
 			goto loop;
 		}
 
@@ -5087,9 +3911,9 @@ static struct usb_driver hub_driver = {
 	.name =		"hub",
 	.probe =	hub_probe,
 	.disconnect =	hub_disconnect,
-	.suspend =	hub_suspend,
-	.resume =	hub_resume,
-	.reset_resume =	hub_reset_resume,
+	.suspend =	usb_hub_suspend,
+	.resume =	usb_hub_resume,
+	.reset_resume =	usb_hub_reset_resume,
 	.pre_reset =	hub_pre_reset,
 	.post_reset =	hub_post_reset,
 	.unlocked_ioctl = hub_ioctl,
@@ -5249,7 +4073,7 @@ static int descriptors_changed(struct usb_device *udev,
  * autoresume.  The autoresume handler is expected to have already
  * acquired the port lock before calling this routine.
  */
-static int usb_reset_and_verify_device(struct usb_device *udev)
+int usb_reset_and_verify_device(struct usb_device *udev)
 {
 	struct usb_device		*parent_hdev = udev->parent;
 	struct usb_hub			*parent_hub;
@@ -5394,7 +4218,7 @@ done:
 
 re_enumerate:
 	/* LPM state doesn't matter when we're about to destroy the device. */
-	hub_port_logical_disconnect(parent_hub, port1);
+	usb_hub_port_logical_disconnect(parent_hub, port1);
 	usb_release_bos_descriptor(udev);
 	udev->bos = bos;
 	return -ENODEV;
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index e60cf1d242e9..6e5b3fd2ac2f 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -154,3 +154,51 @@ static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
 	return hub_port_debounce(hub, port1, false);
 }
 
+enum hub_quiescing_type {
+	HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
+};
+
+enum hub_activation_type {
+	HUB_INIT, HUB_INIT2, HUB_INIT3,		/* INITs must come first */
+	HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
+};
+
+/* support routines consumed by hub_pm.c */
+extern int usb_port_is_power_on(struct usb_hub *hub, unsigned portstatus);
+extern void usb_lock_port(struct usb_port *port_dev)
+		__acquires(&port_dev->status_lock);
+extern void usb_unlock_port(struct usb_port *port_dev)
+		__releases(&port_dev->status_lock);
+extern int usb_hub_set_port_link_state(struct usb_hub *hub, int port1,
+			unsigned int link_status);
+extern int usb_set_port_feature(struct usb_device *hdev, int port1, int feature);
+extern int usb_reset_and_verify_device(struct usb_device *udev);
+extern int usb_hub_port_status(struct usb_hub *hub, int port1,
+		u16 *status, u16 *change);
+extern void usb_hub_port_logical_disconnect(struct usb_hub *hub, int port1);
+extern int usb_hub_port_disable(struct usb_hub *hub, int port1, int set_state);
+extern void usb_hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type);
+extern void usb_hub_activate(struct usb_hub *hub, enum hub_activation_type type);
+extern bool usb_hub_port_warm_reset_required(struct usb_hub *hub, int port1,
+		u16 portstatus);
+#ifdef CONFIG_PM
+extern int usb_hub_suspend(struct usb_interface *intf, pm_message_t msg);
+extern int usb_hub_resume(struct usb_interface *intf);
+extern int usb_hub_reset_resume(struct usb_interface *intf);
+#else
+#define usb_hub_suspend		NULL
+#define usb_hub_resume		NULL
+#define usb_hub_reset_resume	NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+extern int usb_hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+		u16 portstatus, u16 portchange)
+		__must_hold(&port_dev->status_lock);
+#else
+static inline int usb_hub_handle_remote_wakeup(struct usb_hub *hub,
+		unsigned int port, u16 portstatus, u16 portchange)
+{
+	return 0;
+}
+#endif
diff --git a/drivers/usb/core/hub_pm.c b/drivers/usb/core/hub_pm.c
new file mode 100644
index 000000000000..6ca8122320e3
--- /dev/null
+++ b/drivers/usb/core/hub_pm.c
@@ -0,0 +1,1130 @@
+/*
+ * USB hub port suspend/resume support and runtime power management
+ */
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
+#include <linux/mutex.h>
+
+#include "hub.h"
+
+/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
+static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
+{
+	int ret = 0;
+
+	if (hub_is_superspeed(hub->hdev)) {
+		if ((portstatus & USB_PORT_STAT_LINK_STATE)
+				== USB_SS_PORT_LS_U3)
+			ret = 1;
+	} else {
+		if (portstatus & USB_PORT_STAT_SUSPEND)
+			ret = 1;
+	}
+
+	return ret;
+}
+
+/* Determine whether the device on a port is ready for a normal resume,
+ * is ready for a reset-resume, or should be disconnected.
+ */
+static int check_port_resume_type(struct usb_device *udev,
+		struct usb_hub *hub, int port1,
+		int status, unsigned portchange, unsigned portstatus)
+{
+	struct usb_port *port_dev = hub->ports[port1 - 1];
+
+	/* Is a warm reset needed to recover the connection? */
+	if (status == 0 && udev->reset_resume
+		&& usb_hub_port_warm_reset_required(hub, port1, portstatus)) {
+		/* pass */;
+	}
+	/* Is the device still present? */
+	else if (status || port_is_suspended(hub, portstatus) ||
+			!usb_port_is_power_on(hub, portstatus) ||
+			!(portstatus & USB_PORT_STAT_CONNECTION)) {
+		if (status >= 0)
+			status = -ENODEV;
+	}
+
+	/* Can't do a normal resume if the port isn't enabled,
+	 * so try a reset-resume instead.
+	 */
+	else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
+		if (udev->persist_enabled)
+			udev->reset_resume = 1;
+		else
+			status = -ENODEV;
+	}
+
+	if (status) {
+		dev_dbg(&port_dev->dev, "status %04x.%04x after resume, %d\n",
+				portchange, portstatus, status);
+	} else if (udev->reset_resume) {
+
+		/* Late port handoff can set status-change bits */
+		if (portchange & USB_PORT_STAT_C_CONNECTION)
+			usb_clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_CONNECTION);
+		if (portchange & USB_PORT_STAT_C_ENABLE)
+			usb_clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_ENABLE);
+	}
+
+	return status;
+}
+
+int usb_disable_ltm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+	/* Check if the roothub and device supports LTM. */
+	if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+			!usb_device_supports_ltm(udev))
+		return 0;
+
+	/* Clear Feature LTM Enable can only be sent if the device is
+	 * configured.
+	 */
+	if (!udev->actconfig)
+		return 0;
+
+	return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+			USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+			USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+	/* Check if the roothub and device supports LTM. */
+	if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+			!usb_device_supports_ltm(udev))
+		return;
+
+	/* Set Feature LTM Enable can only be sent if the device is
+	 * configured.
+	 */
+	if (!udev->actconfig)
+		return;
+
+	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+			USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+			USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
+/*
+ * usb_enable_remote_wakeup - enable remote wakeup for a device
+ * @udev: target device
+ *
+ * For USB-2 devices: Set the device's remote wakeup feature.
+ *
+ * For USB-3 devices: Assume there's only one function on the device and
+ * enable remote wake for the first interface.  FIXME if the interface
+ * association descriptor shows there's more than one function.
+ */
+static int usb_enable_remote_wakeup(struct usb_device *udev)
+{
+	if (udev->speed < USB_SPEED_SUPER)
+		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+				USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	else
+		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE,
+				USB_INTRF_FUNC_SUSPEND,
+				USB_INTRF_FUNC_SUSPEND_RW |
+					USB_INTRF_FUNC_SUSPEND_LP,
+				NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+/*
+ * usb_disable_remote_wakeup - disable remote wakeup for a device
+ * @udev: target device
+ *
+ * For USB-2 devices: Clear the device's remote wakeup feature.
+ *
+ * For USB-3 devices: Assume there's only one function on the device and
+ * disable remote wake for the first interface.  FIXME if the interface
+ * association descriptor shows there's more than one function.
+ */
+static int usb_disable_remote_wakeup(struct usb_device *udev)
+{
+	if (udev->speed < USB_SPEED_SUPER)
+		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+				USB_DEVICE_REMOTE_WAKEUP, 0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	else
+		return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
+				USB_INTRF_FUNC_SUSPEND,	0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+}
+
+/* Count of wakeup-enabled devices at or below udev */
+static unsigned wakeup_enabled_descendants(struct usb_device *udev)
+{
+	struct usb_hub *hub = usb_hub_to_struct_hub(udev);
+
+	return udev->do_remote_wakeup +
+			(hub ? hub->wakeup_enabled_descendants : 0);
+}
+
+/*
+ * usb_port_suspend - suspend a usb device's upstream port
+ * @udev: device that's no longer in active use, not a root hub
+ * Context: must be able to sleep; device not locked; pm locks held
+ *
+ * Suspends a USB device that isn't in active use, conserving power.
+ * Devices may wake out of a suspend, if anything important happens,
+ * using the remote wakeup mechanism.  They may also be taken out of
+ * suspend by the host, using usb_port_resume().  It's also routine
+ * to disconnect devices while they are suspended.
+ *
+ * This only affects the USB hardware for a device; its interfaces
+ * (and, for hubs, child devices) must already have been suspended.
+ *
+ * Selective port suspend reduces power; most suspended devices draw
+ * less than 500 uA.  It's also used in OTG, along with remote wakeup.
+ * All devices below the suspended port are also suspended.
+ *
+ * Devices leave suspend state when the host wakes them up.  Some devices
+ * also support "remote wakeup", where the device can activate the USB
+ * tree above them to deliver data, such as a keypress or packet.  In
+ * some cases, this wakes the USB host.
+ *
+ * Suspending OTG devices may trigger HNP, if that's been enabled
+ * between a pair of dual-role devices.  That will change roles, such
+ * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
+ *
+ * Devices on USB hub ports have only one "suspend" state, corresponding
+ * to ACPI D2, "may cause the device to lose some context".
+ * State transitions include:
+ *
+ *   - suspend, resume ... when the VBUS power link stays live
+ *   - suspend, disconnect ... VBUS lost
+ *
+ * Once VBUS drop breaks the circuit, the port it's using has to go through
+ * normal re-enumeration procedures, starting with enabling VBUS power.
+ * Other than re-initializing the hub (plug/unplug, except for root hubs),
+ * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
+ * timer, no SRP, no requests through sysfs.
+ *
+ * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
+ * suspended until their bus goes into global suspend (i.e., the root
+ * hub is suspended).  Nevertheless, we change @udev->state to
+ * USB_STATE_SUSPENDED as this is the device's "logical" state.  The actual
+ * upstream port setting is stored in @udev->port_is_suspended.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
+{
+	struct usb_hub	*hub = usb_hub_to_struct_hub(udev->parent);
+	struct usb_port *port_dev = hub->ports[udev->portnum - 1];
+	int		port1 = udev->portnum;
+	int		status;
+	bool		really_suspend = true;
+
+	usb_lock_port(port_dev);
+
+	/* enable remote wakeup when appropriate; this lets the device
+	 * wake up the upstream hub (including maybe the root hub).
+	 *
+	 * NOTE:  OTG devices may issue remote wakeup (or SRP) even when
+	 * we don't explicitly enable it here.
+	 */
+	if (udev->do_remote_wakeup) {
+		status = usb_enable_remote_wakeup(udev);
+		if (status) {
+			dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
+					status);
+			/* bail if autosuspend is requested */
+			if (PMSG_IS_AUTO(msg))
+				goto err_wakeup;
+		}
+	}
+
+	/* disable USB2 hardware LPM */
+	if (udev->usb2_hw_lpm_enabled == 1)
+		usb_set_usb2_hardware_lpm(udev, 0);
+
+	if (usb_disable_ltm(udev)) {
+		dev_err(&udev->dev, "Failed to disable LTM before suspend\n.");
+		status = -ENOMEM;
+		if (PMSG_IS_AUTO(msg))
+			goto err_ltm;
+	}
+	if (usb_unlocked_disable_lpm(udev)) {
+		dev_err(&udev->dev, "Failed to disable LPM before suspend\n.");
+		status = -ENOMEM;
+		if (PMSG_IS_AUTO(msg))
+			goto err_lpm3;
+	}
+
+	/* see 7.1.7.6 */
+	if (hub_is_superspeed(hub->hdev))
+		status = usb_hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
+
+	/*
+	 * For system suspend, we do not need to enable the suspend feature
+	 * on individual USB-2 ports.  The devices will automatically go
+	 * into suspend a few ms after the root hub stops sending packets.
+	 * The USB 2.0 spec calls this "global suspend".
+	 *
+	 * However, many USB hubs have a bug: They don't relay wakeup requests
+	 * from a downstream port if the port's suspend feature isn't on.
+	 * Therefore we will turn on the suspend feature if udev or any of its
+	 * descendants is enabled for remote wakeup.
+	 */
+	else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
+		status = usb_set_port_feature(hub->hdev, port1,
+				USB_PORT_FEAT_SUSPEND);
+	else {
+		really_suspend = false;
+		status = 0;
+	}
+	if (status) {
+		dev_dbg(&port_dev->dev, "can't suspend, status %d\n", status);
+
+		/* Try to enable USB3 LPM and LTM again */
+		usb_unlocked_enable_lpm(udev);
+ err_lpm3:
+		usb_enable_ltm(udev);
+ err_ltm:
+		/* Try to enable USB2 hardware LPM again */
+		if (udev->usb2_hw_lpm_capable == 1)
+			usb_set_usb2_hardware_lpm(udev, 1);
+
+		if (udev->do_remote_wakeup)
+			(void) usb_disable_remote_wakeup(udev);
+ err_wakeup:
+
+		/* System sleep transitions should never fail */
+		if (!PMSG_IS_AUTO(msg))
+			status = 0;
+	} else {
+		dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
+				(PMSG_IS_AUTO(msg) ? "auto-" : ""),
+				udev->do_remote_wakeup);
+		if (really_suspend) {
+			udev->port_is_suspended = 1;
+
+			/* device has up to 10 msec to fully suspend */
+			msleep(10);
+		}
+		usb_set_device_state(udev, USB_STATE_SUSPENDED);
+	}
+
+	if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled
+			&& test_and_clear_bit(port1, hub->child_usage_bits))
+		pm_runtime_put_sync(&port_dev->dev);
+
+	usb_mark_last_busy(hub->hdev);
+
+	usb_unlock_port(port_dev);
+	return status;
+}
+
+/*
+ * If the USB "suspend" state is in use (rather than "global suspend"),
+ * many devices will be individually taken out of suspend state using
+ * special "resume" signaling.  This routine kicks in shortly after
+ * hardware resume signaling is finished, either because of selective
+ * resume (by host) or remote wakeup (by device) ... now see what changed
+ * in the tree that's rooted at this device.
+ *
+ * If @udev->reset_resume is set then the device is reset before the
+ * status check is done.
+ */
+static int finish_port_resume(struct usb_device *udev)
+{
+	int	status = 0;
+	u16	devstatus = 0;
+
+	/* caller owns the udev device lock */
+	dev_dbg(&udev->dev, "%s\n",
+		udev->reset_resume ? "finish reset-resume" : "finish resume");
+
+	/* usb ch9 identifies four variants of SUSPENDED, based on what
+	 * state the device resumes to.  Linux currently won't see the
+	 * first two on the host side; they'd be inside hub_port_init()
+	 * during many timeouts, but khubd can't suspend until later.
+	 */
+	usb_set_device_state(udev, udev->actconfig
+			? USB_STATE_CONFIGURED
+			: USB_STATE_ADDRESS);
+
+	/* 10.5.4.5 says not to reset a suspended port if the attached
+	 * device is enabled for remote wakeup.  Hence the reset
+	 * operation is carried out here, after the port has been
+	 * resumed.
+	 */
+	if (udev->reset_resume) {
+		/*
+		 * If the device morphs or switches modes when it is reset,
+		 * we don't want to perform a reset-resume.  We'll fail the
+		 * resume, which will cause a logical disconnect, and then
+		 * the device will be rediscovered.
+		 */
+ retry_reset_resume:
+		if (udev->quirks & USB_QUIRK_RESET)
+			status = -ENODEV;
+		else
+			status = usb_reset_and_verify_device(udev);
+	}
+
+	/* 10.5.4.5 says be sure devices in the tree are still there.
+	 * For now let's assume the device didn't go crazy on resume,
+	 * and device drivers will know about any resume quirks.
+	 */
+	if (status == 0) {
+		devstatus = 0;
+		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
+
+		/* If a normal resume failed, try doing a reset-resume */
+		if (status && !udev->reset_resume && udev->persist_enabled) {
+			dev_dbg(&udev->dev, "retry with reset-resume\n");
+			udev->reset_resume = 1;
+			goto retry_reset_resume;
+		}
+	}
+
+	if (status) {
+		dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
+				status);
+	/*
+	 * There are a few quirky devices which violate the standard
+	 * by claiming to have remote wakeup enabled after a reset,
+	 * which crash if the feature is cleared, hence check for
+	 * udev->reset_resume
+	 */
+	} else if (udev->actconfig && !udev->reset_resume) {
+		if (udev->speed < USB_SPEED_SUPER) {
+			if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
+				status = usb_disable_remote_wakeup(udev);
+		} else {
+			status = usb_get_status(udev, USB_RECIP_INTERFACE, 0,
+					&devstatus);
+			if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP
+					| USB_INTRF_STAT_FUNC_RW))
+				status = usb_disable_remote_wakeup(udev);
+		}
+
+		if (status)
+			dev_dbg(&udev->dev,
+				"disable remote wakeup, status %d\n",
+				status);
+		status = 0;
+	}
+	return status;
+}
+
+/*
+ * usb_port_resume - re-activate a suspended usb device's upstream port
+ * @udev: device to re-activate, not a root hub
+ * Context: must be able to sleep; device not locked; pm locks held
+ *
+ * This will re-activate the suspended device, increasing power usage
+ * while letting drivers communicate again with its endpoints.
+ * USB resume explicitly guarantees that the power session between
+ * the host and the device is the same as it was when the device
+ * suspended.
+ *
+ * If @udev->reset_resume is set then this routine won't check that the
+ * port is still enabled.  Furthermore, finish_port_resume() above will
+ * reset @udev.  The end result is that a broken power session can be
+ * recovered and @udev will appear to persist across a loss of VBUS power.
+ *
+ * For example, if a host controller doesn't maintain VBUS suspend current
+ * during a system sleep or is reset when the system wakes up, all the USB
+ * power sessions below it will be broken.  This is especially troublesome
+ * for mass-storage devices containing mounted filesystems, since the
+ * device will appear to have disconnected and all the memory mappings
+ * to it will be lost.  Using the USB_PERSIST facility, the device can be
+ * made to appear as if it had not disconnected.
+ *
+ * This facility can be dangerous.  Although usb_reset_and_verify_device() makes
+ * every effort to insure that the same device is present after the
+ * reset as before, it cannot provide a 100% guarantee.  Furthermore it's
+ * quite possible for a device to remain unaltered but its media to be
+ * changed.  If the user replaces a flash memory card while the system is
+ * asleep, he will have only himself to blame when the filesystem on the
+ * new card is corrupted and the system crashes.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_port_resume(struct usb_device *udev, pm_message_t msg)
+{
+	struct usb_hub	*hub = usb_hub_to_struct_hub(udev->parent);
+	struct usb_port *port_dev = hub->ports[udev->portnum  - 1];
+	int		port1 = udev->portnum;
+	int		status;
+	u16		portchange, portstatus;
+
+	if (!test_and_set_bit(port1, hub->child_usage_bits)) {
+		status = pm_runtime_get_sync(&port_dev->dev);
+		if (status < 0) {
+			dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
+					status);
+			return status;
+		}
+	}
+
+	usb_lock_port(port_dev);
+
+	/* Skip the initial Clear-Suspend step for a remote wakeup */
+	status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
+	if (status == 0 && !port_is_suspended(hub, portstatus))
+		goto SuspendCleared;
+
+	/* see 7.1.7.7; affects power usage, but not budgeting */
+	if (hub_is_superspeed(hub->hdev))
+		status = usb_hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
+	else
+		status = usb_clear_port_feature(hub->hdev,
+				port1, USB_PORT_FEAT_SUSPEND);
+	if (status) {
+		dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
+	} else {
+		/* drive resume for at least 20 msec */
+		dev_dbg(&udev->dev, "usb %sresume\n",
+				(PMSG_IS_AUTO(msg) ? "auto-" : ""));
+		msleep(25);
+
+		/* Virtual root hubs can trigger on GET_PORT_STATUS to
+		 * stop resume signaling.  Then finish the resume
+		 * sequence.
+		 */
+		status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
+
+		/* TRSMRCY = 10 msec */
+		msleep(10);
+	}
+
+ SuspendCleared:
+	if (status == 0) {
+		udev->port_is_suspended = 0;
+		if (hub_is_superspeed(hub->hdev)) {
+			if (portchange & USB_PORT_STAT_C_LINK_STATE)
+				usb_clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_PORT_LINK_STATE);
+		} else {
+			if (portchange & USB_PORT_STAT_C_SUSPEND)
+				usb_clear_port_feature(hub->hdev, port1,
+						USB_PORT_FEAT_C_SUSPEND);
+		}
+	}
+
+	status = check_port_resume_type(udev,
+			hub, port1, status, portchange, portstatus);
+	if (status == 0)
+		status = finish_port_resume(udev);
+	if (status < 0) {
+		dev_dbg(&udev->dev, "can't resume, status %d\n", status);
+		usb_hub_port_logical_disconnect(hub, port1);
+	} else  {
+		/* Try to enable USB2 hardware LPM */
+		if (udev->usb2_hw_lpm_capable == 1)
+			usb_set_usb2_hardware_lpm(udev, 1);
+
+		/* Try to enable USB3 LTM and LPM */
+		usb_enable_ltm(udev);
+		usb_unlocked_enable_lpm(udev);
+	}
+
+	usb_unlock_port(port_dev);
+
+	return status;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+int usb_remote_wakeup(struct usb_device *udev)
+{
+	int	status = 0;
+
+	usb_lock_device(udev);
+	if (udev->state == USB_STATE_SUSPENDED) {
+		dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
+		status = usb_autoresume_device(udev);
+		if (status == 0) {
+			/* Let the drivers do their thing, then... */
+			usb_autosuspend_device(udev);
+		}
+	}
+	usb_unlock_device(udev);
+	return status;
+}
+
+/* Returns 1 if there was a remote wakeup and a connect status change. */
+int usb_hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+		u16 portstatus, u16 portchange)
+		__must_hold(&port_dev->status_lock)
+{
+	struct usb_port *port_dev = hub->ports[port - 1];
+	struct usb_device *hdev;
+	struct usb_device *udev;
+	int connect_change = 0;
+	int ret;
+
+	hdev = hub->hdev;
+	udev = port_dev->child;
+	if (!hub_is_superspeed(hdev)) {
+		if (!(portchange & USB_PORT_STAT_C_SUSPEND))
+			return 0;
+		usb_clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
+	} else {
+		if (!udev || udev->state != USB_STATE_SUSPENDED ||
+				 (portstatus & USB_PORT_STAT_LINK_STATE) !=
+				 USB_SS_PORT_LS_U0)
+			return 0;
+	}
+
+	if (udev) {
+		/* TRSMRCY = 10 msec */
+		msleep(10);
+
+		usb_unlock_port(port_dev);
+		ret = usb_remote_wakeup(udev);
+		usb_lock_port(port_dev);
+		if (ret < 0)
+			connect_change = 1;
+	} else {
+		ret = -ENODEV;
+		usb_hub_port_disable(hub, port, 1);
+	}
+	dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
+	return connect_change;
+}
+
+#else
+
+static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
+		u16 portstatus, u16 portchange)
+{
+	return 0;
+}
+
+#endif
+
+static int check_ports_changed(struct usb_hub *hub)
+{
+	int port1;
+
+	for (port1 = 1; port1 <= hub->hdev->maxchild; ++port1) {
+		u16 portstatus, portchange;
+		int status;
+
+		status = usb_hub_port_status(hub, port1, &portstatus, &portchange);
+		if (!status && portchange)
+			return 1;
+	}
+	return 0;
+}
+
+int usb_hub_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+	struct usb_hub		*hub = usb_get_intfdata(intf);
+	struct usb_device	*hdev = hub->hdev;
+	unsigned		port1;
+	int			status;
+
+	/*
+	 * Warn if children aren't already suspended.
+	 * Also, add up the number of wakeup-enabled descendants.
+	 */
+	hub->wakeup_enabled_descendants = 0;
+	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+		struct usb_port *port_dev = hub->ports[port1 - 1];
+		struct usb_device *udev = port_dev->child;
+
+		if (udev && udev->can_submit) {
+			dev_warn(&port_dev->dev, "not suspended yet\n");
+			if (PMSG_IS_AUTO(msg))
+				return -EBUSY;
+		}
+		if (udev)
+			hub->wakeup_enabled_descendants +=
+					wakeup_enabled_descendants(udev);
+	}
+
+	if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
+		/* check if there are changes pending on hub ports */
+		if (check_ports_changed(hub)) {
+			if (PMSG_IS_AUTO(msg))
+				return -EBUSY;
+			pm_wakeup_event(&hdev->dev, 2000);
+		}
+	}
+
+	if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
+		/* Enable hub to send remote wakeup for all ports. */
+		for (port1 = 1; port1 <= hdev->maxchild; port1++) {
+			status = usb_set_port_feature(hdev,
+					port1 |
+					USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
+					USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
+					USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
+					USB_PORT_FEAT_REMOTE_WAKE_MASK);
+		}
+	}
+
+	dev_dbg(&intf->dev, "%s\n", __func__);
+
+	/* stop khubd and related activity */
+	usb_hub_quiesce(hub, HUB_SUSPEND);
+	return 0;
+}
+
+int usb_hub_resume(struct usb_interface *intf)
+{
+	struct usb_hub *hub = usb_get_intfdata(intf);
+
+	dev_dbg(&intf->dev, "%s\n", __func__);
+	usb_hub_activate(hub, HUB_RESUME);
+	return 0;
+}
+
+int usb_hub_reset_resume(struct usb_interface *intf)
+{
+	struct usb_hub *hub = usb_get_intfdata(intf);
+
+	dev_dbg(&intf->dev, "%s\n", __func__);
+	usb_hub_activate(hub, HUB_RESET_RESUME);
+	return 0;
+}
+
+/**
+ * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
+ * @rhdev: struct usb_device for the root hub
+ *
+ * The USB host controller driver calls this function when its root hub
+ * is resumed and Vbus power has been interrupted or the controller
+ * has been reset.  The routine marks @rhdev as having lost power.
+ * When the hub driver is resumed it will take notice and carry out
+ * power-session recovery for all the "USB-PERSIST"-enabled child devices;
+ * the others will be disconnected.
+ */
+void usb_root_hub_lost_power(struct usb_device *rhdev)
+{
+	dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+	rhdev->reset_resume = 1;
+}
+EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
+
+static const char * const usb3_lpm_names[]  = {
+	"U0",
+	"U1",
+	"U2",
+	"U3",
+};
+
+/*
+ * Send a Set SEL control transfer to the device, prior to enabling
+ * device-initiated U1 or U2.  This lets the device know the exit latencies from
+ * the time the device initiates a U1 or U2 exit, to the time it will receive a
+ * packet from the host.
+ *
+ * This function will fail if the SEL or PEL values for udev are greater than
+ * the maximum allowed values for the link state to be enabled.
+ */
+static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
+{
+	struct usb_set_sel_req *sel_values;
+	unsigned long long u1_sel;
+	unsigned long long u1_pel;
+	unsigned long long u2_sel;
+	unsigned long long u2_pel;
+	int ret;
+
+	if (udev->state != USB_STATE_CONFIGURED)
+		return 0;
+
+	/* Convert SEL and PEL stored in ns to us */
+	u1_sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
+	u1_pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
+	u2_sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
+	u2_pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
+
+	/*
+	 * Make sure that the calculated SEL and PEL values for the link
+	 * state we're enabling aren't bigger than the max SEL/PEL
+	 * value that will fit in the SET SEL control transfer.
+	 * Otherwise the device would get an incorrect idea of the exit
+	 * latency for the link state, and could start a device-initiated
+	 * U1/U2 when the exit latencies are too high.
+	 */
+	if ((state == USB3_LPM_U1 &&
+				(u1_sel > USB3_LPM_MAX_U1_SEL_PEL ||
+				 u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) ||
+			(state == USB3_LPM_U2 &&
+			 (u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
+			  u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
+		dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n",
+				usb3_lpm_names[state], u1_sel, u1_pel);
+		return -EINVAL;
+	}
+
+	/*
+	 * If we're enabling device-initiated LPM for one link state,
+	 * but the other link state has a too high SEL or PEL value,
+	 * just set those values to the max in the Set SEL request.
+	 */
+	if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL)
+		u1_sel = USB3_LPM_MAX_U1_SEL_PEL;
+
+	if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL)
+		u1_pel = USB3_LPM_MAX_U1_SEL_PEL;
+
+	if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL)
+		u2_sel = USB3_LPM_MAX_U2_SEL_PEL;
+
+	if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL)
+		u2_pel = USB3_LPM_MAX_U2_SEL_PEL;
+
+	/*
+	 * usb_enable_lpm() can be called as part of a failed device reset,
+	 * which may be initiated by an error path of a mass storage driver.
+	 * Therefore, use GFP_NOIO.
+	 */
+	sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO);
+	if (!sel_values)
+		return -ENOMEM;
+
+	sel_values->u1_sel = u1_sel;
+	sel_values->u1_pel = u1_pel;
+	sel_values->u2_sel = cpu_to_le16(u2_sel);
+	sel_values->u2_pel = cpu_to_le16(u2_pel);
+
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			USB_REQ_SET_SEL,
+			USB_RECIP_DEVICE,
+			0, 0,
+			sel_values, sizeof *(sel_values),
+			USB_CTRL_SET_TIMEOUT);
+	kfree(sel_values);
+	return ret;
+}
+
+/*
+ * Enable or disable device-initiated U1 or U2 transitions.
+ */
+static int usb_set_device_initiated_lpm(struct usb_device *udev,
+		enum usb3_link_state state, bool enable)
+{
+	int ret;
+	int feature;
+
+	switch (state) {
+	case USB3_LPM_U1:
+		feature = USB_DEVICE_U1_ENABLE;
+		break;
+	case USB3_LPM_U2:
+		feature = USB_DEVICE_U2_ENABLE;
+		break;
+	default:
+		dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
+				__func__, enable ? "enable" : "disable");
+		return -EINVAL;
+	}
+
+	if (udev->state != USB_STATE_CONFIGURED) {
+		dev_dbg(&udev->dev, "%s: Can't %s %s state for unconfigured device.\n",
+				__func__, enable ? "enable" : "disable",
+				usb3_lpm_names[state]);
+		return 0;
+	}
+
+	if (enable) {
+		/*
+		 * Now send the control transfer to enable device-initiated LPM
+		 * for either U1 or U2.
+		 */
+		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_SET_FEATURE,
+				USB_RECIP_DEVICE,
+				feature,
+				0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	} else {
+		ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+				USB_REQ_CLEAR_FEATURE,
+				USB_RECIP_DEVICE,
+				feature,
+				0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+	}
+	if (ret < 0) {
+		dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
+				enable ? "Enable" : "Disable",
+				usb3_lpm_names[state]);
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static int usb_set_lpm_timeout(struct usb_device *udev,
+		enum usb3_link_state state, int timeout)
+{
+	int ret;
+	int feature;
+
+	switch (state) {
+	case USB3_LPM_U1:
+		feature = USB_PORT_FEAT_U1_TIMEOUT;
+		break;
+	case USB3_LPM_U2:
+		feature = USB_PORT_FEAT_U2_TIMEOUT;
+		break;
+	default:
+		dev_warn(&udev->dev, "%s: Can't set timeout for non-U1 or U2 state.\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (state == USB3_LPM_U1 && timeout > USB3_LPM_U1_MAX_TIMEOUT &&
+			timeout != USB3_LPM_DEVICE_INITIATED) {
+		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, which is a reserved value.\n",
+				usb3_lpm_names[state], timeout);
+		return -EINVAL;
+	}
+
+	ret = usb_set_port_feature(udev->parent,
+			USB_PORT_LPM_TIMEOUT(timeout) | udev->portnum,
+			feature);
+	if (ret < 0) {
+		dev_warn(&udev->dev, "Failed to set %s timeout to 0x%x, error code %i\n",
+				usb3_lpm_names[state],
+				timeout, ret);
+		return -EBUSY;
+	}
+	if (state == USB3_LPM_U1)
+		udev->u1_params.timeout = timeout;
+	else
+		udev->u2_params.timeout = timeout;
+	return 0;
+}
+
+/*
+ * Enable the hub-initiated U1/U2 idle timeouts, and enable device-initiated
+ * U1/U2 entry.
+ *
+ * We will attempt to enable U1 or U2, but there are no guarantees that the
+ * control transfers to set the hub timeout or enable device-initiated U1/U2
+ * will be successful.
+ *
+ * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
+ * driver know about it.  If that call fails, it should be harmless, and just
+ * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
+ */
+static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+		enum usb3_link_state state)
+{
+	int timeout, ret;
+	__u8 u1_mel = udev->bos->ss_cap->bU1devExitLat;
+	__le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat;
+
+	/* If the device says it doesn't have *any* exit latency to come out of
+	 * U1 or U2, it's probably lying.  Assume it doesn't implement that link
+	 * state.
+	 */
+	if ((state == USB3_LPM_U1 && u1_mel == 0) ||
+			(state == USB3_LPM_U2 && u2_mel == 0))
+		return;
+
+	/*
+	 * First, let the device know about the exit latencies
+	 * associated with the link state we're about to enable.
+	 */
+	ret = usb_req_set_sel(udev, state);
+	if (ret < 0) {
+		dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n",
+				usb3_lpm_names[state]);
+		return;
+	}
+
+	/* We allow the host controller to set the U1/U2 timeout internally
+	 * first, so that it can change its schedule to account for the
+	 * additional latency to send data to a device in a lower power
+	 * link state.
+	 */
+	timeout = hcd->driver->enable_usb3_lpm_timeout(hcd, udev, state);
+
+	/* xHCI host controller doesn't want to enable this LPM state. */
+	if (timeout == 0)
+		return;
+
+	if (timeout < 0) {
+		dev_warn(&udev->dev, "Could not enable %s link state, xHCI error %i.\n",
+				usb3_lpm_names[state], timeout);
+		return;
+	}
+
+	if (usb_set_lpm_timeout(udev, state, timeout))
+		/* If we can't set the parent hub U1/U2 timeout,
+		 * device-initiated LPM won't be allowed either, so let the xHCI
+		 * host know that this link state won't be enabled.
+		 */
+		hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
+
+	/* Only a configured device will accept the Set Feature U1/U2_ENABLE */
+	else if (udev->actconfig)
+		usb_set_device_initiated_lpm(udev, state, true);
+
+}
+
+/*
+ * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated
+ * U1/U2 entry.
+ *
+ * If this function returns -EBUSY, the parent hub will still allow U1/U2 entry.
+ * If zero is returned, the parent will not allow the link to go into U1/U2.
+ *
+ * If zero is returned, device-initiated U1/U2 entry may still be enabled, but
+ * it won't have an effect on the bus link state because the parent hub will
+ * still disallow device-initiated U1/U2 entry.
+ *
+ * If zero is returned, the xHCI host controller may still think U1/U2 entry is
+ * possible.  The result will be slightly more bus bandwidth will be taken up
+ * (to account for U1/U2 exit latency), but it should be harmless.
+ */
+static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
+		enum usb3_link_state state)
+{
+	int feature;
+
+	switch (state) {
+	case USB3_LPM_U1:
+		feature = USB_PORT_FEAT_U1_TIMEOUT;
+		break;
+	case USB3_LPM_U2:
+		feature = USB_PORT_FEAT_U2_TIMEOUT;
+		break;
+	default:
+		dev_warn(&udev->dev, "%s: Can't disable non-U1 or U2 state.\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	if (usb_set_lpm_timeout(udev, state, 0))
+		return -EBUSY;
+
+	usb_set_device_initiated_lpm(udev, state, false);
+
+	if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
+		dev_warn(&udev->dev, "Could not disable xHCI %s timeout, bus schedule bandwidth may be impacted.\n",
+				usb3_lpm_names[state]);
+	return 0;
+}
+
+/*
+ * Disable hub-initiated and device-initiated U1 and U2 entry.
+ * Caller must own the bandwidth_mutex.
+ *
+ * This will call usb_enable_lpm() on failure, which will decrement
+ * lpm_disable_count, and will re-enable LPM if lpm_disable_count reaches zero.
+ */
+int usb_disable_lpm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd;
+
+	if (!udev || !udev->parent ||
+			udev->speed != USB_SPEED_SUPER ||
+			!udev->lpm_capable)
+		return 0;
+
+	hcd = bus_to_hcd(udev->bus);
+	if (!hcd || !hcd->driver->disable_usb3_lpm_timeout)
+		return 0;
+
+	udev->lpm_disable_count++;
+	if ((udev->u1_params.timeout == 0 && udev->u2_params.timeout == 0))
+		return 0;
+
+	/* If LPM is enabled, attempt to disable it. */
+	if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
+		goto enable_lpm;
+	if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
+		goto enable_lpm;
+
+	return 0;
+
+enable_lpm:
+	usb_enable_lpm(udev);
+	return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(usb_disable_lpm);
+
+/* Grab the bandwidth_mutex before calling usb_disable_lpm() */
+int usb_unlocked_disable_lpm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+	int ret;
+
+	if (!hcd)
+		return -EINVAL;
+
+	mutex_lock(hcd->bandwidth_mutex);
+	ret = usb_disable_lpm(udev);
+	mutex_unlock(hcd->bandwidth_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
+
+/*
+ * Attempt to enable device-initiated and hub-initiated U1 and U2 entry.  The
+ * xHCI host policy may prevent U1 or U2 from being enabled.
+ *
+ * Other callers may have disabled link PM, so U1 and U2 entry will be disabled
+ * until the lpm_disable_count drops to zero.  Caller must own the
+ * bandwidth_mutex.
+ */
+void usb_enable_lpm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd;
+
+	if (!udev || !udev->parent ||
+			udev->speed != USB_SPEED_SUPER ||
+			!udev->lpm_capable)
+		return;
+
+	udev->lpm_disable_count--;
+	hcd = bus_to_hcd(udev->bus);
+	/* Double check that we can both enable and disable LPM.
+	 * Device must be configured to accept set feature U1/U2 timeout.
+	 */
+	if (!hcd || !hcd->driver->enable_usb3_lpm_timeout ||
+			!hcd->driver->disable_usb3_lpm_timeout)
+		return;
+
+	if (udev->lpm_disable_count > 0)
+		return;
+
+	usb_enable_link_state(hcd, udev, USB3_LPM_U1);
+	usb_enable_link_state(hcd, udev, USB3_LPM_U2);
+}
+EXPORT_SYMBOL_GPL(usb_enable_lpm);
+
+/* Grab the bandwidth_mutex before calling usb_enable_lpm() */
+void usb_unlocked_enable_lpm(struct usb_device *udev)
+{
+	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+	if (!hcd)
+		return;
+
+	mutex_lock(hcd->bandwidth_mutex);
+	usb_enable_lpm(udev);
+	mutex_unlock(hcd->bandwidth_mutex);
+}
+EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index d9d08720c386..abfc6ab2d524 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -75,7 +75,39 @@ extern int usb_resume_complete(struct device *dev);
 extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
 extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
 
+extern int usb_disable_lpm(struct usb_device *udev);
+extern void usb_enable_lpm(struct usb_device *udev);
+extern int usb_unlocked_disable_lpm(struct usb_device *udev);
+extern void usb_unlocked_enable_lpm(struct usb_device *udev);
+extern int usb_disable_ltm(struct usb_device *udev);
+extern void usb_enable_ltm(struct usb_device *udev);
 #else
+static inline int usb_disable_lpm(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline void usb_enable_lpm(struct usb_device *udev)
+{
+}
+
+static inline int usb_unlocked_disable_lpm(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline void usb_unlocked_enable_lpm(struct usb_device *udev)
+{
+}
+
+static inline int usb_disable_ltm(struct usb_device *udev)
+{
+	return 0;
+}
+
+static inline void usb_enable_ltm(struct usb_device *udev)
+{
+}
 
 static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 {
@@ -93,12 +125,13 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
 extern void usb_autosuspend_device(struct usb_device *udev);
 extern int usb_autoresume_device(struct usb_device *udev);
-extern int usb_remote_wakeup(struct usb_device *dev);
 extern int usb_runtime_suspend(struct device *dev);
 extern int usb_runtime_resume(struct device *dev);
 extern int usb_runtime_idle(struct device *dev);
 extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable);
 
+extern int usb_remote_wakeup(struct usb_device *dev);
+
 #else
 
 #define usb_autosuspend_device(udev)		do {} while (0)
@@ -111,6 +144,12 @@ static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
 {
 	return 0;
 }
+
+static inline int usb_remote_wakeup(struct usb_device *dev)
+{
+	return 0;
+}
+
 #endif
 
 extern struct bus_type usb_bus_type;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 909b56380c2f..db4e37d1382e 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -690,15 +690,6 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
 { }
 #endif
 
-extern int usb_disable_lpm(struct usb_device *udev);
-extern void usb_enable_lpm(struct usb_device *udev);
-/* Same as above, but these functions lock/unlock the bandwidth_mutex. */
-extern int usb_unlocked_disable_lpm(struct usb_device *udev);
-extern void usb_unlocked_enable_lpm(struct usb_device *udev);
-
-extern int usb_disable_ltm(struct usb_device *udev);
-extern void usb_enable_ltm(struct usb_device *udev);
-
 static inline bool usb_device_supports_ltm(struct usb_device *udev)
 {
 	if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap)

--
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