From 752fba784681c2af0611a9d52ee1e8e2db8969a0 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Thu, 9 Jul 2015 16:29:10 -0700 Subject: libpayload: usb: Add support for SuperSpeed hubs This patch adds support for the SuperSpeed half of USB 3.0 hubs, which previously prevented SuperSpeed devices behind those hubs from working. BRANCH=None BUG=chrome-os-partner:39877 TEST=Played around with multiple hubs and devices on Oak and Falco, can no longer find a combination that doesn't work. Change-Id: I20815be95769e33d399b7ad91c3020687234e059 Signed-off-by: Patrick Georgi Original-Commit-Id: 3db96ece20d2304e7f6f6aa333cf114037c48a3e Original-Change-Id: I2dd6c9c3607a24a7d78c308911e3d254d5f8d91d Original-Signed-off-by: Julius Werner Original-Reviewed-on: https://chromium-review.googlesource.com/284577 Original-Reviewed-by: Patrick Georgi Original-Tested-by: chunfeng yun Reviewed-on: http://review.coreboot.org/10958 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- payloads/libpayload/drivers/usb/usb.c | 12 +++----- payloads/libpayload/drivers/usb/usbhub.c | 42 ++++++++++++++++++++++---- payloads/libpayload/drivers/usb/xhci_debug.c | 4 +-- payloads/libpayload/drivers/usb/xhci_devconf.c | 6 ++-- 4 files changed, 46 insertions(+), 18 deletions(-) (limited to 'payloads') diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c index e00d92f00a..69d1c39778 100644 --- a/payloads/libpayload/drivers/usb/usb.c +++ b/payloads/libpayload/drivers/usb/usb.c @@ -556,17 +556,13 @@ set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr) #endif break; case hub_device: - if (speed < SUPER_SPEED) { - usb_debug ("hub (2.0)\n"); + usb_debug ("hub\n"); #if IS_ENABLED(CONFIG_LP_USB_HUB) - dev->init = usb_hub_init; - return dev->address; + dev->init = usb_hub_init; + return dev->address; #else - usb_debug ("NOTICE: USB hub support not compiled in\n"); + usb_debug ("NOTICE: USB hub support not compiled in\n"); #endif - } else { - usb_debug ("hub (3.0) - not yet supported!\n"); - } break; case cdc_device: usb_debug("CDC\n"); diff --git a/payloads/libpayload/drivers/usb/usbhub.c b/payloads/libpayload/drivers/usb/usbhub.c index e12fd92c3f..f75141eae5 100644 --- a/payloads/libpayload/drivers/usb/usbhub.c +++ b/payloads/libpayload/drivers/usb/usbhub.c @@ -42,6 +42,8 @@ #define SEL_PORT_RESET 0x4 #define SEL_PORT_POWER 0x8 #define SEL_C_PORT_CONNECTION 0x10 +/* request type (USB 3.0 hubs only) */ +#define SET_HUB_DEPTH 12 static int usb_hub_port_status_changed(usbdev_t *const dev, const int port) @@ -93,17 +95,21 @@ usb_hub_port_speed(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 && (buf[0] & PORT_ENABLE)) { - /* bit 10 9 + /* SuperSpeed hubs can only have SuperSpeed devices. */ + if (dev->speed == SUPER_SPEED) + return SUPER_SPEED; + + /*[bit] 10 9 (USB 2.0 port status word) * 0 0 full speed * 0 1 low speed * 1 0 high speed - * 1 1 super speed (hack, not in spec!) + * 1 1 invalid */ ret = (buf[0] >> 9) & 0x3; - } else { - ret = -1; + if (ret != 0x3) + return ret; } - return ret; + return -1; } static int @@ -118,6 +124,27 @@ usb_hub_start_port_reset(usbdev_t *const dev, const int port) return set_feature (dev, port, SEL_PORT_RESET, DR_PORT); } +static void usb_hub_set_hub_depth(usbdev_t *const dev) +{ + dev_req_t dr = { + .bmRequestType = gen_bmRequestType(host_to_device, + class_type, dev_recp), + .bRequest = SET_HUB_DEPTH, + .wValue = 0, + .wIndex = 0, + .wLength = 0, + }; + usbdev_t *parent = dev; + while (parent->hub > 0) { + parent = dev->controller->devices[parent->hub]; + dr.wValue++; + } + int ret = dev->controller->control(dev, OUT, sizeof(dr), &dr, 0, NULL); + if (ret < 0) + usb_debug("Failed SET_HUB_DEPTH(%d) on hub %d: %d\n", + dr.wValue, dev->address, ret); +} + static const generic_hub_ops_t usb_hub_ops = { .hub_status_changed = NULL, .port_status_changed = usb_hub_port_status_changed, @@ -134,13 +161,16 @@ static const generic_hub_ops_t usb_hub_ops = { void usb_hub_init(usbdev_t *const dev) { + int type = dev->speed == SUPER_SPEED ? 0x2a : 0x29; /* similar enough */ hub_descriptor_t desc; /* won't fit the whole thing, we don't care */ if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type, - dev_recp), 0x29, 0, &desc, sizeof(desc)) != sizeof(desc)) { + dev_recp), type, 0, &desc, sizeof(desc)) != sizeof(desc)) { usb_debug("get_descriptor(HUB) failed\n"); usb_detach_device(dev->controller, dev->address); return; } + if (dev->speed == SUPER_SPEED) + usb_hub_set_hub_depth(dev); generic_hub_init(dev, desc.bNbrPorts, &usb_hub_ops); } diff --git a/payloads/libpayload/drivers/usb/xhci_debug.c b/payloads/libpayload/drivers/usb/xhci_debug.c index d50f6dba30..f8d2309612 100644 --- a/payloads/libpayload/drivers/usb/xhci_debug.c +++ b/payloads/libpayload/drivers/usb/xhci_debug.c @@ -80,8 +80,8 @@ xhci_dump_devctx(const devctx_t *const dc, const u32 ctx_mask) int i; if (ctx_mask & 1) xhci_dump_slotctx(dc->slot); - for (i = 0; i < SC_GET(CTXENT, dc->slot); ++i) { - if (ctx_mask & (2 << i)) + for (i = 1; i <= SC_GET(CTXENT, dc->slot); ++i) { + if (ctx_mask & (1 << i)) xhci_dump_epctx(dc->ep[i]); } } diff --git a/payloads/libpayload/drivers/usb/xhci_devconf.c b/payloads/libpayload/drivers/usb/xhci_devconf.c index 32cd2918c1..ead130e7d1 100644 --- a/payloads/libpayload/drivers/usb/xhci_devconf.c +++ b/payloads/libpayload/drivers/usb/xhci_devconf.c @@ -267,10 +267,11 @@ _free_ic_return: static int xhci_finish_hub_config(usbdev_t *const dev, inputctx_t *const ic) { + int type = dev->speed == SUPER_SPEED ? 0x2a : 0x29; /* similar enough */ hub_descriptor_t desc; if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type, - dev_recp), 0x29, 0, &desc, sizeof(desc)) != sizeof(desc)) { + dev_recp), type, 0, &desc, sizeof(desc)) != sizeof(desc)) { xhci_debug("Failed to fetch hub descriptor\n"); return COMMUNICATION_ERROR; } @@ -386,8 +387,9 @@ xhci_finish_device_config(usbdev_t *const dev) ic->dev.slot->f1 = di->ctx.slot->f1; ic->dev.slot->f2 = di->ctx.slot->f2; ic->dev.slot->f3 = di->ctx.slot->f3; + /* f4 *must* be 0 in the Input Context... yeah, it's weird, I know. */ - if (dev->descriptor->bDeviceClass == 0x09 && dev->speed < SUPER_SPEED) { + if (dev->descriptor->bDeviceClass == 0x09) { ret = xhci_finish_hub_config(dev, ic); if (ret) goto _free_return; -- cgit v1.2.3