aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-20 21:08:12 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 19:35:09 -0400
commit9262c19d14c433a6a1ba25c3ff897cb89e412309 (patch)
tree50f83b859b251a3e842953e4fb9619a05ece02da /drivers/usb
parent600856c231ccb0cbf8afcf09066a8ab2a93ab03d (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.c8
-rw-r--r--drivers/usb/core/hub.h10
-rw-r--r--drivers/usb/core/port.c13
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,
112extern int usb_clear_port_feature(struct usb_device *hdev, 112extern int usb_clear_port_feature(struct usb_device *hdev,
113 int port1, int feature); 113 int port1, int feature);
114 114
115static 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
115static inline int hub_port_debounce_be_connected(struct usb_hub *hub, 125static 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);