9
0
Fork 0

Merge branch 'for-next/usb-host'

Conflicts:
	drivers/usb/core/Makefile
This commit is contained in:
Sascha Hauer 2014-08-07 20:34:39 +02:00
commit 45615e3ec1
22 changed files with 4706 additions and 740 deletions

View File

@ -22,20 +22,114 @@
#include <usb/usb.h>
#include <getopt.h>
/* 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("Scan for USB devices.")
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)

View File

@ -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

486
drivers/usb/core/hub.c Normal file
View File

@ -0,0 +1,486 @@
/*
* hub.c - USB hub support
*
* Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, 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 <common.h>
#include <init.h>
#include <malloc.h>
#include <errno.h>
#include <scsi.h>
#include <usb/usb.h>
#include <usb/usb_defs.h>
#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);

7
drivers/usb/core/hub.h Normal file
View File

@ -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 */

View File

@ -53,6 +53,9 @@
#include <usb/usb.h>
#include <usb/ch9.h>
#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 @@ static int usb_get_descriptor(struct usb_device *dev, unsigned char type,
*
* 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 @@ static int usb_new_device(struct usb_device *dev)
/* 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 @@ static int usb_new_device(struct usb_device *dev)
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 @@ static int usb_new_device(struct usb_device *dev)
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 @@ err_out:
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 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
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;

9
drivers/usb/core/usb.h Normal file
View File

@ -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 */

View File

@ -23,3 +23,21 @@ config USB_OHCI_AT91
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.

View File

@ -3,3 +3,5 @@ obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o
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

View File

@ -57,66 +57,66 @@ struct ehci_priv {
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 @@ fail:
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 <usb/ulpi.h>
/*
@ -503,12 +493,12 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
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 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
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 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
}
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 @@ static int ehci_detect(struct device_d *dev)
{
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)

View File

@ -18,26 +18,12 @@
#ifndef USB_EHCI_H
#define USB_EHCI_H
#include <io.h>
#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 @@ struct ehci_hcor {
#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))

1509
drivers/usb/host/xhci-hcd.c Normal file

File diff suppressed because it is too large Load Diff

647
drivers/usb/host/xhci-hub.c Normal file
View File

@ -0,0 +1,647 @@
/*
* xHCI USB 3.0 Root Hub
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* 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 <asm/mmu.h>
#include <clock.h>
#include <common.h>
#include <io.h>
#include <linux/err.h>
#include <usb/usb.h>
#include <usb/xhci.h>
#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;
}

View File

@ -0,0 +1,45 @@
/*
* PCI driver for xHCI controllers
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* 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 <common.h>
#include <init.h>
#include <io.h>
#include <linux/pci.h>
#include <usb/xhci.h>
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);

1279
drivers/usb/host/xhci.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
#include <driver.h>
#include <usb/usb.h>
#include <usb/ehci.h>
#include <regulator.h>
#include <usb/chipidea-imx.h>
#include <usb/ulpi.h>
#include <usb/fsl_usb2.h>
@ -31,9 +32,12 @@ struct imx_chipidea {
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 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
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 @@ static int imx_chipidea_probe_dt(struct imx_chipidea *ci)
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 @@ static int imx_chipidea_probe(struct device_d *dev)
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 @@ static int imx_chipidea_probe(struct device_d *dev)
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;
};

View File

@ -282,6 +282,22 @@ void barebox_set_hostname(const char *);
} \
)
/**
* 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; \

View File

@ -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

279
include/usb/ch11.h Normal file
View File

@ -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 <linux/types.h> /* __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 */

View File

@ -37,6 +37,7 @@
enum imx_usb_mode {
IMX_USB_MODE_HOST,
IMX_USB_MODE_DEVICE,
IMX_USB_MODE_OTG,
};
struct imxusb_platformdata {

View File

@ -24,6 +24,7 @@
#include <driver.h>
#include <usb/ch9.h>
#include <usb/ch11.h>
#include <usb/usb_defs.h>
#include <asm/byteorder.h>
@ -150,12 +151,12 @@ struct usb_host {
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_clear_halt(struct usb_device *dev, int pipe);
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 @@ void usb_rescan(int force);
/*************************************************************************
* 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 @@ enum usb_phy_interface {
USBPHY_INTERFACE_MODE_SERIAL,
USBPHY_INTERFACE_MODE_HSIC,
};
extern struct list_head usb_device_list;
#endif /*_USB_H_ */

View File

@ -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
@ -175,67 +112,39 @@
* Hub defines
*/
/*
* 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_ */

33
include/usb/xhci.h Normal file
View File

@ -0,0 +1,33 @@
/*
* xHCI host controller driver
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*
* 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