Merge 3.8-rc4 into usb-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 18 Jan 2013 17:17:17 +0000 (09:17 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 18 Jan 2013 17:17:17 +0000 (09:17 -0800)
This pulls in all of the -rc4 fixes into usb-next to sync things up.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
17 files changed:
drivers/usb/core/devices.c
drivers/usb/core/generic.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb.h
drivers/usb/host/ehci-mxc.c
drivers/usb/host/ohci-q.c
drivers/usb/host/xhci-ring.c
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/usb3503.c [new file with mode: 0644]
drivers/usb/serial/keyspan.c
drivers/usb/storage/uas.c
include/linux/platform_data/usb3503.h [new file with mode: 0644]
tools/usb/testusb.c

index cbacea9..e33224e 100644 (file)
@@ -316,17 +316,23 @@ static char *usb_dump_iad_descriptor(char *start, char *end,
  */
 static char *usb_dump_config_descriptor(char *start, char *end,
                                const struct usb_config_descriptor *desc,
-                               int active)
+                               int active, int speed)
 {
+       int mul;
+
        if (start > end)
                return start;
+       if (speed == USB_SPEED_SUPER)
+               mul = 8;
+       else
+               mul = 2;
        start += sprintf(start, format_config,
                         /* mark active/actual/current cfg. */
                         active ? '*' : ' ',
                         desc->bNumInterfaces,
                         desc->bConfigurationValue,
                         desc->bmAttributes,
-                        desc->bMaxPower * 2);
+                        desc->bMaxPower * mul);
        return start;
 }
 
@@ -342,7 +348,8 @@ static char *usb_dump_config(int speed, char *start, char *end,
        if (!config)
                /* getting these some in 2.3.7; none in 2.3.6 */
                return start + sprintf(start, "(null Cfg. desc.)\n");
-       start = usb_dump_config_descriptor(start, end, &config->desc, active);
+       start = usb_dump_config_descriptor(start, end, &config->desc, active,
+                       speed);
        for (i = 0; i < USB_MAXIADS; i++) {
                if (config->intf_assoc[i] == NULL)
                        break;
index eff2010..271e761 100644 (file)
@@ -100,7 +100,7 @@ int usb_choose_configuration(struct usb_device *udev)
                 */
 
                /* Rule out configs that draw too much bus current */
-               if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+               if (usb_get_max_power(udev, c) > udev->bus_mA) {
                        insufficient_power++;
                        continue;
                }
index 4225d5e..5f6da8b 100644 (file)
@@ -2506,7 +2506,6 @@ int usb_add_hcd(struct usb_hcd *hcd,
        }
 
        /* starting here, usbcore will pay attention to this root hub */
-       rhdev->bus_mA = min(500u, hcd->power_budget);
        if ((retval = register_root_hub(hcd)) != 0)
                goto err_register_root_hub;
 
index 957ed2c..dc6ebd1 100644 (file)
@@ -1351,6 +1351,8 @@ static int hub_configure(struct usb_hub *hub,
        unsigned int pipe;
        int maxp, ret, i;
        char *message = "out of memory";
+       unsigned unit_load;
+       unsigned full_load;
 
        hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
        if (!hub->buffer) {
@@ -1397,6 +1399,13 @@ static int hub_configure(struct usb_hub *hub,
        }
 
        wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+       if (hub_is_superspeed(hdev)) {
+               unit_load = 150;
+               full_load = 900;
+       } else {
+               unit_load = 100;
+               full_load = 500;
+       }
 
        /* FIXME for USB 3.0, skip for now */
        if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
@@ -1516,40 +1525,44 @@ static int hub_configure(struct usb_hub *hub,
                goto fail;
        }
        le16_to_cpus(&hubstatus);
+       hcd = bus_to_hcd(hdev->bus);
        if (hdev == hdev->bus->root_hub) {
-               if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
-                       hub->mA_per_port = 500;
+               if (hcd->power_budget > 0)
+                       hdev->bus_mA = hcd->power_budget;
+               else
+                       hdev->bus_mA = full_load * hdev->maxchild;
+               if (hdev->bus_mA >= full_load)
+                       hub->mA_per_port = full_load;
                else {
                        hub->mA_per_port = hdev->bus_mA;
                        hub->limited_power = 1;
                }
        } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+               int remaining = hdev->bus_mA -
+                       hub->descriptor->bHubContrCurrent;
+
                dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
                        hub->descriptor->bHubContrCurrent);
                hub->limited_power = 1;
-               if (hdev->maxchild > 0) {
-                       int remaining = hdev->bus_mA -
-                                       hub->descriptor->bHubContrCurrent;
 
-                       if (remaining < hdev->maxchild * 100)
-                               dev_warn(hub_dev,
+               if (remaining < hdev->maxchild * unit_load)
+                       dev_warn(hub_dev,
                                        "insufficient power available "
                                        "to use all downstream ports\n");
-                       hub->mA_per_port = 100;         /* 7.2.1.1 */
-               }
+               hub->mA_per_port = unit_load;   /* 7.2.1 */
+
        } else {        /* Self-powered external hub */
                /* FIXME: What about battery-powered external hubs that
                 * provide less current per port? */
-               hub->mA_per_port = 500;
+               hub->mA_per_port = full_load;
        }
-       if (hub->mA_per_port < 500)
+       if (hub->mA_per_port < full_load)
                dev_dbg(hub_dev, "%umA bus power budget for each child\n",
                                hub->mA_per_port);
 
        /* Update the HCD's internal representation of this hub before khubd
         * starts getting port status changes for devices under the hub.
         */
-       hcd = bus_to_hcd(hdev->bus);
        if (hcd->driver->update_hub_device) {
                ret = hcd->driver->update_hub_device(hcd, hdev,
                                &hub->tt, GFP_KERNEL);
@@ -2535,77 +2548,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                        return ret;
 
                /* The port state is unknown until the reset completes. */
-               if ((portstatus & USB_PORT_STAT_RESET))
-                       goto delay;
-
-               /*
-                * Some buggy devices require a warm reset to be issued even
-                * when the port appears not to be connected.
-                */
-               if (!warm) {
-                       /*
-                        * Some buggy devices can cause an NEC host controller
-                        * to transition to the "Error" state after a hot port
-                        * reset.  This will show up as the port state in
-                        * "Inactive", and the port may also report a
-                        * disconnect.  Forcing a warm port reset seems to make
-                        * the device work.
-                        *
-                        * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
-                        */
-                       if (hub_port_warm_reset_required(hub, portstatus)) {
-                               int ret;
-
-                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_CONNECTION);
-                               if (portchange & USB_PORT_STAT_C_LINK_STATE)
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
-                               if (portchange & USB_PORT_STAT_C_RESET)
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_RESET);
-                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
-                                               port1);
-                               ret = hub_port_reset(hub, port1,
-                                               udev, HUB_BH_RESET_TIME,
-                                               true);
-                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_CONNECTION);
-                               return ret;
-                       }
-                       /* Device went away? */
-                       if (!(portstatus & USB_PORT_STAT_CONNECTION))
-                               return -ENOTCONN;
-
-                       /* bomb out completely if the connection bounced */
-                       if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                               return -ENOTCONN;
-
-                       if ((portstatus & USB_PORT_STAT_ENABLE)) {
-                               if (hub_is_wusb(hub))
-                                       udev->speed = USB_SPEED_WIRELESS;
-                               else if (hub_is_superspeed(hub->hdev))
-                                       udev->speed = USB_SPEED_SUPER;
-                               else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
-                                       udev->speed = USB_SPEED_HIGH;
-                               else if (portstatus & USB_PORT_STAT_LOW_SPEED)
-                                       udev->speed = USB_SPEED_LOW;
-                               else
-                                       udev->speed = USB_SPEED_FULL;
-                               return 0;
-                       }
-               } else {
-                       if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
-                                       hub_port_warm_reset_required(hub,
-                                               portstatus))
-                               return -ENOTCONN;
-
-                       return 0;
-               }
+               if (!(portstatus & USB_PORT_STAT_RESET))
+                       break;
 
-delay:
                /* switch to the long delay after two short delay failures */
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;
@@ -2615,20 +2560,54 @@ delay:
                        port1, warm ? "warm " : "", delay);
        }
 
-       return -EBUSY;
+       if ((portstatus & USB_PORT_STAT_RESET))
+               return -EBUSY;
+
+       if (hub_port_warm_reset_required(hub, portstatus))
+               return -ENOTCONN;
+
+       /* Device went away? */
+       if (!(portstatus & USB_PORT_STAT_CONNECTION))
+               return -ENOTCONN;
+
+       /* bomb out completely if the connection bounced.  A USB 3.0
+        * connection may bounce if multiple warm resets were issued,
+        * but the device may have successfully re-connected. Ignore it.
+        */
+       if (!hub_is_superspeed(hub->hdev) &&
+                       (portchange & USB_PORT_STAT_C_CONNECTION))
+               return -ENOTCONN;
+
+       if (!(portstatus & USB_PORT_STAT_ENABLE))
+               return -EBUSY;
+
+       if (!udev)
+               return 0;
+
+       if (hub_is_wusb(hub))
+               udev->speed = USB_SPEED_WIRELESS;
+       else if (hub_is_superspeed(hub->hdev))
+               udev->speed = USB_SPEED_SUPER;
+       else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+               udev->speed = USB_SPEED_HIGH;
+       else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+               udev->speed = USB_SPEED_LOW;
+       else
+               udev->speed = USB_SPEED_FULL;
+       return 0;
 }
 
 static void hub_port_finish_reset(struct usb_hub *hub, int port1,
-                       struct usb_device *udev, int *status, bool warm)
+                       struct usb_device *udev, int *status)
 {
        switch (*status) {
        case 0:
-               if (!warm) {
-                       struct usb_hcd *hcd;
-                       /* TRSTRCY = 10 ms; plus some extra */
-                       msleep(10 + 40);
+               /* TRSTRCY = 10 ms; plus some extra */
+               msleep(10 + 40);
+               if (udev) {
+                       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
                        update_devnum(udev, 0);
-                       hcd = bus_to_hcd(udev->bus);
                        /* The xHC may think the device is already reset,
                         * so ignore the status.
                         */
@@ -2640,14 +2619,15 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
        case -ENODEV:
                clear_port_feature(hub->hdev,
                                port1, USB_PORT_FEAT_C_RESET);
-               /* FIXME need disconnect() for NOTATTACHED device */
                if (hub_is_superspeed(hub->hdev)) {
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_CONNECTION);
                }
-               if (!warm)
+               if (udev)
                        usb_set_device_state(udev, *status
                                        ? USB_STATE_NOTATTACHED
                                        : USB_STATE_DEFAULT);
@@ -2660,18 +2640,30 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm)
 {
        int i, status;
+       u16 portchange, portstatus;
 
-       if (!warm) {
-               /* Block EHCI CF initialization during the port reset.
-                * Some companion controllers don't like it when they mix.
-                */
-               down_read(&ehci_cf_port_reset_rwsem);
-       } else {
-               if (!hub_is_superspeed(hub->hdev)) {
+       if (!hub_is_superspeed(hub->hdev)) {
+               if (warm) {
                        dev_err(hub->intfdev, "only USB3 hub support "
                                                "warm reset\n");
                        return -EINVAL;
                }
+               /* Block EHCI CF initialization during the port reset.
+                * Some companion controllers don't like it when they mix.
+                */
+               down_read(&ehci_cf_port_reset_rwsem);
+       } else if (!warm) {
+               /*
+                * If the caller hasn't explicitly requested a warm reset,
+                * double check and see if one is needed.
+                */
+               status = hub_port_status(hub, port1,
+                                       &portstatus, &portchange);
+               if (status < 0)
+                       goto done;
+
+               if (hub_port_warm_reset_required(hub, portstatus))
+                       warm = true;
        }
 
        /* Reset the port */
@@ -2692,10 +2684,33 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                                status);
                }
 
-               /* return on disconnect or reset */
+               /* Check for disconnect or reset */
                if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
-                       hub_port_finish_reset(hub, port1, udev, &status, warm);
-                       goto done;
+                       hub_port_finish_reset(hub, port1, udev, &status);
+
+                       if (!hub_is_superspeed(hub->hdev))
+                               goto done;
+
+                       /*
+                        * If a USB 3.0 device migrates from reset to an error
+                        * state, re-issue the warm reset.
+                        */
+                       if (hub_port_status(hub, port1,
+                                       &portstatus, &portchange) < 0)
+                               goto done;
+
+                       if (!hub_port_warm_reset_required(hub, portstatus))
+                               goto done;
+
+                       /*
+                        * If the port is in SS.Inactive or Compliance Mode, the
+                        * hot or warm reset failed.  Try another warm reset.
+                        */
+                       if (!warm) {
+                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+                                               port1);
+                               warm = true;
+                       }
                }
 
                dev_dbg (hub->intfdev,
@@ -2709,7 +2724,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                port1);
 
 done:
-       if (!warm)
+       if (!hub_is_superspeed(hub->hdev))
                up_read(&ehci_cf_port_reset_rwsem);
 
        return status;
@@ -2945,9 +2960,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 
        /* see 7.1.7.6 */
        if (hub_is_superspeed(hub->hdev))
-               status = set_port_feature(hub->hdev,
-                               port1 | (USB_SS_PORT_LS_U3 << 3),
-                               USB_PORT_FEAT_LINK_STATE);
+               status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
        else
                status = set_port_feature(hub->hdev, port1,
                                                USB_PORT_FEAT_SUSPEND);
@@ -3123,9 +3136,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
        /* see 7.1.7.7; affects power usage, but not budgeting */
        if (hub_is_superspeed(hub->hdev))
-               status = set_port_feature(hub->hdev,
-                               port1 | (USB_SS_PORT_LS_U0 << 3),
-                               USB_PORT_FEAT_LINK_STATE);
+               status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
        else
                status = clear_port_feature(hub->hdev,
                                port1, USB_PORT_FEAT_SUSPEND);
@@ -4212,16 +4223,23 @@ hub_power_remaining (struct usb_hub *hub)
        for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
                struct usb_device       *udev = hub->ports[port1 - 1]->child;
                int                     delta;
+               unsigned                unit_load;
 
                if (!udev)
                        continue;
+               if (hub_is_superspeed(udev))
+                       unit_load = 150;
+               else
+                       unit_load = 100;
 
-               /* Unconfigured devices may not use more than 100mA,
-                * or 8mA for OTG ports */
+               /*
+                * Unconfigured devices may not use more than one unit load,
+                * or 8mA for OTG ports
+                */
                if (udev->actconfig)
-                       delta = udev->actconfig->desc.bMaxPower * 2;
+                       delta = usb_get_max_power(udev, udev->actconfig);
                else if (port1 != udev->bus->otg_port || hdev->parent)
-                       delta = 100;
+                       delta = unit_load;
                else
                        delta = 8;
                if (delta > hub->mA_per_port)
@@ -4256,6 +4274,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        le16_to_cpu(hub->descriptor->wHubCharacteristics);
        struct usb_device *udev;
        int status, i;
+       unsigned unit_load;
 
        dev_dbg (hub_dev,
                "port %d, status %04x, change %04x, %s\n",
@@ -4345,6 +4364,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        goto done;
                return;
        }
+       if (hub_is_superspeed(hub->hdev))
+               unit_load = 150;
+       else
+               unit_load = 100;
 
        for (i = 0; i < SET_CONFIG_TRIES; i++) {
 
@@ -4392,7 +4415,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                 * on the parent.
                 */
                if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
-                               && udev->bus_mA <= 100) {
+                               && udev->bus_mA <= unit_load) {
                        u16     devstat;
 
                        status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
@@ -4706,12 +4729,21 @@ static void hub_events(void)
                         */
                        if (hub_port_warm_reset_required(hub, portstatus)) {
                                int status;
+                               struct usb_device *udev =
+                                       hub->ports[i - 1]->child;
 
                                dev_dbg(hub_dev, "warm reset port %d\n", i);
-                               status = hub_port_reset(hub, i, NULL,
-                                               HUB_BH_RESET_TIME, true);
-                               if (status < 0)
-                                       hub_port_disable(hub, i, 1);
+                               if (!udev) {
+                                       status = hub_port_reset(hub, i,
+                                                       NULL, HUB_BH_RESET_TIME,
+                                                       true);
+                                       if (status < 0)
+                                               hub_port_disable(hub, i, 1);
+                               } else {
+                                       usb_lock_device(udev);
+                                       status = usb_reset_device(udev);
+                                       usb_unlock_device(udev);
+                               }
                                connect_change = 0;
                        }
 
index 131f736..444d30e 100644 (file)
@@ -1751,7 +1751,7 @@ free_interfaces:
                        }
                }
 
-               i = dev->bus_mA - cp->desc.bMaxPower * 2;
+               i = dev->bus_mA - usb_get_max_power(dev, cp);
                if (i < 0)
                        dev_warn(&dev->dev, "new config #%d exceeds power "
                                        "limit by %dmA\n",
index 818e4a0..3f81a3d 100644 (file)
@@ -17,7 +17,7 @@
 #include "usb.h"
 
 /* Active configuration fields */
-#define usb_actconfig_show(field, multiplier, format_string)           \
+#define usb_actconfig_show(field, format_string)                       \
 static ssize_t  show_##field(struct device *dev,                       \
                struct device_attribute *attr, char *buf)               \
 {                                                                      \
@@ -28,18 +28,31 @@ static ssize_t  show_##field(struct device *dev,                    \
        actconfig = udev->actconfig;                                    \
        if (actconfig)                                                  \
                return sprintf(buf, format_string,                      \
-                               actconfig->desc.field * multiplier);    \
+                               actconfig->desc.field);                 \
        else                                                            \
                return 0;                                               \
 }                                                                      \
 
-#define usb_actconfig_attr(field, multiplier, format_string)           \
-usb_actconfig_show(field, multiplier, format_string)                   \
-static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+#define usb_actconfig_attr(field, format_string)               \
+       usb_actconfig_show(field, format_string)                \
+       static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+usb_actconfig_attr(bNumInterfaces, "%2d\n")
+usb_actconfig_attr(bmAttributes, "%2x\n")
 
-usb_actconfig_attr(bNumInterfaces, 1, "%2d\n")
-usb_actconfig_attr(bmAttributes, 1, "%2x\n")
-usb_actconfig_attr(bMaxPower, 2, "%3dmA\n")
+static ssize_t show_bMaxPower(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev;
+       struct usb_host_config *actconfig;
+
+       udev = to_usb_device(dev);
+       actconfig = udev->actconfig;
+       if (!actconfig)
+               return 0;
+       return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+}
+static DEVICE_ATTR(bMaxPower, S_IRUGO, show_bMaxPower, NULL);
 
 static ssize_t show_configuration_string(struct device *dev,
                struct device_attribute *attr, char *buf)
@@ -56,7 +69,7 @@ static ssize_t show_configuration_string(struct device *dev,
 static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL);
 
 /* configuration value is always present, and r/w */
-usb_actconfig_show(bConfigurationValue, 1, "%u\n");
+usb_actconfig_show(bConfigurationValue, "%u\n");
 
 static ssize_t
 set_bConfigurationValue(struct device *dev, struct device_attribute *attr,
index 1c528c1..fb7d8fc 100644 (file)
@@ -38,6 +38,15 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
 
+static inline unsigned usb_get_max_power(struct usb_device *udev,
+               struct usb_host_config *c)
+{
+       /* SuperSpeed power is in 8 mA units; others are in 2 mA units */
+       unsigned mul = (udev->speed == USB_SPEED_SUPER ? 8 : 2);
+
+       return c->desc.bMaxPower * mul;
+}
+
 extern void usb_kick_khubd(struct usb_device *dev);
 extern int usb_match_one_id_intf(struct usb_device *dev,
                                 struct usb_host_interface *intf,
index ec7f5d2..6b8c158 100644 (file)
@@ -94,7 +94,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        struct usb_hcd *hcd;
        struct resource *res;
        int irq, ret;
-       unsigned int flags;
        struct ehci_mxc_priv *priv;
        struct device *dev = &pdev->dev;
        struct ehci_hcd *ehci;
@@ -205,25 +204,6 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        if (ret)
                goto err_add;
 
-       if (pdata->otg) {
-               /*
-                * efikamx and efikasb have some hardware bug which is
-                * preventing usb to work unless CHRGVBUS is set.
-                * It's in violation of USB specs
-                */
-               if (machine_is_mx51_efikamx() || machine_is_mx51_efikasb()) {
-                       flags = usb_phy_io_read(pdata->otg,
-                                                       ULPI_OTG_CTRL);
-                       flags |= ULPI_OTG_CTRL_CHRGVBUS;
-                       ret = usb_phy_io_write(pdata->otg, flags,
-                                                       ULPI_OTG_CTRL);
-                       if (ret) {
-                               dev_err(dev, "unable to set CHRVBUS\n");
-                               goto err_add;
-                       }
-               }
-       }
-
        return 0;
 
 err_add:
index 7482cfb..88731b7 100644 (file)
@@ -44,6 +44,7 @@ __acquires(ohci->lock)
        // ASSERT (urb->hcpriv != 0);
 
        urb_free_priv (ohci, urb->hcpriv);
+       urb->hcpriv = NULL;
        if (likely(status == -EINPROGRESS))
                status = 0;
 
index 59fb5c6..f697209 100644 (file)
@@ -2706,13 +2706,11 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        u32 status;
-       union xhci_trb *trb;
        u64 temp_64;
        union xhci_trb *event_ring_deq;
        dma_addr_t deq;
 
        spin_lock(&xhci->lock);
-       trb = xhci->event_ring->dequeue;
        /* Check if the xHC generated the interrupt, or the irq is shared */
        status = xhci_readl(xhci, &xhci->op_regs->status);
        if (status == 0xffffffff)
index fecde69..3b1a3f4 100644 (file)
@@ -250,3 +250,9 @@ config USB_EZUSB_FX2
        help
          Say Y here if you need EZUSB device support.
          (Cypress FX/FX2/FX2LP microcontrollers)
+
+config USB_HSIC_USB3503
+       tristate "USB3503 HSIC to USB20 Driver"
+       depends on I2C
+       help
+         This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
index 3e99a64..3e1bd70 100644 (file)
@@ -26,5 +26,6 @@ obj-$(CONFIG_USB_TRANCEVIBRATOR)      += trancevibrator.o
 obj-$(CONFIG_USB_USS720)               += uss720.o
 obj-$(CONFIG_USB_SEVSEG)               += usbsevseg.o
 obj-$(CONFIG_USB_YUREX)                        += yurex.o
+obj-$(CONFIG_USB_HSIC_USB3503)         += usb3503.o
 
 obj-$(CONFIG_USB_SISUSBVGA)            += sisusbvga/
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
new file mode 100644 (file)
index 0000000..dc2c993
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Driver for SMSC USB3503 USB 2.0 hub controller driver
+ *
+ * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.com)
+ *
+ * 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
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb3503.h>
+
+#define USB3503_VIDL           0x00
+#define USB3503_VIDM           0x01
+#define USB3503_PIDL           0x02
+#define USB3503_PIDM           0x03
+#define USB3503_DIDL           0x04
+#define USB3503_DIDM           0x05
+
+#define USB3503_CFG1           0x06
+#define USB3503_SELF_BUS_PWR   (1 << 7)
+
+#define USB3503_CFG2           0x07
+#define USB3503_CFG3           0x08
+#define USB3503_NRD            0x09
+
+#define USB3503_PDS            0x0a
+#define USB3503_PORT1          (1 << 1)
+#define USB3503_PORT2          (1 << 2)
+#define USB3503_PORT3          (1 << 3)
+
+#define USB3503_SP_ILOCK       0xe7
+#define USB3503_SPILOCK_CONNECT        (1 << 1)
+#define USB3503_SPILOCK_CONFIG (1 << 0)
+
+#define USB3503_CFGP           0xee
+#define USB3503_CLKSUSP                (1 << 7)
+
+struct usb3503 {
+       enum usb3503_mode       mode;
+       struct i2c_client       *client;
+       int     gpio_intn;
+       int     gpio_reset;
+       int     gpio_connect;
+};
+
+static int usb3503_write_register(struct i2c_client *client,
+               char reg, char data)
+{
+       return i2c_smbus_write_byte_data(client, reg, data);
+}
+
+static int usb3503_read_register(struct i2c_client *client, char reg)
+{
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int usb3503_set_bits(struct i2c_client *client, char reg, char req)
+{
+       int err;
+
+       err = usb3503_read_register(client, reg);
+       if (err < 0)
+               return err;
+
+       err = usb3503_write_register(client, reg, err | req);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int usb3503_clear_bits(struct i2c_client *client, char reg, char req)
+{
+       int err;
+
+       err = usb3503_read_register(client, reg);
+       if (err < 0)
+               return err;
+
+       err = usb3503_write_register(client, reg, err & ~req);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int usb3503_reset(int gpio_reset, int state)
+{
+       if (gpio_is_valid(gpio_reset))
+               gpio_set_value(gpio_reset, state);
+
+       /* Wait RefClk when RESET_N is released, otherwise Hub will
+        * not transition to Hub Communication Stage.
+        */
+       if (state)
+               msleep(100);
+
+       return 0;
+}
+
+static int usb3503_switch_mode(struct usb3503 *hub, enum usb3503_mode mode)
+{
+       struct i2c_client *i2c = hub->client;
+       int err = 0;
+
+       switch (mode) {
+       case USB3503_MODE_HUB:
+               usb3503_reset(hub->gpio_reset, 1);
+
+               /* SP_ILOCK: set connect_n, config_n for config */
+               err = usb3503_write_register(i2c, USB3503_SP_ILOCK,
+                               (USB3503_SPILOCK_CONNECT
+                                | USB3503_SPILOCK_CONFIG));
+               if (err < 0) {
+                       dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err);
+                       goto err_hubmode;
+               }
+
+               /* PDS : Port2,3 Disable For Self Powered Operation */
+               err = usb3503_set_bits(i2c, USB3503_PDS,
+                               (USB3503_PORT2 | USB3503_PORT3));
+               if (err < 0) {
+                       dev_err(&i2c->dev, "PDS failed (%d)\n", err);
+                       goto err_hubmode;
+               }
+
+               /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */
+               err = usb3503_set_bits(i2c, USB3503_CFG1, USB3503_SELF_BUS_PWR);
+               if (err < 0) {
+                       dev_err(&i2c->dev, "CFG1 failed (%d)\n", err);
+                       goto err_hubmode;
+               }
+
+               /* SP_LOCK: clear connect_n, config_n for hub connect */
+               err = usb3503_clear_bits(i2c, USB3503_SP_ILOCK,
+                               (USB3503_SPILOCK_CONNECT
+                                | USB3503_SPILOCK_CONFIG));
+               if (err < 0) {
+                       dev_err(&i2c->dev, "SP_ILOCK failed (%d)\n", err);
+                       goto err_hubmode;
+               }
+
+               hub->mode = mode;
+               dev_info(&i2c->dev, "switched to HUB mode\n");
+               break;
+
+       case USB3503_MODE_STANDBY:
+               usb3503_reset(hub->gpio_reset, 0);
+
+               hub->mode = mode;
+               dev_info(&i2c->dev, "switched to STANDBY mode\n");
+               break;
+
+       default:
+               dev_err(&i2c->dev, "unknown mode is request\n");
+               err = -EINVAL;
+               break;
+       }
+
+err_hubmode:
+       return err;
+}
+
+static int usb3503_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+       struct usb3503_platform_data *pdata = i2c->dev.platform_data;
+       struct usb3503 *hub;
+       int err = -ENOMEM;
+
+       hub = kzalloc(sizeof(struct usb3503), GFP_KERNEL);
+       if (!hub) {
+               dev_err(&i2c->dev, "private data alloc fail\n");
+               return err;
+       }
+
+       i2c_set_clientdata(i2c, hub);
+       hub->client = i2c;
+
+       if (!pdata) {
+               dev_dbg(&i2c->dev, "missing platform data\n");
+               goto err_out;
+       } else {
+               hub->gpio_intn          = pdata->gpio_intn;
+               hub->gpio_connect       = pdata->gpio_connect;
+               hub->gpio_reset         = pdata->gpio_reset;
+               hub->mode               = pdata->initial_mode;
+       }
+
+       if (gpio_is_valid(hub->gpio_intn)) {
+               err = gpio_request_one(hub->gpio_intn,
+                               GPIOF_OUT_INIT_HIGH, "usb3503 intn");
+               if (err) {
+                       dev_err(&i2c->dev,
+                                       "unable to request GPIO %d as connect pin (%d)\n",
+                                       hub->gpio_intn, err);
+                       goto err_out;
+               }
+       }
+
+       if (gpio_is_valid(hub->gpio_connect)) {
+               err = gpio_request_one(hub->gpio_connect,
+                               GPIOF_OUT_INIT_HIGH, "usb3503 connect");
+               if (err) {
+                       dev_err(&i2c->dev,
+                                       "unable to request GPIO %d as connect pin (%d)\n",
+                                       hub->gpio_connect, err);
+                       goto err_gpio_connect;
+               }
+       }
+
+       if (gpio_is_valid(hub->gpio_reset)) {
+               err = gpio_request_one(hub->gpio_reset,
+                               GPIOF_OUT_INIT_LOW, "usb3503 reset");
+               if (err) {
+                       dev_err(&i2c->dev,
+                                       "unable to request GPIO %d as reset pin (%d)\n",
+                                       hub->gpio_reset, err);
+                       goto err_gpio_reset;
+               }
+       }
+
+       usb3503_switch_mode(hub, pdata->initial_mode);
+
+       dev_info(&i2c->dev, "%s: probed on  %s mode\n", __func__,
+                       (hub->mode == USB3503_MODE_HUB) ? "hub" : "standby");
+
+       return 0;
+
+err_gpio_reset:
+       if (gpio_is_valid(hub->gpio_connect))
+               gpio_free(hub->gpio_connect);
+err_gpio_connect:
+       if (gpio_is_valid(hub->gpio_intn))
+               gpio_free(hub->gpio_intn);
+err_out:
+       kfree(hub);
+
+       return err;
+}
+
+static int usb3503_remove(struct i2c_client *i2c)
+{
+       struct usb3503 *hub = i2c_get_clientdata(i2c);
+
+       if (gpio_is_valid(hub->gpio_intn))
+               gpio_free(hub->gpio_intn);
+       if (gpio_is_valid(hub->gpio_connect))
+               gpio_free(hub->gpio_connect);
+       if (gpio_is_valid(hub->gpio_reset))
+               gpio_free(hub->gpio_reset);
+
+       kfree(hub);
+
+       return 0;
+}
+
+static const struct i2c_device_id usb3503_id[] = {
+       { USB3503_I2C_NAME, 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, usb3503_id);
+
+static struct i2c_driver usb3503_driver = {
+       .driver = {
+               .name = USB3503_I2C_NAME,
+       },
+       .probe          = usb3503_probe,
+       .remove         = usb3503_remove,
+       .id_table       = usb3503_id,
+};
+
+static int __init usb3503_init(void)
+{
+       return i2c_add_driver(&usb3503_driver);
+}
+
+static void __exit usb3503_exit(void)
+{
+       i2c_del_driver(&usb3503_driver);
+}
+
+module_init(usb3503_init);
+module_exit(usb3503_exit);
+
+MODULE_AUTHOR("Dongjin Kim <tobetter@gmail.com>");
+MODULE_DESCRIPTION("USB3503 USB HUB driver");
+MODULE_LICENSE("GPL");
index 97bc49f..3d95637 100644 (file)
@@ -298,7 +298,7 @@ static void usa26_indat_callback(struct urb *urb)
        endpoint = usb_pipeendpoint(urb->pipe);
 
        if (status) {
-               dev_dbg(&urb->dev->dev,"%s - nonzero status: %x on endpoint %d.\n",
+               dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
                        __func__, status, endpoint);
                return;
        }
@@ -532,7 +532,7 @@ static void usa28_instat_callback(struct urb *urb)
 
        /*
        dev_dbg(&urb->dev->dev,
-               "%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__,
+               "%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__,
                data[0], data[1], data[2], data[3], data[4], data[5],
                data[6], data[7], data[8], data[9], data[10], data[11]);
        */
index 98b98ee..ebb9972 100644 (file)
@@ -66,6 +66,8 @@ enum {
        DATA_OUT_URB_INFLIGHT   = (1 << 10),
        COMMAND_COMPLETED       = (1 << 11),
        COMMAND_ABORTED         = (1 << 12),
+       UNLINK_DATA_URBS        = (1 << 13),
+       IS_IN_WORK_LIST         = (1 << 14),
 };
 
 /* Overrides scsi_pointer */
@@ -82,11 +84,36 @@ struct uas_cmd_info {
 static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                                struct uas_dev_info *devinfo, gfp_t gfp);
 static void uas_do_work(struct work_struct *work);
+static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
 
 static DECLARE_WORK(uas_work, uas_do_work);
 static DEFINE_SPINLOCK(uas_work_lock);
 static LIST_HEAD(uas_work_list);
 
+static void uas_unlink_data_urbs(struct uas_dev_info *devinfo,
+                                struct uas_cmd_info *cmdinfo)
+{
+       unsigned long flags;
+
+       /*
+        * The UNLINK_DATA_URBS flag makes sure uas_try_complete
+        * (called by urb completion) doesn't release cmdinfo
+        * underneath us.
+        */
+       spin_lock_irqsave(&devinfo->lock, flags);
+       cmdinfo->state |= UNLINK_DATA_URBS;
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+
+       if (cmdinfo->data_in_urb)
+               usb_unlink_urb(cmdinfo->data_in_urb);
+       if (cmdinfo->data_out_urb)
+               usb_unlink_urb(cmdinfo->data_out_urb);
+
+       spin_lock_irqsave(&devinfo->lock, flags);
+       cmdinfo->state &= ~UNLINK_DATA_URBS;
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+}
+
 static void uas_do_work(struct work_struct *work)
 {
        struct uas_cmd_info *cmdinfo;
@@ -106,6 +133,8 @@ static void uas_do_work(struct work_struct *work)
                struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
                spin_lock_irqsave(&devinfo->lock, flags);
                err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+               if (!err)
+                       cmdinfo->state &= ~IS_IN_WORK_LIST;
                spin_unlock_irqrestore(&devinfo->lock, flags);
                if (err) {
                        list_del(&cmdinfo->list);
@@ -117,6 +146,45 @@ static void uas_do_work(struct work_struct *work)
        }
 }
 
+static void uas_abort_work(struct uas_dev_info *devinfo)
+{
+       struct uas_cmd_info *cmdinfo;
+       struct uas_cmd_info *temp;
+       struct list_head list;
+       unsigned long flags;
+
+       spin_lock_irq(&uas_work_lock);
+       list_replace_init(&uas_work_list, &list);
+       spin_unlock_irq(&uas_work_lock);
+
+       spin_lock_irqsave(&devinfo->lock, flags);
+       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+               struct scsi_pointer *scp = (void *)cmdinfo;
+               struct scsi_cmnd *cmnd = container_of(scp,
+                                                       struct scsi_cmnd, SCp);
+               struct uas_dev_info *di = (void *)cmnd->device->hostdata;
+
+               if (di == devinfo) {
+                       cmdinfo->state |= COMMAND_ABORTED;
+                       cmdinfo->state &= ~IS_IN_WORK_LIST;
+                       if (devinfo->resetting) {
+                               /* uas_stat_cmplt() will not do that
+                                * when a device reset is in
+                                * progress */
+                               cmdinfo->state &= ~COMMAND_INFLIGHT;
+                       }
+                       uas_try_complete(cmnd, __func__);
+               } else {
+                       /* not our uas device, relink into list */
+                       list_del(&cmdinfo->list);
+                       spin_lock_irq(&uas_work_lock);
+                       list_add_tail(&cmdinfo->list, &uas_work_list);
+                       spin_unlock_irq(&uas_work_lock);
+               }
+       }
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+}
+
 static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
 {
        struct sense_iu *sense_iu = urb->transfer_buffer;
@@ -168,7 +236,7 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
        struct uas_cmd_info *ci = (void *)&cmnd->SCp;
 
        scmd_printk(KERN_INFO, cmnd, "%s %p tag %d, inflight:"
-                   "%s%s%s%s%s%s%s%s%s%s%s%s\n",
+                   "%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                    caller, cmnd, cmnd->request->tag,
                    (ci->state & SUBMIT_STATUS_URB)     ? " s-st"  : "",
                    (ci->state & ALLOC_DATA_IN_URB)     ? " a-in"  : "",
@@ -181,7 +249,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
                    (ci->state & DATA_IN_URB_INFLIGHT)  ? " IN"    : "",
                    (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT"   : "",
                    (ci->state & COMMAND_COMPLETED)     ? " done"  : "",
-                   (ci->state & COMMAND_ABORTED)       ? " abort" : "");
+                   (ci->state & COMMAND_ABORTED)       ? " abort" : "",
+                   (ci->state & UNLINK_DATA_URBS)      ? " unlink": "",
+                   (ci->state & IS_IN_WORK_LIST)       ? " work"  : "");
 }
 
 static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
@@ -192,7 +262,8 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
        WARN_ON(!spin_is_locked(&devinfo->lock));
        if (cmdinfo->state & (COMMAND_INFLIGHT |
                              DATA_IN_URB_INFLIGHT |
-                             DATA_OUT_URB_INFLIGHT))
+                             DATA_OUT_URB_INFLIGHT |
+                             UNLINK_DATA_URBS))
                return -EBUSY;
        BUG_ON(cmdinfo->state & COMMAND_COMPLETED);
        cmdinfo->state |= COMMAND_COMPLETED;
@@ -217,6 +288,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
        if (err) {
                spin_lock(&uas_work_lock);
                list_add_tail(&cmdinfo->list, &uas_work_list);
+               cmdinfo->state |= IS_IN_WORK_LIST;
                spin_unlock(&uas_work_lock);
                schedule_work(&uas_work);
        }
@@ -274,16 +346,9 @@ static void uas_stat_cmplt(struct urb *urb)
                        uas_sense(urb, cmnd);
                if (cmnd->result != 0) {
                        /* cancel data transfers on error */
-                       if (cmdinfo->state & DATA_IN_URB_INFLIGHT) {
-                               spin_unlock_irqrestore(&devinfo->lock, flags);
-                               usb_unlink_urb(cmdinfo->data_in_urb);
-                               spin_lock_irqsave(&devinfo->lock, flags);
-                       }
-                       if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) {
-                               spin_unlock_irqrestore(&devinfo->lock, flags);
-                               usb_unlink_urb(cmdinfo->data_out_urb);
-                               spin_lock_irqsave(&devinfo->lock, flags);
-                       }
+                       spin_unlock_irqrestore(&devinfo->lock, flags);
+                       uas_unlink_data_urbs(devinfo, cmdinfo);
+                       spin_lock_irqsave(&devinfo->lock, flags);
                }
                cmdinfo->state &= ~COMMAND_INFLIGHT;
                uas_try_complete(cmnd, __func__);
@@ -579,6 +644,12 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
        BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
 
+       if (devinfo->resetting) {
+               cmnd->result = DID_ERROR << 16;
+               cmnd->scsi_done(cmnd);
+               return 0;
+       }
+
        spin_lock_irqsave(&devinfo->lock, flags);
        if (devinfo->cmnd) {
                spin_unlock_irqrestore(&devinfo->lock, flags);
@@ -623,6 +694,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
                }
                spin_lock(&uas_work_lock);
                list_add_tail(&cmdinfo->list, &uas_work_list);
+               cmdinfo->state |= IS_IN_WORK_LIST;
                spin_unlock(&uas_work_lock);
                schedule_work(&uas_work);
        }
@@ -689,8 +761,23 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
        uas_log_cmd_state(cmnd, __func__);
        spin_lock_irqsave(&devinfo->lock, flags);
        cmdinfo->state |= COMMAND_ABORTED;
-       spin_unlock_irqrestore(&devinfo->lock, flags);
-       ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
+       if (cmdinfo->state & IS_IN_WORK_LIST) {
+               spin_lock(&uas_work_lock);
+               list_del(&cmdinfo->list);
+               cmdinfo->state &= ~IS_IN_WORK_LIST;
+               spin_unlock(&uas_work_lock);
+       }
+       if (cmdinfo->state & COMMAND_INFLIGHT) {
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
+       } else {
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               uas_unlink_data_urbs(devinfo, cmdinfo);
+               spin_lock_irqsave(&devinfo->lock, flags);
+               uas_try_complete(cmnd, __func__);
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               ret = SUCCESS;
+       }
        return ret;
 }
 
@@ -709,6 +796,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
        int err;
 
        devinfo->resetting = 1;
+       uas_abort_work(devinfo);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
@@ -954,10 +1042,12 @@ static void uas_disconnect(struct usb_interface *intf)
        struct Scsi_Host *shost = usb_get_intfdata(intf);
        struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
 
-       scsi_remove_host(shost);
+       devinfo->resetting = 1;
+       uas_abort_work(devinfo);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
+       scsi_remove_host(shost);
        uas_free_streams(devinfo);
        kfree(devinfo);
 }
diff --git a/include/linux/platform_data/usb3503.h b/include/linux/platform_data/usb3503.h
new file mode 100644 (file)
index 0000000..85dcc70
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __USB3503_H__
+#define __USB3503_H__
+
+#define USB3503_I2C_NAME       "usb3503"
+
+enum usb3503_mode {
+       USB3503_MODE_UNKNOWN,
+       USB3503_MODE_HUB,
+       USB3503_MODE_STANDBY,
+};
+
+struct usb3503_platform_data {
+       enum usb3503_mode       initial_mode;
+       int     gpio_intn;
+       int     gpio_connect;
+       int     gpio_reset;
+};
+
+#endif
index 68d0734..643cd77 100644 (file)
@@ -507,10 +507,8 @@ usage:
                        return handle_testdev (entry) != entry;
                }
                status = pthread_create (&entry->thread, 0, handle_testdev, entry);
-               if (status) {
+               if (status)
                        perror ("pthread_create");
-                       continue;
-               }
        }
        if (device) {
                struct testdev          dev;