]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/usb_hub.c
biosemu: include <asm/io.h> header
[karo-tx-uboot.git] / common / usb_hub.c
index 576e0e6a932eb68c719850bf4d4cc2d861370997..b5eeb62fbe55ecab41fe74abd3dea5209695a01f 100644 (file)
@@ -43,6 +43,7 @@
 #include <common.h>
 #include <command.h>
 #include <asm/processor.h>
+#include <asm/unaligned.h>
 #include <linux/ctype.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
@@ -109,6 +110,7 @@ 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 */
@@ -116,8 +118,10 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
        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);
-               wait_ms(hub->desc.bPwrOn2PwrGood * 2);
        }
+
+       /* Wait at least 100 msec for power to become stable */
+       mdelay(max(pgood_delay, (unsigned)100));
 }
 
 void usb_hub_reset(void)
@@ -150,22 +154,22 @@ int hub_port_reset(struct usb_device *dev, int port,
                        unsigned short *portstat)
 {
        int tries;
-       struct usb_port_status portsts;
+       ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
        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);
+               mdelay(200);
 
-               if (usb_get_port_status(dev, port + 1, &portsts) < 0) {
+               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);
+               portstatus = le16_to_cpu(portsts->wPortStatus);
+               portchange = le16_to_cpu(portsts->wPortChange);
 
                USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
                                portstatus, portchange,
@@ -184,7 +188,7 @@ int hub_port_reset(struct usb_device *dev, int port,
                if (portstatus & USB_PORT_STAT_ENABLE)
                        break;
 
-               wait_ms(200);
+               mdelay(200);
        }
 
        if (tries == MAX_TRIES) {
@@ -203,19 +207,19 @@ int hub_port_reset(struct usb_device *dev, int port,
 void usb_hub_port_connect_change(struct usb_device *dev, int port)
 {
        struct usb_device *usb;
-       struct usb_port_status portsts;
+       ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
        unsigned short portstatus;
 
        /* Check status */
-       if (usb_get_port_status(dev, port + 1, &portsts) < 0) {
+       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);
+       portstatus = le16_to_cpu(portsts->wPortStatus);
        USB_HUB_PRINTF("portstatus %x, change %x, %s\n",
                        portstatus,
-                       le16_to_cpu(portsts.wPortChange),
+                       le16_to_cpu(portsts->wPortChange),
                        portspeed(portstatus));
 
        /* Clear the connection change status */
@@ -229,7 +233,7 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
                if (!(portstatus & USB_PORT_STAT_CONNECTION))
                        return;
        }
-       wait_ms(200);
+       mdelay(200);
 
        /* Reset the port */
        if (hub_port_reset(dev, port, &portstatus) < 0) {
@@ -237,10 +241,10 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
                return;
        }
 
-       wait_ms(200);
+       mdelay(200);
 
        /* Allocate a new device struct for it */
-       usb = usb_alloc_new_device();
+       usb = usb_alloc_new_device(dev->controller);
 
        if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                usb->speed = USB_SPEED_HIGH;
@@ -255,6 +259,8 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
        /* Run it through the hoops (find a driver, etc) */
        if (usb_new_device(usb)) {
                /* Woops, disable the port */
+               usb_free_device();
+               dev->children[port] = NULL;
                USB_HUB_PRINTF("hub: disabling port %d\n", port + 1);
                usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE);
        }
@@ -264,7 +270,9 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
 static int usb_hub_configure(struct usb_device *dev)
 {
        int i;
-       unsigned char buffer[USB_BUFSIZ], *bitmap;
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ);
+       unsigned char *bitmap;
+       short hubCharacteristics;
        struct usb_hub_descriptor *descriptor;
        struct usb_hub_device *hub;
 #ifdef USB_HUB_DEBUG
@@ -300,8 +308,9 @@ static int usb_hub_configure(struct usb_device *dev)
        }
        memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength);
        /* adjust 16bit values */
-       hub->desc.wHubCharacteristics =
-                               le16_to_cpu(descriptor->wHubCharacteristics);
+       put_unaligned(le16_to_cpu(get_unaligned(
+                       &descriptor->wHubCharacteristics)),
+                       &hub->desc.wHubCharacteristics);
        /* set the bitmap */
        bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0];
        /* devices not removable by default */
@@ -318,7 +327,8 @@ static int usb_hub_configure(struct usb_device *dev)
        dev->maxchild = descriptor->bNbrPorts;
        USB_HUB_PRINTF("%d ports detected\n", dev->maxchild);
 
-       switch (hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) {
+       hubCharacteristics = get_unaligned(&hub->desc.wHubCharacteristics);
+       switch (hubCharacteristics & HUB_CHAR_LPSM) {
        case 0x00:
                USB_HUB_PRINTF("ganged power switching\n");
                break;
@@ -331,12 +341,12 @@ static int usb_hub_configure(struct usb_device *dev)
                break;
        }
 
-       if (hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND)
+       if (hubCharacteristics & 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) {
+       switch (hubCharacteristics & HUB_CHAR_OCPM) {
        case 0x00:
                USB_HUB_PRINTF("global over-current protection\n");
                break;
@@ -386,16 +396,39 @@ static int usb_hub_configure(struct usb_device *dev)
        usb_hub_power_on(hub);
 
        for (i = 0; i < dev->maxchild; i++) {
-               struct usb_port_status portsts;
+               ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
                unsigned short portstatus, portchange;
+               int ret;
+               ulong start = get_timer(0);
+
+               /*
+                * Wait for (whichever finishes first)
+                *  - A maximum of 10 seconds
+                *    This is a purely observational value driven by connecting
+                *    a few broken pen drives and taking the max * 1.5 approach
+                *  - connection_change and connection state to report same
+                *    state
+                */
+               do {
+                       ret = usb_get_port_status(dev, i + 1, portsts);
+                       if (ret < 0) {
+                               USB_HUB_PRINTF("get_port_status failed\n");
+                               break;
+                       }
+
+                       portstatus = le16_to_cpu(portsts->wPortStatus);
+                       portchange = le16_to_cpu(portsts->wPortChange);
 
-               if (usb_get_port_status(dev, i + 1, &portsts) < 0) {
-                       USB_HUB_PRINTF("get_port_status failed\n");
+                       if ((portchange & USB_PORT_STAT_C_CONNECTION) ==
+                               (portstatus & USB_PORT_STAT_CONNECTION))
+                               break;
+
+                       mdelay(100);
+               } while (get_timer(start) < CONFIG_SYS_HZ * 10);
+
+               if (ret < 0)
                        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);