summaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2013-10-29 19:50:27 (GMT)
committerScott Wood <scottwood@freescale.com>2013-10-29 19:50:37 (GMT)
commitd0ebef8230e267ec47d4d4a65fe3262e2ebb8026 (patch)
tree24b8bb342576f543dac42d59821c4feb7ce07453 /drivers/usb/core
parent041f2bc64a985b30328de4cb596f04fd913a85de (diff)
downloadlinux-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.c2
-rw-r--r--drivers/usb/core/hcd-pci.c23
-rw-r--r--drivers/usb/core/hcd.c4
-rw-r--r--drivers/usb/core/hub.c178
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;
}