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>
This commit is contained in:
parent
e44a4e8787
commit
752fba7846
|
@ -556,17 +556,13 @@ set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr)
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case hub_device:
|
case hub_device:
|
||||||
if (speed < SUPER_SPEED) {
|
usb_debug ("hub\n");
|
||||||
usb_debug ("hub (2.0)\n");
|
|
||||||
#if IS_ENABLED(CONFIG_LP_USB_HUB)
|
#if IS_ENABLED(CONFIG_LP_USB_HUB)
|
||||||
dev->init = usb_hub_init;
|
dev->init = usb_hub_init;
|
||||||
return dev->address;
|
return dev->address;
|
||||||
#else
|
#else
|
||||||
usb_debug ("NOTICE: USB hub support not compiled in\n");
|
usb_debug ("NOTICE: USB hub support not compiled in\n");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
usb_debug ("hub (3.0) - not yet supported!\n");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case cdc_device:
|
case cdc_device:
|
||||||
usb_debug("CDC\n");
|
usb_debug("CDC\n");
|
||||||
|
|
|
@ -42,6 +42,8 @@
|
||||||
#define SEL_PORT_RESET 0x4
|
#define SEL_PORT_RESET 0x4
|
||||||
#define SEL_PORT_POWER 0x8
|
#define SEL_PORT_POWER 0x8
|
||||||
#define SEL_C_PORT_CONNECTION 0x10
|
#define SEL_C_PORT_CONNECTION 0x10
|
||||||
|
/* request type (USB 3.0 hubs only) */
|
||||||
|
#define SET_HUB_DEPTH 12
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usb_hub_port_status_changed(usbdev_t *const dev, const int port)
|
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];
|
unsigned short buf[2];
|
||||||
int ret = get_status (dev, port, DR_PORT, sizeof(buf), buf);
|
int ret = get_status (dev, port, DR_PORT, sizeof(buf), buf);
|
||||||
if (ret >= 0 && (buf[0] & PORT_ENABLE)) {
|
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 0 full speed
|
||||||
* 0 1 low speed
|
* 0 1 low speed
|
||||||
* 1 0 high speed
|
* 1 0 high speed
|
||||||
* 1 1 super speed (hack, not in spec!)
|
* 1 1 invalid
|
||||||
*/
|
*/
|
||||||
ret = (buf[0] >> 9) & 0x3;
|
ret = (buf[0] >> 9) & 0x3;
|
||||||
} else {
|
if (ret != 0x3)
|
||||||
ret = -1;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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);
|
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 = {
|
static const generic_hub_ops_t usb_hub_ops = {
|
||||||
.hub_status_changed = NULL,
|
.hub_status_changed = NULL,
|
||||||
.port_status_changed = usb_hub_port_status_changed,
|
.port_status_changed = usb_hub_port_status_changed,
|
||||||
|
@ -134,13 +161,16 @@ static const generic_hub_ops_t usb_hub_ops = {
|
||||||
void
|
void
|
||||||
usb_hub_init(usbdev_t *const dev)
|
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 */
|
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,
|
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_debug("get_descriptor(HUB) failed\n");
|
||||||
usb_detach_device(dev->controller, dev->address);
|
usb_detach_device(dev->controller, dev->address);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev->speed == SUPER_SPEED)
|
||||||
|
usb_hub_set_hub_depth(dev);
|
||||||
generic_hub_init(dev, desc.bNbrPorts, &usb_hub_ops);
|
generic_hub_init(dev, desc.bNbrPorts, &usb_hub_ops);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,8 @@ xhci_dump_devctx(const devctx_t *const dc, const u32 ctx_mask)
|
||||||
int i;
|
int i;
|
||||||
if (ctx_mask & 1)
|
if (ctx_mask & 1)
|
||||||
xhci_dump_slotctx(dc->slot);
|
xhci_dump_slotctx(dc->slot);
|
||||||
for (i = 0; i < SC_GET(CTXENT, dc->slot); ++i) {
|
for (i = 1; i <= SC_GET(CTXENT, dc->slot); ++i) {
|
||||||
if (ctx_mask & (2 << i))
|
if (ctx_mask & (1 << i))
|
||||||
xhci_dump_epctx(dc->ep[i]);
|
xhci_dump_epctx(dc->ep[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,10 +267,11 @@ _free_ic_return:
|
||||||
static int
|
static int
|
||||||
xhci_finish_hub_config(usbdev_t *const dev, inputctx_t *const ic)
|
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;
|
hub_descriptor_t desc;
|
||||||
|
|
||||||
if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type,
|
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");
|
xhci_debug("Failed to fetch hub descriptor\n");
|
||||||
return COMMUNICATION_ERROR;
|
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->f1 = di->ctx.slot->f1;
|
||||||
ic->dev.slot->f2 = di->ctx.slot->f2;
|
ic->dev.slot->f2 = di->ctx.slot->f2;
|
||||||
ic->dev.slot->f3 = di->ctx.slot->f3;
|
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);
|
ret = xhci_finish_hub_config(dev, ic);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto _free_return;
|
goto _free_return;
|
||||||
|
|
Loading…
Reference in New Issue