• Sarah Sharp's avatar
    USB: Add support to enable/disable USB3 link states. · 1ea7e0e8
    Sarah Sharp authored
    There are various functions within the USB core that will need to
    disable USB 3.0 link power states.  For example, when a USB device
    driver is being bound to an interface, we need to disable USB 3.0 LPM
    until we know if the driver will allow hub-initiated LPM transitions.
    Another example is when the USB core is switching alternate interface
    settings.  The USB 3.0 timeout values are dependent on what endpoints
    are enabled, so we want to ensure that LPM is disabled until the new alt
    setting is fully installed.
    
    Multiple functions need to disable LPM, and those functions can even be
    nested.  For example, usb_bind_interface() could disable LPM, and then
    call into the driver probe function, which may attempt to switch to a
    different alt setting.  Therefore, we need to keep a count of the number
    of functions that require LPM to be disabled at any point in time.
    
    Introduce two new USB core API calls, usb_disable_lpm() and
    usb_enable_lpm().  These functions increment and decrement a new
    variable in the usb_device, lpm_disable_count.  If usb_disable_lpm()
    fails, it will call usb_enable_lpm() in order to balance the
    lpm_disable_count.
    
    These two new functions must be called with the bandwidth_mutex locked.
    If the bandwidth_mutex is not already held by the caller, it should
    instead call usb_unlocked_disable_lpm() and usb_enable_lpm(), which take
    the bandwidth_mutex before calling usb_disable_lpm() and
    usb_enable_lpm(), respectively.
    
    Introduce a new variable (timeout) in the usb3_lpm_params structure to
    keep track of the currently enabled U1/U2 timeout values.  When
    usb_disable_lpm() is called, and the USB device has the U1 or U2
    timeouts set to a non-zero value (meaning either device-initiated or
    hub-initiated LPM is enabled), attempt to disable LPM, regardless of the
    state of the lpm_disable_count.  We want to ensure that all callers can
    be guaranteed that LPM is disabled if usb_disable_lpm() returns zero.
    
    Otherwise the following scenario could occur:
    
    1. Driver A is being bound to interface 1.  usb_probe_interface()
    disables LPM.  Driver A doesn't care if hub-initiated LPM is enabled, so
    even though usb_disable_lpm() fails, the probe of the driver continues,
    and the bandwidth mutex is dropped.
    
    2. Meanwhile, Driver B is being bound to interface 2.
    usb_probe_interface() grabs the bandwidth mutex and calls
    usb_disable_lpm().  That call should attempt to disable LPM, even
    though the lpm_disable_count is set to 1 by Driver A.
    
    For usb_enable_lpm(), we attempt to enable LPM only when the
    lpm_disable_count is zero.  If some step in enabling LPM fails, it will
    only have a minimal impact on power consumption, and all USB device
    drivers should still work properly.  Therefore don't bother to return
    any error codes.
    
    Don't enable device-initiated LPM if the device is unconfigured.  The
    USB device will only accept the U1/U2_ENABLE control transfers in the
    configured state.  Do enable hub-initiated LPM in that case, since
    devices are allowed to accept the LGO_Ux link commands in any state.
    
    Don't enable or disable LPM if the device is marked as not being LPM
    capable.  This can happen if:
     - the USB device doesn't have a SS BOS descriptor,
     - the device's parent hub has a zeroed bHeaderDecodeLatency value, or
     - the xHCI host doesn't support LPM.
    Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
    Cc: Andiry Xu <andiry.xu@amd.com>
    Cc: Alan Stern <stern@rowland.harvard.edu>
    Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
    1ea7e0e8
hub.c 138 KB