From f911e51efbbe821bcfb7cb6cb481d6d6c8cc2f2d Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Thu, 9 Mar 2017 14:14:07 -0800 Subject: [PATCH] UPSTREAM: libpayload: usbhub: Force enumeration of all connected ports on init We have found a non-compliant USB hub (RealTek RTS 5413) that does not set a port's Connect Status Change bit on its USB 3.0 half if the port had already been connected while the hub was being reset. To work around this bug, this patch adds code to initially request the status of every port after a hub was enumerated, clear the Connect Status Change bit if set, and then enumerate the port iff it is currently connected, regardless of whether the change bit was set. A similar behavior can also be found in the Linux kernel. BRANCH=oak BUG=b:35929438 TEST=Booted Elm with this change, my USB 3.0 sticks enumerate now even if they had been plugged in since boot. Change-Id: If438b4acac2c509c7f22d9cc2470a014560bb00e Signed-off-by: Patrick Georgi Original-Commit-Id: 5fae8294104ae62353466298fae493e717056a28 Original-Change-Id: I8a28252eb94f005f04866d06e4fc61ea265cee89 Original-Signed-off-by: Julius Werner Original-Reviewed-on: https://review.coreboot.org/18729 Original-Tested-by: build bot (Jenkins) Original-Reviewed-by: Patrick Georgi Reviewed-on: https://chromium-review.googlesource.com/455825 --- payloads/libpayload/drivers/usb/usbhub.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/payloads/libpayload/drivers/usb/usbhub.c b/payloads/libpayload/drivers/usb/usbhub.c index f75141eae5..340e47ae3d 100644 --- a/payloads/libpayload/drivers/usb/usbhub.c +++ b/payloads/libpayload/drivers/usb/usbhub.c @@ -158,6 +158,21 @@ static const generic_hub_ops_t usb_hub_ops = { .reset_port = generic_hub_resetport, }; +/* Clear CSC if set and enumerate port if it's connected regardless of change + bits. Some broken hubs don't set CSC if already connected during reset. */ +static void +usb_hub_port_initialize(usbdev_t *const dev, const int port) +{ + unsigned short buf[2]; + int ret = get_status(dev, port, DR_PORT, sizeof(buf), buf); + if (ret < 0) + return; + if (buf[1] & PORT_CONNECTION) + clear_feature(dev, port, SEL_C_PORT_CONNECTION, DR_PORT); + if (buf[0] & PORT_CONNECTION) + generic_hub_scanport(dev, port); +} + void usb_hub_init(usbdev_t *const dev) { @@ -172,5 +187,10 @@ usb_hub_init(usbdev_t *const dev) if (dev->speed == SUPER_SPEED) usb_hub_set_hub_depth(dev); - generic_hub_init(dev, desc.bNbrPorts, &usb_hub_ops); + if (generic_hub_init(dev, desc.bNbrPorts, &usb_hub_ops) < 0) + return; + + int port; + for (port = 1; port <= GEN_HUB(dev)->num_ports; ++port) + usb_hub_port_initialize(dev, port); }