summaryrefslogtreecommitdiff
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c30
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;
}
}