diff options
author | Scott Wood <scottwood@freescale.com> | 2013-10-29 19:50:27 (GMT) |
---|---|---|
committer | Scott Wood <scottwood@freescale.com> | 2013-10-29 19:50:37 (GMT) |
commit | d0ebef8230e267ec47d4d4a65fe3262e2ebb8026 (patch) | |
tree | 24b8bb342576f543dac42d59821c4feb7ce07453 /drivers/usb/core | |
parent | 041f2bc64a985b30328de4cb596f04fd913a85de (diff) | |
download | linux-fsl-qoriq-d0ebef8230e267ec47d4d4a65fe3262e2ebb8026.tar.xz |
Revert to v3.8 (no RT, no stable)
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/devio.c | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 178 |
4 files changed, 95 insertions, 112 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index ea0a9a1..b78fbe2 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -738,8 +738,6 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: - if ((index & ~USB_DIR_IN) == 0) - return 0; ret = findintfep(ps->dev, index); if (ret >= 0) ret = checkintf(ps, ret); diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 2b487d4..622b4a4 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -173,7 +173,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) struct hc_driver *driver; struct usb_hcd *hcd; int retval; - int hcd_irq = 0; if (usb_disabled()) return -ENODEV; @@ -188,19 +187,15 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) return -ENODEV; dev->current_state = PCI_D0; - /* - * The xHCI driver has its own irq management - * make sure irq setup is not touched for xhci in generic hcd code + /* The xHCI driver supports MSI and MSI-X, + * so don't fail if the BIOS doesn't provide a legacy IRQ. */ - if ((driver->flags & HCD_MASK) != HCD_USB3) { - if (!dev->irq) { - dev_err(&dev->dev, - "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", - pci_name(dev)); - retval = -ENODEV; - goto disable_pci; - } - hcd_irq = dev->irq; + if (!dev->irq && (driver->flags & HCD_MASK) != HCD_USB3) { + dev_err(&dev->dev, + "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", + pci_name(dev)); + retval = -ENODEV; + goto disable_pci; } hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); @@ -250,7 +245,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_master(dev); - retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); + retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (retval != 0) goto unmap_registers; set_hs_companion(dev, hcd); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 59c4d3c..8e64adf 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2217,7 +2217,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) * when the first handler doesn't use it. So let's just * assume it's never used. */ - local_irq_save_nort(flags); + local_irq_save(flags); if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) rc = IRQ_NONE; @@ -2226,7 +2226,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) else rc = IRQ_HANDLED; - local_irq_restore_nort(flags); + local_irq_restore(flags); return rc; } EXPORT_SYMBOL_GPL(usb_hcd_irq); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2a89588..cbf7168 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2538,35 +2538,70 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if ((portstatus & USB_PORT_STAT_RESET)) goto delay; - 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. + /* + * Some buggy devices require a warm reset to be issued even + * when the port appears not to be connected. */ - if (!hub_is_superspeed(hub->hdev) && - (portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; - - if ((portstatus & USB_PORT_STAT_ENABLE)) { - if (!udev) + 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; - 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; } @@ -2584,16 +2619,16 @@ delay: } static void hub_port_finish_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, int *status) + struct usb_device *udev, int *status, bool warm) { switch (*status) { case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - if (udev) { - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - + if (!warm) { + struct usb_hcd *hcd; + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); update_devnum(udev, 0); + hcd = bus_to_hcd(udev->bus); /* The xHC may think the device is already reset, * so ignore the status. */ @@ -2605,15 +2640,14 @@ 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 (udev) + if (!warm) usb_set_device_state(udev, *status ? USB_STATE_NOTATTACHED : USB_STATE_DEFAULT); @@ -2626,30 +2660,18 @@ 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 (!hub_is_superspeed(hub->hdev)) { - if (warm) { - dev_err(hub->intfdev, "only USB3 hub support " - "warm reset\n"); - return -EINVAL; - } + 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 (!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; + } else { + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support " + "warm reset\n"); + return -EINVAL; + } } /* Reset the port */ @@ -2670,33 +2692,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, status); } - /* Check for disconnect or reset */ + /* return on disconnect or reset */ if (status == 0 || status == -ENOTCONN || status == -ENODEV) { - 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; - } + hub_port_finish_reset(hub, port1, udev, &status, warm); + goto done; } dev_dbg (hub->intfdev, @@ -2710,7 +2709,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, port1); done: - if (!hub_is_superspeed(hub->hdev)) + if (!warm) up_read(&ehci_cf_port_reset_rwsem); return status; @@ -4741,21 +4740,12 @@ 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); - 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); - } + status = hub_port_reset(hub, i, NULL, + HUB_BH_RESET_TIME, true); + if (status < 0) + hub_port_disable(hub, i, 1); connect_change = 0; } |