]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - common/usb.c
Allow arch-specific setting of global_data in board_init_f_mem()
[karo-tx-uboot.git] / common / usb.c
index 5a49502f8e04ff4d231ac26953ed15c6c9292fc7..fbaf8ecbe232ce63170228727df1ed7fa23b060f 100644 (file)
@@ -28,6 +28,7 @@
  */
 #include <common.h>
 #include <command.h>
+#include <dm.h>
 #include <asm/processor.h>
 #include <linux/compiler.h>
 #include <linux/ctype.h>
 
 #define USB_BUFSIZ     512
 
-static struct usb_device usb_dev[USB_MAX_DEVICE];
-static int dev_index;
 static int asynch_allowed;
-
 char usb_started; /* flag for the started/stopped USB status */
 
+#ifndef CONFIG_DM_USB
+static struct usb_device usb_dev[USB_MAX_DEVICE];
+static int dev_index;
+
 #ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
 #define CONFIG_USB_MAX_CONTROLLER_COUNT 1
 #endif
@@ -146,6 +148,32 @@ int usb_stop(void)
        return 0;
 }
 
+/******************************************************************************
+ * Detect if a USB device has been plugged or unplugged.
+ */
+int usb_detect_change(void)
+{
+       int i, j;
+       int change = 0;
+
+       for (j = 0; j < USB_MAX_DEVICE; j++) {
+               for (i = 0; i < usb_dev[j].maxchild; i++) {
+                       struct usb_port_status status;
+
+                       if (usb_get_port_status(&usb_dev[j], i + 1,
+                                               &status) < 0)
+                               /* USB request failed */
+                               continue;
+
+                       if (le16_to_cpu(status.wPortChange) &
+                           USB_PORT_STAT_C_CONNECTION)
+                               change++;
+               }
+       }
+
+       return change;
+}
+
 /*
  * disables the asynch behaviour of the control message. This is used for data
  * transfers that uses the exclusiv access to the control and bulk messages.
@@ -158,6 +186,7 @@ int usb_disable_asynch(int disable)
        asynch_allowed = !disable;
        return old_value;
 }
+#endif /* !CONFIG_DM_USB */
 
 
 /*-------------------------------------------------------------------
@@ -189,6 +218,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe,
                        void *data, unsigned short size, int timeout)
 {
        ALLOC_CACHE_ALIGN_BUFFER(struct devrequest, setup_packet, 1);
+       int err;
 
        if ((timeout == 0) && (!asynch_allowed)) {
                /* request for a asynch control pipe is not allowed */
@@ -206,8 +236,9 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe,
              request, requesttype, value, index, size);
        dev->status = USB_ST_NOT_PROC; /*not yet processed */
 
-       if (submit_control_msg(dev, pipe, data, size, setup_packet) < 0)
-               return -EIO;
+       err = submit_control_msg(dev, pipe, data, size, setup_packet);
+       if (err < 0)
+               return err;
        if (timeout == 0)
                return (int)size;
 
@@ -821,6 +852,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
  * the USB device are static allocated [USB_MAX_DEVICE].
  */
 
+#ifndef CONFIG_DM_USB
 
 /* returns a pointer to the device with the index [index].
  * if the device is not assigned (dev->devnum==-1) returns NULL
@@ -877,31 +909,54 @@ __weak int usb_alloc_device(struct usb_device *udev)
 {
        return 0;
 }
+#endif /* !CONFIG_DM_USB */
 
-int usb_legacy_port_reset(struct usb_device *hub, int portnr)
+static int usb_hub_port_reset(struct usb_device *dev, struct usb_device *hub)
 {
        if (hub) {
                unsigned short portstatus;
                int err;
 
                /* reset the port for the second time */
-               err = legacy_hub_port_reset(hub, portnr - 1, &portstatus);
+               err = legacy_hub_port_reset(hub, dev->portnr - 1, &portstatus);
                if (err < 0) {
-                       printf("\n     Couldn't reset port %i\n", portnr);
+                       printf("\n     Couldn't reset port %i\n", dev->portnr);
                        return err;
                }
        } else {
-               usb_reset_root_port();
+               usb_reset_root_port(dev);
        }
 
        return 0;
 }
 
-static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
+static int get_descriptor_len(struct usb_device *dev, int len, int expect_len)
 {
        __maybe_unused struct usb_device_descriptor *desc;
        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
+       int err;
+
+       desc = (struct usb_device_descriptor *)tmpbuf;
 
+       err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, len);
+       if (err < expect_len) {
+               if (err < 0) {
+                       printf("unable to get device descriptor (error=%d)\n",
+                               err);
+                       return err;
+               } else {
+                       printf("USB device descriptor short read (expected %i, got %i)\n",
+                               expect_len, err);
+                       return -EIO;
+               }
+       }
+       memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor));
+
+       return 0;
+}
+
+static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
+{
        /*
         * This is a Windows scheme of initialization sequence, with double
         * reset of the device (Linux uses the same sequence)
@@ -915,19 +970,22 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
         * send 64-byte GET-DEVICE-DESCRIPTOR request.  Since the descriptor is
         * only 18 bytes long, this will terminate with a short packet.  But if
         * the maxpacket size is 8 or 16 the device may be waiting to transmit
-        * some more, or keeps on retransmitting the 8 byte header. */
+        * some more, or keeps on retransmitting the 8 byte header.
+        */
 
-       desc = (struct usb_device_descriptor *)tmpbuf;
-       dev->descriptor.bMaxPacketSize0 = 64;       /* Start off at 64 bytes  */
-       /* Default to 64 byte max packet size */
-       dev->maxpacketsize = PACKET_SIZE_64;
-       dev->epmaxpacketin[0] = 64;
-       dev->epmaxpacketout[0] = 64;
+       if (dev->speed == USB_SPEED_LOW) {
+               dev->descriptor.bMaxPacketSize0 = 8;
+               dev->maxpacketsize = PACKET_SIZE_8;
+       } else {
+               dev->descriptor.bMaxPacketSize0 = 64;
+               dev->maxpacketsize = PACKET_SIZE_64;
+       }
+       dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
+       dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
 
        if (do_read) {
                int err;
 
-               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64);
                /*
                 * Validate we've received only at least 8 bytes, not that we've
                 * received the entire descriptor. The reasoning is:
@@ -943,18 +1001,9 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
                 * bytes of data with the maxpacket guessed as 64 (above) yields a
                 * request for 1 packet.
                 */
-               if (err < 8) {
-                       if (err < 0) {
-                               printf("unable to get device descriptor (error=%d)\n",
-                                      err);
-                               return err;
-                       } else {
-                               printf("USB device descriptor short read (expected %i, got %i)\n",
-                                      (int)sizeof(dev->descriptor), err);
-                               return -EIO;
-                       }
-               }
-               memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor));
+               err = get_descriptor_len(dev, 64, 8);
+               if (err)
+                       return err;
        }
 
        dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
@@ -980,19 +1029,10 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
        return 0;
 }
 
-/*
- * By the time we get here, the device has gotten a new device ID
- * and is in the default state. We need to identify the thing and
- * get the ball rolling..
- *
- * Returns 0 for success, != 0 for error.
- */
-int usb_new_device(struct usb_device *dev)
+static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read,
+                             struct usb_device *parent)
 {
-       bool do_read = true;
-       int addr, err;
-       int tmp;
-       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
+       int err;
 
        /*
         * Allocate usb 3.0 device context.
@@ -1000,28 +1040,15 @@ int usb_new_device(struct usb_device *dev)
         * and related data structures first. This call does that.
         * Refer to sec 4.3.2 in xHCI spec rev1.0
         */
-       if (usb_alloc_device(dev)) {
+       err = usb_alloc_device(dev);
+       if (err) {
                printf("Cannot allocate device context to get SLOT_ID\n");
-               return -1;
+               return err;
        }
-
-       /* We still haven't set the Address yet */
-       addr = dev->devnum;
-       dev->devnum = 0;
-
-       /*
-        * XHCI needs to issue a Address device command to setup
-        * proper device context structures, before it can interact
-        * with the device. So a get_descriptor will fail before any
-        * of that is done for XHCI unlike EHCI.
-        */
-#ifdef CONFIG_USB_XHCI
-       do_read = false;
-#endif
        err = usb_setup_descriptor(dev, do_read);
        if (err)
                return err;
-       err = usb_legacy_port_reset(dev->parent, dev->portnr);
+       err = usb_hub_port_reset(dev, parent);
        if (err)
                return err;
 
@@ -1032,45 +1059,49 @@ int usb_new_device(struct usb_device *dev)
        if (err < 0) {
                printf("\n      USB device not accepting new address " \
                        "(error=%lX)\n", dev->status);
-               return -EIO;
+               return err;
        }
 
        mdelay(10);     /* Let the SET_ADDRESS settle */
 
-       tmp = sizeof(dev->descriptor);
+       return 0;
+}
+
+int usb_select_config(struct usb_device *dev)
+{
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
+       int err;
+
+       err = get_descriptor_len(dev, USB_DT_DEVICE_SIZE, USB_DT_DEVICE_SIZE);
+       if (err)
+               return err;
 
-       err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
-                                tmpbuf, sizeof(dev->descriptor));
-       if (err < tmp) {
-               if (err < 0)
-                       printf("unable to get device descriptor (error=%d)\n",
-                              err);
-               else
-                       printf("USB device descriptor short read " \
-                               "(expected %i, got %i)\n", tmp, err);
-               return -EIO;
-       }
-       memcpy(&dev->descriptor, tmpbuf, sizeof(dev->descriptor));
        /* correct le values */
        le16_to_cpus(&dev->descriptor.bcdUSB);
        le16_to_cpus(&dev->descriptor.idVendor);
        le16_to_cpus(&dev->descriptor.idProduct);
        le16_to_cpus(&dev->descriptor.bcdDevice);
+
        /* only support for one config for now */
        err = usb_get_configuration_no(dev, tmpbuf, 0);
        if (err < 0) {
                printf("usb_new_device: Cannot read configuration, " \
                       "skipping device %04x:%04x\n",
                       dev->descriptor.idVendor, dev->descriptor.idProduct);
-               return -EIO;
+               return err;
        }
        usb_parse_config(dev, tmpbuf, 0);
        usb_set_maxpacket(dev);
-       /* we set the default configuration here */
-       if (usb_set_configuration(dev, dev->config.desc.bConfigurationValue)) {
+       /*
+        * we set the default configuration here
+        * This seems premature. If the driver wants a different configuration
+        * it will need to select itself.
+        */
+       err = usb_set_configuration(dev, dev->config.desc.bConfigurationValue);
+       if (err < 0) {
                printf("failed to set default configuration " \
                        "len %d, status %lX\n", dev->act_len, dev->status);
-               return -EIO;
+               return err;
        }
        debug("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
              dev->descriptor.iManufacturer, dev->descriptor.iProduct,
@@ -1090,10 +1121,62 @@ int usb_new_device(struct usb_device *dev)
        debug("Manufacturer %s\n", dev->mf);
        debug("Product      %s\n", dev->prod);
        debug("SerialNumber %s\n", dev->serial);
-       /* now prode if the device is a hub */
-       usb_hub_probe(dev, 0);
+
+       return 0;
+}
+
+int usb_setup_device(struct usb_device *dev, bool do_read,
+                    struct usb_device *parent)
+{
+       int addr;
+       int ret;
+
+       /* We still haven't set the Address yet */
+       addr = dev->devnum;
+       dev->devnum = 0;
+
+       ret = usb_prepare_device(dev, addr, do_read, parent);
+       if (ret)
+               return ret;
+       ret = usb_select_config(dev);
+
+       return ret;
+}
+
+#ifndef CONFIG_DM_USB
+/*
+ * By the time we get here, the device has gotten a new device ID
+ * and is in the default state. We need to identify the thing and
+ * get the ball rolling..
+ *
+ * Returns 0 for success, != 0 for error.
+ */
+int usb_new_device(struct usb_device *dev)
+{
+       bool do_read = true;
+       int err;
+
+       /*
+        * XHCI needs to issue a Address device command to setup
+        * proper device context structures, before it can interact
+        * with the device. So a get_descriptor will fail before any
+        * of that is done for XHCI unlike EHCI.
+        */
+#ifdef CONFIG_USB_XHCI
+       do_read = false;
+#endif
+       err = usb_setup_device(dev, do_read, dev->parent);
+       if (err)
+               return err;
+
+       /* Now probe if the device is a hub */
+       err = usb_hub_probe(dev, 0);
+       if (err < 0)
+               return err;
+
        return 0;
 }
+#endif
 
 __weak
 int board_usb_init(int index, enum usb_init_type init)
@@ -1106,4 +1189,14 @@ int board_usb_cleanup(int index, enum usb_init_type init)
 {
        return 0;
 }
+
+bool usb_device_has_child_on_port(struct usb_device *parent, int port)
+{
+#ifdef CONFIG_DM_USB
+       return false;
+#else
+       return parent->children[port] != NULL;
+#endif
+}
+
 /* EOF */