Commit 9262c19d authored by Dan Williams's avatar Dan Williams Committed by Greg Kroah-Hartman

usb: disable port power control if not supported in wHubCharacteristics

A hub indicates whether it supports per-port power control via the
wHubCharacteristics field in its descriptor.  If it is not supported
a hub will still emulate ClearPortPower(PORT_POWER) requests by
stopping the link state machine.  However, since this does not save
power do not bother suspending.

This also consolidates support checks into a
hub_is_port_power_switchable() helper.
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 600856c2
...@@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) ...@@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
int port1; int port1;
unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
unsigned delay; unsigned delay;
u16 wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
/* Enable power on each port. Some hubs have reserved values /* Enable power on each port. Some hubs have reserved values
* of LPSM (> 2) in their descriptors, even though they are * of LPSM (> 2) in their descriptors, even though they are
...@@ -827,7 +825,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) ...@@ -827,7 +825,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
* but only emulate it. In all cases, the ports won't work * but only emulate it. In all cases, the ports won't work
* unless we send these messages to the hub. * unless we send these messages to the hub.
*/ */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) if (hub_is_port_power_switchable(hub))
dev_dbg(hub->intfdev, "enabling power on all ports\n"); dev_dbg(hub->intfdev, "enabling power on all ports\n");
else else
dev_dbg(hub->intfdev, "trying to enable port power on " dev_dbg(hub->intfdev, "trying to enable port power on "
...@@ -4417,8 +4415,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, ...@@ -4417,8 +4415,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
struct usb_device *hdev = hub->hdev; struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev; struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus); struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
unsigned wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev; struct usb_device *udev;
int status, i; int status, i;
unsigned unit_load; unsigned unit_load;
...@@ -4503,7 +4499,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, ...@@ -4503,7 +4499,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
test_bit(port1, hub->removed_bits)) { test_bit(port1, hub->removed_bits)) {
/* maybe switch power back on (e.g. root hub was reset) */ /* maybe switch power back on (e.g. root hub was reset) */
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 if (hub_is_port_power_switchable(hub)
&& !port_is_power_on(hub, portstatus)) && !port_is_power_on(hub, portstatus))
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
......
...@@ -112,6 +112,16 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1, ...@@ -112,6 +112,16 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1,
extern int usb_clear_port_feature(struct usb_device *hdev, extern int usb_clear_port_feature(struct usb_device *hdev,
int port1, int feature); int port1, int feature);
static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
{
__le16 hcs;
if (!hub)
return false;
hcs = hub->descriptor->wHubCharacteristics;
return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
}
static inline int hub_port_debounce_be_connected(struct usb_hub *hub, static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
int port1) int port1)
{ {
......
...@@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) ...@@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
pm_runtime_set_active(&port_dev->dev); pm_runtime_set_active(&port_dev->dev);
/* It would be dangerous if user space couldn't /*
* prevent usb device from being powered off. So don't * Do not enable port runtime pm if the hub does not support
* enable port runtime pm if failed to expose port's pm qos. * power switching. Also, userspace must have final say of
* whether a port is permitted to power-off. Do not enable
* runtime pm if we fail to expose pm_qos_no_power_off.
*/ */
if (!dev_pm_qos_expose_flags(&port_dev->dev, if (hub_is_port_power_switchable(hub)
PM_QOS_FLAG_NO_POWER_OFF)) && dev_pm_qos_expose_flags(&port_dev->dev,
PM_QOS_FLAG_NO_POWER_OFF) == 0)
pm_runtime_enable(&port_dev->dev); pm_runtime_enable(&port_dev->dev);
device_enable_async_suspend(&port_dev->dev); device_enable_async_suspend(&port_dev->dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment