Merge branch 'for-next/usb-host'
Conflicts: drivers/usb/core/Makefile
This commit is contained in:
commit
45615e3ec1
108
commands/usb.c
108
commands/usb.c
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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);
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
|
@ -37,6 +37,7 @@
|
|||
enum imx_usb_mode {
|
||||
IMX_USB_MODE_HOST,
|
||||
IMX_USB_MODE_DEVICE,
|
||||
IMX_USB_MODE_OTG,
|
||||
};
|
||||
|
||||
struct imxusb_platformdata {
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue