]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/usb_hub.c
dm: exynos: dts: Adjust device tree files for U-Boot
[karo-tx-uboot.git] / common / usb_hub.c
index 0d79ec3ea84abfc8d61988cbfc30c8ba17cec60e..c416e5e0b31790e2bc7570fd113f7b52bb2a4502 100644 (file)
@@ -1,5 +1,4 @@
 /*
- *
  * Most of this source has been derived from the Linux USB
  * project:
  * (C) Copyright Linus Torvalds 1999
  * Adapted for U-Boot:
  * (C) Copyright 2001 Denis Peter, MPL AG Switzerland
  *
- * 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 as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- *
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 /****************************************************************************
 static struct usb_hub_device hub_dev[USB_MAX_HUB];
 static int usb_hub_index;
 
+__weak void usb_hub_reset_devices(int port)
+{
+       return;
+}
 
 static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
 {
@@ -124,7 +110,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
                ret = usb_get_port_status(dev, i + 1, portsts);
                if (ret < 0) {
                        debug("port %d: get_port_status failed\n", i + 1);
-                       return;
+                       continue;
                }
 
                /*
@@ -139,7 +125,7 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
                portstatus = le16_to_cpu(portsts->wPortStatus);
                if (portstatus & (USB_PORT_STAT_POWER << 1)) {
                        debug("port %d: Port power change failed\n", i + 1);
-                       return;
+                       continue;
                }
        }
 
@@ -148,8 +134,11 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
                debug("port %d returns %lX\n", i + 1, dev->status);
        }
 
-       /* Wait at least 100 msec for power to become stable */
-       mdelay(max(pgood_delay, (unsigned)100));
+       /*
+        * Wait for power to become stable,
+        * plus spec-defined max time for device to connect
+        */
+       mdelay(pgood_delay + 1000);
 }
 
 void usb_hub_reset(void)
@@ -220,9 +209,22 @@ int hub_port_reset(struct usb_device *dev, int port,
                      (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;
+               /*
+                * Perhaps we should check for the following here:
+                * - C_CONNECTION hasn't been set.
+                * - CONNECTION is still set.
+                *
+                * Doing so would ensure that the device is still connected
+                * to the bus, and hasn't been unplugged or replaced while the
+                * USB bus reset was going on.
+                *
+                * However, if we do that, then (at least) a San Disk Ultra
+                * USB 3.0 16GB device fails to reset on (at least) an NVIDIA
+                * Tegra Jetson TK1 board. For some reason, the device appears
+                * to briefly drop off the bus when this second bus reset is
+                * executed, yet if we retry this loop, it'll eventually come
+                * back after another reset or two.
+                */
 
                if (portstatus & USB_PORT_STAT_ENABLE)
                        break;
@@ -316,7 +318,7 @@ void usb_hub_port_connect_change(struct usb_device *dev, int port)
 
 static int usb_hub_configure(struct usb_device *dev)
 {
-       int i;
+       int i, length;
        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, USB_BUFSIZ);
        unsigned char *bitmap;
        short hubCharacteristics;
@@ -337,20 +339,14 @@ static int usb_hub_configure(struct usb_device *dev)
        }
        descriptor = (struct usb_hub_descriptor *)buffer;
 
-       /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */
-       i = descriptor->bLength;
-       if (i > USB_BUFSIZ) {
-               debug("usb_hub_configure: failed to get hub " \
-                     "descriptor - too long: %d\n", descriptor->bLength);
-               return -1;
-       }
+       length = min(descriptor->bLength, sizeof(struct usb_hub_descriptor));
 
-       if (usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) {
+       if (usb_get_hub_descriptor(dev, buffer, length) < 0) {
                debug("usb_hub_configure: failed to get hub " \
                      "descriptor 2nd giving up %lX\n", dev->status);
                return -1;
        }
-       memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength);
+       memcpy((unsigned char *)&hub->desc, buffer, length);
        /* adjust 16bit values */
        put_unaligned(le16_to_cpu(get_unaligned(
                        &descriptor->wHubCharacteristics)),
@@ -440,6 +436,14 @@ static int usb_hub_configure(struct usb_device *dev)
              "" : "no ");
        usb_hub_power_on(hub);
 
+       /*
+        * Reset any devices that may be in a bad state when applying
+        * the power.  This is a __weak function.  Resetting of the devices
+        * should occur in the board file of the device.
+        */
+       for (i = 0; i < dev->maxchild; i++)
+               usb_hub_reset_devices(i + 1);
+
        for (i = 0; i < dev->maxchild; i++) {
                ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
                unsigned short portstatus, portchange;
@@ -485,7 +489,11 @@ static int usb_hub_configure(struct usb_device *dev)
                              i + 1, portstatus);
                        usb_clear_port_feature(dev, i + 1,
                                                USB_PORT_FEAT_C_ENABLE);
-
+                       /*
+                        * The following hack causes a ghost device problem
+                        * to Faraday EHCI
+                        */
+#ifndef CONFIG_USB_EHCI_FARADAY
                        /* 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 */
@@ -497,6 +505,7 @@ static int usb_hub_configure(struct usb_device *dev)
                                      "re-enabling...\n", i + 1);
                                      usb_hub_port_connect_change(dev, i);
                        }
+#endif
                }
                if (portstatus & USB_PORT_STAT_SUSPEND) {
                        debug("port %d suspend change\n", i + 1);