/*
- *
* 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+
*/
/****************************************************************************
#include <asm/4xx_pci.h>
#endif
+#ifndef CONFIG_USB_HUB_MIN_POWER_ON_DELAY
+#define CONFIG_USB_HUB_MIN_POWER_ON_DELAY 100
+#endif
+
#define USB_BUFSIZ 512
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)
{
int ret;
dev = hub->pusb_dev;
- /* Enable power to the ports */
+
+ /*
+ * Enable power to the ports:
+ * Here we Power-cycle the ports: aka,
+ * turning them off and turning on again.
+ */
debug("enabling power on all ports\n");
for (i = 0; i < dev->maxchild; i++) {
- /*
- * Power-cycle the ports here: aka,
- * turning them off and turning on again.
- */
usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
debug("port %d returns %lX\n", i + 1, dev->status);
+ }
- /* Wait at least 2*bPwrOn2PwrGood for PP to change */
- mdelay(pgood_delay);
+ /* Wait at least 2*bPwrOn2PwrGood for PP to change */
+ mdelay(pgood_delay);
+ for (i = 0; i < dev->maxchild; i++) {
ret = usb_get_port_status(dev, i + 1, portsts);
if (ret < 0) {
debug("port %d: get_port_status failed\n", i + 1);
- return;
+ continue;
}
/*
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;
}
+ }
+ for (i = 0; i < dev->maxchild; i++) {
usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
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 */
+ mdelay(max(pgood_delay, CONFIG_USB_HUB_MIN_POWER_ON_DELAY));
}
void usb_hub_reset(void)
static inline char *portspeed(int portstatus)
{
- if (portstatus & (1 << USB_PORT_FEAT_SUPERSPEED))
- return "5 Gb/s";
- else 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";
+ char *speed_str;
+
+ switch (portstatus & USB_PORT_STAT_SPEED_MASK) {
+ case USB_PORT_STAT_SUPER_SPEED:
+ speed_str = "5 Gb/s";
+ break;
+ case USB_PORT_STAT_HIGH_SPEED:
+ speed_str = "480 Mb/s";
+ break;
+ case USB_PORT_STAT_LOW_SPEED:
+ speed_str = "1.5 Mb/s";
+ break;
+ default:
+ speed_str = "12 Mb/s";
+ break;
+ }
+
+ return speed_str;
}
int hub_port_reset(struct usb_device *dev, int port,
/* Allocate a new device struct for it */
usb = usb_alloc_new_device(dev->controller);
- if (portstatus & USB_PORT_STAT_SUPER_SPEED)
+ switch (portstatus & USB_PORT_STAT_SPEED_MASK) {
+ case USB_PORT_STAT_SUPER_SPEED:
usb->speed = USB_SPEED_SUPER;
- else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ break;
+ case USB_PORT_STAT_HIGH_SPEED:
usb->speed = USB_SPEED_HIGH;
- else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ break;
+ case USB_PORT_STAT_LOW_SPEED:
usb->speed = USB_SPEED_LOW;
- else
+ break;
+ default:
usb->speed = USB_SPEED_FULL;
+ break;
+ }
dev->children[port] = usb;
usb->parent = dev;
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;
}
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)),
"" : "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;
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 */
"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);