diff options
author | Dan Williams <dan.j.williams@intel.com> | 2014-05-20 18:08:57 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-27 16:38:53 -0700 |
commit | 7ad3c47088f9faec463f5226e5e968a5c3b0e593 (patch) | |
tree | c0712608896ada6eef80671212b3d766d823618f /drivers/usb/core/hub.h | |
parent | d5c3834e4af3acc4d7fc52faba2711c666655632 (diff) | |
download | linux-7ad3c47088f9faec463f5226e5e968a5c3b0e593.tar.bz2 |
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/hub.h')
-rw-r--r-- | drivers/usb/core/hub.h | 15 |
1 files changed, 15 insertions, 0 deletions
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 { * @connect_type: port's connect type * @location: opaque representation of platform connector location * @portnum: port index num based one + * @is_superspeed cache super-speed status */ struct usb_port { struct usb_device *child; @@ -98,6 +99,7 @@ struct usb_port { enum usb_port_connect_type connect_type; usb_port_location_t location; u8 portnum; + unsigned int is_superspeed:1; }; #define to_usb_port(_dev) \ @@ -125,6 +127,19 @@ static inline bool hub_is_port_power_switchable(struct usb_hub *hub) return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM; } +static inline int hub_is_superspeed(struct usb_device *hdev) +{ + return hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS; +} + +static inline unsigned hub_power_on_good_delay(struct usb_hub *hub) +{ + unsigned delay = hub->descriptor->bPwrOn2PwrGood * 2; + + /* Wait at least 100 msec for power to become stable */ + return max(delay, 100U); +} + static inline int hub_port_debounce_be_connected(struct usb_hub *hub, int port1) { |