>From 1d9326d5db29f2dca8639e8929eac780e1bd29a3 Mon Sep 17 00:00:00 2001
From: Marek Vasut <marex@denx.de>
Date: Sat, 2 Apr 2016 00:20:37 +0200
Subject: [PATCH] Revert "usb: Change power-on / scanning timeout handling"

This reverts commit c998da0d67091f800933e59b8693913764a9e8f4.
---
 common/usb_hub.c | 317 +++++++++++++++++--------------------------------------
 include/usb.h    |   4 -
 2 files changed, 94 insertions(+), 227 deletions(-)

diff --git a/common/usb_hub.c b/common/usb_hub.c
index e6a2cdb..d621f50 100644
--- a/common/usb_hub.c
+++ b/common/usb_hub.c
@@ -30,7 +30,6 @@
 #include <asm/processor.h>
 #include <asm/unaligned.h>
 #include <linux/ctype.h>
-#include <linux/list.h>
 #include <asm/byteorder.h>
 #ifdef CONFIG_SANDBOX
 #include <asm/state.h>
@@ -50,19 +49,9 @@ DECLARE_GLOBAL_DATA_PTR;
 #define HUB_SHORT_RESET_TIME	20
 #define HUB_LONG_RESET_TIME	200
 
-#define PORT_OVERCURRENT_MAX_SCAN_COUNT		3
-
-struct usb_device_scan {
-	struct usb_device *dev;		/* USB hub device to scan */
-	struct usb_hub_device *hub;	/* USB hub struct */
-	int port;			/* USB port to scan */
-	struct list_head list;
-};
-
 /* TODO(sjg@chromium.org): Remove this when CONFIG_DM_USB is defined */
 static struct usb_hub_device hub_dev[USB_MAX_HUB];
 static int usb_hub_index;
-static LIST_HEAD(usb_scan_list);
 
 __weak void usb_hub_reset_devices(int port)
 {
@@ -120,15 +109,6 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
 		debug("port %d returns %lX\n", i + 1, dev->status);
 	}
 
-#ifdef CONFIG_SANDBOX
-	/*
-	 * Don't set timeout / delay values here. This results
-	 * in these values still being reset to 0.
-	 */
-	if (state_get_skip_delays())
-		return;
-#endif
-
 	/*
 	 * Wait for power to become stable,
 	 * plus spec-defined max time for device to connect
@@ -140,30 +120,12 @@ static void usb_hub_power_on(struct usb_hub_device *hub)
 		pgood_delay = max(pgood_delay,
 			          (unsigned)simple_strtol(env, NULL, 0));
 	debug("pgood_delay=%dms\n", pgood_delay);
-
-	/*
-	 * Do a minimum delay of the larger value of 100ms or pgood_delay
-	 * so that the power can stablize before the devices are queried
-	 */
-	hub->query_delay = get_timer(0) + max(100, (int)pgood_delay);
-
-	/*
-	 * Record the power-on timeout here. The max. delay (timeout)
-	 * will be done based on this value in the USB port loop in
-	 * usb_hub_configure() later.
-	 */
-	hub->connect_timeout = hub->query_delay + 1000;
-	debug("devnum=%d poweron: query_delay=%d connect_timeout=%d\n",
-	      dev->devnum, max(100, (int)pgood_delay),
-	      max(100, (int)pgood_delay) + 1000);
+	mdelay(pgood_delay + 1000);
 }
 
 void usb_hub_reset(void)
 {
 	usb_hub_index = 0;
-
-	/* Zero out global hub_dev in case its re-used again */
-	memset(hub_dev, 0, sizeof(hub_dev));
 }
 
 static struct usb_hub_device *usb_hub_allocate(void)
@@ -370,168 +332,6 @@ int usb_hub_port_connect_change(struct usb_device *dev, int port)
 	return ret;
 }
 
-static int usb_scan_port(struct usb_device_scan *usb_scan)
-{
-	ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
-	unsigned short portstatus;
-	unsigned short portchange;
-	struct usb_device *dev;
-	struct usb_hub_device *hub;
-	int ret = 0;
-	int i;
-
-	dev = usb_scan->dev;
-	hub = usb_scan->hub;
-	i = usb_scan->port;
-
-	/*
-	 * Don't talk to the device before the query delay is expired.
-	 * This is needed for voltages to stabalize.
-	 */
-	if (get_timer(0) < hub->query_delay)
-		return 0;
-
-	ret = usb_get_port_status(dev, i + 1, portsts);
-	if (ret < 0) {
-		debug("get_port_status failed\n");
-		if (get_timer(0) >= hub->connect_timeout) {
-			debug("devnum=%d port=%d: timeout\n",
-			      dev->devnum, i + 1);
-			/* Remove this device from scanning list */
-			list_del(&usb_scan->list);
-			free(usb_scan);
-			return 0;
-		}
-	}
-
-	portstatus = le16_to_cpu(portsts->wPortStatus);
-	portchange = le16_to_cpu(portsts->wPortChange);
-	debug("Port %d Status %X Change %X\n", i + 1, portstatus, portchange);
-
-	/* No connection change happened, wait a bit more. */
-	if (!(portchange & USB_PORT_STAT_C_CONNECTION)) {
-		if (get_timer(0) >= hub->connect_timeout) {
-			debug("devnum=%d port=%d: timeout\n",
-			      dev->devnum, i + 1);
-			/* Remove this device from scanning list */
-			list_del(&usb_scan->list);
-			free(usb_scan);
-			return 0;
-		}
-		return 0;
-	}
-
-	/* Test if the connection came up, and if not exit */
-	if (!(portstatus & USB_PORT_STAT_CONNECTION))
-		return 0;
-
-	/* A new USB device is ready at this point */
-	debug("devnum=%d port=%d: USB dev found\n", dev->devnum, i + 1);
-
-	usb_hub_port_connect_change(dev, i);
-
-	if (portchange & USB_PORT_STAT_C_ENABLE) {
-		debug("port %d enable change, status %x\n", 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
-		 */
-		if (!(portstatus & USB_PORT_STAT_ENABLE) &&
-		    (portstatus & USB_PORT_STAT_CONNECTION) &&
-		    usb_device_has_child_on_port(dev, i)) {
-			debug("already running port %i disabled by hub (EMI?), 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);
-		usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND);
-	}
-
-	if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
-		debug("port %d over-current change\n", i + 1);
-		usb_clear_port_feature(dev, i + 1,
-				       USB_PORT_FEAT_C_OVER_CURRENT);
-		/* Only power-on this one port */
-		usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
-		hub->overcurrent_count[i]++;
-
-		/*
-		 * If the max-scan-count is not reached, return without removing
-		 * the device from scan-list. This will re-issue a new scan.
-		 */
-		if (hub->overcurrent_count[i] <=
-		    PORT_OVERCURRENT_MAX_SCAN_COUNT)
-			return 0;
-
-		/* Otherwise the device will get removed */
-		printf("Port %d over-current occured %d times\n", i + 1,
-		       hub->overcurrent_count[i]);
-	}
-
-	if (portchange & USB_PORT_STAT_C_RESET) {
-		debug("port %d reset change\n", i + 1);
-		usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET);
-	}
-
-	/*
-	 * We're done with this device, so let's remove this device from
-	 * scanning list
-	 */
-	list_del(&usb_scan->list);
-	free(usb_scan);
-
-	return 0;
-}
-
-static int usb_device_list_scan(void)
-{
-	struct usb_device_scan *usb_scan;
-	struct usb_device_scan *tmp;
-	static int running;
-	int ret = 0;
-
-	/* Only run this loop once for each controller */
-	if (running)
-		return 0;
-
-	running = 1;
-
-	while (1) {
-		/* We're done, once the list is empty again */
-		if (list_empty(&usb_scan_list))
-			goto out;
-
-		list_for_each_entry_safe(usb_scan, tmp, &usb_scan_list, list) {
-			int ret;
-
-			/* Scan this port */
-			ret = usb_scan_port(usb_scan);
-			if (ret)
-				goto out;
-		}
-	}
-
-out:
-	/*
-	 * This USB controller has finished scanning all its connected
-	 * USB devices. Set "running" back to 0, so that other USB controllers
-	 * will scan their devices too.
-	 */
-	running = 0;
-
-	return ret;
-}
 
 static int usb_hub_configure(struct usb_device *dev)
 {
@@ -666,33 +466,104 @@ static int usb_hub_configure(struct usb_device *dev)
 	for (i = 0; i < dev->maxchild; i++)
 		usb_hub_reset_devices(i + 1);
 
-	/*
-	 * Only add the connected USB devices, including potential hubs,
-	 * to a scanning list. This list will get scanned and devices that
-	 * are detected (either via port connected or via port timeout)
-	 * will get removed from this list. Scanning of the devices on this
-	 * list will continue until all devices are removed.
-	 */
 	for (i = 0; i < dev->maxchild; i++) {
-		struct usb_device_scan *usb_scan;
+		ALLOC_CACHE_ALIGN_BUFFER(struct usb_port_status, portsts, 1);
+		unsigned short portstatus, portchange;
+		int ret;
+		ulong start = get_timer(0);
+		uint delay = CONFIG_SYS_HZ;
+
+#ifdef CONFIG_SANDBOX
+		if (state_get_skip_delays())
+			delay = 0;
+#endif
+#ifdef CONFIG_DM_USB
+		debug("\n\nScanning '%s' port %d\n", dev->dev->name, i + 1);
+#else
+		debug("\n\nScanning port %d\n", i + 1);
+#endif
+		/*
+		 * 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) {
+				debug("get_port_status failed\n");
+				break;
+			}
+
+			portstatus = le16_to_cpu(portsts->wPortStatus);
+			portchange = le16_to_cpu(portsts->wPortChange);
+
+			/* No connection change happened, wait a bit more. */
+			if (!(portchange & USB_PORT_STAT_C_CONNECTION))
+				continue;
+
+			/* Test if the connection came up, and if so, exit. */
+			if (portstatus & USB_PORT_STAT_CONNECTION)
+				break;
+
+		} while (get_timer(start) < delay);
+
+		if (ret < 0)
+			continue;
 
-		usb_scan = calloc(1, sizeof(*usb_scan));
-		if (!usb_scan) {
-			printf("Can't allocate memory for USB device!\n");
-			return -ENOMEM;
+		debug("Port %d Status %X Change %X\n",
+		      i + 1, portstatus, portchange);
+
+		if (portchange & USB_PORT_STAT_C_CONNECTION) {
+			debug("port %d connection change\n", i + 1);
+			usb_hub_port_connect_change(dev, i);
+		}
+		if (portchange & USB_PORT_STAT_C_ENABLE) {
+			debug("port %d enable change, status %x\n",
+			      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 */
+			if (!(portstatus & USB_PORT_STAT_ENABLE) &&
+			     (portstatus & USB_PORT_STAT_CONNECTION) &&
+			     usb_device_has_child_on_port(dev, i)) {
+				debug("already running port %i "  \
+				      "disabled by hub (EMI?), " \
+				      "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);
+			usb_clear_port_feature(dev, i + 1,
+						USB_PORT_FEAT_SUSPEND);
 		}
-		usb_scan->dev = dev;
-		usb_scan->hub = hub;
-		usb_scan->port = i;
-		list_add_tail(&usb_scan->list, &usb_scan_list);
-	}
 
-	/*
-	 * And now call the scanning code which loops over the generated list
-	 */
-	ret = usb_device_list_scan();
+		if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+			debug("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);
+		}
 
-	return ret;
+		if (portchange & USB_PORT_STAT_C_RESET) {
+			debug("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_check(struct usb_device *dev, int ifnum)
diff --git a/include/usb.h b/include/usb.h
index 5adad36..ed2336b 100644
--- a/include/usb.h
+++ b/include/usb.h
@@ -556,10 +556,6 @@ struct usb_hub_descriptor {
 struct usb_hub_device {
 	struct usb_device *pusb_dev;
 	struct usb_hub_descriptor desc;
-
-	ulong connect_timeout;		/* Device connection timeout in ms */
-	ulong query_delay;		/* Device query delay in ms */
-	int overcurrent_count[USB_MAXCHILDREN];	/* Over-current counter */
 };
 
 #ifdef CONFIG_DM_USB
-- 
2.7.0

