diff --git a/commands/usb.c b/commands/usb.c index 073c79c..a37d503 100644 --- a/commands/usb.c +++ b/commands/usb.c @@ -22,20 +22,114 @@ #include #include +/* shows the device tree recursively */ +static void usb_show_tree_graph(struct usb_device *dev, char *pre) +{ + int i, index; + int has_child, last_child; + + index = strlen(pre); + printf(" %s", pre); + /* check if the device has connected children */ + has_child = 0; + + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) + has_child = 1; + } + + /* check if we are the last one */ + last_child = 1; + + if (dev->parent) { + for (i = 0; i < dev->parent->maxchild; i++) { + if (dev->parent->children[i]) + last_child = 0; + + if (dev->parent->children[i] == dev) + last_child = 1; + } /* for all children of the parent */ + printf("\b+-"); + + /* correct last child */ + if (last_child) + pre[index - 1] = ' '; + } else { + /* if not root hub */ + printf(" "); + } + + printf("%d ", dev->devnum); + + pre[index++] = ' '; + pre[index++] = has_child ? '|' : ' '; + pre[index] = 0; + + printf("ID %04x:%04x\n", dev->descriptor->idVendor, dev->descriptor->idProduct); + + if (strlen(dev->mf) || strlen(dev->prod) || strlen(dev->serial)) + printf(" %s %s %s %s\n", pre, dev->mf, dev->prod, dev->serial); + + printf(" %s\n", pre); + + if (dev->maxchild > 0) { + for (i = 0; i < dev->maxchild; i++) { + if (dev->children[i] != NULL) { + usb_show_tree_graph(dev->children[i], pre); + pre[index] = 0; + } + } + } +} + +/* main routine for the tree command */ +static void usb_show_tree(struct usb_device *dev) +{ + char preamble[32]; + + memset(preamble, 0, 32); + usb_show_tree_graph(dev, &preamble[0]); +} + +static void usb_show_devices(bool tree) +{ + struct usb_device *dev; + + list_for_each_entry(dev, &usb_device_list, list) { + if (tree) { + if (dev->parent == NULL) + usb_show_tree(dev); + } else { + printf("Bus %03d Device %03d: ID %04x:%04x %s\n", + dev->host->busnum, dev->devnum, + dev->descriptor->idVendor, + dev->descriptor->idProduct, + dev->prod); + } + } +} + static int do_usb(int argc, char *argv[]) { int opt; - int force = 0; + int tree = 0, show = 0; - while ((opt = getopt(argc, argv, "f")) > 0) { + while ((opt = getopt(argc, argv, "ts")) > 0) { switch (opt) { - case 'f': - force = 1; + case 't': + tree = 1; + show = 1; + break; + case 's': + show = 1; break; } } - usb_rescan(force); + usb_rescan(); + + if (show) + usb_show_devices(tree); return 0; } @@ -45,12 +139,14 @@ BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Options:") BAREBOX_CMD_HELP_OPT("-f", "force rescan") +BAREBOX_CMD_HELP_OPT("-s", "show devices") +BAREBOX_CMD_HELP_OPT("-t", "show USB tree") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(usb) .cmd = do_usb, BAREBOX_CMD_DESC("(re-)detect USB devices") - BAREBOX_CMD_OPTS("[-f]") + BAREBOX_CMD_OPTS("[-fts]") BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) BAREBOX_CMD_HELP(cmd_usb_help) BAREBOX_CMD_COMPLETE(empty_complete) diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 8002c63..58f6c5e 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -1,4 +1,3 @@ - -obj-$(CONFIG_USB_HOST) += usb.o +obj-$(CONFIG_USB_HOST) += usb.o hub.o obj-$(CONFIG_USB) += common.o obj-$(CONFIG_OFDEVICE) += of.o diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c new file mode 100644 index 0000000..d42b47c --- /dev/null +++ b/drivers/usb/core/hub.c @@ -0,0 +1,486 @@ +/* + * hub.c - USB hub support + * + * Copyright (c) 2011 Sascha Hauer , Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" +#include "hub.h" + +#define USB_BUFSIZ 512 + +static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); +} + +static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, + port, NULL, 0, USB_CNTL_TIMEOUT); +} + +static int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +static int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + + +static void usb_hub_power_on(struct usb_hub_device *hub) +{ + int i; + struct usb_device *dev; + unsigned pgood_delay = hub->desc.bPwrOn2PwrGood * 2; + + dev = hub->pusb_dev; + + /* + * Enable power to the ports: + * Here we Power-cycle the ports: aka, + * turning them off and turning on again. + */ + for (i = 0; i < dev->maxchild; i++) { + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + dev_dbg(&dev->dev, "port %d returns %lX\n", i + 1, dev->status); + } + + /* Wait at least 2 * bPwrOn2PwrGood for PP to change */ + mdelay(pgood_delay); + + /* Enable power to the ports */ + dev_dbg(&dev->dev, "enabling power on all ports\n"); + + for (i = 0; i < dev->maxchild; i++) { + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + dev_dbg(&dev->dev, "port %d returns %lX\n", i + 1, dev->status); + } + + /* power on is encoded in 2ms increments -> times 2 for the actual delay */ + mdelay(pgood_delay + 1000); +} + +#define MAX_TRIES 5 + +static inline char *portspeed(int portstatus) +{ + if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + return "480 Mb/s"; + else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} + +int hub_port_reset(struct usb_device *dev, int port, + unsigned short *portstat) +{ + int tries; + struct usb_port_status portsts; + unsigned short portstatus, portchange; + + dev_dbg(&dev->dev, "hub_port_reset: resetting port %d...\n", port); + for (tries = 0; tries < MAX_TRIES; tries++) { + + usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); + wait_ms(200); + + if (usb_get_port_status(dev, port + 1, &portsts) < 0) { + dev_dbg(&dev->dev, "get_port_status failed status %lX\n", + dev->status); + return -1; + } + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + + dev_dbg(&dev->dev, "portstatus %x, change %x, %s\n", + portstatus, portchange, + portspeed(portstatus)); + + dev_dbg(&dev->dev, "STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ + " USB_PORT_STAT_ENABLE %d\n", + (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, + (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + + if ((portchange & USB_PORT_STAT_C_CONNECTION) || + !(portstatus & USB_PORT_STAT_CONNECTION)) + return -1; + + if (portstatus & USB_PORT_STAT_ENABLE) + break; + + wait_ms(200); + } + + if (tries == MAX_TRIES) { + dev_dbg(&dev->dev, "Cannot enable port %i after %i retries, " \ + "disabling port.\n", port + 1, MAX_TRIES); + dev_dbg(&dev->dev, "Maybe the USB cable is bad?\n"); + return -1; + } + + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); + *portstat = portstatus; + return 0; +} + + +static void usb_hub_port_connect_change(struct usb_device *dev, int port) +{ + struct usb_device *usb; + struct usb_port_status portsts; + unsigned short portstatus, portchange; + + /* Check status */ + if (usb_get_port_status(dev, port + 1, &portsts) < 0) { + dev_dbg(&dev->dev, "get_port_status failed\n"); + return; + } + + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dev_dbg(&dev->dev, "portstatus %x, change %x, %s\n", + portstatus, portchange, portspeed(portstatus)); + + /* Clear the connection change status */ + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); + + /* Disconnect any existing devices under this port */ + if (dev->children[port] && !(portstatus & USB_PORT_STAT_CONNECTION)) { + dev_dbg(&dev->dev, "disconnect detected on port %d\n", port + 1); + usb_remove_device(dev->children[port]); + return; + } + + /* Remove disabled but connected devices */ + if (dev->children[port] && !(portstatus & USB_PORT_STAT_ENABLE)) + usb_remove_device(dev->children[port]); + + wait_ms(200); + + /* Reset the port */ + if (hub_port_reset(dev, port, &portstatus) < 0) { + dev_warn(&dev->dev, "cannot reset port %i!?\n", port + 1); + return; + } + + wait_ms(200); + + /* Allocate a new device struct for it */ + usb = usb_alloc_new_device(); + usb->dev.parent = &dev->dev; + usb->host = dev->host; + + if (portstatus & USB_PORT_STAT_HIGH_SPEED) + usb->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + usb->speed = USB_SPEED_LOW; + else + usb->speed = USB_SPEED_FULL; + + dev->children[port] = usb; + usb->parent = dev; + usb->portnr = port + 1; + + /* Run it through the hoops (find a driver, etc) */ + if (usb_new_device(usb)) { + /* Woops, disable the port */ + dev_dbg(&dev->dev, "hub: disabling port %d\n", port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); + usb_free_device(usb); + return; + } + + device_detect(&usb->dev); +} + +static int usb_hub_configure_port(struct usb_device *dev, int port) +{ + struct usb_port_status portsts; + unsigned short portstatus, portchange; + int connect_change = 0; + + if (usb_get_port_status(dev, port + 1, &portsts) < 0) { + dev_dbg(&dev->dev, "get_port_status failed\n"); + return -EIO; + } + + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dev_dbg(&dev->dev, "Port %d Status %X Change %X\n", + port + 1, portstatus, portchange); + + if (portchange & USB_PORT_STAT_C_CONNECTION) { + dev_dbg(&dev->dev, "port %d connection change\n", port + 1); + connect_change = 1; + } + if (portchange & USB_PORT_STAT_C_ENABLE) { + dev_dbg(&dev->dev, "port %d enable change, status %x\n", + port + 1, portstatus); + usb_clear_port_feature(dev, port + 1, + USB_PORT_FEAT_C_ENABLE); + + /* EM interference sometimes causes bad shielded USB + * devices to be shutdown by the hub, this hack enables + * them again. Works at least with mouse driver */ + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && + ((dev->children[port]))) { + dev_dbg(&dev->dev, "already running port %i " \ + "disabled by hub (EMI?), " \ + "re-enabling...\n", port + 1); + connect_change = 1; + } + } + + if (connect_change) + usb_hub_port_connect_change(dev, port); + + if (portstatus & USB_PORT_STAT_SUSPEND) { + dev_dbg(&dev->dev, "port %d suspend change\n", port + 1); + usb_clear_port_feature(dev, port + 1, + USB_PORT_FEAT_SUSPEND); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + dev_dbg(&dev->dev, "port %d over-current change\n", port + 1); + usb_clear_port_feature(dev, port + 1, + USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(dev->hub); + } + + if (portchange & USB_PORT_STAT_C_RESET) { + dev_dbg(&dev->dev, "port %d reset change\n", port + 1); + usb_clear_port_feature(dev, port + 1, + USB_PORT_FEAT_C_RESET); + } + + return 0; +} + +static int usb_hub_configure(struct usb_device *dev) +{ + unsigned char buffer[USB_BUFSIZ], *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_hub_status *hubsts; + int i; + struct usb_hub_device *hub; + + hub = xzalloc(sizeof (*hub)); + dev->hub = hub; + + hub->pusb_dev = dev; + /* Get the the hub descriptor */ + if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { + dev_dbg(&dev->dev, "%s: failed to get hub " \ + "descriptor, giving up %lX\n", __func__, dev->status); + return -1; + } + descriptor = (struct usb_hub_descriptor *)buffer; + + /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */ + i = descriptor->bLength; + if (i > USB_BUFSIZ) { + dev_dbg(&dev->dev, "%s: failed to get hub " \ + "descriptor - too long: %d\n", __func__, + descriptor->bLength); + return -1; + } + + if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { + dev_dbg(&dev->dev, "%s: failed to get hub " \ + "descriptor 2nd giving up %lX\n", __func__, dev->status); + return -1; + } + memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); + /* adjust 16bit values */ + hub->desc.wHubCharacteristics = + le16_to_cpu(descriptor->wHubCharacteristics); + /* set the bitmap */ + bitmap = (unsigned char *)&hub->desc.u.hs.DeviceRemovable[0]; + /* devices not removable by default */ + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); + bitmap = (unsigned char *)&hub->desc.u.hs.PortPwrCtrlMask[0]; + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.u.hs.DeviceRemovable[i] = descriptor->u.hs.DeviceRemovable[i]; + + for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.u.hs.PortPwrCtrlMask[i] = descriptor->u.hs.PortPwrCtrlMask[i]; + + dev->maxchild = descriptor->bNbrPorts; + dev_dbg(&dev->dev, "%d ports detected\n", dev->maxchild); + + switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) { + case 0x00: + dev_dbg(&dev->dev, "ganged power switching\n"); + break; + case 0x01: + dev_dbg(&dev->dev, "individual port power switching\n"); + break; + case 0x02: + case 0x03: + dev_dbg(&dev->dev, "unknown reserved power switching mode\n"); + break; + } + + if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND) + dev_dbg(&dev->dev, "part of a compound device\n"); + else + dev_dbg(&dev->dev, "standalone hub\n"); + + switch (hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) { + case 0x00: + dev_dbg(&dev->dev, "global over-current protection\n"); + break; + case 0x08: + dev_dbg(&dev->dev, "individual port over-current protection\n"); + break; + case 0x10: + case 0x18: + dev_dbg(&dev->dev, "no over-current protection\n"); + break; + } + + dev_dbg(&dev->dev, "power on to power good time: %dms\n", + descriptor->bPwrOn2PwrGood * 2); + dev_dbg(&dev->dev, "hub controller current requirement: %dmA\n", + descriptor->bHubContrCurrent); + + for (i = 0; i < dev->maxchild; i++) + dev_dbg(&dev->dev, "port %d is%s removable\n", i + 1, + hub->desc.u.hs.DeviceRemovable[(i + 1) / 8] & \ + (1 << ((i + 1) % 8)) ? " not" : ""); + + if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { + dev_dbg(&dev->dev, "%s: failed to get Status - " \ + "too long: %d\n", __func__, descriptor->bLength); + return -1; + } + + if (usb_get_hub_status(dev, buffer) < 0) { + dev_dbg(&dev->dev, "%s: failed to get Status %lX\n", __func__, + dev->status); + return -1; + } + + hubsts = (struct usb_hub_status *)buffer; + dev_dbg(&dev->dev, "get_hub_status returned status %X, change %X\n", + le16_to_cpu(hubsts->wHubStatus), + le16_to_cpu(hubsts->wHubChange)); + dev_dbg(&dev->dev, "local power source is %s\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ + "lost (inactive)" : "good"); + dev_dbg(&dev->dev, "%sover-current condition exists\n", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ + "" : "no "); + usb_hub_power_on(hub); + + return 0; +} + +static int usb_hub_configure_ports(struct usb_device *dev) +{ + int i; + + for (i = 0; i < dev->maxchild; i++) + usb_hub_configure_port(dev, i); + + return 0; +} + +static int usb_hub_detect(struct device_d *dev) +{ + struct usb_device *usbdev = container_of(dev, struct usb_device, dev); + int i; + + usb_hub_configure_ports(usbdev); + + for (i = 0; i < usbdev->maxchild; i++) { + if (usbdev->children[i]) + device_detect(&usbdev->children[i]->dev); + } + + return 0; +} + +static int usb_hub_probe(struct usb_device *usbdev, + const struct usb_device_id *id) +{ + usbdev->dev.detect = usb_hub_detect; + + return usb_hub_configure(usbdev); +} + +static void usb_hub_disconnect(struct usb_device *usbdev) +{ + free(usbdev->hub); +} + +/* Table with supported devices, most specific first. */ +static struct usb_device_id usb_hubage_usb_ids[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, + .bInterfaceClass = USB_CLASS_HUB, + }, + { } +}; + + +/*********************************************************************** + * USB Storage driver initialization and registration + ***********************************************************************/ + +static struct usb_driver usb_hubage_driver = { + .name = "usb-hub", + .id_table = usb_hubage_usb_ids, + .probe = usb_hub_probe, + .disconnect = usb_hub_disconnect, +}; + +static int __init usb_hub_init(void) +{ + return usb_driver_register(&usb_hubage_driver); +} +device_initcall(usb_hub_init); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h new file mode 100644 index 0000000..fcf1f0c --- /dev/null +++ b/drivers/usb/core/hub.h @@ -0,0 +1,7 @@ +#ifndef __CORE_HUB_H +#define __CORE_HUB_H + +int hub_port_reset(struct usb_device *dev, int port, + unsigned short *portstat); + +#endif /* __CORE_HUB_H */ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index dea5f6e..3f3d595 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -53,6 +53,9 @@ #include #include +#include "usb.h" +#include "hub.h" + /* #define USB_DEBUG */ #ifdef USB_DEBUG @@ -63,15 +66,12 @@ #define USB_BUFSIZ 512 +static int dev_count; static int dev_index; static int asynch_allowed; -static int usb_hub_probe(struct usb_device *dev, int ifnum); -static int hub_port_reset(struct usb_device *dev, int port, - unsigned short *portstat); - static LIST_HEAD(host_list); -static LIST_HEAD(usb_device_list); +LIST_HEAD(usb_device_list); static void print_usb_device(struct usb_device *dev) { @@ -292,13 +292,12 @@ * * Returns 0 for success, != 0 for error. */ -static int usb_new_device(struct usb_device *dev) +int usb_new_device(struct usb_device *dev) { int addr, err; int tmp; void *buf; struct usb_device_descriptor *desc; - int port = -1; struct usb_device *parent = dev->parent; unsigned short portstatus; char str[16]; @@ -339,24 +338,10 @@ /* find the port number we're at */ if (parent) { - int j; - - for (j = 0; j < parent->maxchild; j++) { - if (parent->children[j] == dev) { - port = j; - break; - } - } - if (port < 0) { - printf("%s: cannot locate device's port.\n", __func__); - err = -ENODEV; - goto err_out; - } - /* reset the port for the second time */ - err = hub_port_reset(dev->parent, port, &portstatus); + err = hub_port_reset(dev->parent, dev->portnr - 1, &portstatus); if (err < 0) { - printf("\n Couldn't reset port %i\n", port); + printf("\n Couldn't reset port %i\n", dev->portnr); goto err_out; } } @@ -434,22 +419,21 @@ dev->serial, sizeof(dev->serial)); if (parent) { - sprintf(dev->dev.name, "%s-%d", parent->dev.name, port); + sprintf(dev->dev.name, "%s-%d", parent->dev.name, dev->portnr - 1); } else { sprintf(dev->dev.name, "usb%d", dev->host->busnum); } dev->dev.id = DEVICE_ID_SINGLE; - if (dev->host->hw_dev) - dev->dev.parent = dev->host->hw_dev; - register_device(&dev->dev); - - /* now prode if the device is a hub */ - usb_hub_probe(dev, 0); - print_usb_device(dev); + err = register_device(&dev->dev); + if (err) { + printf("Failed to register device: %s\n", strerror(-err)); + goto err_out; + } + dev_add_param_int_ro(&dev->dev, "iManufacturer", dev->descriptor->iManufacturer, "%d"); dev_add_param_int_ro(&dev->dev, "iProduct", @@ -465,6 +449,7 @@ dev_add_param_int_ro(&dev->dev, "idProduct", le16_to_cpu(dev->descriptor->idProduct), "%04x"); list_add_tail(&dev->list, &usb_device_list); + dev_count++; err = 0; @@ -473,72 +458,87 @@ return err; } -static struct usb_device *usb_alloc_new_device(void) +void usb_free_device(struct usb_device *usbdev) +{ + dma_free(usbdev->descriptor); + dma_free(usbdev->setup_packet); + free(usbdev); +} + +void usb_remove_device(struct usb_device *usbdev) +{ + int i; + + if (!usbdev) + return; + + for (i = 0; i < usbdev->maxchild; i++) + usb_remove_device(usbdev->children[i]); + if (usbdev->parent && usbdev->portnr) + usbdev->parent->children[usbdev->portnr - 1] = NULL; + list_del(&usbdev->list); + dev_count--; + + if (unregister_device(&usbdev->dev)) + dev_err(&usbdev->dev, "failed to unregister\n"); + else + dev_info(&usbdev->dev, "removed\n"); + + usb_free_device(usbdev); +} + +struct usb_device *usb_alloc_new_device(void) { struct usb_device *usbdev = xzalloc(sizeof (*usbdev)); - if (!usbdev) - return NULL; - - usbdev->devnum = dev_index + 1; + usbdev->devnum = ++dev_index; usbdev->maxchild = 0; usbdev->dev.bus = &usb_bus_type; usbdev->setup_packet = dma_alloc(sizeof(*usbdev->setup_packet)); usbdev->descriptor = dma_alloc(sizeof(*usbdev->descriptor)); - dev_index++; - return usbdev; } -int usb_host_detect(struct usb_host *host, int force) +int usb_host_detect(struct usb_host *host) { - struct usb_device *dev, *tmp; int ret; - if (host->scanned && !force) - return -EBUSY; + if (!host->root_dev) { + ret = host->init(host); + if (ret) + return ret; - list_for_each_entry_safe(dev, tmp, &usb_device_list, list) { - if (dev->host != host) - continue; + host->root_dev = usb_alloc_new_device(); + host->root_dev->dev.parent = host->hw_dev; + host->root_dev->host = host; - list_del(&dev->list); - unregister_device(&dev->dev); - free(dev->hub); - dma_free(dev->setup_packet); - dma_free(dev->descriptor); - free(dev); + ret = usb_new_device(host->root_dev); + if (ret) { + usb_free_device(host->root_dev); + return ret; + } } - ret = host->init(host); - if (ret) - return ret; - - dev = usb_alloc_new_device(); - dev->host = host; - usb_new_device(dev); - - host->scanned = 1; + device_detect(&host->root_dev->dev); return 0; } -void usb_rescan(int force) +void usb_rescan(void) { struct usb_host *host; int ret; pr_info("USB: scanning bus for devices...\n"); - dev_index = 0; list_for_each_entry(host, &host_list, list) { - ret = usb_host_detect(host, force); + ret = usb_host_detect(host); if (ret) continue; } - pr_info("%d USB Device(s) found\n", dev_index); + pr_info("%d USB Device(s) found\n", dev_count); } /* @@ -927,406 +927,6 @@ return err; } -/**************************************************************************** - * HUB "Driver" - * Probes device for being a hub and configurate it - */ - -#undef USB_HUB_DEBUG - -#ifdef USB_HUB_DEBUG -#define USB_HUB_PRINTF(fmt, args...) printf(fmt , ##args) -#else -#define USB_HUB_PRINTF(fmt, args...) -#endif - -static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); -} - -static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, - port, NULL, 0, USB_CNTL_TIMEOUT); -} - -static int usb_set_port_feature(struct usb_device *dev, int port, int feature) -{ - return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - USB_REQ_SET_FEATURE, USB_RT_PORT, feature, - port, NULL, 0, USB_CNTL_TIMEOUT); -} - -static int usb_get_hub_status(struct usb_device *dev, void *data) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, - data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); -} - -static int usb_get_port_status(struct usb_device *dev, int port, void *data) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, - data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); -} - - -static void usb_hub_power_on(struct usb_hub_device *hub) -{ - int i; - struct usb_device *dev; - - dev = hub->pusb_dev; - /* Enable power to the ports */ - USB_HUB_PRINTF("enabling power on all ports\n"); - for (i = 0; i < dev->maxchild; i++) { - usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); - USB_HUB_PRINTF("port %d returns %lX\n", i + 1, dev->status); - } - /* power on is encoded in 2ms increments -> times 2 for the actual delay */ - mdelay(hub->desc.bPwrOn2PwrGood*2); -} - -#define MAX_TRIES 5 - -static inline char *portspeed(int portstatus) -{ - if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) - return "480 Mb/s"; - else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) - return "1.5 Mb/s"; - else - return "12 Mb/s"; -} - -static int hub_port_reset(struct usb_device *dev, int port, - unsigned short *portstat) -{ - int tries; - struct usb_port_status portsts; - unsigned short portstatus, portchange; - - USB_HUB_PRINTF("hub_port_reset: resetting port %d...\n", port); - for (tries = 0; tries < MAX_TRIES; tries++) { - - usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); - wait_ms(200); - - if (usb_get_port_status(dev, port + 1, &portsts) < 0) { - USB_HUB_PRINTF("get_port_status failed status %lX\n", - dev->status); - return -1; - } - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - - USB_HUB_PRINTF("portstatus %x, change %x, %s\n", - portstatus, portchange, - portspeed(portstatus)); - - USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d" \ - " USB_PORT_STAT_ENABLE %d\n", - (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, - (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, - (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); - - if ((portchange & USB_PORT_STAT_C_CONNECTION) || - !(portstatus & USB_PORT_STAT_CONNECTION)) - return -1; - - if (portstatus & USB_PORT_STAT_ENABLE) - break; - - wait_ms(200); - } - - if (tries == MAX_TRIES) { - USB_HUB_PRINTF("Cannot enable port %i after %i retries, " \ - "disabling port.\n", port + 1, MAX_TRIES); - USB_HUB_PRINTF("Maybe the USB cable is bad?\n"); - return -1; - } - - usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); - *portstat = portstatus; - return 0; -} - - -static void usb_hub_port_connect_change(struct usb_device *dev, int port) -{ - struct usb_device *usb; - struct usb_port_status portsts; - unsigned short portstatus, portchange; - - /* Check status */ - if (usb_get_port_status(dev, port + 1, &portsts) < 0) { - USB_HUB_PRINTF("get_port_status failed\n"); - return; - } - - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - USB_HUB_PRINTF("portstatus %x, change %x, %s\n", - portstatus, portchange, portspeed(portstatus)); - - /* Clear the connection change status */ - usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); - - /* Disconnect any existing devices under this port */ - if (((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) { - USB_HUB_PRINTF("usb_disconnect(&hub->children[port]);\n"); - /* Return now if nothing is connected */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return; - } - wait_ms(200); - - /* Reset the port */ - if (hub_port_reset(dev, port, &portstatus) < 0) { - printf("cannot reset port %i!?\n", port + 1); - return; - } - - wait_ms(200); - - /* Allocate a new device struct for it */ - usb = usb_alloc_new_device(); - usb->host = dev->host; - - if (portstatus & USB_PORT_STAT_HIGH_SPEED) - usb->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) - usb->speed = USB_SPEED_LOW; - else - usb->speed = USB_SPEED_FULL; - - dev->children[port] = usb; - usb->parent = dev; - /* Run it through the hoops (find a driver, etc) */ - if (usb_new_device(usb)) { - /* Woops, disable the port */ - USB_HUB_PRINTF("hub: disabling port %d\n", port + 1); - usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); - } -} - - -static int usb_hub_configure(struct usb_device *dev) -{ - unsigned char buffer[USB_BUFSIZ], *bitmap; - struct usb_hub_descriptor *descriptor; - struct usb_hub_status *hubsts; - int i; - struct usb_hub_device *hub; - - hub = xzalloc(sizeof (*hub)); - dev->hub = hub; - - hub->pusb_dev = dev; - /* Get the the hub descriptor */ - if (usb_get_hub_descriptor(dev, buffer, 4) < 0) { - USB_HUB_PRINTF("%s: failed to get hub " \ - "descriptor, giving up %lX\n", __func__, dev->status); - return -1; - } - descriptor = (struct usb_hub_descriptor *)buffer; - - /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */ - i = descriptor->bLength; - if (i > USB_BUFSIZ) { - USB_HUB_PRINTF("%s: failed to get hub " \ - "descriptor - too long: %d\n", __func__, - descriptor->bLength); - return -1; - } - - if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) { - USB_HUB_PRINTF("%s: failed to get hub " \ - "descriptor 2nd giving up %lX\n", __func__, dev->status); - return -1; - } - memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); - /* adjust 16bit values */ - hub->desc.wHubCharacteristics = - le16_to_cpu(descriptor->wHubCharacteristics); - /* set the bitmap */ - bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; - /* devices not removable by default */ - memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); - bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; - memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ - - for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) - hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; - - for (i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) - hub->desc.DeviceRemovable[i] = descriptor->PortPowerCtrlMask[i]; - - dev->maxchild = descriptor->bNbrPorts; - USB_HUB_PRINTF("%d ports detected\n", dev->maxchild); - - switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) { - case 0x00: - USB_HUB_PRINTF("ganged power switching\n"); - break; - case 0x01: - USB_HUB_PRINTF("individual port power switching\n"); - break; - case 0x02: - case 0x03: - USB_HUB_PRINTF("unknown reserved power switching mode\n"); - break; - } - - if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND) - USB_HUB_PRINTF("part of a compound device\n"); - else - USB_HUB_PRINTF("standalone hub\n"); - - switch (hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) { - case 0x00: - USB_HUB_PRINTF("global over-current protection\n"); - break; - case 0x08: - USB_HUB_PRINTF("individual port over-current protection\n"); - break; - case 0x10: - case 0x18: - USB_HUB_PRINTF("no over-current protection\n"); - break; - } - - USB_HUB_PRINTF("power on to power good time: %dms\n", - descriptor->bPwrOn2PwrGood * 2); - USB_HUB_PRINTF("hub controller current requirement: %dmA\n", - descriptor->bHubContrCurrent); - - for (i = 0; i < dev->maxchild; i++) - USB_HUB_PRINTF("port %d is%s removable\n", i + 1, - hub->desc.DeviceRemovable[(i + 1) / 8] & \ - (1 << ((i + 1) % 8)) ? " not" : ""); - - if (sizeof(struct usb_hub_status) > USB_BUFSIZ) { - USB_HUB_PRINTF("%s: failed to get Status - " \ - "too long: %d\n", __func__, descriptor->bLength); - return -1; - } - - if (usb_get_hub_status(dev, buffer) < 0) { - USB_HUB_PRINTF("%s: failed to get Status %lX\n", __func__, - dev->status); - return -1; - } - - hubsts = (struct usb_hub_status *)buffer; - USB_HUB_PRINTF("get_hub_status returned status %X, change %X\n", - le16_to_cpu(hubsts->wHubStatus), - le16_to_cpu(hubsts->wHubChange)); - USB_HUB_PRINTF("local power source is %s\n", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? \ - "lost (inactive)" : "good"); - USB_HUB_PRINTF("%sover-current condition exists\n", - (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? \ - "" : "no "); - usb_hub_power_on(hub); - - for (i = 0; i < dev->maxchild; i++) { - struct usb_port_status portsts; - unsigned short portstatus, portchange; - - if (usb_get_port_status(dev, i + 1, &portsts) < 0) { - USB_HUB_PRINTF("get_port_status failed\n"); - continue; - } - - portstatus = le16_to_cpu(portsts.wPortStatus); - portchange = le16_to_cpu(portsts.wPortChange); - USB_HUB_PRINTF("Port %d Status %X Change %X\n", - i + 1, portstatus, portchange); - - if (portchange & USB_PORT_STAT_C_CONNECTION) { - USB_HUB_PRINTF("port %d connection change\n", i + 1); - usb_hub_port_connect_change(dev, i); - } - if (portchange & USB_PORT_STAT_C_ENABLE) { - USB_HUB_PRINTF("port %d enable change, status %x\n", - i + 1, portstatus); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_ENABLE); - - /* EM interference sometimes causes bad shielded USB - * devices to be shutdown by the hub, this hack enables - * them again. Works at least with mouse driver */ - if (!(portstatus & USB_PORT_STAT_ENABLE) && - (portstatus & USB_PORT_STAT_CONNECTION) && - ((dev->children[i]))) { - USB_HUB_PRINTF("already running port %i " \ - "disabled by hub (EMI?), " \ - "re-enabling...\n", i + 1); - usb_hub_port_connect_change(dev, i); - } - } - if (portstatus & USB_PORT_STAT_SUSPEND) { - USB_HUB_PRINTF("port %d suspend change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_SUSPEND); - } - - if (portchange & USB_PORT_STAT_C_OVERCURRENT) { - USB_HUB_PRINTF("port %d over-current change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_OVER_CURRENT); - usb_hub_power_on(hub); - } - - if (portchange & USB_PORT_STAT_C_RESET) { - USB_HUB_PRINTF("port %d reset change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_RESET); - } - } /* end for i all ports */ - - return 0; -} - -static int usb_hub_probe(struct usb_device *dev, int ifnum) -{ - struct usb_interface *iface; - struct usb_endpoint_descriptor *ep; - int ret; - - iface = &dev->config.interface[ifnum]; - /* Is it a hub? */ - if (iface->desc.bInterfaceClass != USB_CLASS_HUB) - return 0; - /* Some hubs have a subclass of 1, which AFAICT according to the */ - /* specs is not defined, but it works */ - if ((iface->desc.bInterfaceSubClass != 0) && - (iface->desc.bInterfaceSubClass != 1)) - return 0; - /* Multiple endpoints? What kind of mutant ninja-hub is this? */ - if (iface->desc.bNumEndpoints != 1) - return 0; - ep = &iface->ep_desc[0]; - /* Output endpoint? Curiousier and curiousier.. */ - if (!(ep->bEndpointAddress & USB_DIR_IN)) - return 0; - /* If it's not an interrupt endpoint, we'd better punt! */ - if ((ep->bmAttributes & 3) != 3) - return 0; - /* We found a hub */ - USB_HUB_PRINTF("USB hub found\n"); - ret = usb_hub_configure(dev); - return ret; -} - int usb_driver_register(struct usb_driver *drv) { drv->driver.name = drv->name; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h new file mode 100644 index 0000000..a5bb255 --- /dev/null +++ b/drivers/usb/core/usb.h @@ -0,0 +1,9 @@ +#ifndef __CORE_USB_H +#define __CORE_USB_H + +struct usb_device *usb_alloc_new_device(void); +void usb_free_device(struct usb_device *dev); +int usb_new_device(struct usb_device *dev); +void usb_remove_device(struct usb_device *dev); + +#endif /* __CORE_USB_H */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index a69e758..12a090c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -23,3 +23,21 @@ bool "AT91 OHCI driver" endif + +config USB_XHCI + bool "xHCI driver" + help + The eXtensible Host Controller Interface (xHCI) is standard for + USB 3.0 "SuperSpeed" host controller hardware. xHCI specification + defines support for all USB device speeds from USB 3.0 down to + USB 1.1 without the need for companion controllers. + + This driver currently only supports virtual USB 2.0 ports, if you + plan to use USB 3.0 devices, use a USB 2.0 cable in between. + +config USB_XHCI_PCI + depends on PCI + select USB_XHCI + bool "PCI xHCI driver" + help + Enables support for PCI attached xHCI controllers. diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 156fc7f..0478d34 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o obj-$(CONFIG_USB_OHCI) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_AT91) += ohci-at91.o +obj-$(CONFIG_USB_XHCI) += xhci-hcd.o xhci-hub.o +obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index d30c3aa..c0ea8d0 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -57,66 +57,66 @@ static struct descriptor { struct usb_hub_descriptor hub; struct usb_device_descriptor device; - struct usb_linux_config_descriptor config; - struct usb_linux_interface_descriptor interface; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; struct usb_endpoint_descriptor endpoint; } __attribute__ ((packed)) descriptor = { - { - 0x8, /* bDescLength */ - 0x29, /* bDescriptorType: hub descriptor */ - 2, /* bNrPorts -- runtime modified */ - 0, /* wHubCharacteristics */ - 10, /* bPwrOn2PwrGood */ - 0, /* bHubCntrCurrent */ - {}, /* Device removable */ - {} /* at most 7 ports! XXX */ + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 2, /* runtime modified */ + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 10, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {}, + .u.hs.PortPwrCtrlMask = {} }, - { - 0x12, /* bLength */ - 1, /* bDescriptorType: UDESC_DEVICE */ - 0x0002, /* bcdUSB: v2.0 */ - 9, /* bDeviceClass: UDCLASS_HUB */ - 0, /* bDeviceSubClass: UDSUBCLASS_HUB */ - 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ - 64, /* bMaxPacketSize: 64 bytes */ - 0x0000, /* idVendor */ - 0x0000, /* idProduct */ - 0x0001, /* bcdDevice */ - 1, /* iManufacturer */ - 2, /* iProduct */ - 0, /* iSerialNumber */ - 1 /* bNumConfigurations: 1 */ + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0002), /* v2.0 */ + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = 1, /* bDeviceProtocol: UDPROTO_HSHUBSTT */ + .bMaxPacketSize0 = 64, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = __constant_cpu_to_le16(0x0001), + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1 }, - { - 0x9, - 2, /* bDescriptorType: UDESC_CONFIG */ - cpu_to_le16(0x19), - 1, /* bNumInterface */ - 1, /* bConfigurationValue */ - 0, /* iConfiguration */ - 0x40, /* bmAttributes: UC_SELF_POWER */ - 0 /* bMaxPower */ + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0 }, - { - 0x9, /* bLength */ - 4, /* bDescriptorType: UDESC_INTERFACE */ - 0, /* bInterfaceNumber */ - 0, /* bAlternateSetting */ - 1, /* bNumEndpoints */ - 9, /* bInterfaceClass: UICLASS_HUB */ - 0, /* bInterfaceSubClass: UISUBCLASS_HUB */ - 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ - 0 /* iInterface */ + .interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, /* bInterfaceProtocol: UIPROTO_HSHUBSTT */ + .iInterface = 0 }, - { - 0x7, /* bLength */ - 5, /* bDescriptorType: UDESC_ENDPOINT */ - 0x81, /* bEndpointAddress: - * UE_DIR_IN | EHCI_INTR_ENDPT - */ - 3, /* bmAttributes: UE_INTERRUPT */ - 8, 0, /* wMaxPacketSize */ - 255 /* bInterval */ + .endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x81, /* UE_DIR_IN | EHCI_INTR_ENDPT */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16((USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 }, }; @@ -436,16 +436,6 @@ return -1; } -static inline int min3(int a, int b, int c) -{ - - if (b < a) - a = b; - if (c < a) - a = c; - return a; -} - #ifdef CONFIG_MACH_EFIKA_MX_SMARTBOOK #include /* @@ -503,12 +493,12 @@ case USB_DT_DEVICE: dev_dbg(ehci->dev, "USB_DT_DEVICE request\n"); srcptr = &descriptor.device; - srclen = 0x12; + srclen = descriptor.device.bLength; break; case USB_DT_CONFIG: dev_dbg(ehci->dev, "USB_DT_CONFIG config\n"); srcptr = &descriptor.config; - srclen = 0x19; + srclen = le16_to_cpu(descriptor.config.wTotalLength); break; case USB_DT_STRING: dev_dbg(ehci->dev, "USB_DT_STRING config\n"); @@ -543,7 +533,7 @@ case USB_DT_HUB: dev_dbg(ehci->dev, "USB_DT_HUB config\n"); srcptr = &descriptor.hub; - srclen = 0x8; + srclen = descriptor.hub.bLength; break; default: dev_dbg(ehci->dev, "unknown value %x\n", le16_to_cpu(req->value)); @@ -717,7 +707,7 @@ } wait_ms(1); - len = min3(srclen, le16_to_cpu(req->length), length); + len = min3(srclen, (int)le16_to_cpu(req->length), length); if (srcptr != NULL && len > 0) memcpy(buffer, srcptr, len); else @@ -871,7 +861,7 @@ { struct ehci_priv *ehci = dev->priv; - return usb_host_detect(&ehci->host, 0); + return usb_host_detect(&ehci->host); } int ehci_register(struct device_d *dev, struct ehci_data *data) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5d899cc..d71d056 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -18,26 +18,12 @@ #ifndef USB_EHCI_H #define USB_EHCI_H +#include + #if !defined(CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 16 #endif -/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ -#define DeviceRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) - -#define DeviceOutRequest \ - ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) - -#define InterfaceRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - -#define EndpointRequest \ - ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - -#define EndpointOutRequest \ - ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) - /* * Register Space. */ @@ -84,39 +70,15 @@ #define USBMODE_CM_HC (3 << 0) /* host controller mode */ #define USBMODE_CM_IDLE (0 << 0) /* idle state */ -/* Interface descriptor */ -struct usb_linux_interface_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bInterfaceNumber; - unsigned char bAlternateSetting; - unsigned char bNumEndpoints; - unsigned char bInterfaceClass; - unsigned char bInterfaceSubClass; - unsigned char bInterfaceProtocol; - unsigned char iInterface; -} __attribute__ ((packed)); +static inline void ehci_writel(__u32 __iomem *regs, const unsigned int val) +{ + writel(val, regs); +} -/* Configuration descriptor information.. */ -struct usb_linux_config_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short wTotalLength; - unsigned char bNumInterfaces; - unsigned char bConfigurationValue; - unsigned char iConfiguration; - unsigned char bmAttributes; - unsigned char MaxPower; -} __attribute__ ((packed)); - -#if defined CONFIG_EHCI_DESC_BIG_ENDIAN -#define ehci_readl(x) (*((volatile u32 *)(x))) -#define ehci_writel(a, b) (*((volatile u32 *)(a)) = ((volatile u32)b)) -#else -#define ehci_readl(x) cpu_to_le32((*((volatile u32 *)(x)))) -#define ehci_writel(a, b) (*((volatile u32 *)(a)) = \ - cpu_to_le32(((volatile u32)b))) -#endif +static inline unsigned int ehci_readl(__u32 __iomem *regs) +{ + return readl(regs); +} #if defined CONFIG_EHCI_MMIO_BIG_ENDIAN #define hc32_to_cpu(x) be32_to_cpu((x)) diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c new file mode 100644 index 0000000..9253419 --- /dev/null +++ b/drivers/usb/host/xhci-hcd.c @@ -0,0 +1,1509 @@ +/* + * xHCI HCD driver + * + * Sebastian Hesselbarth + * + * Some code borrowed from the Linux xHCI driver + * Author: Sarah Sharp + * Copyright (C) 2008 Intel Corp. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xhci.h" + +/* + * xHCI ring handling + */ + +static int xhci_ring_is_last_trb(struct xhci_ring *ring, union xhci_trb *trb) +{ + if (ring->type == TYPE_EVENT) + return trb == &ring->trbs[NUM_EVENT_TRBS]; + else + return TRB_TYPE_LINK(le32_to_cpu(trb->link.control)); +} + +static int xhci_ring_increment(struct xhci_ring *ring, bool enqueue) +{ + union xhci_trb **queue = (enqueue) ? &ring->enqueue : &ring->dequeue; + + (*queue)++; + + if (!xhci_ring_is_last_trb(ring, *queue)) + return 0; + + if (ring->type == TYPE_EVENT) { + *queue = &ring->trbs[0]; + ring->cycle_state ^= 1; + } else { + u32 ctrl = le32_to_cpu((*queue)->link.control); + void *p = (void *)(dma_addr_t) + le64_to_cpu((*queue)->link.segment_ptr); + + ctrl = (ctrl & ~TRB_CYCLE) | ring->cycle_state; + (*queue)->link.control = cpu_to_le32(ctrl); + + if (enqueue) + ring->enqueue = p; + else + ring->dequeue = p; + + if (ctrl & LINK_TOGGLE) + ring->cycle_state ^= 1; + } + + return 0; +} + +static int xhci_ring_issue_trb(struct xhci_ring *ring, union xhci_trb *trb) +{ + union xhci_trb *enq = ring->enqueue; + int i; + + /* Pass TRB to hardware */ + trb->generic.field[3] &= ~TRB_CYCLE; + trb->generic.field[3] |= ring->cycle_state; + for (i = 0; i < 4; i++) + enq->generic.field[i] = cpu_to_le32(trb->generic.field[i]); + + xhci_ring_increment(ring, 1); + + return 0; +} + +static void xhci_ring_init(struct xhci_ring *ring, int num_trbs, + enum xhci_ring_type type) +{ + ring->type = type; + ring->cycle_state = 1; + ring->num_trbs = num_trbs; + ring->enqueue = ring->dequeue = &ring->trbs[0]; + + /* Event ring is not linked */ + if (type == TYPE_EVENT) + return; + + ring->trbs[num_trbs-1].link.segment_ptr = + cpu_to_le64((dma_addr_t)&ring->trbs[0]); + ring->trbs[num_trbs-1].link.control = + cpu_to_le32(TRB_TYPE(TRB_LINK) | LINK_TOGGLE); +} + +static struct xhci_ring *xhci_get_endpoint_ring(struct xhci_hcd *xhci) +{ + struct xhci_ring *ring; + + if (list_empty(&xhci->rings_list)) { + dev_err(xhci->dev, "no more endpoint rings available\n"); + return NULL; + } + + ring = list_last_entry(&xhci->rings_list, struct xhci_ring, list); + list_del_init(&ring->list); + + return ring; +} + +static void xhci_put_endpoint_ring(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + if (!ring) + return; + + memset(ring->trbs, 0, ring->num_trbs * sizeof(union xhci_trb)); + list_add_tail(&ring->list, &xhci->rings_list); +} + +/* + * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the + * core and HCDs. Find the index for an endpoint given its descriptor. + * Use the return value to right shift 1 for the bitmask. + * + * Index = (epnum * 2) + direction - 1, + * where direction = 0 for OUT, 1 for IN. + * For control endpoints, the IN index is used (OUT index is unused), so + * index = (epnum * 2) + direction - 1 = (epnum * 2) + 1 - 1 = (epnum * 2) + */ +static unsigned int xhci_get_endpoint_index(u8 epaddress, u8 epattributes) +{ + u8 epnum = epaddress & USB_ENDPOINT_NUMBER_MASK; + u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; + unsigned int index; + + if (xfer == USB_ENDPOINT_XFER_CONTROL) + index = (unsigned int)(epnum * 2); + else + index = (unsigned int)(epnum * 2) + + ((epaddress & USB_DIR_IN) ? 1 : 0) - 1; + + return index; +} + +static u8 xhci_get_endpoint_type(u8 epaddress, u8 epattributes) +{ + int in = epaddress & USB_ENDPOINT_DIR_MASK; + u8 xfer = epattributes & USB_ENDPOINT_XFERTYPE_MASK; + u8 type; + + switch (xfer) { + case USB_ENDPOINT_XFER_CONTROL: + type = CTRL_EP; + break; + case USB_ENDPOINT_XFER_ISOC: + type = (in) ? ISOC_IN_EP : ISOC_OUT_EP; + break; + case USB_ENDPOINT_XFER_BULK: + type = (in) ? BULK_IN_EP : BULK_OUT_EP; + break; + case USB_ENDPOINT_XFER_INT: + type = (in) ? INT_IN_EP : INT_OUT_EP; + break; + } + + return type; +} + +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + * + */ +static u32 xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + u32 interval; + + interval = clamp_val(ep->bInterval, 1, 16) - 1; + /* + * Full speed isoc endpoints specify interval in frames, + * not microframes. We are using microframes everywhere, + * so adjust accordingly. + */ + if (udev->speed == USB_SPEED_FULL) + interval += 3; /* 1 frame = 2^3 uframes */ + + return interval; +} + +/* + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static u32 xhci_microframes_to_exponent(struct usb_device *udev, + struct usb_endpoint_descriptor *ep, u32 desc_interval, + u32 min_exponent, u32 max_exponent) +{ + u32 interval; + + interval = fls(desc_interval) - 1; + return clamp_val(interval, min_exponent, max_exponent); +} + +static inline u32 xhci_parse_microframe_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + if (ep->bInterval == 0) + return 0; + return xhci_microframes_to_exponent(udev, ep, ep->bInterval, 0, 15); +} + + +static inline u32 xhci_parse_frame_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + return xhci_microframes_to_exponent(udev, ep, ep->bInterval * 8, 3, 10); +} + +static u32 xhci_get_endpoint_interval(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + u32 interval = 0; + + switch (udev->speed) { + case USB_SPEED_HIGH: + /* Max NAK rate */ + if (type == USB_ENDPOINT_XFER_CONTROL || + type == USB_ENDPOINT_XFER_BULK) { + interval = xhci_parse_microframe_interval(udev, ep); + break; + } + /* Fall through - SS and HS isoc/int have same decoding */ + case USB_SPEED_SUPER: + if (type == USB_ENDPOINT_XFER_ISOC || + type == USB_ENDPOINT_XFER_INT) + interval = xhci_parse_exponent_interval(udev, ep); + break; + case USB_SPEED_FULL: + if (type == USB_ENDPOINT_XFER_ISOC) { + interval = xhci_parse_exponent_interval(udev, ep); + break; + } + /* + * Fall through for interrupt endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + case USB_SPEED_LOW: + if (type == USB_ENDPOINT_XFER_ISOC || + type == USB_ENDPOINT_XFER_INT) + interval = xhci_parse_frame_interval(udev, ep); + break; + } + + return interval; +} + +/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. + * High speed endpoint descriptors can define "the number of additional + * transaction opportunities per microframe", but that goes in the Max Burst + * endpoint context field. + */ +static u32 xhci_get_endpoint_mult(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (udev->speed != USB_SPEED_SUPER || type != USB_ENDPOINT_XFER_ISOC) + return 0; + /* FIXME: return ss_ep_comp_descriptor.bmAttributes */ + return 0; +} + +/* Return the maximum endpoint service interval time (ESIT) payload. + * Basically, this is the maxpacket size, multiplied by the burst size + * and mult size. + */ +static u32 xhci_get_max_esit_payload(struct usb_device *udev, + struct usb_endpoint_descriptor *ep) +{ + u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + int max_burst; + int max_packet; + u16 mps; + + /* Only applies for interrupt or isochronous endpoints */ + if (type != USB_ENDPOINT_XFER_INT && type != USB_ENDPOINT_XFER_ISOC) + return 0; + + /* FIXME: return ss_ep_comp_descriptor.wBytesPerInterval */ + if (udev->speed == USB_SPEED_SUPER) + return 0; + + mps = le16_to_cpu(ep->wMaxPacketSize); + max_packet = GET_MAX_PACKET(mps); + max_burst = (mps & 0x1800) >> 11; + /* A 0 in max burst means 1 transfer per ESIT */ + return max_packet * (max_burst + 1); +} + +int xhci_handshake(void __iomem *p, u32 mask, u32 done, int usec) +{ + u32 result; + u64 start; + + start = get_time_ns(); + + while (1) { + result = readl(p) & mask; + if (result == done) + return 0; + if (is_timeout(start, usec * USECOND)) + return -ETIMEDOUT; + } +} + +int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb) +{ + int ret; + + ret = xhci_ring_issue_trb(&xhci->cmd_ring, trb); + if (ret) + return ret; + + /* Ring the bell */ + writel(DB_VALUE_HOST, &xhci->dba->doorbell[0]); + readl(&xhci->dba->doorbell[0]); + + return 0; +} + +static void xhci_set_event_dequeue(struct xhci_hcd *xhci) +{ + u64 reg64; + + reg64 = xhci_read_64(&xhci->ir_set->erst_dequeue); + reg64 &= ERST_PTR_MASK; + /* + * Don't clear the EHB bit (which is RW1C) because + * there might be more events to service. + */ + reg64 &= ~ERST_EHB; + reg64 |= (dma_addr_t)xhci->event_ring.dequeue & + ~(dma_addr_t)ERST_PTR_MASK; + + /* Update HC event ring dequeue pointer */ + xhci_write_64(reg64, &xhci->ir_set->erst_dequeue); +} + +int xhci_wait_for_event(struct xhci_hcd *xhci, u8 type, union xhci_trb *trb) +{ + while (true) { + union xhci_trb *deq = xhci->event_ring.dequeue; + u8 event_type; + int i, ret; + + ret = xhci_handshake(&deq->event_cmd.flags, + cpu_to_le32(TRB_CYCLE), + cpu_to_le32(xhci->event_ring.cycle_state), + XHCI_CMD_DEFAULT_TIMEOUT / USECOND); + if (ret) { + dev_err(xhci->dev, "Timeout while waiting for event\n"); + return ret; + } + + for (i = 0; i < 4; i++) + trb->generic.field[i] = + le32_to_cpu(deq->generic.field[i]); + + xhci_set_event_dequeue(xhci); + xhci_ring_increment(&xhci->event_ring, 0); + + event_type = TRB_FIELD_TO_TYPE(trb->event_cmd.flags); + + switch (event_type) { + case TRB_PORT_STATUS: + dev_dbg(xhci->dev, "Event PortStatusChange %u\n", + GET_PORT_ID(trb->generic.field[0])); + break; + case TRB_TRANSFER: + dev_dbg(xhci->dev, "Event Transfer %u\n", + GET_COMP_CODE(trb->event_cmd.status)); + ret = -GET_COMP_CODE(trb->event_cmd.status); + if (ret == -COMP_SUCCESS) + ret = 0; + break; + case TRB_COMPLETION: + dev_dbg(xhci->dev, "Event CommandCompletion %u\n", + GET_COMP_CODE(trb->event_cmd.status)); + ret = -GET_COMP_CODE(trb->event_cmd.status); + if (ret == -COMP_SUCCESS) + ret = 0; + break; + default: + dev_err(xhci->dev, "unhandled event %u (%02x) [%08x %08x %08x %08x]\n", + event_type, event_type, + trb->generic.field[0], trb->generic.field[1], + trb->generic.field[2], trb->generic.field[3]); + } + + if (event_type == type) + return ret; + } + return -ENOSYS; +} + +static struct xhci_virtual_device *xhci_find_virtdev(struct xhci_hcd *xhci, + struct usb_device *udev) +{ + struct xhci_virtual_device *vdev; + + list_for_each_entry(vdev, &xhci->vdev_list, list) + if (vdev->udev == udev) + return vdev; + + return NULL; +} + +static struct xhci_virtual_device *xhci_alloc_virtdev(struct xhci_hcd *xhci, + struct usb_device *udev) +{ + struct xhci_virtual_device *vdev; + size_t sz_ctx, sz_ictx, sz_dctx; + void *p; + + vdev = xzalloc(sizeof(*vdev)); + vdev->udev = udev; + list_add_tail(&vdev->list, &xhci->vdev_list); + + sz_ctx = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + /* Device Context: 64B aligned */ + sz_dctx = ALIGN(sz_ctx, 64); + /* Input Control Context: 64B aligned */ + sz_ictx = ALIGN(sz_ctx + HCC_CTX_SIZE(xhci->hcc_params), 64); + + vdev->dma_size = sz_ictx + sz_dctx; + p = vdev->dma = dma_alloc_coherent(vdev->dma_size); + memset(vdev->dma, 0, vdev->dma_size); + + vdev->out_ctx = p; p += sz_dctx; + vdev->in_ctx = p; p += sz_ictx; + + return vdev; +} + +static void xhci_free_virtdev(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + int i; + + for (i = 0; i < USB_MAXENDPOINTS; i++) + if (vdev->ep[i]) + xhci_put_endpoint_ring(xhci, vdev->ep[i]); + + list_del(&vdev->list); + dma_free_coherent(vdev->dma, vdev->dma_size); + free(vdev); +} + +static int xhci_virtdev_issue_transfer(struct xhci_virtual_device *vdev, + u8 ep, union xhci_trb *trb, bool ringbell) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct xhci_ring *ring = vdev->ep[ep]; + int ret; + + ret = xhci_ring_issue_trb(ring, trb); + if (ret || !ringbell) + return ret; + + /* Ring the bell */ + writel(DB_VALUE(ep, 0), &xhci->dba->doorbell[vdev->slot_id]); + readl(&xhci->dba->doorbell[vdev->slot_id]); + + return 0; +} + +static void xhci_virtdev_zero_in_ctx(struct xhci_virtual_device *vdev) +{ + int i; + + /* When a device's add flag and drop flag are zero, any subsequent + * configure endpoint command will leave that endpoint's state + * untouched. Make sure we don't leave any old state in the input + * endpoint contexts. + */ + vdev->in_ctx->icc.drop_flags = 0; + vdev->in_ctx->icc.add_flags = 0; + vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); + /* Endpoint 0 is always valid */ + vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(1)); + for (i = 1; i < 31; i++) { + vdev->in_ctx->ep[i].ep_info = 0; + vdev->in_ctx->ep[i].ep_info2 = 0; + vdev->in_ctx->ep[i].deq = 0; + vdev->in_ctx->ep[i].tx_info = 0; + } +} + +static int xhci_virtdev_disable_slot(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + union xhci_trb trb; + int ret; + + /* Issue Disable Slot Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.flags = TRB_TYPE(TRB_DISABLE_SLOT) | + SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_print_trb(xhci, &trb, "Request DisableSlot"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response DisableSlot"); + + /* Clear Device Context Base Address Array */ + xhci->dcbaa[vdev->slot_id] = 0; + + return ret; +} + +static int xhci_virtdev_enable_slot(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + union xhci_trb trb; + int slot_id; + int ret; + + /* Issue Enable Slot Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.flags = TRB_TYPE(TRB_ENABLE_SLOT); + xhci_print_trb(xhci, &trb, "Request EnableSlot"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response EnableSlot"); + if (ret) + return ret; + + slot_id = TRB_TO_SLOT_ID(trb.event_cmd.flags); + if (slot_id == 0) { + dev_err(xhci->dev, "EnableSlot returned reserved slot ID 0\n"); + return -EINVAL; + } + + vdev->slot_id = slot_id; + + return 0; +} + +int xhci_virtdev_reset(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + union xhci_trb trb; + int ret; + + /* If device is not setup, there is no point in resetting it */ + if (GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)) == + SLOT_STATE_DISABLED) + return 0; + + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.flags = TRB_TYPE(TRB_RESET_DEV) | + SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_print_trb(xhci, &trb, "Request Reset"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response Reset"); + + /* + * The Reset Device command can't fail, according to the 0.95/0.96 spec, + * unless we tried to reset a slot ID that wasn't enabled, + * or the device wasn't in the addressed or configured state. + */ + switch (GET_COMP_CODE(trb.event_cmd.status)) { + case COMP_CMD_ABORT: + case COMP_CMD_STOP: + dev_warn(xhci->dev, "Timeout waiting for reset device command\n"); + ret = -ETIMEDOUT; + break; + case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */ + case COMP_CTX_STATE: /* 0.96 completion code for same thing */ + /* Don't treat this as an error. May change my mind later. */ + ret = 0; + case COMP_SUCCESS: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* + * Once a hub descriptor is fetched for a device, we need to update the xHC's + * internal data structures for the device. + */ +static int xhci_virtdev_update_hub_device(struct xhci_virtual_device *vdev, + void *buffer, int length) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct usb_hub_descriptor *desc = buffer; + union xhci_trb trb; + u32 dev_info, dev_info2, tt_info; + u8 maxchild; + u16 hubchar; + int ret; + + /* Need at least first byte of wHubCharacteristics */ + if (length < 4) + return 0; + /* Skip already configured hub device */ + if (vdev->out_ctx->slot.dev_info & DEV_HUB) + return 0; + + maxchild = desc->bNbrPorts; + hubchar = le16_to_cpu(desc->wHubCharacteristics); + + /* update slot context */ + memcpy(&vdev->in_ctx->slot, &vdev->out_ctx->slot, + sizeof(struct xhci_slot_ctx)); + vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); + vdev->in_ctx->icc.drop_flags = 0; + vdev->in_ctx->slot.dev_state = 0; + dev_info = le32_to_cpu(vdev->in_ctx->slot.dev_info); + dev_info2 = le32_to_cpu(vdev->in_ctx->slot.dev_info2); + tt_info = le32_to_cpu(vdev->in_ctx->slot.tt_info); + + dev_info |= DEV_HUB; + /* HS Multi-TT in bDeviceProtocol */ + if (vdev->udev->speed == USB_SPEED_HIGH && + vdev->udev->descriptor->bDeviceProtocol == USB_HUB_PR_HS_MULTI_TT) + dev_info |= DEV_MTT; + if (xhci->hci_version > 0x95) { + dev_info2 |= XHCI_MAX_PORTS(maxchild); + /* Set TT think time - convert from ns to FS bit times. + * 0 = 8 FS bit times, 1 = 16 FS bit times, + * 2 = 24 FS bit times, 3 = 32 FS bit times. + * + * xHCI 1.0: this field shall be 0 if the device is not a + * High-speed hub. + */ + if (xhci->hci_version < 0x100 || + vdev->udev->speed == USB_SPEED_HIGH) { + u32 think_time = (hubchar & HUB_CHAR_TTTT) >> 5; + tt_info |= TT_THINK_TIME(think_time); + } + } + vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); + vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); + vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + + /* Issue Configure Endpoint or Evaluate Context Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + trb.event_cmd.flags = SLOT_ID_FOR_TRB(vdev->slot_id); + if (xhci->hci_version > 0x95) + trb.event_cmd.flags |= TRB_TYPE(TRB_CONFIG_EP); + else + trb.event_cmd.flags |= TRB_TYPE(TRB_EVAL_CONTEXT); + xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response ConfigureEndpoint"); + xhci_virtdev_zero_in_ctx(vdev); + + return ret; +} + +static int xhci_virtdev_update_hub_status(struct xhci_virtual_device *vhub, + int port) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vhub->udev->host); + struct usb_device *udev = vhub->udev->children[port - 1]; + struct xhci_virtual_device *vdev; + + if (!udev) + return 0; + + /* Check if we have a virtual device for it */ + vdev = xhci_find_virtdev(xhci, udev); + if (vdev) + xhci_virtdev_detach(vdev); + + return 0; +} + +static int xhci_virtdev_configure(struct xhci_virtual_device *vdev, int config) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct usb_device *udev = vdev->udev; + union xhci_trb trb; + u32 add_flags = 0, last_ctx; + int i, j; + int ret; + + for (i = 0; i < udev->config.no_of_if; i++) { + struct usb_interface *intf = &udev->config.interface[i]; + + for (j = 0; j < intf->no_of_ep; j++) { + struct usb_endpoint_descriptor *ep = &intf->ep_desc[j]; + u8 type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + u8 eptype = xhci_get_endpoint_type(ep->bEndpointAddress, + ep->bmAttributes); + u8 epi = xhci_get_endpoint_index(ep->bEndpointAddress, + ep->bmAttributes); + struct xhci_ep_ctx *ctx = &vdev->in_ctx->ep[epi]; + u32 mps, interval, mult, esit, max_packet, max_burst; + u32 ep_info, ep_info2, tx_info; + + vdev->ep[epi] = xhci_get_endpoint_ring(xhci); + if (!vdev->ep[epi]) + return -ENOMEM; + /* FIXME: set correct ring type */ + xhci_ring_init(vdev->ep[epi], NUM_TRANSFER_TRBS, + TYPE_BULK); + add_flags |= BIT(epi+1); + + mps = le16_to_cpu(ep->wMaxPacketSize); + interval = xhci_get_endpoint_interval(vdev->udev, ep); + mult = xhci_get_endpoint_mult(vdev->udev, ep); + esit = xhci_get_max_esit_payload(vdev->udev, ep); + max_packet = GET_MAX_PACKET(mps); + max_burst = 0; + + ep_info = EP_INTERVAL(interval) | EP_MULT(mult); + ep_info2 = EP_TYPE(eptype); + if (type == USB_ENDPOINT_XFER_ISOC) + ep_info2 |= ERROR_COUNT(0); + else + ep_info2 |= ERROR_COUNT(3); + + switch (udev->speed) { + case USB_SPEED_SUPER: + /* FIXME: max_burst = ss_ep_comp.bMaxBurst */ + max_burst = 0; + break; + case USB_SPEED_HIGH: + /* Some devices get this wrong */ + if (type == USB_ENDPOINT_XFER_BULK) + max_packet = 512; + if (type == USB_ENDPOINT_XFER_ISOC || + type == USB_ENDPOINT_XFER_INT) + max_burst = (mps & 0x1800) >> 11; + break; + case USB_SPEED_FULL: + case USB_SPEED_LOW: + break; + } + ep_info2 |= MAX_PACKET(max_packet) | MAX_BURST(max_burst); + + tx_info = MAX_ESIT_PAYLOAD_FOR_EP(esit); + switch (type) { + case USB_ENDPOINT_XFER_CONTROL: + tx_info |= AVG_TRB_LENGTH_FOR_EP(8); + break; + case USB_ENDPOINT_XFER_ISOC: + case USB_ENDPOINT_XFER_BULK: + tx_info |= AVG_TRB_LENGTH_FOR_EP(3 * 1024); + break; + case USB_ENDPOINT_XFER_INT: + tx_info |= AVG_TRB_LENGTH_FOR_EP(1 * 1024); + break; + } + + ctx->ep_info = cpu_to_le32(ep_info); + ctx->ep_info2 = cpu_to_le32(ep_info2); + ctx->tx_info = cpu_to_le32(tx_info); + ctx->deq = + cpu_to_le64((dma_addr_t)vdev->ep[epi]->enqueue | + vdev->ep[epi]->cycle_state); + } + } + + last_ctx = fls(add_flags) - 1; + + /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ + vdev->in_ctx->icc.add_flags = cpu_to_le32(add_flags); + vdev->in_ctx->icc.add_flags |= cpu_to_le32(SLOT_FLAG); + vdev->in_ctx->icc.add_flags &= cpu_to_le32(~EP0_FLAG); + vdev->in_ctx->icc.drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + + /* Don't issue the command if there's no endpoints to update. */ + if (vdev->in_ctx->icc.add_flags == cpu_to_le32(SLOT_FLAG) && + vdev->in_ctx->icc.drop_flags == 0) + return 0; + + vdev->in_ctx->slot.dev_info &= cpu_to_le32(~LAST_CTX_MASK); + vdev->in_ctx->slot.dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); + + /* Issue Configure Endpoint Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | + SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_print_trb(xhci, &trb, "Request ConfigureEndpoint"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response ConfigureEndpoint"); + xhci_virtdev_zero_in_ctx(vdev); + + return ret; +} + +static int xhci_virtdev_deconfigure(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + union xhci_trb trb; + int ret; + + /* Issue Deconfigure Endpoint Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + trb.event_cmd.flags = TRB_TYPE(TRB_CONFIG_EP) | TRB_DC | + SLOT_ID_FOR_TRB(vdev->slot_id); + xhci_print_trb(xhci, &trb, "Request DeconfigureEndpoint"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response DeconfigureEndpoint"); + xhci_virtdev_zero_in_ctx(vdev); + + return ret; +} + +static int xhci_virtdev_init(struct xhci_virtual_device *vdev) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + struct usb_device *top_dev; + int max_packets; + u32 route = 0, dev_info, dev_info2, tt_info, ep_info2, tx_info; + bool on_hs_hub = false; + int hs_slot_id = 0; + + /* + * Find the root hub port this device is under, also determine SlotID + * of possible external HS hub a LS/FS device could be connected to. + */ + for (top_dev = vdev->udev; top_dev->parent && top_dev->parent->parent; + top_dev = top_dev->parent) { + if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) + route = (route << 4) | (top_dev->portnr & 0xf); + if (top_dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB && + top_dev->parent->speed != USB_SPEED_LOW && + top_dev->parent->speed != USB_SPEED_FULL) { + on_hs_hub |= true; + if (!hs_slot_id) { + struct xhci_virtual_device *vhub = + xhci_find_virtdev(xhci, top_dev->parent); + hs_slot_id = vhub->slot_id; + } + } + } + + /* 4.3.3 3) Initalize Input Slot Context */ + dev_info = LAST_CTX(1); + switch (vdev->udev->speed) { + case USB_SPEED_SUPER: + dev_info |= SLOT_SPEED_SS; + max_packets = 512; + break; + case USB_SPEED_HIGH: + dev_info |= SLOT_SPEED_HS; + max_packets = 64; + break; + case USB_SPEED_FULL: + dev_info |= SLOT_SPEED_FS; + max_packets = 64; + break; + case USB_SPEED_LOW: + dev_info |= SLOT_SPEED_LS; + max_packets = 8; + break; + default: + max_packets = 0; + break; + } + dev_info |= route; + dev_info2 = ROOT_HUB_PORT(top_dev->portnr); + tt_info = 0; + + /* Is this a LS/FS device under an external HS hub? */ + if (on_hs_hub && (vdev->udev->speed == USB_SPEED_FULL || + vdev->udev->speed == USB_SPEED_LOW)) { + dev_info |= DEV_MTT; + tt_info |= (top_dev->portnr << 8) | hs_slot_id; + } + + vdev->in_ctx->slot.dev_info = cpu_to_le32(dev_info); + vdev->in_ctx->slot.dev_info2 = cpu_to_le32(dev_info2); + vdev->in_ctx->slot.tt_info = cpu_to_le32(tt_info); + + /* 4.3.3 4) Initalize Transfer Ring */ + vdev->ep[0] = xhci_get_endpoint_ring(xhci); + if (!vdev->ep[0]) + return -ENOMEM; + xhci_ring_init(vdev->ep[0], NUM_TRANSFER_TRBS, TYPE_CTRL); + + /* 4.3.3 5) Initialize Input Control Endpoint 0 Context */ + ep_info2 = EP_TYPE(CTRL_EP) | MAX_BURST(0) | ERROR_COUNT(3); + ep_info2 |= MAX_PACKET(max_packets); + tx_info = AVG_TRB_LENGTH_FOR_EP(8); + vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(ep_info2); + vdev->in_ctx->ep[0].tx_info = cpu_to_le32(tx_info); + vdev->in_ctx->ep[0].deq = cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); + + /* 4.3.3 6+7) Initalize and Set Device Context Base Address Array */ + xhci->dcbaa[vdev->slot_id] = cpu_to_le64((dma_addr_t)vdev->out_ctx); + + return 0; +} + +static int xhci_virtdev_setup(struct xhci_virtual_device *vdev, + enum xhci_setup_dev setup) +{ + struct xhci_hcd *xhci = to_xhci_hcd(vdev->udev->host); + union xhci_trb trb; + int ret; + + /* + * If this is the first Set Address since device + * plug-in then initialize Slot Context + */ + if (!vdev->in_ctx->slot.dev_info) + xhci_virtdev_init(vdev); + else { + /* Otherwise, update Control Ring Dequeue pointer */ + vdev->in_ctx->ep[0].deq = + cpu_to_le64((dma_addr_t)vdev->ep[0]->enqueue | + vdev->ep[0]->cycle_state); + /* + * FS devices have MaxPacketSize0 of 8 or 64, we start + * with 64. If assumtion was wrong, fix it up here. + */ + if (vdev->udev->speed == USB_SPEED_FULL && + vdev->udev->maxpacketsize == PACKET_SIZE_8) { + u32 info = le32_to_cpu(vdev->in_ctx->ep[0].ep_info2); + info &= ~MAX_PACKET_MASK; + info |= MAX_PACKET(8); + vdev->in_ctx->ep[0].ep_info2 = cpu_to_le32(info); + } + } + + vdev->in_ctx->icc.add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); + vdev->in_ctx->icc.drop_flags = 0; + + /* Issue Address Device Command */ + memset(&trb, 0, sizeof(union xhci_trb)); + xhci_write_64((dma_addr_t)vdev->in_ctx, &trb.event_cmd.cmd_trb); + trb.event_cmd.flags = TRB_TYPE(TRB_ADDR_DEV) | + SLOT_ID_FOR_TRB(vdev->slot_id); + if (setup == SETUP_CONTEXT_ONLY) + trb.event_cmd.flags |= TRB_BSR; + xhci_print_trb(xhci, &trb, "Request AddressDevice"); + xhci_issue_command(xhci, &trb); + ret = xhci_wait_for_event(xhci, TRB_COMPLETION, &trb); + xhci_print_trb(xhci, &trb, "Response AddressDevice"); + xhci_virtdev_zero_in_ctx(vdev); + + return ret; +} + +static int xhci_virtdev_set_address(struct xhci_virtual_device *vdev) +{ + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ADDRESS); +} + +static int xhci_virtdev_enable(struct xhci_virtual_device *vdev) +{ + return xhci_virtdev_setup(vdev, SETUP_CONTEXT_ONLY); +} + +static int xhci_virtdev_attach(struct xhci_hcd *xhci, struct usb_device *udev) +{ + struct xhci_virtual_device *vdev; + int ret; + + vdev = xhci_alloc_virtdev(xhci, udev); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + ret = xhci_virtdev_enable_slot(vdev); + if (ret) + return ret; + + return xhci_virtdev_enable(vdev); +} + +int xhci_virtdev_detach(struct xhci_virtual_device *vdev) +{ + xhci_virtdev_deconfigure(vdev); + xhci_virtdev_disable_slot(vdev); + xhci_free_virtdev(vdev); + + return 0; +} + +static int xhci_submit_normal(struct usb_device *udev, unsigned long pipe, + void *buffer, int length) +{ + struct usb_host *host = udev->host; + struct xhci_hcd *xhci = to_xhci_hcd(host); + struct xhci_virtual_device *vdev; + union xhci_trb trb; + u8 epaddr = (usb_pipein(pipe) ? USB_DIR_IN : USB_DIR_OUT) | + usb_pipeendpoint(pipe); + u8 epi = xhci_get_endpoint_index(epaddr, usb_pipetype(pipe)); + int ret; + + vdev = xhci_find_virtdev(xhci, udev); + if (!vdev) + return -ENODEV; + + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", + __func__, udev, vdev, vdev->slot_id, + GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), epi, + vdev->in_ctx, vdev->out_ctx); + + /* Normal TRB */ + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); + /* FIXME: TD remainder */ + trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); + trb.event_cmd.flags = TRB_TYPE(TRB_NORMAL) | TRB_IOC; + if (usb_pipein(pipe)) + trb.event_cmd.flags |= TRB_ISP; + xhci_print_trb(xhci, &trb, "Request Normal"); + xhci_virtdev_issue_transfer(vdev, epi, &trb, true); + ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); + xhci_print_trb(xhci, &trb, "Response Normal"); + + switch (ret) { + case -COMP_SHORT_TX: + udev->status = 0; + udev->act_len = length - EVENT_TRB_LEN(trb.event_cmd.status); + return 0; + case 0: + udev->status = 0; + udev->act_len = 0; + return 0; + case -ETIMEDOUT: + udev->status = USB_ST_CRC_ERR; + return -1; + default: + return -1; + } +} + +static int xhci_submit_control(struct usb_device *udev, unsigned long pipe, + void *buffer, int length, struct devrequest *req) +{ + struct usb_host *host = udev->host; + struct xhci_hcd *xhci = to_xhci_hcd(host); + struct xhci_virtual_device *vdev; + union xhci_trb trb; + u16 typeReq = (req->requesttype << 8) | req->request; + int ret; + + dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", + __func__, req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->index), + le16_to_cpu(req->length), le16_to_cpu(req->length)); + + vdev = xhci_find_virtdev(xhci, udev); + if (!vdev) { + ret = xhci_virtdev_attach(xhci, udev); + if (ret) + return ret; + vdev = xhci_find_virtdev(xhci, udev); + } + if (!vdev) + return -ENODEV; + + dev_dbg(xhci->dev, "%s udev %p vdev %p slot %u state %u epi %u in_ctx %p out_ctx %p\n", + __func__, udev, vdev, vdev->slot_id, + GET_SLOT_STATE(le32_to_cpu(vdev->out_ctx->slot.dev_state)), 0, + vdev->in_ctx, vdev->out_ctx); + + if (req->request == USB_REQ_SET_ADDRESS) + return xhci_virtdev_set_address(vdev); + if (req->request == USB_REQ_SET_CONFIGURATION) { + ret = xhci_virtdev_configure(vdev, le16_to_cpu(req->value)); + if (ret) + return ret; + } + + /* Setup TRB */ + memset(&trb, 0, sizeof(union xhci_trb)); + trb.generic.field[0] = le16_to_cpu(req->value) << 16 | + req->request << 8 | req->requesttype; + trb.generic.field[1] = le16_to_cpu(req->length) << 16 | + le16_to_cpu(req->index); + trb.event_cmd.status = TRB_LEN(8) | TRB_INTR_TARGET(0); + trb.event_cmd.flags = TRB_TYPE(TRB_SETUP) | TRB_IDT; + if (xhci->hci_version == 0x100 && length > 0) { + if (req->requesttype & USB_DIR_IN) + trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_IN); + else + trb.event_cmd.flags |= TRB_TX_TYPE(TRB_DATA_OUT); + } + xhci_print_trb(xhci, &trb, "Request Setup "); + xhci_virtdev_issue_transfer(vdev, 0, &trb, false); + + /* Data TRB */ + if (length > 0) { + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.cmd_trb = cpu_to_le64((dma_addr_t)buffer); + /* FIXME: TD remainder */ + trb.event_cmd.status = TRB_LEN(length) | TRB_INTR_TARGET(0); + trb.event_cmd.flags = TRB_TYPE(TRB_DATA) | TRB_IOC; + if (req->requesttype & USB_DIR_IN) + trb.event_cmd.flags |= TRB_ISP | TRB_DIR_IN; + xhci_print_trb(xhci, &trb, "Request Data "); + xhci_virtdev_issue_transfer(vdev, 0, &trb, false); + } + + /* Status TRB */ + memset(&trb, 0, sizeof(union xhci_trb)); + trb.event_cmd.status = TRB_INTR_TARGET(0); + if (length > 0 && req->requesttype & USB_DIR_IN) + trb.event_cmd.flags = 0; + else + trb.event_cmd.flags = TRB_DIR_IN; + trb.event_cmd.flags |= TRB_TYPE(TRB_STATUS) | TRB_IOC; + xhci_print_trb(xhci, &trb, "Request Status"); + xhci_virtdev_issue_transfer(vdev, 0, &trb, true); + + if (length > 0 && req->requesttype & USB_DIR_IN) { + ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); + xhci_print_trb(xhci, &trb, "Response Data "); + if (ret == -COMP_SHORT_TX) + length -= EVENT_TRB_LEN(trb.event_cmd.status); + else if (ret < 0) + return ret; + } + + ret = xhci_wait_for_event(xhci, TRB_TRANSFER, &trb); + xhci_print_trb(xhci, &trb, "Response Status"); + if (ret < 0) + return ret; + + /* + * usb core doesn't notify us about device events on + * external Hubs, track it ourselves. + */ + if (typeReq == GetHubDescriptor) + xhci_virtdev_update_hub_device(vdev, buffer, length); + if (typeReq == ClearPortFeature && + cpu_to_le16(req->value) == USB_PORT_FEAT_C_CONNECTION) + xhci_virtdev_update_hub_status(vdev, le16_to_cpu(req->index)); + + return length; +} + +/* + * xHCI host controller driver + */ + +static void xhci_dma_alloc(struct xhci_hcd *xhci) +{ + size_t sz_sp, sz_spa, sz_dca, sz_cmd, sz_evt, sz_erst, sz_ep; + u64 reg64; + void *p; + int i, num_ep; + + /* Scratchpad buffers: PAGE_SIZE aligned */ + sz_sp = ALIGN(xhci->num_sp * xhci->page_size, xhci->page_size); + /* Device Context Array: 64B aligned */ + sz_dca = ALIGN(xhci->max_slots * sizeof(u64), 64); + /* Command Ring: 64B aligned */ + sz_cmd = ALIGN(NUM_COMMAND_TRBS * sizeof(union xhci_trb), 64); + /* Event Ring: 64B aligned */ + sz_evt = NUM_EVENT_SEGM * + ALIGN(NUM_EVENT_TRBS * sizeof(union xhci_trb), 64); + /* Event Ring Segment Table: 64B aligned */ + sz_erst = ALIGN(NUM_EVENT_SEGM * sizeof(struct xhci_erst_entry), 64); + /* Scratchpad Buffer Array: 64B aligned */ + sz_spa = ALIGN(xhci->num_sp * sizeof(u64), 64); + + xhci->dma_size = sz_sp + sz_spa + sz_dca + sz_cmd + sz_evt + sz_erst; + + /* + * Endpoint Transfer Ring: 16B aligned + * + * We allocate up to MAX_EP_RINGS from the rest of the PAGE + * for virtual devices to pick-up (and return) for endpoint trbs. + */ + sz_ep = ALIGN(NUM_TRANSFER_TRBS * sizeof(union xhci_trb), 16); + + num_ep = PAGE_ALIGN(xhci->dma_size) - + MIN_EP_RINGS * sz_ep - xhci->dma_size; + num_ep /= sz_ep; + num_ep = max(MAX_EP_RINGS, MIN_EP_RINGS + num_ep); + xhci->dma_size += num_ep * sz_ep; + + p = xhci->dma = dma_alloc_coherent(xhci->dma_size); + memset(xhci->dma, 0, xhci->dma_size); + + xhci->sp = p; p += sz_sp; + xhci->dcbaa = p; p += sz_dca; + xhci->cmd_ring.trbs = p; p += sz_cmd; + xhci->event_ring.trbs = p; p += sz_evt; + xhci->event_erst = p; p += sz_erst; + xhci->sp_array = p; p += sz_spa; + + xhci->rings = xzalloc(num_ep * sizeof(*xhci->rings)); + for (i = 0; i < num_ep; i++) { + xhci->rings[i].trbs = p; + p += sz_ep; + xhci_put_endpoint_ring(xhci, &xhci->rings[i]); + } + + /* Setup Scratchpad Buffer Array and Base Address in Device Context */ + reg64 = cpu_to_le64((dma_addr_t)xhci->sp); + for (i = 0; i < xhci->num_sp; i++, reg64 += xhci->page_size) + xhci->sp_array[i] = cpu_to_le64(reg64); + if (xhci->num_sp) + xhci->dcbaa[0] = cpu_to_le64((dma_addr_t)xhci->sp_array); + + /* Setup Event Ring Segment Table and Event Ring */ + reg64 = (dma_addr_t)&xhci->event_ring.trbs[0]; + xhci->event_erst[0].seg_addr = cpu_to_le64(reg64); + xhci->event_erst[0].seg_size = cpu_to_le32(NUM_EVENT_TRBS); + xhci_ring_init(&xhci->event_ring, NUM_EVENT_TRBS, TYPE_EVENT); + + /* Setup Command Ring */ + xhci_ring_init(&xhci->cmd_ring, NUM_COMMAND_TRBS, TYPE_COMMAND); +} + +static int xhci_halt(struct xhci_hcd *xhci) +{ + u32 reg = readl(&xhci->op_regs->status); + u32 mask = ~XHCI_IRQS; + + if (!(reg & STS_HALT)) + mask &= ~CMD_RUN; + + /* disable any IRQs and begin halting process */ + reg = readl(&xhci->op_regs->command); + reg &= mask; + writel(reg, &xhci->op_regs->command); + + return xhci_handshake(&xhci->op_regs->status, + STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); +} + +static int xhci_reset(struct xhci_hcd *xhci) +{ + u32 reg; + int ret; + + reg = readl(&xhci->op_regs->command); + reg |= CMD_RESET; + writel(reg, &xhci->op_regs->command); + + ret = xhci_handshake(&xhci->op_regs->command, + CMD_RESET, 0, 10 * SECOND / USECOND); + if (ret) { + dev_err(xhci->dev, "failed to reset\n"); + return ret; + } + + return 0; +} + +static int xhci_start(struct xhci_hcd *xhci) +{ + u32 reg; + int ret, i; + + reg = readl(&xhci->op_regs->command); + reg |= CMD_RUN; + writel(reg, &xhci->op_regs->command); + + ret = xhci_handshake(&xhci->op_regs->status, + STS_HALT, 0, XHCI_MAX_HALT_USEC); + if (ret) { + dev_err(xhci->dev, "failed to start\n"); + return ret; + } + + /* Ensure ports are powered-off */ + for (i = 0; i < xhci->num_usb_ports; i++) + xhci_hub_port_power(xhci, i, false); + + return 0; +} + +static int xhci_init(struct usb_host *host) +{ + struct xhci_hcd *xhci = to_xhci_hcd(host); + u32 reg; + u64 reg64; + int i, tmp, ret; + + ret = xhci_halt(xhci); + if (ret) + return ret; + + ret = xhci_reset(xhci); + if (ret) + return ret; + + tmp = readl(&xhci->op_regs->page_size); + for (i = 0; i < 16; i++) { + if ((0x1 & tmp) != 0) + break; + tmp >>= 1; + } + if (i < 16) + tmp = (1 << (i+12)); + else + dev_warn(xhci->dev, "unsupported page size %d\n", tmp); + /* Use 4K pages, since that's common and the minimum the HC supports */ + xhci->page_shift = 12; + xhci->page_size = 1 << xhci->page_shift; + + xhci->rootdev = 0; + xhci->num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); + xhci->max_slots = HCS_MAX_SLOTS(xhci->hcs_params1); + xhci_dma_alloc(xhci); + + ret = xhci_hub_setup_ports(xhci); + if (ret) + return ret; + + /* + * Program the Max Device Slots Enabled (MaxSlotsEn) field in the + * CONFIG register (5.4.7) with the max number of slots HC can handle. + */ + reg = readl(&xhci->op_regs->config_reg); + reg |= (xhci->max_slots & HCS_SLOTS_MASK); + writel(reg, &xhci->op_regs->config_reg); + + /* + * Program the Device Context Base Address Array Pointer (DCBAAP) + * register (5.4.6) with a 64-bit address pointing to where the + * Device Context Base Address Array is located. + */ + xhci_write_64((dma_addr_t)xhci->dcbaa, &xhci->op_regs->dcbaa_ptr); + + /* + * Define the Command Ring Dequeue Pointer by programming the + * Command Ring Control Register (5.4.5) with a 64-bit address + * pointing to the starting address of the first TRB of the Command + * Ring. + */ + reg64 = xhci_read_64(&xhci->op_regs->cmd_ring); + reg64 = (reg64 & (u64)CMD_RING_RSVD_BITS) | + ((dma_addr_t)&xhci->cmd_ring.trbs[0] & + ~(dma_addr_t)CMD_RING_RSVD_BITS) | + xhci->cmd_ring.cycle_state; + xhci_write_64(reg64, &xhci->op_regs->cmd_ring); + + reg = readl(&xhci->cap_regs->db_off) & DBOFF_MASK; + xhci->dba = (void __iomem *)xhci->cap_regs + reg; + xhci->ir_set = &xhci->run_regs->ir_set[0]; + + reg64 = (dma_addr_t)&xhci->event_ring.trbs[0] & + ~(dma_addr_t)CMD_RING_RSVD_BITS; + xhci->event_erst[i].seg_addr = cpu_to_le64(reg64); + xhci->event_erst[i].seg_size = cpu_to_le32(NUM_EVENT_TRBS); + reg = readl(&xhci->ir_set->erst_size) & ~ERST_SIZE_MASK; + writel(reg | NUM_EVENT_SEGM, &xhci->ir_set->erst_size); + xhci_set_event_dequeue(xhci); + + reg64 = xhci_read_64(&xhci->ir_set->erst_base); + reg64 &= ERST_PTR_MASK; + reg64 |= (dma_addr_t)xhci->event_erst & + ~(dma_addr_t)CMD_RING_RSVD_BITS; + xhci_write_64(reg64, &xhci->ir_set->erst_base); + + /* + * Write the USBCMD (5.4.1) to turn the host controller ON via + * setting the Run/Stop (R/S) bit to ‘1’. This operation allows the + * xHC to begin accepting doorbell references. + */ + + return xhci_start(xhci); + + /* + * At this point, the host controller is up and running and the Root + * Hub ports (5.4.8) will begin reporting device connects, etc., + * and system software may begin enumerating devices. + * System software may follow the procedures described in section 4.3, + * to enumerate attached devices. + * + * USB2 (LS/FS/HS) devices require the port reset process to advance + * the port to the Enabled state. Once USB2 ports are Enabled, the port + * is active with SOFs occurring on the port, but the Pipe Schedules + * have not yet been enabled. + * + * SS ports automatically advance to the Enabled state if a successful + * device attach is detected. + */ +} + +static int xhci_submit_bulk_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, int timeout) +{ + return xhci_submit_normal(dev, pipe, buffer, length); +} + +static int xhci_submit_control_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, struct devrequest *setup, int timeout) +{ + struct xhci_hcd *xhci = to_xhci_hcd(dev->host); + + /* Catch Root Hub requests */ + if (usb_pipedevice(pipe) == xhci->rootdev) { + if (xhci->rootdev == 0) + dev->speed = USB_SPEED_HIGH; + return xhci_hub_control(dev, pipe, buffer, length, setup); + } + + return xhci_submit_control(dev, pipe, buffer, length, setup); +} + +static int xhci_submit_int_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, int interval) +{ + struct xhci_hcd *xhci = to_xhci_hcd(dev->host); + + dev_err(xhci->dev, "Interrupt messages not supported\n"); + + return -ENOTSUPP; +} + +static int xhci_detect(struct device_d *dev) +{ + struct xhci_hcd *xhci = dev->priv; + + return usb_host_detect(&xhci->host); +} + +int xhci_register(struct device_d *dev, struct xhci_data *data) +{ + struct usb_host *host; + struct xhci_hcd *xhci; + + xhci = xzalloc(sizeof(*xhci)); + host = &xhci->host; + INIT_LIST_HEAD(&xhci->vdev_list); + xhci->dev = dev; + xhci->cap_regs = data->regs; + xhci->op_regs = (void __iomem *)xhci->cap_regs + + HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)); + xhci->run_regs = (void __iomem *)xhci->cap_regs + + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); + xhci->hcc_capbase = readl(&xhci->cap_regs->hc_capbase); + xhci->hci_version = HC_VERSION(xhci->hcc_capbase); + xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); + + host->hw_dev = dev; + host->init = xhci_init; + host->submit_int_msg = xhci_submit_int_msg; + host->submit_control_msg = xhci_submit_control_msg; + host->submit_bulk_msg = xhci_submit_bulk_msg; + + dev->priv = xhci; + dev->detect = xhci_detect; + + usb_register_host(host); + + dev_info(dev, "USB xHCI %x.%02x\n", + xhci->hci_version >> 8, xhci->hci_version & 0xff); + + return 0; +} + +/* + * xHCI platform driver + */ + +static int xhci_probe(struct device_d *dev) +{ + struct xhci_data data = {}; + + data.regs = dev_request_mem_region(dev, 0); + + return xhci_register(dev, &data); +} + +static void xhci_remove(struct device_d *dev) +{ + struct xhci_hcd *xhci = dev->priv; + xhci_halt(xhci); +} + +static struct driver_d xhci_driver = { + .name = "xHCI", + .probe = xhci_probe, + .remove = xhci_remove, +}; +device_platform_driver(xhci_driver); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c new file mode 100644 index 0000000..bf95257 --- /dev/null +++ b/drivers/usb/host/xhci-hub.c @@ -0,0 +1,647 @@ +/* + * xHCI USB 3.0 Root Hub + * + * Sebastian Hesselbarth + * + * This currently does not support any SuperSpeed capabilities. + * + * Some code borrowed from the Linux xHCI driver + * Author: Sarah Sharp + * Copyright (C) 2008 Intel Corp. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include + +#include "xhci.h" + +static const struct usb_root_hub_info usb_rh_info = { + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 0, /* runtime modified */ + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 10, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {}, + .u.hs.PortPwrCtrlMask = {} + }, + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0002), /* v2.0 */ + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = USB_HUB_PR_HS_MULTI_TT, + .bMaxPacketSize0 = 64, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = __constant_cpu_to_le16(0x0001), + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1 + }, + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = __constant_cpu_to_le16(USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0 + }, + .interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x81, /* UE_DIR_IN | EHCI_INTR_ENDPT */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16((USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 + } +}; + +static void xhci_setup_common_hub_descriptor(struct xhci_hcd *xhci, + struct usb_hub_descriptor *desc, int ports) +{ + u16 val; + + /* xhci section 5.4.9 says 20ms max */ + desc->bPwrOn2PwrGood = 10; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = xhci->num_usb_ports; + + val = 0; + /* Bits 1:0 - support per-port power switching, or power always on */ + if (HCC_PPC(xhci->hcc_params)) + val |= HUB_CHAR_INDV_PORT_LPSM; + else + val |= HUB_CHAR_NO_LPSM; + /* Bit 2 - root hubs are not part of a compound device */ + /* Bits 4:3 - individual port over current protection */ + val |= HUB_CHAR_INDV_PORT_OCPM; + /* Bits 6:5 - no TTs in root ports */ + /* Bit 7 - no port indicators */ + desc->wHubCharacteristics = cpu_to_le16(val); +} + +static void xhci_setup_usb2_hub_descriptor(struct xhci_hcd *xhci) +{ + struct usb_hub_descriptor *desc = &xhci->usb_info.hub; + __u8 port_removable[(USB_MAXCHILDREN + 1 + 7) / 8]; + int ports; + u32 portsc; + u16 val; + int i; + + ports = xhci->num_usb_ports; + xhci_setup_common_hub_descriptor(xhci, desc, ports); + desc->bDescriptorType = USB_DT_HUB; + val = 1 + (ports / 8); + desc->bLength = USB_DT_HUB_NONVAR_SIZE + 2 * val; + + /* The Device Removable bits are reported on a byte granularity. + * If the port doesn't exist within that byte, the bit is set to 0. + */ + memset(port_removable, 0, sizeof(port_removable)); + for (i = 0; i < ports; i++) { + portsc = readl(xhci->usb_ports[i]); + /* If a device is removable, PORTSC reports a 0, same as in the + * hub descriptor DeviceRemovable bits. + */ + if (portsc & PORT_DEV_REMOVE) + /* This math is hairy because bit 0 of DeviceRemovable + * is reserved, and bit 1 is for port 1, etc. + */ + port_removable[(i + 1) / 8] |= 1 << ((i + 1) % 8); + } + + /* ch11.h defines a hub descriptor that has room for USB_MAXCHILDREN + * ports on it. The USB 2.0 specification says that there are two + * variable length fields at the end of the hub descriptor: + * DeviceRemovable and PortPwrCtrlMask. But since we can have less than + * USB_MAXCHILDREN ports, we may need to use the DeviceRemovable array + * to set PortPwrCtrlMask bits. PortPwrCtrlMask must always be set to + * 0xFF, so we initialize the both arrays (DeviceRemovable and + * PortPwrCtrlMask) to 0xFF. Then we set the DeviceRemovable for each + * set of ports that actually exist. + */ + memset(desc->u.hs.DeviceRemovable, 0xff, + sizeof(desc->u.hs.DeviceRemovable)); + memset(desc->u.hs.PortPwrCtrlMask, 0xff, + sizeof(desc->u.hs.PortPwrCtrlMask)); + + for (i = 0; i < (ports + 1 + 7) / 8; i++) + memset(&desc->u.hs.DeviceRemovable[i], port_removable[i], + sizeof(__u8)); +} + +/* FIXME: usb core does not know about USB_SPEED_SUPER at all */ +static __maybe_unused void xhci_setup_usb3_hub_descriptor(struct xhci_hcd *xhci) +{ + struct usb_hub_descriptor *desc = &xhci->usb_info.hub; + int ports; + u16 port_removable; + u32 portsc; + int i; + + ports = xhci->num_usb_ports; + xhci_setup_common_hub_descriptor(xhci, desc, ports); + desc->bDescriptorType = USB_DT_SS_HUB; + desc->bLength = USB_DT_SS_HUB_SIZE; + /* + * header decode latency should be zero for roothubs, + * see section 4.23.5.2. + */ + desc->u.ss.bHubHdrDecLat = 0; + desc->u.ss.wHubDelay = 0; + port_removable = 0; + /* bit 0 is reserved, bit 1 is for port 1, etc. */ + for (i = 0; i < ports; i++) { + portsc = readl(xhci->usb_ports[i]); + if (portsc & PORT_DEV_REMOVE) + port_removable |= 1 << (i + 1); + } + desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable); +} + +static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, + __le32 __iomem *addr, u8 major_revision, int max_caps) +{ + u32 reg, port_offset, port_count; + int i; + + if (major_revision > 0x03) { + dev_warn(xhci->dev, "Ignoring unknown port speed, Ext Cap %p, rev %02x\n", + addr, major_revision); + return; + } + + /* Port offset and count in the third dword, see section 7.2 */ + reg = readl(addr + 2); + port_offset = XHCI_EXT_PORT_OFF(reg); + port_count = XHCI_EXT_PORT_COUNT(reg); + + /* Port count includes the current port offset */ + if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) + /* WTF? "Valid values are ‘1’ to MaxPorts" */ + return; + + /* cache usb2 port capabilities */ + if (major_revision < 0x03 && xhci->num_ext_caps < max_caps) + xhci->ext_caps[xhci->num_ext_caps++] = reg; + + port_offset--; + for (i = port_offset; i < (port_offset + port_count); i++) { + /* Duplicate entry. Ignore the port if the revisions differ. */ + if (xhci->port_array[i] != 0) { + dev_warn(xhci->dev, "Duplicate port entry, Ext Cap %p, port %u\n", + addr, i); + dev_warn(xhci->dev, "Port was marked as USB %u, duplicated as USB %u\n", + xhci->port_array[i], major_revision); + /* + * Only adjust the roothub port counts if we haven't + * found a similar duplicate. + */ + if (xhci->port_array[i] != major_revision && + xhci->port_array[i] != DUPLICATE_ENTRY) { + xhci->num_usb_ports--; + xhci->port_array[i] = DUPLICATE_ENTRY; + } + continue; + } + xhci->port_array[i] = major_revision; + xhci->num_usb_ports++; + } +} + +int xhci_hub_setup_ports(struct xhci_hcd *xhci) +{ + u32 offset, tmp_offset; + __le32 __iomem *addr, *tmp_addr; + unsigned int num_ports; + int i, cap_count = 0; + + offset = HCC_EXT_CAPS(xhci->hcc_params); + if (offset == 0) { + dev_err(xhci->dev, "No Extended Capability Registers\n"); + return -ENODEV; + } + + addr = &xhci->cap_regs->hc_capbase + offset; + + /* count extended protocol capability entries for later caching */ + tmp_addr = addr; + tmp_offset = offset; + do { + u32 cap_id = readl(tmp_addr); + + if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) + cap_count++; + + tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id); + tmp_addr += tmp_offset; + } while (tmp_offset); + + num_ports = HCS_MAX_PORTS(xhci->hcs_params1); + xhci->port_array = xzalloc(num_ports * sizeof(*xhci->port_array)); + xhci->ext_caps = xzalloc(cap_count * sizeof(*xhci->ext_caps)); + + while (1) { + u32 cap_id = readl(addr); + + if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) + xhci_add_in_port(xhci, num_ports, addr, + (u8)XHCI_EXT_PORT_MAJOR(cap_id), + cap_count); + offset = XHCI_EXT_CAPS_NEXT(cap_id); + if (!offset || xhci->num_usb_ports == num_ports) + break; + addr += offset; + } + + if (xhci->num_usb_ports == 0) { + dev_err(xhci->dev, "No ports on the roothubs?\n"); + return -ENODEV; + } + + xhci->usb_ports = xzalloc(num_ports * sizeof(*xhci->usb_ports)); + for (i = 0; i < num_ports; i++) + xhci->usb_ports[i] = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * i; + memcpy(&xhci->usb_info, &usb_rh_info, sizeof(usb_rh_info)); + xhci_setup_usb2_hub_descriptor(xhci); + + return 0; +} + +/* + * These bits are Read Only (RO) and should be saved and written to the + * registers: 0, 3, 10:13, 30 + * connect status, over-current status, port speed, and device removable. + * connect status and port speed are also sticky - meaning they're in + * the AUX well and they aren't changed by a hot, warm, or cold reset. + */ +#define XHCI_PORT_RO (PORT_CONNECT | PORT_OC | DEV_SPEED_MASK | \ + PORT_DEV_REMOVE) +/* + * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit: + * bits 5:8, 9, 14:15, 25:27 + * link state, port power, port indicator state, "wake on" enable state + */ +#define XHCI_PORT_RWS (PORT_PLS_MASK | PORT_POWER | PORT_LED_MASK | \ + PORT_WKCONN_E | PORT_WKDISC_E | PORT_WKOC_E) +/* + * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect: + * bit 4 (port reset) + */ +#define XHCI_PORT_RW1S (PORT_RESET) +/* + * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect: + * bits 1, 17, 18, 19, 20, 21, 22, 23 + * port enable/disable, and + * change bits: connect, PED, warm port reset changed (reserved 0 for USB 2.0), + * over-current, reset, link state, and L1 change + */ +#define XHCI_PORT_RW1CS (PORT_PE | PORT_CSC | PORT_PEC | PORT_WRC | \ + PORT_OCC | PORT_RC | PORT_PLC | PORT_CEC) +/* + * Bit 16 is RW, and writing a '1' to it causes the link state control to be + * latched in + */ +#define XHCI_PORT_RW (PORT_LINK_STROBE) +/* + * These bits are Reserved Zero (RsvdZ) and zero should be written to them: + * bits 2, 24, 28:31 + */ +#define XHCI_PORT_RZ (BIT(2) | BIT(24) | (0xf<<28)) + +/* + * Given a port state, this function returns a value that would result in the + * port being in the same state, if the value was written to the port status + * control register. + * Save Read Only (RO) bits and save read/write bits where + * writing a 0 clears the bit and writing a 1 sets the bit (RWS). + * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect. + */ +static u32 inline xhci_port_state_to_neutral(u32 state) +{ + /* Save read-only status and port state */ + return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); +} + +static int xhci_hub_finish_port_detach(struct xhci_hcd *xhci, int port) +{ + struct xhci_virtual_device *vdev, *temp; + union xhci_trb trb; + int ret; + + ret = xhci_wait_for_event(xhci, TRB_PORT_STATUS, &trb); + if (ret) + return ret; + + /* Tear-down any attached virtual devices */ + list_for_each_entry_safe(vdev, temp, &xhci->vdev_list, list) + if (vdev->udev && vdev->udev->portnr == port) + xhci_virtdev_detach(vdev); + + return 0; +} + +static int xhci_hub_finish_port_reset(struct xhci_hcd *xhci, int port) +{ + struct xhci_virtual_device *vdev; + union xhci_trb trb; + int ret; + + ret = xhci_wait_for_event(xhci, TRB_PORT_STATUS, &trb); + if (ret) + return ret; + + /* Reset any attached virtual devices */ + list_for_each_entry(vdev, &xhci->vdev_list, list) + if (vdev->udev && vdev->udev->portnr == port) + xhci_virtdev_reset(vdev); + + return 0; +} + +void xhci_hub_port_power(struct xhci_hcd *xhci, int port, + bool enable) +{ + u32 reg = readl(xhci->usb_ports[port]); + + reg = xhci_port_state_to_neutral(reg); + if (enable) + reg |= PORT_POWER; + else + reg &= ~PORT_POWER; + writel(reg, xhci->usb_ports[port]); +} + +static __maybe_unused int xhci_hub_port_warm_reset(struct xhci_hcd *xhci, int port) +{ + void __iomem *portsc = xhci->usb_ports[port]; + u32 reg; + + reg = xhci_port_state_to_neutral(readl(portsc)); + writel(reg | PORT_WR, portsc); + return xhci_handshake(portsc, PORT_RESET, 0, 10 * SECOND/USECOND); +} + +int xhci_hub_control(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, struct devrequest *req) +{ + struct usb_host *host = dev->host; + struct xhci_hcd *xhci = to_xhci_hcd(host); + struct usb_root_hub_info *info; + __le32 __iomem **port_array; + int max_ports; + void *srcptr = NULL; + u8 tmpbuf[4]; + u16 typeReq; + int len, port, srclen = 0; + u32 reg; + + dev_dbg(xhci->dev, "%s req %u (%#x), type %u (%#x), value %u (%#x), index %u (%#x), length %u (%#x)\n", + __func__, req->request, req->request, + req->requesttype, req->requesttype, + le16_to_cpu(req->value), le16_to_cpu(req->value), + le16_to_cpu(req->index), le16_to_cpu(req->index), + le16_to_cpu(req->length), le16_to_cpu(req->length)); + + info = &xhci->usb_info; + port_array = xhci->usb_ports; + max_ports = xhci->num_usb_ports; + + typeReq = (req->requesttype << 8) | req->request; + switch (typeReq) { + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + dev_dbg(xhci->dev, "GetDeviceDescriptor %u\n", + le16_to_cpu(req->value) >> 8); + + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_DEVICE: + srcptr = &info->device; + srclen = info->device.bLength; + break; + case USB_DT_CONFIG: + srcptr = &info->config; + srclen = le16_to_cpu(info->config.wTotalLength); + break; + case USB_DT_STRING: + switch (le16_to_cpu(req->value) & 0xff) { + case 0: /* Language */ + srcptr = "\4\3\1\0"; + srclen = 4; + break; + case 1: /* Vendor: "barebox" */ + srcptr = "\20\3b\0a\0r\0e\0b\0o\0x\0"; + srclen = 16; + break; + case 2: /* Product: "USB 3.0 Root Hub" */ + srcptr = "\42\3U\0S\0B\0 \0\63\0.\0\60\0 \0R\0o\0o\0t\0 \0H\0u\0b"; + srclen = 34; + break; + default: + dev_warn(xhci->dev, "unknown string descriptor %x\n", + le16_to_cpu(req->value) >> 8); + goto unknown; + } + break; + } + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + dev_dbg(xhci->dev, "SetDeviceConfiguration\n"); + /* Nothing to do */ + break; + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev_dbg(xhci->dev, "SetDeviceAddress %u\n", + le16_to_cpu(req->value)); + + xhci->rootdev = le16_to_cpu(req->value); + break; + case GetHubDescriptor: + dev_dbg(xhci->dev, "GetHubDescriptor %u\n", + le16_to_cpu(req->value) >> 8); + + switch (le16_to_cpu(req->value) >> 8) { + case USB_DT_HUB: + srcptr = &info->hub; + srclen = info->hub.bLength; + break; + default: + dev_warn(xhci->dev, "unknown descriptor %x\n", + le16_to_cpu(req->value) >> 8); + goto unknown; + } + break; + case GetHubStatus: + dev_dbg(xhci->dev, "GetHubStatus\n"); + + /* No power source, over-current reported per port */ + tmpbuf[0] = 0x00; + tmpbuf[1] = 0x00; + srcptr = tmpbuf; + srclen = 2; + break; + case GetPortStatus: + dev_dbg(xhci->dev, "GetPortStatus %u\n", + le16_to_cpu(req->index)); + + memset(tmpbuf, 0, 4); + + port = le16_to_cpu(req->index); + if (!port || port > max_ports) + goto unknown; + port--; + + /* read PORTSC register */ + reg = readl(port_array[port]); + + if (reg & PORT_CONNECT) { + tmpbuf[0] |= USB_PORT_STAT_CONNECTION; + if (DEV_LOWSPEED(reg)) + tmpbuf[1] |= USB_PORT_STAT_LOW_SPEED >> 8; + else if (DEV_HIGHSPEED(reg)) + tmpbuf[1] |= USB_PORT_STAT_HIGH_SPEED >> 8; + } + if (reg & PORT_PE) + tmpbuf[0] |= USB_PORT_STAT_ENABLE; + if (reg & PORT_OC) + tmpbuf[0] |= USB_PORT_STAT_OVERCURRENT; + if (reg & PORT_RESET) + tmpbuf[0] |= USB_PORT_STAT_RESET; + /* USB 2.0 only */ + if ((reg & PORT_PLS_MASK) == XDEV_U3 && reg & PORT_POWER) + tmpbuf[0] |= USB_PORT_STAT_SUSPEND; + /* USB 2.0 only */ + if (reg & PORT_POWER) + tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; + if (reg & PORT_CSC) + tmpbuf[2] |= USB_PORT_STAT_C_CONNECTION; + if (reg & PORT_PEC) + tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; + if (reg & PORT_OCC) + tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; + if (reg & PORT_RC) + tmpbuf[2] |= USB_PORT_STAT_C_RESET; + srcptr = tmpbuf; + srclen = 4; + break; + case ClearPortFeature: + dev_dbg(xhci->dev, "ClearPortFeature %u %u\n", + le16_to_cpu(req->index), le16_to_cpu(req->value)); + + port = le16_to_cpu(req->index); + if (!port || port > max_ports) + goto unknown; + port--; + + reg = xhci_port_state_to_neutral(readl(port_array[port])); + + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_ENABLE: + reg &= ~PORT_PE; + break; + case USB_PORT_FEAT_POWER: + reg &= ~PORT_POWER; + break; + case USB_PORT_FEAT_C_CONNECTION: + reg |= PORT_CSC; + break; + case USB_PORT_FEAT_C_ENABLE: + reg |= PORT_PEC; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + reg |= PORT_OCC; + break; + case USB_PORT_FEAT_C_RESET: + reg |= PORT_RC; + break; + default: + dev_warn(xhci->dev, "unknown feature %u\n", + le16_to_cpu(req->value)); + goto unknown; + } + writel(reg, port_array[port]); + readl(port_array[port]); + + if ((reg & PORT_CONNECT) == 0 && + le16_to_cpu(req->value) == USB_PORT_FEAT_C_CONNECTION) + xhci_hub_finish_port_detach(xhci, port + 1); + + break; + case SetPortFeature: + dev_dbg(xhci->dev, "SetPortFeature %u %u\n", + le16_to_cpu(req->index), le16_to_cpu(req->value)); + + port = le16_to_cpu(req->index); + if (!port || port > max_ports) + goto unknown; + port--; + + reg = xhci_port_state_to_neutral(readl(port_array[port])); + + switch (le16_to_cpu(req->value)) { + case USB_PORT_FEAT_POWER: + reg |= PORT_POWER; + break; + case USB_PORT_FEAT_RESET: + reg |= PORT_RESET; + break; + default: + dev_warn(xhci->dev, "unknown feature %u\n", + le16_to_cpu(req->value)); + goto unknown; + } + writel(reg, port_array[port]); + readl(port_array[port]); + + if (le16_to_cpu(req->value) == USB_PORT_FEAT_RESET) + xhci_hub_finish_port_reset(xhci, port + 1); + + break; + default: + dev_warn(xhci->dev, "unknown root hub request %u (%#x) type %u (%#x)\n", + req->request, req->request, + req->requesttype, req->requesttype); + goto unknown; + } + + len = min3(srclen, (int)le16_to_cpu(req->length), length); + if (srcptr && len) + memcpy(buffer, srcptr, len); + dev->act_len = len; + dev->status = 0; + + return 0; + +unknown: + dev->act_len = 0; + dev->status = USB_ST_STALLED; + return -ENOTSUPP; +} diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c new file mode 100644 index 0000000..a140b1d --- /dev/null +++ b/drivers/usb/host/xhci-pci.c @@ -0,0 +1,45 @@ +/* + * PCI driver for xHCI controllers + * + * Sebastian Hesselbarth + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +static int xhci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct xhci_data data = {}; + + pci_enable_device(pdev); + pci_set_master(pdev); + + data.regs = pci_iomap(pdev, 0); + + return xhci_register(&pdev->dev, &data); +} + +static DEFINE_PCI_DEVICE_TABLE(xhci_pci_tbl) = { + /* handle any USB 3.0 xHCI controller */ + { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0), }, + { }, +}; + +static struct pci_driver xhci_pci_driver = { + .name = "xHCI PCI", + .id_table = xhci_pci_tbl, + .probe = xhci_pci_probe, +}; + +static int xhci_pci_init(void) +{ + return pci_register_driver(&xhci_pci_driver); +} +device_initcall(xhci_pci_init); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h new file mode 100644 index 0000000..078f881 --- /dev/null +++ b/drivers/usb/host/xhci.h @@ -0,0 +1,1279 @@ +/* + * xHCI USB 3.0 Specification + * + * Sebastian Hesselbarth + * + * Some code borrowed from the Linux xHCI driver + * Author: Sarah Sharp + * Copyright (C) 2008 Intel Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __XHCI_H +#define __XHCI_H + +#define NUM_COMMAND_TRBS 8 +#define NUM_TRANSFER_TRBS 8 +#define NUM_EVENT_SEGM 1 /* only one supported */ +#define NUM_EVENT_TRBS 16 /* minimum 16 TRBS */ +#define MIN_EP_RINGS 3 /* Control + Bulk In/Out */ +#define MAX_EP_RINGS (MIN_EP_RINGS * USB_MAXCHILDREN) + +/* Up to 16 ms to halt an HC */ +#define XHCI_MAX_HALT_USEC (16 * 1000) + +/* Command and Status registers offset from the Operational Registers address */ +#define XHCI_CMD_OFFSET 0x00 +#define XHCI_STS_OFFSET 0x04 +/* HCCPARAMS offset from PCI base address */ +#define XHCI_HCC_PARAMS_OFFSET 0x10 +/* xHCI PCI Configuration Registers */ +#define XHCI_SBRN_OFFSET 0x60 + +/* Max number of USB devices for any host controller - limit in section 6.1 */ +#define MAX_HC_SLOTS 256 +/* Section 5.3.3 - MaxPorts */ +#define MAX_HC_PORTS 127 + +/* + * xHCI register interface. + * This corresponds to the eXtensible Host Controller Interface (xHCI) + * Revision 0.95 specification + */ + +/** + * struct xhci_cap_regs - xHCI Host Controller Capability Registers. + * @hc_capbase: length of the capabilities register and HC version number + * @hcs_params1: HCSPARAMS1 - Structural Parameters 1 + * @hcs_params2: HCSPARAMS2 - Structural Parameters 2 + * @hcs_params3: HCSPARAMS3 - Structural Parameters 3 + * @hcc_params: HCCPARAMS - Capability Parameters + * @db_off: DBOFF - Doorbell array offset + * @run_regs_off: RTSOFF - Runtime register space offset + */ +struct xhci_cap_regs { + __le32 hc_capbase; + __le32 hcs_params1; + __le32 hcs_params2; + __le32 hcs_params3; + __le32 hcc_params; + __le32 db_off; + __le32 run_regs_off; + /* Reserved up to (CAPLENGTH - 0x1C) */ +}; + +/* hc_capbase bitmasks */ +/* bits 7:0 - how long is the Capabilities register */ +#define HC_LENGTH(p) ((p) & 0x00ff) +/* bits 31:16 */ +#define HC_VERSION(p) (((p) >> 16) & 0xffff) + +/* HCSPARAMS1 - hcs_params1 - bitmasks */ +/* bits 0:7, Max Device Slots */ +#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) +#define HCS_SLOTS_MASK 0xff +/* bits 8:18, Max Interrupters */ +#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) +/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) + +/* HCSPARAMS2 - hcs_params2 - bitmasks */ +/* bits 0:3, frames or uframes that SW needs to queue transactions + * ahead of the HW to meet periodic deadlines */ +#define HCS_IST(p) (((p) >> 0) & 0xf) +/* bits 4:7, max number of Event Ring segments */ +#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ +/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */ +#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f) + +/* HCSPARAMS3 - hcs_params3 - bitmasks */ +/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) +/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) + +/* HCCPARAMS - hcc_params - bitmasks */ +/* true: HC can use 64-bit address pointers */ +#define HCC_64BIT_ADDR(p) ((p) & BIT(0)) +/* true: HC can do bandwidth negotiation */ +#define HCC_BANDWIDTH_NEG(p) ((p) & BIT(1)) +/* true: HC uses 64-byte Device Context structures + * FIXME 64-byte context structures aren't supported yet. + */ +#define HCC_64BYTE_CONTEXT(p) ((p) & BIT(2)) +#define HCC_CTX_SIZE(p) (HCC_64BYTE_CONTEXT(p) ? 64 : 32) +/* true: HC has port power switches */ +#define HCC_PPC(p) ((p) & BIT(3)) +/* true: HC has port indicators */ +#define HCC_INDICATOR(p) ((p) & BIT(4)) +/* true: HC has Light HC Reset Capability */ +#define HCC_LIGHT_RESET(p) ((p) & BIT(5)) +/* true: HC supports latency tolerance messaging */ +#define HCC_LTC(p) ((p) & BIT(6)) +/* true: no secondary Stream ID Support */ +#define HCC_NSS(p) ((p) & BIT(7)) +/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) +/* Extended Capabilities pointer from PCI base - section 5.3.6 */ +#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) + +/* db_off bitmask - bits 0:1 reserved */ +#define DBOFF_MASK (~0x3) + +/* run_regs_off bitmask - bits 0:4 reserved */ +#define RTSOFF_MASK (~0x1f) + +/* Number of registers per port */ +#define NUM_PORT_REGS 4 + +#define PORTSC 0 +#define PORTPMSC 1 +#define PORTLI 2 +#define PORTHLPMC 3 + +/** + * struct xhci_op_regs - xHCI Host Controller Operational Registers. + * @command: USBCMD - xHC command register + * @status: USBSTS - xHC status register + * @page_size: This indicates the page size that the host controller + * supports. If bit n is set, the HC supports a page size + * of 2^(n+12), up to a 128MB page size. + * 4K is the minimum page size. + * @cmd_ring: CRP - 64-bit Command Ring Pointer + * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer + * @config_reg: CONFIG - Configure Register + * @port_status_base: PORTSCn - base address for Port Status and Control + * Each port has a Port Status and Control register, + * followed by a Port Power Management Status and Control + * register, a Port Link Info register, and a reserved + * register. + * @port_power_base: PORTPMSCn - base address for + * Port Power Management Status and Control + * @port_link_base: PORTLIn - base address for Port Link Info (current + * Link PM state and control) for USB 2.1 and USB 3.0 + * devices. + */ +struct xhci_op_regs { + __le32 command; + __le32 status; + __le32 page_size; + __le32 reserved1; + __le32 reserved2; + __le32 dev_notification; + __le64 cmd_ring; + /* rsvd: offset 0x20-2F */ + __le32 reserved3[4]; + __le64 dcbaa_ptr; + __le32 config_reg; + /* rsvd: offset 0x3C-3FF */ + __le32 reserved4[241]; + /* port 1 registers, which serve as a base address for other ports */ + __le32 port_status_base; + __le32 port_power_base; + __le32 port_link_base; + __le32 reserved5; + /* registers for ports 2-255 */ + __le32 reserved6[NUM_PORT_REGS*254]; +}; + +/* USBCMD - USB command - command bitmasks */ +/* start/stop HC execution - do not write unless HC is halted*/ +#define CMD_RUN BIT(0) +/* Reset HC - resets internal HC state machine and all registers (except + * PCI config regs). HC does NOT drive a USB reset on the downstream ports. + * The xHCI driver must reinitialize the xHC after setting this bit. + */ +#define CMD_RESET BIT(1) +/* Event Interrupt Enable - a '1' allows interrupts from the host controller */ +#define CMD_EIE BIT(2) +/* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ +#define CMD_HSEIE BIT(3) +/* bits 4:6 are reserved (and should be preserved on writes). */ +/* light reset (port status stays unchanged) - reset completed when this is 0 */ +#define CMD_LRESET BIT(7) +/* host controller save/restore state. */ +#define CMD_CSS BIT(8) +#define CMD_CRS BIT(9) +/* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ +#define CMD_EWE BIT(10) +/* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root + * hubs are in U3 (selective suspend), disconnect, disabled, or powered-off. + * '0' means the xHC can power it off if all ports are in the disconnect, + * disabled, or powered-off state. + */ +#define CMD_PM_INDEX BIT(11) +/* bits 12:31 are reserved (and should be preserved on writes). */ +#define XHCI_IRQS (CMD_EIE | CMD_HSEIE | CMD_EWE) + +/* IMAN - Interrupt Management Register */ +#define IMAN_IE BIT(1) +#define IMAN_IP BIT(0) + +/* USBSTS - USB status - status bitmasks */ +/* HC not running - set to 1 when run/stop bit is cleared. */ +#define STS_HALT BIT(0) +/* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ +#define STS_FATAL BIT(2) +/* event interrupt - clear this prior to clearing any IP flags in IR set*/ +#define STS_EINT BIT(3) +/* port change detect */ +#define STS_PORT BIT(4) +/* bits 5:7 reserved and zeroed */ +/* save state status - '1' means xHC is saving state */ +#define STS_SAVE BIT(8) +/* restore state status - '1' means xHC is restoring state */ +#define STS_RESTORE BIT(9) +/* true: save or restore error */ +#define STS_SRE BIT(10) +/* true: Controller Not Ready to accept doorbell or op reg writes after reset */ +#define STS_CNR BIT(11) +/* true: internal Host Controller Error - SW needs to reset and reinitialize */ +#define STS_HCE BIT(12) +/* bits 13:31 reserved and should be preserved */ + +/* + * DNCTRL - Device Notification Control Register - dev_notification bitmasks + * Generate a device notification event when the HC sees a transaction with a + * notification type that matches a bit set in this bit field. + */ +#define DEV_NOTE_MASK (0xffff) +#define ENABLE_DEV_NOTE(x) BIT(x) +/* Most of the device notification types should only be used for debug. + * SW does need to pay attention to function wake notifications. + */ +#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1) + +/* CRCR - Command Ring Control Register - cmd_ring bitmasks */ +/* bit 0 is the command ring cycle state */ +/* stop ring operation after completion of the currently executing command */ +#define CMD_RING_PAUSE BIT(1) +/* stop ring immediately - abort the currently executing command */ +#define CMD_RING_ABORT BIT(2) +/* true: command ring is running */ +#define CMD_RING_RUNNING BIT(3) +/* bits 4:5 reserved and should be preserved */ +/* Command Ring pointer - bit mask for the lower 32 bits. */ +#define CMD_RING_RSVD_BITS (0x3f) + +/* CONFIG - Configure Register - config_reg bitmasks */ +/* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ +#define MAX_DEVS(p) ((p) & 0xff) +/* bits 8:31 - reserved and should be preserved */ + +/* PORTSC - Port Status and Control Register - port_status_base bitmasks */ +/* true: device connected */ +#define PORT_CONNECT BIT(0) +/* true: port enabled */ +#define PORT_PE BIT(1) +/* bit 2 reserved and zeroed */ +/* true: port has an over-current condition */ +#define PORT_OC BIT(3) +/* true: port reset signaling asserted */ +#define PORT_RESET BIT(4) +/* Port Link State - bits 5:8 + * A read gives the current link PM state of the port, + * a write with Link State Write Strobe set sets the link state. + */ +#define PORT_PLS_MASK (0xf << 5) +#define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) +#define XDEV_U3 (0x3 << 5) +#define XDEV_RESUME (0xf << 5) +/* true: port has power (see HCC_PPC) */ +#define PORT_POWER BIT(9) +/* bits 10:13 indicate device speed: + * 0 - undefined speed - port hasn't be initialized by a reset yet + * 1 - full speed + * 2 - low speed + * 3 - high speed + * 4 - super speed + * 5-15 reserved + */ +#define DEV_SPEED_MASK (0xf << 10) +#define XDEV_FS (0x1 << 10) +#define XDEV_LS (0x2 << 10) +#define XDEV_HS (0x3 << 10) +#define XDEV_SS (0x4 << 10) +#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0<<10)) +#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) +#define DEV_LOWSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_LS) +#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) +#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) +/* Bits 20:23 in the Slot Context are the speed for the device */ +#define SLOT_SPEED_FS (XDEV_FS << 10) +#define SLOT_SPEED_LS (XDEV_LS << 10) +#define SLOT_SPEED_HS (XDEV_HS << 10) +#define SLOT_SPEED_SS (XDEV_SS << 10) +/* Port Indicator Control */ +#define PORT_LED_OFF (0 << 14) +#define PORT_LED_AMBER (1 << 14) +#define PORT_LED_GREEN (2 << 14) +#define PORT_LED_MASK (3 << 14) +/* Port Link State Write Strobe - set this when changing link state */ +#define PORT_LINK_STROBE BIT(16) +/* true: connect status change */ +#define PORT_CSC BIT(17) +/* true: port enable change */ +#define PORT_PEC BIT(18) +/* true: warm reset for a USB 3.0 device is done. A "hot" reset puts the port + * into an enabled state, and the device into the default state. A "warm" reset + * also resets the link, forcing the device through the link training sequence. + * SW can also look at the Port Reset register to see when warm reset is done. + */ +#define PORT_WRC BIT(19) +/* true: over-current change */ +#define PORT_OCC BIT(20) +/* true: reset change - 1 to 0 transition of PORT_RESET */ +#define PORT_RC BIT(21) +/* port link status change - set on some port link state transitions: + * Transition Reason + * ------------------------------------------------------------------------------ + * - U3 to Resume Wakeup signaling from a device + * - Resume to Recovery to U0 USB 3.0 device resume + * - Resume to U0 USB 2.0 device resume + * - U3 to Recovery to U0 Software resume of USB 3.0 device complete + * - U3 to U0 Software resume of USB 2.0 device complete + * - U2 to U0 L1 resume of USB 2.1 device complete + * - U0 to U0 (???) L1 entry rejection by USB 2.1 device + * - U0 to disabled L1 entry error with USB 2.1 device + * - Any state to inactive Error on USB 3.0 port + */ +#define PORT_PLC BIT(22) +/* port configure error change - port failed to configure its link partner */ +#define PORT_CEC BIT(23) +/* Cold Attach Status - xHC can set this bit to report device attached during + * Sx state. Warm port reset should be perfomed to clear this bit and move port + * to connected state. + */ +#define PORT_CAS BIT(24) +/* wake on connect (enable) */ +#define PORT_WKCONN_E BIT(25) +/* wake on disconnect (enable) */ +#define PORT_WKDISC_E BIT(26) +/* wake on over-current (enable) */ +#define PORT_WKOC_E BIT(27) +/* bits 28:29 reserved */ +/* true: device is removable - for USB 3.0 roothub emulation */ +#define PORT_DEV_REMOVE BIT(30) +/* Initiate a warm port reset - complete when PORT_WRC is '1' */ +#define PORT_WR BIT(31) + +/* We mark duplicate entries with -1 */ +#define DUPLICATE_ENTRY ((u8)(-1)) + +/* Port Power Management Status and Control - port_power_base bitmasks */ +/* Inactivity timer value for transitions into U1, in microseconds. + * Timeout can be up to 127us. 0xFF means an infinite timeout. + */ +#define PORT_U1_TIMEOUT(p) ((p) & 0xff) +#define PORT_U1_TIMEOUT_MASK 0xff +/* Inactivity timer value for transitions into U2 */ +#define PORT_U2_TIMEOUT(p) (((p) & 0xff) << 8) +#define PORT_U2_TIMEOUT_MASK (0xff << 8) +/* Bits 24:31 for port testing */ + +/* USB2 Protocol PORTSPMSC */ +#define PORT_L1S_MASK 0x7 +#define PORT_L1S_SUCCESS 0x1 +#define PORT_RWE BIT(3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) +#define PORT_L1DS_MASK (0xff << 8) +#define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE BIT(16) + +/* USB2 Protocol PORTHLPMC */ +#define PORT_HIRDM(p) ((p) & 3) +#define PORT_L1_TIMEOUT(p) (((p) & 0xff) << 2) +#define PORT_BESLD(p) (((p) & 0xf) << 10) + +/* use 512 microseconds as USB2 LPM L1 default timeout. */ +#define XHCI_L1_TIMEOUT 512 + +/* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume latency. + * Safe to use with mixed HIRD and BESL systems (host and device) and is used + * by other operating systems. + * + * XHCI 1.0 errata 8/14/12 Table 13 notes: + * "Software should choose xHC BESL/BESLD field values that do not violate a + * device's resume latency requirements, + * e.g. not program values > '4' if BLC = '1' and a HIRD device is attached, + * or not program values < '4' if BLC = '0' and a BESL device is attached. + */ +#define XHCI_DEFAULT_BESL 4 + +/** + * struct xhci_intr_reg - Interrupt Register Set + * @irq_pending: IMAN - Interrupt Management Register. Used to enable + * interrupts and check for pending interrupts. + * @irq_control: IMOD - Interrupt Moderation Register. + * Used to throttle interrupts. + * @erst_size: Number of segments in the Event Ring Segment Table (ERST). + * @erst_base: ERST base address. + * @erst_dequeue: Event ring dequeue pointer. + * + * Each interrupter (defined by a MSI-X vector) has an event ring and an Event + * Ring Segment Table (ERST) associated with it. The event ring is comprised of + * multiple segments of the same size. The HC places events on the ring and + * "updates the Cycle bit in the TRBs to indicate to software the current + * position of the Enqueue Pointer." The HCD (Linux) processes those events and + * updates the dequeue pointer. + */ +struct xhci_intr_reg { + __le32 irq_pending; + __le32 irq_control; + __le32 erst_size; + __le32 rsvd; + __le64 erst_base; + __le64 erst_dequeue; +}; + +/* irq_pending bitmasks */ +#define ER_IRQ_PENDING(p) ((p) & 0x1) +/* bits 2:31 need to be preserved */ +/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */ +#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe) +#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2) +#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2)) + +/* irq_control bitmasks */ +/* Minimum interval between interrupts (in 250ns intervals). The interval + * between interrupts will be longer if there are no events on the event ring. + * Default is 4000 (1 ms). + */ +#define ER_IRQ_INTERVAL_MASK 0xffff +/* Counter used to count down the time to the next interrupt - HW use only */ +#define ER_IRQ_COUNTER_MASK (0xffff << 16) + +/* erst_size bitmasks */ +/* Preserve bits 16:31 of erst_size */ +#define ERST_SIZE_MASK (0xffff << 16) + +/* erst_dequeue bitmasks */ +/* Dequeue ERST Segment Index (DESI) - Segment number (or alias) + * where the current dequeue pointer lies. This is an optional HW hint. + */ +#define ERST_DESI_MASK 0x7 +/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by + * a work queue (or delayed service routine)? + */ +#define ERST_EHB BIT(3) +#define ERST_PTR_MASK 0xf + +/** + * struct xhci_run_regs + * @microframe_index: MFINDEX - current microframe number + * + * Section 5.5 Host Controller Runtime Registers: + * "Software should read and write these registers using only Dword (32 bit) + * or larger accesses" + */ +struct xhci_run_regs { + __le32 microframe_index; + __le32 rsvd[7]; + struct xhci_intr_reg ir_set[128]; +}; + +/** + * struct doorbell_array + * + * Bits 0 - 7: Endpoint target + * Bits 8 - 15: RsvdZ + * Bits 16 - 31: Stream ID + * + * Section 5.6 + */ +struct xhci_doorbell_array { + __le32 doorbell[256]; +}; + +#define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) +#define DB_VALUE_HOST 0x00000000 + +/** + * struct xhci_slot_ctx + * @dev_info: Route string, device speed, hub info, and last valid endpoint + * @dev_info2: Max exit latency for device number, root hub port number + * @tt_info: tt_info is used to construct split transaction tokens + * @dev_state: slot state and device address + * + * Slot Context - section 6.2.1.1. This assumes the HC uses 32-byte context + * structures. If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the slot context for HC internal use. + */ +struct xhci_slot_ctx { + __le32 dev_info; + __le32 dev_info2; + __le32 tt_info; + __le32 dev_state; + /* offset 0x10 to 0x1f reserved for HC internal use */ + __le32 reserved[4]; +}; + +/* dev_info bitmasks */ +/* Route String - 0:19 */ +#define ROUTE_STRING_MASK 0xfffff +/* Device speed - values defined by PORTSC Device Speed field - 20:23 */ +#define DEV_SPEED (0xf << 20) +/* bit 24 reserved */ +/* Is this LS/FS device connected through a HS hub? - bit 25 */ +#define DEV_MTT BIT(25) +/* Set if the device is a hub - bit 26 */ +#define DEV_HUB BIT(26) +/* Index of the last valid endpoint context in this device context - 27:31 */ +#define LAST_CTX_MASK (0x1f << 27) +#define LAST_CTX(p) ((p) << 27) +#define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1) +#define SLOT_FLAG BIT(0) +#define EP0_FLAG BIT(1) + +/* dev_info2 bitmasks */ +/* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ +#define MAX_EXIT 0xffff +/* Root hub port number that is needed to access the USB device */ +#define ROOT_HUB_PORT(p) (((p) & 0xff) << 16) +#define DEVINFO_TO_ROOT_HUB_PORT(p) (((p) >> 16) & 0xff) +/* Maximum number of ports under a hub device */ +#define XHCI_MAX_PORTS(p) (((p) & 0xff) << 24) + +/* tt_info bitmasks */ +/* + * TT Hub Slot ID - for low or full speed devices attached to a high-speed hub + * The Slot ID of the hub that isolates the high speed signaling from + * this low or full-speed device. '0' if attached to root hub port. + */ +#define TT_SLOT 0xff +/* + * The number of the downstream facing port of the high-speed hub + * '0' if the device is not low or full speed. + */ +#define TT_PORT (0xff << 8) +#define TT_THINK_TIME(p) (((p) & 0x3) << 16) + +/* dev_state bitmasks */ +/* USB device address - assigned by the HC */ +#define DEV_ADDR_MASK 0xff +/* bits 8:26 reserved */ +/* Slot state */ +#define SLOT_STATE (0x1f << 27) +#define GET_SLOT_STATE(p) (((p) & (0x1f << 27)) >> 27) + +#define SLOT_STATE_DISABLED 0x0 +#define SLOT_STATE_ENABLED SLOT_STATE_DISABLED +#define SLOT_STATE_DEFAULT 0x1 +#define SLOT_STATE_ADDRESSED 0x2 +#define SLOT_STATE_CONFIGURED 0x3 + +/** + * struct xhci_ep_ctx + * @ep_info: endpoint state, streams, mult, and interval information. + * @ep_info2: information on endpoint type, max packet size, max burst size, + * error count, and whether the HC will force an event for all + * transactions. + * @deq: 64-bit ring dequeue pointer address. If the endpoint only + * defines one stream, this points to the endpoint transfer ring. + * Otherwise, it points to a stream context array, which has a + * ring pointer for each flow. + * @tx_info: Average TRB lengths for the endpoint ring and + * max payload within an Endpoint Service Interval Time (ESIT). + * + * Endpoint Context - section 6.2.1.2. This assumes the HC uses 32-byte context + * structures. If the HC uses 64-byte contexts, there is an additional 32 bytes + * reserved at the end of the endpoint context for HC internal use. + */ +struct xhci_ep_ctx { + __le32 ep_info; + __le32 ep_info2; + __le64 deq; + __le32 tx_info; + /* offset 0x14 - 0x1f reserved for HC internal use */ + __le32 reserved[3]; +}; + +/* ep_info bitmasks */ +/* + * Endpoint State - bits 0:2 + * 0 - disabled + * 1 - running + * 2 - halted due to halt condition - ok to manipulate endpoint ring + * 3 - stopped + * 4 - TRB error + * 5-7 - reserved + */ +#define EP_STATE_MASK 0xf +#define EP_STATE_DISABLED 0x0 +#define EP_STATE_RUNNING 0x1 +#define EP_STATE_HALTED 0x2 +#define EP_STATE_STOPPED 0x3 +#define EP_STATE_ERROR 0x4 +/* Mult - Max number of burtst within an interval, in EP companion desc. */ +#define EP_MULT(p) (((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) +/* bits 10:14 are Max Primary Streams */ +/* bit 15 is Linear Stream Array */ +/* Interval - period between requests to an endpoint - 125u increments. */ +#define EP_INTERVAL(p) (((p) & 0xff) << 16) +#define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) +#define EP_MAXPSTREAMS_MASK (0x1f << 10) +#define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) +/* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ +#define EP_HAS_LSA BIT(15) + +/* ep_info2 bitmasks */ +/* + * Force Event - generate transfer events for all TRBs for this endpoint + * This will tell the HC to ignore the IOC and ISP flags (for debugging only). + */ +#define FORCE_EVENT BIT(0) +#define ERROR_COUNT(p) (((p) & 0x3) << 1) +#define CTX_TO_EP_TYPE(p) (((p) >> 3) & 0x7) +#define EP_TYPE(p) ((p) << 3) +#define ISOC_OUT_EP 0x1 +#define BULK_OUT_EP 0x2 +#define INT_OUT_EP 0x3 +#define CTRL_EP 0x4 +#define ISOC_IN_EP 0x5 +#define BULK_IN_EP 0x6 +#define INT_IN_EP 0x7 +/* bit 6 reserved */ +/* bit 7 is Host Initiate Disable - for disabling stream selection */ +#define MAX_BURST(p) (((p) & 0xff) << 8) +#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) +#define MAX_PACKET(p) (((p) & 0xffff) << 16) +#define MAX_PACKET_MASK (0xffff << 16) +#define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) + +/* Get max packet size from ep desc. Bit 10..0 specify the max packet size. + * USB2.0 spec 9.6.6. + */ +#define GET_MAX_PACKET(p) ((p) & 0x7ff) + +/* tx_info bitmasks */ +#define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) +#define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) + +/* deq bitmasks */ +#define EP_CTX_CYCLE_MASK BIT(0) +#define SCTX_DEQ_MASK (~0xfL) + +/** + * struct xhci_input_control_context + * Input control context; see section 6.2.5. + * + * @drop_context: set the bit of the endpoint context you want to disable + * @add_context: set the bit of the endpoint context you want to enable + */ +struct xhci_input_control_ctx { + __le32 drop_flags; + __le32 add_flags; + __le32 rsvd2[6]; +}; + +#define EP_IS_ADDED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))) +#define EP_IS_DROPPED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) + +/* drop context bitmasks */ +#define DROP_EP(x) BIT(x) +/* add context bitmasks */ +#define ADD_EP(x) BIT(x) + +struct xhci_stream_ctx { + /* 64-bit stream ring address, cycle state, and stream type */ + __le64 stream_ring; + /* offset 0x14 - 0x1f reserved for HC internal use */ + __le32 reserved[2]; +}; + +/* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ +#define SCT_FOR_CTX(p) (((p) & 0x7) << 1) +/* Secondary stream array type, dequeue pointer is to a transfer ring */ +#define SCT_SEC_TR 0x0 +/* Primary stream array type, dequeue pointer is to a transfer ring */ +#define SCT_PRI_TR 0x1 +/* Dequeue pointer is for a secondary stream array (SSA) with 8 entries */ +#define SCT_SSA_8 0x2 +#define SCT_SSA_16 0x3 +#define SCT_SSA_32 0x4 +#define SCT_SSA_64 0x5 +#define SCT_SSA_128 0x6 +#define SCT_SSA_256 0x7 + +#define SMALL_STREAM_ARRAY_SIZE 256 +#define MEDIUM_STREAM_ARRAY_SIZE 1024 + +/* "Block" sizes in bytes the hardware uses for different device speeds. + * The logic in this part of the hardware limits the number of bits the hardware + * can use, so must represent bandwidth in a less precise manner to mimic what + * the scheduler hardware computes. + */ +#define FS_BLOCK 1 +#define HS_BLOCK 4 +#define SS_BLOCK 16 +#define DMI_BLOCK 32 + +/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated + * with each byte transferred. SuperSpeed devices have an initial overhead to + * set up bursts. These are in blocks, see above. LS overhead has already been + * translated into FS blocks. + */ +#define DMI_OVERHEAD 8 +#define DMI_OVERHEAD_BURST 4 +#define SS_OVERHEAD 8 +#define SS_OVERHEAD_BURST 32 +#define HS_OVERHEAD 26 +#define FS_OVERHEAD 20 +#define LS_OVERHEAD 128 + +/* The TTs need to claim roughly twice as much bandwidth (94 bytes per + * microframe ~= 24Mbps) of the HS bus as the devices can actually use because + * of overhead associated with split transfers crossing microframe boundaries. + * 31 blocks is pure protocol overhead. + */ +#define TT_HS_OVERHEAD (31 + 94) +#define TT_DMI_OVERHEAD (25 + 12) + +/* Bandwidth limits in blocks */ +#define FS_BW_LIMIT 1285 +#define TT_BW_LIMIT 1320 +#define HS_BW_LIMIT 1607 +#define SS_BW_LIMIT_IN 3906 +#define DMI_BW_LIMIT_IN 3906 +#define SS_BW_LIMIT_OUT 3906 +#define DMI_BW_LIMIT_OUT 3906 + +/* Percentage of bus bandwidth reserved for non-periodic transfers */ +#define FS_BW_RESERVED 10 +#define HS_BW_RESERVED 20 +#define SS_BW_RESERVED 10 + +enum xhci_overhead_type { + LS_OVERHEAD_TYPE = 0, + FS_OVERHEAD_TYPE, + HS_OVERHEAD_TYPE, +}; + +struct xhci_transfer_event { + /* 64-bit buffer address, or immediate data */ + __le64 buffer; + __le32 transfer_len; + /* This field is interpreted differently based on the type of TRB */ + __le32 flags; +}; + +/* Transfer event TRB length bit mask */ +/* bits 0:23 */ +#define EVENT_TRB_LEN(p) ((p) & 0xffffff) + +/** Transfer Event bit fields **/ +#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) + +/* Completion Code - only applicable for some types of TRBs */ +#define COMP_CODE_MASK (0xff << 24) +#define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24) +#define COMP_SUCCESS 1 +/* Data Buffer Error */ +#define COMP_DB_ERR 2 +/* Babble Detected Error */ +#define COMP_BABBLE 3 +/* USB Transaction Error */ +#define COMP_TX_ERR 4 +/* TRB Error - some TRB field is invalid */ +#define COMP_TRB_ERR 5 +/* Stall Error - USB device is stalled */ +#define COMP_STALL 6 +/* Resource Error - HC doesn't have memory for that device configuration */ +#define COMP_ENOMEM 7 +/* Bandwidth Error - not enough room in schedule for this dev config */ +#define COMP_BW_ERR 8 +/* No Slots Available Error - HC ran out of device slots */ +#define COMP_ENOSLOTS 9 +/* Invalid Stream Type Error */ +#define COMP_STREAM_ERR 10 +/* Slot Not Enabled Error - doorbell rung for disabled device slot */ +#define COMP_EBADSLT 11 +/* Endpoint Not Enabled Error */ +#define COMP_EBADEP 12 +/* Short Packet */ +#define COMP_SHORT_TX 13 +/* Ring Underrun - doorbell rung for an empty isoc OUT ep ring */ +#define COMP_UNDERRUN 14 +/* Ring Overrun - isoc IN ep ring is empty when ep is scheduled to RX */ +#define COMP_OVERRUN 15 +/* Virtual Function Event Ring Full Error */ +#define COMP_VF_FULL 16 +/* Parameter Error - Context parameter is invalid */ +#define COMP_EINVAL 17 +/* Bandwidth Overrun Error - isoc ep exceeded its allocated bandwidth */ +#define COMP_BW_OVER 18 +/* Context State Error - illegal context state transition requested */ +#define COMP_CTX_STATE 19 +/* No Ping Response Error - HC didn't get PING_RESPONSE in time to TX */ +#define COMP_PING_ERR 20 +/* Event Ring is full */ +#define COMP_ER_FULL 21 +/* Incompatible Device Error */ +#define COMP_DEV_ERR 22 +/* Missed Service Error - HC couldn't service an isoc ep within interval */ +#define COMP_MISSED_INT 23 +/* Successfully stopped command ring */ +#define COMP_CMD_STOP 24 +/* Successfully aborted current command and stopped command ring */ +#define COMP_CMD_ABORT 25 +/* Stopped - transfer was terminated by a stop endpoint command */ +#define COMP_STOP 26 +/* Same as COMP_EP_STOPPED, but the transferred length in the event is invalid */ +#define COMP_STOP_INVAL 27 +/* Control Abort Error - Debug Capability - control pipe aborted */ +#define COMP_DBG_ABORT 28 +/* Max Exit Latency Too Large Error */ +#define COMP_MEL_ERR 29 +/* TRB type 30 reserved */ +/* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ +#define COMP_BUFF_OVER 31 +/* Event Lost Error - xHC has an "internal event overrun condition" */ +#define COMP_ISSUES 32 +/* Undefined Error - reported when other error codes don't apply */ +#define COMP_UNKNOWN 33 +/* Invalid Stream ID Error */ +#define COMP_STRID_ERR 34 +/* Secondary Bandwidth Error - may be returned by a Configure Endpoint cmd */ +#define COMP_2ND_BW_ERR 35 +/* Split Transaction Error */ +#define COMP_SPLIT_ERR 36 + +struct xhci_link_trb { + /* 64-bit segment pointer*/ + __le64 segment_ptr; + __le32 intr_target; + __le32 control; +}; + +/* control bitfields */ +#define LINK_TOGGLE BIT(1) + +/* Command completion event TRB */ +struct xhci_event_cmd { + /* Pointer to command TRB, or the value passed by the event data trb */ + __le64 cmd_trb; + __le32 status; + __le32 flags; +}; + +/* flags bitmasks */ + +/* Address device - disable SetAddress */ +#define TRB_BSR BIT(9) +enum xhci_setup_dev { + SETUP_CONTEXT_ONLY, + SETUP_CONTEXT_ADDRESS, +}; + +/* bits 16:23 are the virtual function ID */ +/* bits 24:31 are the slot ID */ +#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24) +#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24) + +/* Configure Endpoint Command TRB - deconfigure */ +#define TRB_DC BIT(9) + +/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */ +#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1) +#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16) + +#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23) +#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) +#define LAST_EP_INDEX 30 + +/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */ +#define TRB_TO_STREAM_ID(p) ((((p) & (0xffff << 16)) >> 16)) +#define STREAM_ID_FOR_TRB(p) ((((p)) & 0xffff) << 16) +#define SCT_FOR_TRB(p) (((p) << 1) & 0x7) + +/* Port Status Change Event TRB fields */ +/* Port ID - bits 31:24 */ +#define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) + +/* Normal TRB fields */ +/* transfer_len bitmasks - bits 0:16 */ +#define TRB_LEN(p) ((p) & 0x1ffff) +/* Interrupter Target - which MSI-X vector to target the completion event at */ +#define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) +#define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) + +/* Cycle bit - indicates TRB ownership by HC or HCD */ +#define TRB_CYCLE BIT(0) +/* + * Force next event data TRB to be evaluated before task switch. + * Used to pass OS data back after a TD completes. + */ +#define TRB_ENT BIT(1) +/* Interrupt on short packet */ +#define TRB_ISP BIT(2) +/* Set PCIe no snoop attribute */ +#define TRB_NO_SNOOP BIT(3) +/* Chain multiple TRBs into a TD */ +#define TRB_CHAIN BIT(4) +/* Interrupt on completion */ +#define TRB_IOC BIT(5) +/* The buffer pointer contains immediate data */ +#define TRB_IDT BIT(6) + +/* Block Event Interrupt */ +#define TRB_BEI BIT(9) + +/* Control transfer TRB specific fields */ +#define TRB_DIR_IN BIT(16) +#define TRB_TX_TYPE(p) ((p) << 16) +#define TRB_DATA_OUT 2 +#define TRB_DATA_IN 3 + +/* Isochronous TRB specific fields */ +#define TRB_SIA BIT(31) + +struct xhci_generic_trb { + __le32 field[4]; +}; + +union xhci_trb { + struct xhci_link_trb link; + struct xhci_transfer_event trans_event; + struct xhci_event_cmd event_cmd; + struct xhci_generic_trb generic; +}; + +/* TRB bit mask */ +#define TRB_TYPE_BITMASK (0xfc00) +#define TRB_TYPE(p) ((p) << 10) +#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10) +/* TRB type IDs */ +/* bulk, interrupt, isoc scatter/gather, and control data stage */ +#define TRB_NORMAL 1 +/* setup stage for control transfers */ +#define TRB_SETUP 2 +/* data stage for control transfers */ +#define TRB_DATA 3 +/* status stage for control transfers */ +#define TRB_STATUS 4 +/* isoc transfers */ +#define TRB_ISOC 5 +/* TRB for linking ring segments */ +#define TRB_LINK 6 +#define TRB_EVENT_DATA 7 +/* Transfer Ring No-op (not for the command ring) */ +#define TRB_TR_NOOP 8 +/* Command TRBs */ +/* Enable Slot Command */ +#define TRB_ENABLE_SLOT 9 +/* Disable Slot Command */ +#define TRB_DISABLE_SLOT 10 +/* Address Device Command */ +#define TRB_ADDR_DEV 11 +/* Configure Endpoint Command */ +#define TRB_CONFIG_EP 12 +/* Evaluate Context Command */ +#define TRB_EVAL_CONTEXT 13 +/* Reset Endpoint Command */ +#define TRB_RESET_EP 14 +/* Stop Transfer Ring Command */ +#define TRB_STOP_RING 15 +/* Set Transfer Ring Dequeue Pointer Command */ +#define TRB_SET_DEQ 16 +/* Reset Device Command */ +#define TRB_RESET_DEV 17 +/* Force Event Command (opt) */ +#define TRB_FORCE_EVENT 18 +/* Negotiate Bandwidth Command (opt) */ +#define TRB_NEG_BANDWIDTH 19 +/* Set Latency Tolerance Value Command (opt) */ +#define TRB_SET_LT 20 +/* Get port bandwidth Command */ +#define TRB_GET_BW 21 +/* Force Header Command - generate a transaction or link management packet */ +#define TRB_FORCE_HEADER 22 +/* No-op Command - not for transfer rings */ +#define TRB_CMD_NOOP 23 +/* TRB IDs 24-31 reserved */ +/* Event TRBS */ +/* Transfer Event */ +#define TRB_TRANSFER 32 +/* Command Completion Event */ +#define TRB_COMPLETION 33 +/* Port Status Change Event */ +#define TRB_PORT_STATUS 34 +/* Bandwidth Request Event (opt) */ +#define TRB_BANDWIDTH_EVENT 35 +/* Doorbell Event (opt) */ +#define TRB_DOORBELL 36 +/* Host Controller Event */ +#define TRB_HC_EVENT 37 +/* Device Notification Event - device sent function wake notification */ +#define TRB_DEV_NOTE 38 +/* MFINDEX Wrap Event - microframe counter wrapped */ +#define TRB_MFINDEX_WRAP 39 +/* TRB IDs 40-47 reserved, 48-63 is vendor-defined */ + +/* Nec vendor-specific command completion event. */ +#define TRB_NEC_CMD_COMP 48 +/* Get NEC firmware revision. */ +#define TRB_NEC_GET_FW 49 + +#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) +/* Above, but for __le32 types -- can avoid work by swapping constants: */ +#define TRB_TYPE_LINK_LE32(x) \ + (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == cpu_to_le32(TRB_TYPE(TRB_LINK))) +#define TRB_TYPE_NOOP_LE32(x) \ + (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) + +#define NEC_FW_MINOR(p) (((p) >> 0) & 0xff) +#define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff) + +/* + * TRBS_PER_SEGMENT must be a multiple of 4, + * since the command ring is 64-byte aligned. + * It must also be greater than 16. + */ +#define TRBS_PER_SEGMENT 64 +/* Allow two commands + a link TRB, along with any reserved command TRBs */ +#define MAX_RSVD_CMD_TRBS (TRBS_PER_SEGMENT - 3) +#define TRB_SEGMENT_SIZE (TRBS_PER_SEGMENT * 16) +#define TRB_SEGMENT_SHIFT (ilog2(TRB_SEGMENT_SIZE)) +/* TRB buffer pointers can't cross 64KB boundaries */ +#define TRB_MAX_BUFF_SHIFT 16 +#define TRB_MAX_BUFF_SIZE (1 << TRB_MAX_BUFF_SHIFT) + +/* xHCI command default timeout value */ +#define XHCI_CMD_DEFAULT_TIMEOUT (5 * SECOND) + +struct xhci_erst_entry { + /* 64-bit event ring segment address */ + __le64 seg_addr; + __le32 seg_size; + /* Set to zero */ + __le32 rsvd; +}; + +/* + * Each segment table entry is 4*32bits long. 1K seems like an ok size: + * (1K bytes * 8bytes/bit) / (4*32 bits) = 64 segment entries in the table, + * meaning 64 ring segments. + * Initial allocated size of the ERST, in number of entries */ +#define ERST_NUM_SEGS 1 +/* Initial allocated size of the ERST, in number of entries */ +#define ERST_SIZE 64 +/* Initial number of event segment rings allocated */ +#define ERST_ENTRIES 1 +/* Poll every 60 seconds */ +#define POLL_TIMEOUT 60 +/* Stop endpoint command timeout (secs) for URB cancellation watchdog timer */ +#define XHCI_STOP_EP_CMD_TIMEOUT 5 +/* XXX: Make these module parameters */ + +/* + * It can take up to 20 ms to transition from RExit to U0 on the + * Intel Lynx Point LP xHCI host. + */ +#define XHCI_MAX_REXIT_TIMEOUT (20 * MSECONDS) + +#define XHCI_MAX_EXT_CAPS 50 + +#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) +#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) +#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) + +/* Extended capability register fields */ +#define XHCI_EXT_CAPS_ID(p) (((p)>>0)&0xff) +#define XHCI_EXT_CAPS_NEXT(p) (((p)>>8)&0xff) +#define XHCI_EXT_CAPS_VAL(p) ((p)>>16) +/* Extended capability IDs - ID 0 reserved */ +#define XHCI_EXT_CAPS_LEGACY 1 +#define XHCI_EXT_CAPS_PROTOCOL 2 +#define XHCI_EXT_CAPS_PM 3 +#define XHCI_EXT_CAPS_VIRT 4 +#define XHCI_EXT_CAPS_ROUTE 5 +/* IDs 6-9 reserved */ +#define XHCI_EXT_CAPS_DEBUG 10 +/* USB Legacy Support Capability - section 7.1.1 */ +#define XHCI_HC_BIOS_OWNED BIT(16) +#define XHCI_HC_OS_OWNED BIT(24) + +/* USB Legacy Support Capability - section 7.1.1 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_SUPPORT_OFFSET 0x00 + +/* USB Legacy Support Control and Status Register - section 7.1.2 */ +/* Add this offset, plus the value of xECP in HCCPARAMS to the base address */ +#define XHCI_LEGACY_CONTROL_OFFSET 0x04 +/* bits 1:3, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ +#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17)) +#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29) + +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C BIT(16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC BIT(19) +#define XHCI_BLC BIT(20) + +/* + * Registers should always be accessed with double word or quad word accesses. + * + * Some xHCI implementations may support 64-bit address pointers. Registers + * with 64-bit address pointers should be written to with dword accesses by + * writing the low dword first (ptr[0]), then the high dword (ptr[1]) second. + * xHCI implementations that do not support 64-bit address pointers will ignore + * the high dword, and write order is irrelevant. + */ +static inline u64 xhci_read_64(__le64 __iomem *regs) +{ + __u32 __iomem *ptr = (__u32 __iomem *)regs; + u64 val_lo = readl(ptr); + u64 val_hi = readl(ptr + 1); + return val_lo + (val_hi << 32); +} +static inline void xhci_write_64(const u64 val, __le64 __iomem *regs) +{ + __u32 __iomem *ptr = (__u32 __iomem *)regs; + u32 val_lo = lower_32_bits(val); + u32 val_hi = upper_32_bits(val); + + writel(val_lo, ptr); + writel(val_hi, ptr + 1); +} + +/* + * Barebox xHCI housekeeping structs + */ + +enum xhci_ring_type { + TYPE_CTRL = 0, + TYPE_ISOC, + TYPE_BULK, + TYPE_INTR, + TYPE_STREAM, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_ring { + struct list_head list; + union xhci_trb *trbs; + union xhci_trb *enqueue; + union xhci_trb *dequeue; + enum xhci_ring_type type; + int num_trbs; + int cycle_state; +}; + +struct xhci_device_context { + struct xhci_slot_ctx slot; + struct xhci_ep_ctx ep[31]; +}; + +struct xhci_input_context { + struct xhci_input_control_ctx icc; + struct xhci_slot_ctx slot; + struct xhci_ep_ctx ep[31]; +}; + +struct xhci_virtual_device { + struct list_head list; + struct usb_device *udev; + void *dma; + size_t dma_size; + int slot_id; + struct xhci_ring *ep[USB_MAXENDPOINTS]; + struct xhci_input_context *in_ctx; + struct xhci_device_context *out_ctx; +}; + +struct usb_root_hub_info { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __attribute__ ((packed)); + +struct xhci_hcd { + struct usb_host host; + struct device_d *dev; + struct xhci_cap_regs __iomem *cap_regs; + struct xhci_op_regs __iomem *op_regs; + struct xhci_run_regs __iomem *run_regs; + struct xhci_doorbell_array __iomem *dba; + struct xhci_intr_reg __iomem *ir_set; + /* Cached register copies of read-only HC data */ + u32 hcs_params1; + u32 hcs_params2; + u32 hcs_params3; + u32 hcc_capbase; + u32 hcc_params; + u16 hci_version; + int max_slots; + int num_sp; + int page_size; + int page_shift; + void *dma; + size_t dma_size; + __le64 *dcbaa; + void *sp; + __le64 *sp_array; + struct xhci_ring cmd_ring; + struct xhci_ring event_ring; + struct xhci_ring *rings; + struct list_head rings_list; + struct xhci_erst_entry *event_erst; + u8 *port_array; + int rootdev; + struct list_head vdev_list; + u32 *ext_caps; + unsigned int num_ext_caps; + __le32 __iomem **usb_ports; + unsigned int num_usb_ports; + struct usb_root_hub_info usb_info; +}; + +#define to_xhci_hcd(_h) \ + container_of(_h, struct xhci_hcd, host) + +int xhci_handshake(void __iomem *p, u32 mask, u32 done, int usec); + +int xhci_issue_command(struct xhci_hcd *xhci, union xhci_trb *trb); +int xhci_wait_for_event(struct xhci_hcd *xhci, u8 type, union xhci_trb *trb); + +int xhci_virtdev_reset(struct xhci_virtual_device *vdev); +int xhci_virtdev_detach(struct xhci_virtual_device *vdev); + +int xhci_hub_setup_ports(struct xhci_hcd *xhci); +void xhci_hub_port_power(struct xhci_hcd *xhci, int port, bool enable); +int xhci_hub_control(struct usb_device *dev, unsigned long pipe, + void *buffer, int length, struct devrequest *req); + +static inline void xhci_print_trb(struct xhci_hcd *xhci, + union xhci_trb *trb, const char *desc) +{ + dev_dbg(xhci->dev, "%s [%08x %08x %08x %08x]\n", desc, + trb->generic.field[0], trb->generic.field[1], + trb->generic.field[2], trb->generic.field[3]); +} + +#endif diff --git a/drivers/usb/imx/chipidea-imx.c b/drivers/usb/imx/chipidea-imx.c index 9b6829b..62feae8 100644 --- a/drivers/usb/imx/chipidea-imx.c +++ b/drivers/usb/imx/chipidea-imx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -31,9 +32,12 @@ void __iomem *base; struct ehci_data data; unsigned long flags; - enum imx_usb_mode mode; + uint32_t mode; int portno; enum usb_phy_interface phymode; + struct param_d *param_mode; + int role_registered; + struct regulator *vbus; }; static int imx_chipidea_port_init(void *drvdata) @@ -99,6 +103,19 @@ case USB_DR_MODE_PERIPHERAL: ci->mode = IMX_USB_MODE_DEVICE; break; + case USB_DR_MODE_OTG: + ci->mode = IMX_USB_MODE_OTG; + break; + case USB_DR_MODE_UNKNOWN: + /* + * No dr_mode specified. This means it can either be OTG + * for port 0 or host mode for the other host-only ports. + */ + if (ci->portno == 0) + ci->mode = IMX_USB_MODE_OTG; + else + ci->mode = IMX_USB_MODE_HOST; + break; } ci->phymode = of_usb_get_phy_mode(ci->dev->device_node, NULL); @@ -129,6 +146,72 @@ return 0; } +static int ci_register_role(struct imx_chipidea *ci) +{ + if (ci->role_registered) + return -EBUSY; + + if (ci->mode == IMX_USB_MODE_HOST) { + if (IS_ENABLED(CONFIG_USB_EHCI)) { + ci->role_registered = 1; + return ehci_register(ci->dev, &ci->data); + } else { + dev_err(ci->dev, "Host support not available\n"); + return -ENODEV; + } + } + + if (ci->mode == IMX_USB_MODE_DEVICE) { + if (IS_ENABLED(CONFIG_USB_GADGET_DRIVER_ARC)) { + ci->role_registered = 1; + return ci_udc_register(ci->dev, ci->base); + } else { + dev_err(ci->dev, "USB device support not available\n"); + return -ENODEV; + } + } + + return 0; +} + +static int ci_set_mode(struct param_d *param, void *priv) +{ + struct imx_chipidea *ci = priv; + + if (ci->role_registered) + return -EBUSY; + + return ci_register_role(ci); +} + +static const char *ci_mode_names[] = { + "host", "peripheral", "otg" +}; + +static struct device_d imx_otg_device = { + .name = "otg", + .id = DEVICE_ID_SINGLE, +}; + +static int ci_register_otg_device(struct imx_chipidea *ci) +{ + int ret; + + if (imx_otg_device.parent) + return -EBUSY; + + imx_otg_device.parent = ci->dev; + + ret = register_device(&imx_otg_device); + if (ret) + return ret; + + ci->param_mode = dev_add_param_enum(&imx_otg_device, "mode", + ci_set_mode, NULL, &ci->mode, + ci_mode_names, ARRAY_SIZE(ci_mode_names), ci); + return 0; +} + static int imx_chipidea_probe(struct device_d *dev) { struct imxusb_platformdata *pdata = dev->platform_data; @@ -154,6 +237,10 @@ ci->mode = pdata->mode; } + ci->vbus = regulator_get(dev, "vbus"); + + regulator_enable(ci->vbus); + base = dev_request_mem_region(dev, 0); if (!base) return -ENODEV; @@ -178,14 +265,10 @@ ci->data.hcor = base + 0x140; ci->data.flags = EHCI_HAS_TT; - if (ci->mode == IMX_USB_MODE_HOST && IS_ENABLED(CONFIG_USB_EHCI)) { - ret = ehci_register(dev, &ci->data); - } else if (ci->mode == IMX_USB_MODE_DEVICE && IS_ENABLED(CONFIG_USB_GADGET_DRIVER_ARC)) { - ret = ci_udc_register(dev, base); - } else { - dev_err(dev, "No supported role\n"); - ret = -ENODEV; - } + if (ci->mode == IMX_USB_MODE_OTG) + ret = ci_register_otg_device(ci); + else + ret = ci_register_role(ci); return ret; }; diff --git a/include/common.h b/include/common.h index ca817aa..de63571 100644 --- a/include/common.h +++ b/include/common.h @@ -282,6 +282,22 @@ } \ ) +/** + * upper_32_bits - return bits 32-63 of a number + * @n: the number we're accessing + * + * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress + * the "right shift count >= width of type" warning when that quantity is + * 32-bits. + */ +#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) + +/** + * lower_32_bits - return bits 0-31 of a number + * @n: the number we're accessing + */ +#define lower_32_bits(n) ((u32)(n)) + #define abs(x) ({ \ long __x = (x); \ (__x < 0) ? -__x : __x; \ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d512adc..98f12e1 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -34,6 +34,24 @@ (void) (&_max1 == &_max2); \ _max1 > _max2 ? _max1 : _max2; }) +#define min3(x, y, z) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + typeof(z) _min3 = (z); \ + (void) (&_min1 == &_min2); \ + (void) (&_min1 == &_min3); \ + _min1 < _min2 ? (_min1 < _min3 ? _min1 : _min3) : \ + (_min2 < _min3 ? _min2 : _min3); }) + +#define max3(x, y, z) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + typeof(z) _max3 = (z); \ + (void) (&_max1 == &_max2); \ + (void) (&_max1 == &_max3); \ + _max1 > _max2 ? (_max1 > _max3 ? _max1 : _max3) : \ + (_max2 > _max3 ? _max2 : _max3); }) + /** * clamp - return a value clamped to a given range with strict typechecking * @val: current value diff --git a/include/usb/ch11.h b/include/usb/ch11.h new file mode 100644 index 0000000..93f891a --- /dev/null +++ b/include/usb/ch11.h @@ -0,0 +1,279 @@ +/* + * This file holds Hub protocol constants and data structures that are + * defined in chapter 11 (Hub Specification) of the USB 2.0 specification. + * + * It is used/shared between the USB core, the HCDs and couple of other USB + * drivers. + */ + +#ifndef __LINUX_CH11_H +#define __LINUX_CH11_H + +#include /* __u8 etc */ + +/* This is arbitrary. + * From USB 2.0 spec Table 11-13, offset 7, a hub can + * have up to 255 ports. The most yet reported is 10. + * + * Current Wireless USB host hardware (Intel i1480 for example) allows + * up to 22 devices to connect. Upcoming hardware might raise that + * limit. Because the arrays need to add a bit for hub status data, we + * use 31, so plus one evens out to four bytes. + * + * Reduced to 8 max children for Barebox. + */ +#define USB_MAXCHILDREN 8 + +/* + * Hub request types + */ + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +/* + * Hub class requests + * See USB 2.0 spec Table 11-16 + */ +#define HUB_CLEAR_TT_BUFFER 8 +#define HUB_RESET_TT 9 +#define HUB_GET_TT_STATE 10 +#define HUB_STOP_TT 11 + +/* + * Hub class additional requests defined by USB 3.0 spec + * See USB 3.0 spec Table 10-6 + */ +#define HUB_SET_DEPTH 12 +#define HUB_GET_PORT_ERR_COUNT 13 + +/* + * Hub Class feature numbers + * See USB 2.0 spec Table 11-17 + */ +#define C_HUB_LOCAL_POWER 0 +#define C_HUB_OVER_CURRENT 1 + +/* + * Port feature numbers + * See USB 2.0 spec Table 11-17 + */ +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 /* L2 suspend */ +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_L1 5 /* L1 suspend */ +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 /* Should never be used */ +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 +#define USB_PORT_FEAT_C_PORT_L1 23 + +/* + * Port feature selectors added by USB 3.0 spec. + * See USB 3.0 spec Table 10-7 + */ +#define USB_PORT_FEAT_LINK_STATE 5 +#define USB_PORT_FEAT_U1_TIMEOUT 23 +#define USB_PORT_FEAT_U2_TIMEOUT 24 +#define USB_PORT_FEAT_C_PORT_LINK_STATE 25 +#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26 +#define USB_PORT_FEAT_REMOTE_WAKE_MASK 27 +#define USB_PORT_FEAT_BH_PORT_RESET 28 +#define USB_PORT_FEAT_C_BH_PORT_RESET 29 +#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30 + +#define USB_PORT_LPM_TIMEOUT(p) (((p) & 0xff) << 8) + +/* USB 3.0 hub remote wake mask bits, see table 10-14 */ +#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT (1 << 8) +#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT (1 << 9) +#define USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT (1 << 10) + +/* + * Hub Status and Hub Change results + * See USB 2.0 spec Table 11-19 and Table 11-20 + */ +struct usb_port_status { + __le16 wPortStatus; + __le16 wPortChange; +} __attribute__ ((packed)); + +/* + * wPortStatus bit field + * See USB 2.0 spec Table 11-21 + */ +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_L1 0x0020 +/* bits 6 to 7 are reserved */ +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 +/* bits 13 to 15 are reserved */ + +/* + * Additions to wPortStatus bit field from USB 3.0 + * See USB 3.0 spec Table 10-10 + */ +#define USB_PORT_STAT_LINK_STATE 0x01e0 +#define USB_SS_PORT_STAT_POWER 0x0200 +#define USB_SS_PORT_STAT_SPEED 0x1c00 +#define USB_PORT_STAT_SPEED_5GBPS 0x0000 +/* Valid only if port is enabled */ +/* Bits that are the same from USB 2.0 */ +#define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \ + USB_PORT_STAT_ENABLE | \ + USB_PORT_STAT_OVERCURRENT | \ + USB_PORT_STAT_RESET) + +/* + * Definitions for PORT_LINK_STATE values + * (bits 5-8) in wPortStatus + */ +#define USB_SS_PORT_LS_U0 0x0000 +#define USB_SS_PORT_LS_U1 0x0020 +#define USB_SS_PORT_LS_U2 0x0040 +#define USB_SS_PORT_LS_U3 0x0060 +#define USB_SS_PORT_LS_SS_DISABLED 0x0080 +#define USB_SS_PORT_LS_RX_DETECT 0x00a0 +#define USB_SS_PORT_LS_SS_INACTIVE 0x00c0 +#define USB_SS_PORT_LS_POLLING 0x00e0 +#define USB_SS_PORT_LS_RECOVERY 0x0100 +#define USB_SS_PORT_LS_HOT_RESET 0x0120 +#define USB_SS_PORT_LS_COMP_MOD 0x0140 +#define USB_SS_PORT_LS_LOOPBACK 0x0160 + +/* + * wPortChange bit field + * See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10 + * Bits 0 to 5 shown, bits 6 to 15 are reserved + */ +#define USB_PORT_STAT_C_CONNECTION 0x0001 +#define USB_PORT_STAT_C_ENABLE 0x0002 +#define USB_PORT_STAT_C_SUSPEND 0x0004 +#define USB_PORT_STAT_C_OVERCURRENT 0x0008 +#define USB_PORT_STAT_C_RESET 0x0010 +#define USB_PORT_STAT_C_L1 0x0020 +/* + * USB 3.0 wPortChange bit fields + * See USB 3.0 spec Table 10-11 + */ +#define USB_PORT_STAT_C_BH_RESET 0x0020 +#define USB_PORT_STAT_C_LINK_STATE 0x0040 +#define USB_PORT_STAT_C_CONFIG_ERROR 0x0080 + +/* + * wHubCharacteristics (masks) + * See USB 2.0 spec Table 11-13, offset 3 + */ +#define HUB_CHAR_LPSM 0x0003 /* Logical Power Switching Mode mask */ +#define HUB_CHAR_COMMON_LPSM 0x0000 /* All ports power control at once */ +#define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* per-port power control */ +#define HUB_CHAR_NO_LPSM 0x0002 /* no power switching */ + +#define HUB_CHAR_COMPOUND 0x0004 /* hub is part of a compound device */ + +#define HUB_CHAR_OCPM 0x0018 /* Over-Current Protection Mode mask */ +#define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports Over-Current reporting */ +#define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* per-port Over-current reporting */ +#define HUB_CHAR_NO_OCPM 0x0010 /* No Over-current Protection support */ + +#define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */ +#define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */ + +struct usb_hub_status { + __le16 wHubStatus; + __le16 wHubChange; +} __attribute__ ((packed)); + +/* + * Hub Status & Hub Change bit masks + * See USB 2.0 spec Table 11-19 and Table 11-20 + * Bits 0 and 1 for wHubStatus and wHubChange + * Bits 2 to 15 are reserved for both + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + + +/* + * Hub descriptor + * See USB 2.0 spec Table 11-13 + */ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +#define USB_DT_SS_HUB (USB_TYPE_CLASS | 0x0a) +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_SS_HUB_SIZE 12 + +/* + * Hub Device descriptor + * USB Hub class device protocols + */ + +#define USB_HUB_PR_FS 0 /* Full speed hub */ +#define USB_HUB_PR_HS_NO_TT 0 /* Hi-speed hub without TT */ +#define USB_HUB_PR_HS_SINGLE_TT 1 /* Hi-speed hub with single TT */ +#define USB_HUB_PR_HS_MULTI_TT 2 /* Hi-speed hub with multiple TT */ +#define USB_HUB_PR_SS 3 /* Super speed hub */ + +struct usb_hub_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __le16 wHubCharacteristics; + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + + /* 2.0 and 3.0 hubs differ here */ + union { + struct { + /* add 1 bit for hub status change; round to bytes */ + __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8]; + __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8]; + } __attribute__ ((packed)) hs; + + struct { + __u8 bHubHdrDecLat; + __le16 wHubDelay; + __le16 DeviceRemovable; + } __attribute__ ((packed)) ss; + } u; +} __attribute__ ((packed)); + +/* port indicator status selectors, tables 11-7 and 11-25 */ +#define HUB_LED_AUTO 0 +#define HUB_LED_AMBER 1 +#define HUB_LED_GREEN 2 +#define HUB_LED_OFF 3 + +enum hub_led_mode { + INDICATOR_AUTO = 0, + INDICATOR_CYCLE, + /* software blinks for attention: software, hardware, reserved */ + INDICATOR_GREEN_BLINK, INDICATOR_GREEN_BLINK_OFF, + INDICATOR_AMBER_BLINK, INDICATOR_AMBER_BLINK_OFF, + INDICATOR_ALT_BLINK, INDICATOR_ALT_BLINK_OFF +} __attribute__ ((packed)); + +/* Transaction Translator Think Times, in bits */ +#define HUB_TTTT_8_BITS 0x00 +#define HUB_TTTT_16_BITS 0x20 +#define HUB_TTTT_24_BITS 0x40 +#define HUB_TTTT_32_BITS 0x60 + +#endif /* __LINUX_CH11_H */ diff --git a/include/usb/chipidea-imx.h b/include/usb/chipidea-imx.h index 487217c..09e19af 100644 --- a/include/usb/chipidea-imx.h +++ b/include/usb/chipidea-imx.h @@ -37,6 +37,7 @@ enum imx_usb_mode { IMX_USB_MODE_HOST, IMX_USB_MODE_DEVICE, + IMX_USB_MODE_OTG, }; struct imxusb_platformdata { diff --git a/include/usb/usb.h b/include/usb/usb.h index ff5242b..82acf20 100644 --- a/include/usb/usb.h +++ b/include/usb/usb.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -150,12 +151,12 @@ struct device_d *hw_dev; int busnum; - int scanned; + struct usb_device *root_dev; }; int usb_register_host(struct usb_host *); -int usb_host_detect(struct usb_host *host, int force); +int usb_host_detect(struct usb_host *host); int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol); int usb_set_idle(struct usb_device *dev, int ifnum, int duration, @@ -185,7 +186,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size); int usb_set_interface(struct usb_device *dev, int interface, int alternate); -void usb_rescan(int force); +void usb_rescan(void); /* big endian -> little endian conversion */ /* some CPUs are already little endian e.g. the ARM920T */ @@ -311,32 +312,6 @@ /************************************************************************* * Hub Stuff */ -struct usb_port_status { - unsigned short wPortStatus; - unsigned short wPortChange; -} __attribute__ ((packed)); - -struct usb_hub_status { - unsigned short wHubStatus; - unsigned short wHubChange; -} __attribute__ ((packed)); - - -/* Hub descriptor */ -struct usb_hub_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned char bNbrPorts; - unsigned short wHubCharacteristics; - unsigned char bPwrOn2PwrGood; - unsigned char bHubContrCurrent; - unsigned char DeviceRemovable[(USB_MAXCHILDREN+1+7)/8]; - unsigned char PortPowerCtrlMask[(USB_MAXCHILDREN+1+7)/8]; - /* DeviceRemovable and PortPwrCtrlMask want to be variable-length - bitmaps that hold max 255 entries. (bit0 is ignored) */ -} __attribute__ ((packed)); - - struct usb_hub_device { struct usb_device *pusb_dev; struct usb_hub_descriptor desc; @@ -473,4 +448,7 @@ USBPHY_INTERFACE_MODE_SERIAL, USBPHY_INTERFACE_MODE_HSIC, }; + +extern struct list_head usb_device_list; + #endif /*_USB_H_ */ diff --git a/include/usb/usb_defs.h b/include/usb/usb_defs.h index ace20e4..8e32379 100644 --- a/include/usb/usb_defs.h +++ b/include/usb/usb_defs.h @@ -24,17 +24,6 @@ /* USB constants */ -/* Device and/or Interface Class codes */ -#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -#define USB_CLASS_AUDIO 1 -#define USB_CLASS_COMM 2 -#define USB_CLASS_HID 3 -#define USB_CLASS_PRINTER 7 -#define USB_CLASS_MASS_STORAGE 8 -#define USB_CLASS_HUB 9 -#define USB_CLASS_DATA 10 -#define USB_CLASS_VENDOR_SPEC 0xff - /* some HID sub classes */ #define USB_SUB_HID_NONE 0 #define USB_SUB_HID_BOOT 1 @@ -60,53 +49,14 @@ #define US_PR_CBI 0 /* Control/Bulk/Interrupt */ #define US_PR_BULK 0x50 /* bulk only */ -/* USB types */ -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -/* USB recipients */ -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* USB directions */ -#define USB_DIR_OUT 0 -#define USB_DIR_IN 0x80 - /* Descriptor types */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - #define USB_DT_HID (USB_TYPE_CLASS | 0x01) #define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) #define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) -#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) /* Descriptor sizes per descriptor type */ -#define USB_DT_DEVICE_SIZE 18 -#define USB_DT_CONFIG_SIZE 9 -#define USB_DT_INTERFACE_SIZE 9 -#define USB_DT_ENDPOINT_SIZE 7 -#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -#define USB_DT_HUB_NONVAR_SIZE 7 #define USB_DT_HID_SIZE 9 -/* Endpoints */ -#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ -#define USB_ENDPOINT_DIR_MASK 0x80 - -#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ -#define USB_ENDPOINT_XFER_CONTROL 0 -#define USB_ENDPOINT_XFER_ISOC 1 -#define USB_ENDPOINT_XFER_BULK 2 -#define USB_ENDPOINT_XFER_INT 3 - /* USB Packet IDs (PIDs) */ #define USB_PID_UNDEF_0 0xf0 #define USB_PID_OUT 0xe1 @@ -125,19 +75,6 @@ #define USB_PID_STALL 0x1e #define USB_PID_UNDEF_F 0x0f -/* Standard requests */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -#define USB_REQ_SET_FEATURE 0x03 -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - /* HID requests */ #define USB_REQ_GET_REPORT 0x01 #define USB_REQ_GET_IDLE 0x02 @@ -176,66 +113,38 @@ */ /* - * Hub request types - */ - -#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) -#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) - -/* - * Hub Class feature numbers - */ -#define C_HUB_LOCAL_POWER 0 -#define C_HUB_OVER_CURRENT 1 - -/* * Port feature numbers */ -#define USB_PORT_FEAT_CONNECTION 0 -#define USB_PORT_FEAT_ENABLE 1 -#define USB_PORT_FEAT_SUSPEND 2 -#define USB_PORT_FEAT_OVER_CURRENT 3 -#define USB_PORT_FEAT_RESET 4 -#define USB_PORT_FEAT_POWER 8 -#define USB_PORT_FEAT_LOWSPEED 9 #define USB_PORT_FEAT_HIGHSPEED 10 -#define USB_PORT_FEAT_C_CONNECTION 16 -#define USB_PORT_FEAT_C_ENABLE 17 -#define USB_PORT_FEAT_C_SUSPEND 18 -#define USB_PORT_FEAT_C_OVER_CURRENT 19 -#define USB_PORT_FEAT_C_RESET 20 /* wPortStatus bits */ -#define USB_PORT_STAT_CONNECTION 0x0001 -#define USB_PORT_STAT_ENABLE 0x0002 -#define USB_PORT_STAT_SUSPEND 0x0004 -#define USB_PORT_STAT_OVERCURRENT 0x0008 -#define USB_PORT_STAT_RESET 0x0010 -#define USB_PORT_STAT_POWER 0x0100 -#define USB_PORT_STAT_LOW_SPEED 0x0200 -#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */ #define USB_PORT_STAT_SPEED \ (USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED) -/* wPortChange bits */ -#define USB_PORT_STAT_C_CONNECTION 0x0001 -#define USB_PORT_STAT_C_ENABLE 0x0002 -#define USB_PORT_STAT_C_SUSPEND 0x0004 -#define USB_PORT_STAT_C_OVERCURRENT 0x0008 -#define USB_PORT_STAT_C_RESET 0x0010 +/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */ +#define DeviceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) -/* wHubCharacteristics (masks) */ -#define HUB_CHAR_LPSM 0x0003 -#define HUB_CHAR_COMPOUND 0x0004 -#define HUB_CHAR_OCPM 0x0018 +#define DeviceOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8) -/* - *Hub Status & Hub Change bit masks - */ -#define HUB_STATUS_LOCAL_POWER 0x0001 -#define HUB_STATUS_OVERCURRENT 0x0002 +#define InterfaceRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) -#define HUB_CHANGE_LOCAL_POWER 0x0001 -#define HUB_CHANGE_OVERCURRENT 0x0002 +#define EndpointRequest \ + ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +#define EndpointOutRequest \ + ((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8) + +/* class requests from the USB 2.0 hub spec, table 11-15 */ +/* GetBusState and SetHubDescriptor are optional, omitted */ +#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) +#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) +#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) +#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) +#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) +#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) +#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) #endif /*_USB_DEFS_H_ */ diff --git a/include/usb/xhci.h b/include/usb/xhci.h new file mode 100644 index 0000000..1a3138b --- /dev/null +++ b/include/usb/xhci.h @@ -0,0 +1,33 @@ +/* + * xHCI host controller driver + * + * Sebastian Hesselbarth + * + * Some code borrowed from the Linux xHCI driver + * Author: Sarah Sharp + * Copyright (C) 2008 Intel Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __XHCI_HCD_H +#define __XHCI_HCD_H + +struct xhci_data { + void __iomem *regs; +}; + +int xhci_register(struct device_d *dev, struct xhci_data *data); + +#endif