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