aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-20 21:08:57 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-27 19:38:53 -0400
commit7ad3c47088f9faec463f5226e5e968a5c3b0e593 (patch)
treec0712608896ada6eef80671212b3d766d823618f /drivers/usb/core
parentd5c3834e4af3acc4d7fc52faba2711c666655632 (diff)
usb: block suspension of superspeed port while hispeed peer is active
ClearPortFeature(PORT_POWER) on a usb3 port places the port in either a DSPORT.Powered-off-detect / DSPORT.Powered-off-reset loop, or the DSPORT.Powered-off state. There is no way to ensure that RX terminations will persist in this state, so it is possible a device will degrade to its usb2 connection. Prevent this by blocking power-off of a usb3 port while its usb2 peer is active, and powering on a usb3 port before its usb2 peer. By default the latency between peer power-on events is 0. In order for the device to not see usb2 active while usb3 is still powering up inject the hub recommended power_on_good delay. In support of satisfying the power_on_good delay outside of hub_power_on() refactor the places where the delay is consumed to call a new hub_power_on_good_delay() helper. Finally, because this introduces several new checks for whether a port is_superspeed, cache that disctinction at port creation so that we don't need to keep looking up the parent hub device. Acked-by: Alan Stern <stern@rowland.harvard.edu> [alan]: add a 'superspeed' flag to the port Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c22
-rw-r--r--drivers/usb/core/hub.h15
-rw-r--r--drivers/usb/core/port.c73
3 files changed, 93 insertions, 17 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 31a492a4a881..e492bca74425 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -36,11 +36,6 @@
36#define USB_VENDOR_GENESYS_LOGIC 0x05e3 36#define USB_VENDOR_GENESYS_LOGIC 0x05e3
37#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 37#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
38 38
39static inline int hub_is_superspeed(struct usb_device *hdev)
40{
41 return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
42}
43
44/* Protect struct usb_device->state and ->children members 39/* Protect struct usb_device->state and ->children members
45 * Note: Both are also protected by ->dev.sem, except that ->state can 40 * Note: Both are also protected by ->dev.sem, except that ->state can
46 * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ 41 * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */
@@ -822,14 +817,9 @@ int usb_hub_clear_tt_buffer(struct urb *urb)
822} 817}
823EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer); 818EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer);
824 819
825/* If do_delay is false, return the number of milliseconds the caller 820static void hub_power_on(struct usb_hub *hub, bool do_delay)
826 * needs to delay.
827 */
828static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
829{ 821{
830 int port1; 822 int port1;
831 unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
832 unsigned delay;
833 823
834 /* Enable power on each port. Some hubs have reserved values 824 /* Enable power on each port. Some hubs have reserved values
835 * of LPSM (> 2) in their descriptors, even though they are 825 * of LPSM (> 2) in their descriptors, even though they are
@@ -848,12 +838,8 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
848 else 838 else
849 usb_clear_port_feature(hub->hdev, port1, 839 usb_clear_port_feature(hub->hdev, port1,
850 USB_PORT_FEAT_POWER); 840 USB_PORT_FEAT_POWER);
851
852 /* Wait at least 100 msec for power to become stable */
853 delay = max(pgood_delay, (unsigned) 100);
854 if (do_delay) 841 if (do_delay)
855 msleep(delay); 842 msleep(hub_power_on_good_delay(hub));
856 return delay;
857} 843}
858 844
859static int hub_hub_status(struct usb_hub *hub, 845static int hub_hub_status(struct usb_hub *hub,
@@ -1057,7 +1043,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1057 * for HUB_POST_RESET, but it's easier not to. 1043 * for HUB_POST_RESET, but it's easier not to.
1058 */ 1044 */
1059 if (type == HUB_INIT) { 1045 if (type == HUB_INIT) {
1060 delay = hub_power_on(hub, false); 1046 unsigned delay = hub_power_on_good_delay(hub);
1047
1048 hub_power_on(hub, false);
1061 INIT_DELAYED_WORK(&hub->init_work, hub_init_func2); 1049 INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
1062 queue_delayed_work(system_power_efficient_wq, 1050 queue_delayed_work(system_power_efficient_wq,
1063 &hub->init_work, 1051 &hub->init_work,
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 3ef1c2e435cc..906c355e0631 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -89,6 +89,7 @@ struct usb_hub {
89 * @connect_type: port's connect type 89 * @connect_type: port's connect type
90 * @location: opaque representation of platform connector location 90 * @location: opaque representation of platform connector location
91 * @portnum: port index num based one 91 * @portnum: port index num based one
92 * @is_superspeed cache super-speed status
92 */ 93 */
93struct usb_port { 94struct usb_port {
94 struct usb_device *child; 95 struct usb_device *child;
@@ -98,6 +99,7 @@ struct usb_port {
98 enum usb_port_connect_type connect_type; 99 enum usb_port_connect_type connect_type;
99 usb_port_location_t location; 100 usb_port_location_t location;
100 u8 portnum; 101 u8 portnum;
102 unsigned int is_superspeed:1;
101}; 103};
102 104
103#define to_usb_port(_dev) \ 105#define to_usb_port(_dev) \
@@ -125,6 +127,19 @@ static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
125 return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM; 127 return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
126} 128}
127 129
130static inline int hub_is_superspeed(struct usb_device *hdev)
131{
132 return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS;
133}
134
135static inline unsigned hub_power_on_good_delay(struct usb_hub *hub)
136{
137 unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2;
138
139 /* Wait at least 100 msec for power to become stable */
140 return max(delay, 100U);
141}
142
128static inline int hub_port_debounce_be_connected(struct usb_hub *hub, 143static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
129 int port1) 144 int port1)
130{ 145{
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 795778c71e31..827b0d38f73d 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -76,6 +76,7 @@ static int usb_port_runtime_resume(struct device *dev)
76 struct usb_device *hdev = to_usb_device(dev->parent->parent); 76 struct usb_device *hdev = to_usb_device(dev->parent->parent);
77 struct usb_interface *intf = to_usb_interface(dev->parent); 77 struct usb_interface *intf = to_usb_interface(dev->parent);
78 struct usb_hub *hub = usb_hub_to_struct_hub(hdev); 78 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
79 struct usb_port *peer = port_dev->peer;
79 int port1 = port_dev->portnum; 80 int port1 = port_dev->portnum;
80 int retval; 81 int retval;
81 82
@@ -86,10 +87,18 @@ static int usb_port_runtime_resume(struct device *dev)
86 return 0; 87 return 0;
87 } 88 }
88 89
90 /*
91 * Power on our usb3 peer before this usb2 port to prevent a usb3
92 * device from degrading to its usb2 connection
93 */
94 if (!port_dev->is_superspeed && peer)
95 pm_runtime_get_sync(&peer->dev);
96
89 usb_autopm_get_interface(intf); 97 usb_autopm_get_interface(intf);
90 set_bit(port1, hub->busy_bits); 98 set_bit(port1, hub->busy_bits);
91 99
92 retval = usb_hub_set_port_power(hdev, hub, port1, true); 100 retval = usb_hub_set_port_power(hdev, hub, port1, true);
101 msleep(hub_power_on_good_delay(hub));
93 if (port_dev->child && !retval) { 102 if (port_dev->child && !retval) {
94 /* 103 /*
95 * Attempt to wait for usb hub port to be reconnected in order 104 * Attempt to wait for usb hub port to be reconnected in order
@@ -107,6 +116,7 @@ static int usb_port_runtime_resume(struct device *dev)
107 116
108 clear_bit(port1, hub->busy_bits); 117 clear_bit(port1, hub->busy_bits);
109 usb_autopm_put_interface(intf); 118 usb_autopm_put_interface(intf);
119
110 return retval; 120 return retval;
111} 121}
112 122
@@ -116,6 +126,7 @@ static int usb_port_runtime_suspend(struct device *dev)
116 struct usb_device *hdev = to_usb_device(dev->parent->parent); 126 struct usb_device *hdev = to_usb_device(dev->parent->parent);
117 struct usb_interface *intf = to_usb_interface(dev->parent); 127 struct usb_interface *intf = to_usb_interface(dev->parent);
118 struct usb_hub *hub = usb_hub_to_struct_hub(hdev); 128 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
129 struct usb_port *peer = port_dev->peer;
119 int port1 = port_dev->portnum; 130 int port1 = port_dev->portnum;
120 int retval; 131 int retval;
121 132
@@ -135,6 +146,15 @@ static int usb_port_runtime_suspend(struct device *dev)
135 usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); 146 usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
136 clear_bit(port1, hub->busy_bits); 147 clear_bit(port1, hub->busy_bits);
137 usb_autopm_put_interface(intf); 148 usb_autopm_put_interface(intf);
149
150 /*
151 * Our peer usb3 port may now be able to suspend, so
152 * asynchronously queue a suspend request to observe that this
153 * usb2 port is now off.
154 */
155 if (!port_dev->is_superspeed && peer)
156 pm_runtime_put(&peer->dev);
157
138 return retval; 158 return retval;
139} 159}
140#endif 160#endif
@@ -159,6 +179,7 @@ static struct device_driver usb_port_driver = {
159 179
160static int link_peers(struct usb_port *left, struct usb_port *right) 180static int link_peers(struct usb_port *left, struct usb_port *right)
161{ 181{
182 struct usb_port *ss_port, *hs_port;
162 int rc; 183 int rc;
163 184
164 if (left->peer == right && right->peer == left) 185 if (left->peer == right && right->peer == left)
@@ -184,9 +205,36 @@ static int link_peers(struct usb_port *left, struct usb_port *right)
184 return rc; 205 return rc;
185 } 206 }
186 207
208 /*
209 * We need to wake the HiSpeed port to make sure we don't race
210 * setting ->peer with usb_port_runtime_suspend(). Otherwise we
211 * may miss a suspend event for the SuperSpeed port.
212 */
213 if (left->is_superspeed) {
214 ss_port = left;
215 WARN_ON(right->is_superspeed);
216 hs_port = right;
217 } else {
218 ss_port = right;
219 WARN_ON(!right->is_superspeed);
220 hs_port = left;
221 }
222 pm_runtime_get_sync(&hs_port->dev);
223
187 left->peer = right; 224 left->peer = right;
188 right->peer = left; 225 right->peer = left;
189 226
227 /*
228 * The SuperSpeed reference is dropped when the HiSpeed port in
229 * this relationship suspends, i.e. when it is safe to allow a
230 * SuperSpeed connection to drop since there is no risk of a
231 * device degrading to its powered-off HiSpeed connection.
232 *
233 * Also, drop the HiSpeed ref taken above.
234 */
235 pm_runtime_get_sync(&ss_port->dev);
236 pm_runtime_put(&hs_port->dev);
237
190 return 0; 238 return 0;
191} 239}
192 240
@@ -206,14 +254,37 @@ static void link_peers_report(struct usb_port *left, struct usb_port *right)
206 254
207static void unlink_peers(struct usb_port *left, struct usb_port *right) 255static void unlink_peers(struct usb_port *left, struct usb_port *right)
208{ 256{
257 struct usb_port *ss_port, *hs_port;
258
209 WARN(right->peer != left || left->peer != right, 259 WARN(right->peer != left || left->peer != right,
210 "%s and %s are not peers?\n", 260 "%s and %s are not peers?\n",
211 dev_name(&left->dev), dev_name(&right->dev)); 261 dev_name(&left->dev), dev_name(&right->dev));
212 262
263 /*
264 * We wake the HiSpeed port to make sure we don't race its
265 * usb_port_runtime_resume() event which takes a SuperSpeed ref
266 * when ->peer is !NULL.
267 */
268 if (left->is_superspeed) {
269 ss_port = left;
270 hs_port = right;
271 } else {
272 ss_port = right;
273 hs_port = left;
274 }
275
276 pm_runtime_get_sync(&hs_port->dev);
277
213 sysfs_remove_link(&left->dev.kobj, "peer"); 278 sysfs_remove_link(&left->dev.kobj, "peer");
214 right->peer = NULL; 279 right->peer = NULL;
215 sysfs_remove_link(&right->dev.kobj, "peer"); 280 sysfs_remove_link(&right->dev.kobj, "peer");
216 left->peer = NULL; 281 left->peer = NULL;
282
283 /* Drop the SuperSpeed ref held on behalf of the active HiSpeed port */
284 pm_runtime_put(&ss_port->dev);
285
286 /* Drop the ref taken above */
287 pm_runtime_put(&hs_port->dev);
217} 288}
218 289
219/* 290/*
@@ -325,6 +396,8 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
325 port_dev->dev.groups = port_dev_group; 396 port_dev->dev.groups = port_dev_group;
326 port_dev->dev.type = &usb_port_device_type; 397 port_dev->dev.type = &usb_port_device_type;
327 port_dev->dev.driver = &usb_port_driver; 398 port_dev->dev.driver = &usb_port_driver;
399 if (hub_is_superspeed(hub->hdev))
400 port_dev->is_superspeed = 1;
328 dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev), 401 dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),
329 port1); 402 port1);
330 retval = device_register(&port_dev->dev); 403 retval = device_register(&port_dev->dev);