diff options
author | Felipe Balbi <felipe.balbi@linux.intel.com> | 2017-12-14 10:50:39 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-12-15 22:45:43 +0300 |
commit | 886ee36e7205a7b850e2e5c2298a479f581f9b3b (patch) | |
tree | 0c52649fb1d4fb87550c006c000b8fb1b037da16 /drivers/usb/core/hub.c | |
parent | 66222f0d4b0ffba0bae2a6a785dc6eaa67cf532e (diff) | |
download | linux-886ee36e7205a7b850e2e5c2298a479f581f9b3b.tar.xz |
usb: core: add support for USB_REQ_SET_ISOCH_DELAY
USB SS and SSP hubs provide wHubDelay values on their hub descriptor
which we should inform the USB Device about.
The USB Specification 3.0 explains, on section 9.4.11, how to
calculate the value and how to issue the request. Note that a
USB_REQ_SET_ISOCH_DELAY is valid on all device states (Default,
Address, Configured), we just *chose* to issue it from Address state
right after successfully fetching the USB Device Descriptor.
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 916bcd539afa..09bd3c3ad8b1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -38,6 +38,9 @@ #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +#define USB_TP_TRANSMISSION_DELAY 40 /* ns */ +#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -1350,6 +1353,20 @@ static int hub_configure(struct usb_hub *hub, goto fail; } + /* + * Accumulate wHubDelay + 40ns for every hub in the tree of devices. + * The resulting value will be used for SetIsochDelay() request. + */ + if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) { + u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay); + + if (hdev->parent) + delay += hdev->parent->hub_delay; + + delay += USB_TP_TRANSMISSION_DELAY; + hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX); + } + maxchild = hub->descriptor->bNbrPorts; dev_info(hub_dev, "%d port%s detected\n", maxchild, (maxchild == 1) ? "" : "s"); @@ -4597,7 +4614,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (retval >= 0) retval = -EMSGSIZE; } else { + u32 delay; + retval = 0; + + delay = udev->parent->hub_delay; + udev->hub_delay = min_t(u32, delay, + USB_TP_TRANSMISSION_DELAY_MAX); + retval = usb_set_isoch_delay(udev); + if (retval) { + dev_dbg(&udev->dev, + "Failed set isoch delay, error %d\n", + retval); + retval = 0; + } break; } } |