diff options
author | Dan Williams <dan.j.williams@intel.com> | 2014-05-20 21:08:12 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-27 19:35:09 -0400 |
commit | 9262c19d14c433a6a1ba25c3ff897cb89e412309 (patch) | |
tree | 50f83b859b251a3e842953e4fb9619a05ece02da /drivers/usb | |
parent | 600856c231ccb0cbf8afcf09066a8ab2a93ab03d (diff) |
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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hub.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 10 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 13 |
3 files changed, 20 insertions, 11 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5f43c22ba787..77b91888abef 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) | |||
818 | int port1; | 818 | int port1; |
819 | unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; | 819 | unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; |
820 | unsigned delay; | 820 | unsigned delay; |
821 | u16 wHubCharacteristics = | ||
822 | le16_to_cpu(hub->descriptor->wHubCharacteristics); | ||
823 | 821 | ||
824 | /* Enable power on each port. Some hubs have reserved values | 822 | /* Enable power on each port. Some hubs have reserved values |
825 | * of LPSM (> 2) in their descriptors, even though they are | 823 | * 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 | * but only emulate it. In all cases, the ports won't work | 825 | * but only emulate it. In all cases, the ports won't work |
828 | * unless we send these messages to the hub. | 826 | * unless we send these messages to the hub. |
829 | */ | 827 | */ |
830 | if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2) | 828 | if (hub_is_port_power_switchable(hub)) |
831 | dev_dbg(hub->intfdev, "enabling power on all ports\n"); | 829 | dev_dbg(hub->intfdev, "enabling power on all ports\n"); |
832 | else | 830 | else |
833 | dev_dbg(hub->intfdev, "trying to enable port power on " | 831 | 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 | struct usb_device *hdev = hub->hdev; | 4415 | struct usb_device *hdev = hub->hdev; |
4418 | struct device *hub_dev = hub->intfdev; | 4416 | struct device *hub_dev = hub->intfdev; |
4419 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); | 4417 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); |
4420 | unsigned wHubCharacteristics = | ||
4421 | le16_to_cpu(hub->descriptor->wHubCharacteristics); | ||
4422 | struct usb_device *udev; | 4418 | struct usb_device *udev; |
4423 | int status, i; | 4419 | int status, i; |
4424 | unsigned unit_load; | 4420 | unsigned unit_load; |
@@ -4503,7 +4499,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
4503 | test_bit(port1, hub->removed_bits)) { | 4499 | test_bit(port1, hub->removed_bits)) { |
4504 | 4500 | ||
4505 | /* maybe switch power back on (e.g. root hub was reset) */ | 4501 | /* maybe switch power back on (e.g. root hub was reset) */ |
4506 | if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 | 4502 | if (hub_is_port_power_switchable(hub) |
4507 | && !port_is_power_on(hub, portstatus)) | 4503 | && !port_is_power_on(hub, portstatus)) |
4508 | set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); | 4504 | set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); |
4509 | 4505 | ||
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index f9b521e60128..4bd72dd303d5 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h | |||
@@ -112,6 +112,16 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1, | |||
112 | extern int usb_clear_port_feature(struct usb_device *hdev, | 112 | extern int usb_clear_port_feature(struct usb_device *hdev, |
113 | int port1, int feature); | 113 | int port1, int feature); |
114 | 114 | ||
115 | static inline bool hub_is_port_power_switchable(struct usb_hub *hub) | ||
116 | { | ||
117 | __le16 hcs; | ||
118 | |||
119 | if (!hub) | ||
120 | return false; | ||
121 | hcs = hub->descriptor->wHubCharacteristics; | ||
122 | return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM; | ||
123 | } | ||
124 | |||
115 | static inline int hub_port_debounce_be_connected(struct usb_hub *hub, | 125 | static inline int hub_port_debounce_be_connected(struct usb_hub *hub, |
116 | int port1) | 126 | int port1) |
117 | { | 127 | { |
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 37647e080599..168fa6ee3348 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c | |||
@@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) | |||
177 | 177 | ||
178 | pm_runtime_set_active(&port_dev->dev); | 178 | pm_runtime_set_active(&port_dev->dev); |
179 | 179 | ||
180 | /* It would be dangerous if user space couldn't | 180 | /* |
181 | * prevent usb device from being powered off. So don't | 181 | * Do not enable port runtime pm if the hub does not support |
182 | * enable port runtime pm if failed to expose port's pm qos. | 182 | * power switching. Also, userspace must have final say of |
183 | * whether a port is permitted to power-off. Do not enable | ||
184 | * runtime pm if we fail to expose pm_qos_no_power_off. | ||
183 | */ | 185 | */ |
184 | if (!dev_pm_qos_expose_flags(&port_dev->dev, | 186 | if (hub_is_port_power_switchable(hub) |
185 | PM_QOS_FLAG_NO_POWER_OFF)) | 187 | && dev_pm_qos_expose_flags(&port_dev->dev, |
188 | PM_QOS_FLAG_NO_POWER_OFF) == 0) | ||
186 | pm_runtime_enable(&port_dev->dev); | 189 | pm_runtime_enable(&port_dev->dev); |
187 | 190 | ||
188 | device_enable_async_suspend(&port_dev->dev); | 191 | device_enable_async_suspend(&port_dev->dev); |