diff options
-rw-r--r-- | drivers/usb/core/devices.c | 13 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 266 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 31 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 9 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mxc.c | 20 | ||||
-rw-r--r-- | drivers/usb/host/ohci-q.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 2 | ||||
-rw-r--r-- | drivers/usb/misc/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/misc/usb3503.c | 304 | ||||
-rw-r--r-- | drivers/usb/serial/keyspan.c | 4 | ||||
-rw-r--r-- | drivers/usb/storage/uas.c | 122 | ||||
-rw-r--r-- | include/linux/platform_data/usb3503.h | 19 | ||||
-rw-r--r-- | tools/usb/testusb.c | 4 |
17 files changed, 632 insertions, 175 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index cbacea933b1..e33224e2377 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -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; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index eff2010eb63..271e761f563 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -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; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4225d5e7213..5f6da8b2d6a 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 957ed2c4148..dc6ebd1d88d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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; } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 131f73649b6..444d30e3a78 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -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", diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 818e4a024d0..3f81a3dc686 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1c528c1bf0b..fb7d8fcb455 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -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, diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index ec7f5d2c90d..6b8c1584065 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -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: diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 7482cfbe8c5..88731b7c5f4 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -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; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 59fb5c677db..f69720983fc 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -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) diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index fecde69bfa7..3b1a3f4ec5e 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -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. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 3e99a643294..3e1bd70b06e 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -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 index 00000000000..dc2c993ea18 --- /dev/null +++ b/drivers/usb/misc/usb3503.c @@ -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"); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 97bc49f68ef..3d95637f3d6 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -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]); */ diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 98b98eef752..ebb99728551 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -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 index 00000000000..85dcc709f7e --- /dev/null +++ b/include/linux/platform_data/usb3503.h @@ -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 diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 68d0734b208..643cd77fa98 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c @@ -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; |