summaryrefslogtreecommitdiff
path: root/payloads/libpayload/drivers/usb
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2015-07-09 16:29:10 -0700
committerPatrick Georgi <pgeorgi@google.com>2015-07-18 09:40:48 +0200
commit752fba784681c2af0611a9d52ee1e8e2db8969a0 (patch)
treed8edfcd5056c559bd21ea4dfaf78aed9637c5ba4 /payloads/libpayload/drivers/usb
parente44a4e8787b5388f63983c4460b495f53425162c (diff)
downloadcoreboot-752fba784681c2af0611a9d52ee1e8e2db8969a0.tar.xz
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 <pgeorgi@chromium.org> Original-Commit-Id: 3db96ece20d2304e7f6f6aa333cf114037c48a3e Original-Change-Id: I2dd6c9c3607a24a7d78c308911e3d254d5f8d91d Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/284577 Original-Reviewed-by: Patrick Georgi <pgeorgi@chromium.org> Original-Tested-by: chunfeng yun <chunfeng.yun@mediatek.com> Reviewed-on: http://review.coreboot.org/10958 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'payloads/libpayload/drivers/usb')
-rw-r--r--payloads/libpayload/drivers/usb/usb.c12
-rw-r--r--payloads/libpayload/drivers/usb/usbhub.c42
-rw-r--r--payloads/libpayload/drivers/usb/xhci_debug.c4
-rw-r--r--payloads/libpayload/drivers/usb/xhci_devconf.c6
4 files changed, 46 insertions, 18 deletions
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;