libpayload: Bring USB hub driver to a working state

This adds proper device attachment and detachment detection and port enable-
ment to the USB hub driver. Support for split transactions is still missing,
so this works only with USB2.0 devices on hubs in USB2.0 mode and USB1.1
devices on hubs in USB1.1 mode.

Change-Id: I80bf03f3117116a60382b87a4f84366370649915
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: http://review.coreboot.org/1080
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Nico Huber 2012-05-21 16:20:59 +02:00 committed by Stefan Reinauer
parent c95da25ac6
commit d5d024f3e5
1 changed files with 54 additions and 20 deletions

View File

@ -31,8 +31,16 @@
// assume that host_to_device is overwritten if necessary // assume that host_to_device is overwritten if necessary
#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp) #define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp)
#define PORT_RESET 0x4 /* status bits */
#define PORT_POWER 0x8 #define PORT_CONNECTION 0x1
#define PORT_ENABLE 0x2
#define PORT_RESET 0x10
/* status change bits */
#define C_PORT_CONNECTION 0x1
/* feature selectors (for setting / clearing features) */
#define SEL_PORT_RESET 0x4
#define SEL_PORT_POWER 0x8
#define SEL_C_PORT_CONNECTION 0x10
typedef struct { typedef struct {
int num_ports; int num_ports;
@ -53,28 +61,54 @@ usb_hub_destroy (usbdev_t *dev)
static void static void
usb_hub_scanport (usbdev_t *dev, int port) usb_hub_scanport (usbdev_t *dev, int port)
{ {
int timeout;
unsigned short buf[2]; unsigned short buf[2];
get_status (dev, port, DR_PORT, 4, buf); get_status (dev, port, DR_PORT, 4, buf);
int portstatus = ((buf[0] & 1) == 0); if (!(buf[1] & C_PORT_CONNECTION))
int datastatus = (HUB_INST (dev)->ports[port] == -1); /* no change */
if (portstatus == datastatus) return;
return; // no change - FIXME: read right fields for that test
if (!datastatus) { /* clear Port Connection status change */
int devno = HUB_INST (dev)->ports[port]; clear_feature (dev, port, SEL_C_PORT_CONNECTION, DR_PORT);
if (devno == -1)
fatal ("FATAL: illegal devno!\n"); int devno = HUB_INST (dev)->ports[port];
if (devno != -1) {
/* detach device, either because of re-/ or disconnect */
usb_detach_device(dev->controller, devno); usb_detach_device(dev->controller, devno);
HUB_INST (dev)->ports[port] = -1; HUB_INST (dev)->ports[port] = -1;
return;
} }
set_feature (dev, port, PORT_RESET, DR_PORT); if (!(buf[0] & PORT_CONNECTION))
mdelay (20); /* no device connected, nothing to do */
return;
/* wait 100ms for port to become stable */
mdelay (100); // usb20 spec 9.1.2
/* reset port */
set_feature (dev, port, SEL_PORT_RESET, DR_PORT);
/* wait at least 10ms (usb2.0 spec 11.5.1.5: 10ms to 20ms) */
mdelay (10);
/* wait for hub to finish reset */
timeout = 30; /* time out after 10ms (spec) + 20ms (kindly) */
do {
get_status (dev, port, DR_PORT, 4, buf);
mdelay(1); timeout--;
} while ((buf[0] & PORT_RESET) && timeout);
if (!timeout)
debug("Warning: usbhub: port reset timed out.\n");
/* wait for port to be enabled. the hub is responsible for this */
timeout = 500; /* time out after 500ms */
do {
get_status (dev, port, DR_PORT, 4, buf);
mdelay(1); timeout--;
} while (!(buf[0] & PORT_ENABLE) && timeout);
if (!timeout)
debug("Warning: usbhub: port enabling timed out.\n");
get_status (dev, port, DR_PORT, 4, buf); get_status (dev, port, DR_PORT, 4, buf);
/* bit 10 9 /* bit 10 9
* 0 0 full speed * 0 0 full speed
* 0 1 low speed * 0 1 low speed
@ -83,6 +117,9 @@ usb_hub_scanport (usbdev_t *dev, int port)
int speed = ((buf[0] >> 9) & 3) ; int speed = ((buf[0] >> 9) & 3) ;
HUB_INST (dev)->ports[port] = usb_attach_device(dev->controller, dev->address, port, speed); HUB_INST (dev)->ports[port] = usb_attach_device(dev->controller, dev->address, port, speed);
/* clear Port Connection status change */
clear_feature (dev, port, SEL_C_PORT_CONNECTION, DR_PORT);
} }
static int static int
@ -92,10 +129,7 @@ usb_hub_report_port_changes (usbdev_t *dev)
unsigned short buf[2]; unsigned short buf[2];
for (port = 1; port <= HUB_INST (dev)->num_ports; port++) { for (port = 1; port <= HUB_INST (dev)->num_ports; port++) {
get_status (dev, port, DR_PORT, 4, buf); get_status (dev, port, DR_PORT, 4, buf);
// FIXME: proper change detection if (buf[1] & C_PORT_CONNECTION)
int portstatus = ((buf[0] & 1) == 0);
int datastatus = (HUB_INST (dev)->ports[port] == -1);
if (portstatus != datastatus)
return port; return port;
} }
@ -106,7 +140,7 @@ usb_hub_report_port_changes (usbdev_t *dev)
static void static void
usb_hub_enable_port (usbdev_t *dev, int port) usb_hub_enable_port (usbdev_t *dev, int port)
{ {
set_feature (dev, port, PORT_POWER, DR_PORT); set_feature (dev, port, SEL_PORT_POWER, DR_PORT);
mdelay (20); mdelay (20);
} }