diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-07 13:14:23 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-01-07 13:14:23 -0500 |
| commit | 102ee001912f67a7701f26a56ef2bcf84fc78028 (patch) | |
| tree | ef1f70c744a734a745c4c8fbd67294bdbbc7df9a /drivers | |
| parent | 962426e0e2312c5134db13f39d4389a2fa6a13d0 (diff) | |
| parent | 026630d09ba7b6cd44a43b92ad256c28a9e3ddc2 (diff) | |
Merge tag 'for-usb-next-2013-01-03' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next
Sarah writes:
usb-next: Further warm reset improvements
Hi Greg,
Here's some patches for 3.9. They further improve the warm reset
error handling, but they're too big to go into stable. There's also a
patch to remove an unused variable in the xHCI driver.
As I mentioned, you'll need to merge usb-linus into usb-next before
applying these patches.
Sarah Sharp
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/usb/core/hub.c | 207 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-ring.c | 2 |
2 files changed, 107 insertions, 102 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9641e9c1dec5..ae10862fb041 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
| @@ -2535,77 +2535,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, | |||
| 2535 | return ret; | 2535 | return ret; |
| 2536 | 2536 | ||
| 2537 | /* The port state is unknown until the reset completes. */ | 2537 | /* The port state is unknown until the reset completes. */ |
| 2538 | if ((portstatus & USB_PORT_STAT_RESET)) | 2538 | if (!(portstatus & USB_PORT_STAT_RESET)) |
| 2539 | goto delay; | 2539 | break; |
| 2540 | |||
| 2541 | /* | ||
| 2542 | * Some buggy devices require a warm reset to be issued even | ||
| 2543 | * when the port appears not to be connected. | ||
| 2544 | */ | ||
| 2545 | if (!warm) { | ||
| 2546 | /* | ||
| 2547 | * Some buggy devices can cause an NEC host controller | ||
| 2548 | * to transition to the "Error" state after a hot port | ||
| 2549 | * reset. This will show up as the port state in | ||
| 2550 | * "Inactive", and the port may also report a | ||
| 2551 | * disconnect. Forcing a warm port reset seems to make | ||
| 2552 | * the device work. | ||
| 2553 | * | ||
| 2554 | * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 | ||
| 2555 | */ | ||
| 2556 | if (hub_port_warm_reset_required(hub, portstatus)) { | ||
| 2557 | int ret; | ||
| 2558 | |||
| 2559 | if ((portchange & USB_PORT_STAT_C_CONNECTION)) | ||
| 2560 | clear_port_feature(hub->hdev, port1, | ||
| 2561 | USB_PORT_FEAT_C_CONNECTION); | ||
| 2562 | if (portchange & USB_PORT_STAT_C_LINK_STATE) | ||
| 2563 | clear_port_feature(hub->hdev, port1, | ||
| 2564 | USB_PORT_FEAT_C_PORT_LINK_STATE); | ||
| 2565 | if (portchange & USB_PORT_STAT_C_RESET) | ||
| 2566 | clear_port_feature(hub->hdev, port1, | ||
| 2567 | USB_PORT_FEAT_C_RESET); | ||
| 2568 | dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", | ||
| 2569 | port1); | ||
| 2570 | ret = hub_port_reset(hub, port1, | ||
| 2571 | udev, HUB_BH_RESET_TIME, | ||
| 2572 | true); | ||
| 2573 | if ((portchange & USB_PORT_STAT_C_CONNECTION)) | ||
| 2574 | clear_port_feature(hub->hdev, port1, | ||
| 2575 | USB_PORT_FEAT_C_CONNECTION); | ||
| 2576 | return ret; | ||
| 2577 | } | ||
| 2578 | /* Device went away? */ | ||
| 2579 | if (!(portstatus & USB_PORT_STAT_CONNECTION)) | ||
| 2580 | return -ENOTCONN; | ||
| 2581 | |||
| 2582 | /* bomb out completely if the connection bounced */ | ||
| 2583 | if ((portchange & USB_PORT_STAT_C_CONNECTION)) | ||
| 2584 | return -ENOTCONN; | ||
| 2585 | |||
| 2586 | if ((portstatus & USB_PORT_STAT_ENABLE)) { | ||
| 2587 | if (hub_is_wusb(hub)) | ||
| 2588 | udev->speed = USB_SPEED_WIRELESS; | ||
| 2589 | else if (hub_is_superspeed(hub->hdev)) | ||
| 2590 | udev->speed = USB_SPEED_SUPER; | ||
| 2591 | else if (portstatus & USB_PORT_STAT_HIGH_SPEED) | ||
| 2592 | udev->speed = USB_SPEED_HIGH; | ||
| 2593 | else if (portstatus & USB_PORT_STAT_LOW_SPEED) | ||
| 2594 | udev->speed = USB_SPEED_LOW; | ||
| 2595 | else | ||
| 2596 | udev->speed = USB_SPEED_FULL; | ||
| 2597 | return 0; | ||
| 2598 | } | ||
| 2599 | } else { | ||
| 2600 | if (!(portstatus & USB_PORT_STAT_CONNECTION) || | ||
| 2601 | hub_port_warm_reset_required(hub, | ||
| 2602 | portstatus)) | ||
| 2603 | return -ENOTCONN; | ||
| 2604 | |||
| 2605 | return 0; | ||
| 2606 | } | ||
| 2607 | 2540 | ||
| 2608 | delay: | ||
| 2609 | /* switch to the long delay after two short delay failures */ | 2541 | /* switch to the long delay after two short delay failures */ |
| 2610 | if (delay_time >= 2 * HUB_SHORT_RESET_TIME) | 2542 | if (delay_time >= 2 * HUB_SHORT_RESET_TIME) |
| 2611 | delay = HUB_LONG_RESET_TIME; | 2543 | delay = HUB_LONG_RESET_TIME; |
| @@ -2615,20 +2547,54 @@ delay: | |||
| 2615 | port1, warm ? "warm " : "", delay); | 2547 | port1, warm ? "warm " : "", delay); |
| 2616 | } | 2548 | } |
| 2617 | 2549 | ||
| 2618 | return -EBUSY; | 2550 | if ((portstatus & USB_PORT_STAT_RESET)) |
| 2551 | return -EBUSY; | ||
| 2552 | |||
| 2553 | if (hub_port_warm_reset_required(hub, portstatus)) | ||
| 2554 | return -ENOTCONN; | ||
| 2555 | |||
| 2556 | /* Device went away? */ | ||
| 2557 | if (!(portstatus & USB_PORT_STAT_CONNECTION)) | ||
| 2558 | return -ENOTCONN; | ||
| 2559 | |||
| 2560 | /* bomb out completely if the connection bounced. A USB 3.0 | ||
| 2561 | * connection may bounce if multiple warm resets were issued, | ||
| 2562 | * but the device may have successfully re-connected. Ignore it. | ||
| 2563 | */ | ||
| 2564 | if (!hub_is_superspeed(hub->hdev) && | ||
| 2565 | (portchange & USB_PORT_STAT_C_CONNECTION)) | ||
| 2566 | return -ENOTCONN; | ||
| 2567 | |||
| 2568 | if (!(portstatus & USB_PORT_STAT_ENABLE)) | ||
| 2569 | return -EBUSY; | ||
| 2570 | |||
| 2571 | if (!udev) | ||
| 2572 | return 0; | ||
| 2573 | |||
| 2574 | if (hub_is_wusb(hub)) | ||
| 2575 | udev->speed = USB_SPEED_WIRELESS; | ||
| 2576 | else if (hub_is_superspeed(hub->hdev)) | ||
| 2577 | udev->speed = USB_SPEED_SUPER; | ||
| 2578 | else if (portstatus & USB_PORT_STAT_HIGH_SPEED) | ||
| 2579 | udev->speed = USB_SPEED_HIGH; | ||
| 2580 | else if (portstatus & USB_PORT_STAT_LOW_SPEED) | ||
| 2581 | udev->speed = USB_SPEED_LOW; | ||
| 2582 | else | ||
| 2583 | udev->speed = USB_SPEED_FULL; | ||
| 2584 | return 0; | ||
| 2619 | } | 2585 | } |
| 2620 | 2586 | ||
| 2621 | static void hub_port_finish_reset(struct usb_hub *hub, int port1, | 2587 | static void hub_port_finish_reset(struct usb_hub *hub, int port1, |
| 2622 | struct usb_device *udev, int *status, bool warm) | 2588 | struct usb_device *udev, int *status) |
| 2623 | { | 2589 | { |
| 2624 | switch (*status) { | 2590 | switch (*status) { |
| 2625 | case 0: | 2591 | case 0: |
| 2626 | if (!warm) { | 2592 | /* TRSTRCY = 10 ms; plus some extra */ |
| 2627 | struct usb_hcd *hcd; | 2593 | msleep(10 + 40); |
| 2628 | /* TRSTRCY = 10 ms; plus some extra */ | 2594 | if (udev) { |
| 2629 | msleep(10 + 40); | 2595 | struct usb_hcd *hcd = bus_to_hcd(udev->bus); |
| 2596 | |||
| 2630 | update_devnum(udev, 0); | 2597 | update_devnum(udev, 0); |
| 2631 | hcd = bus_to_hcd(udev->bus); | ||
| 2632 | /* The xHC may think the device is already reset, | 2598 | /* The xHC may think the device is already reset, |
| 2633 | * so ignore the status. | 2599 | * so ignore the status. |
| 2634 | */ | 2600 | */ |
| @@ -2640,14 +2606,15 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1, | |||
| 2640 | case -ENODEV: | 2606 | case -ENODEV: |
| 2641 | clear_port_feature(hub->hdev, | 2607 | clear_port_feature(hub->hdev, |
| 2642 | port1, USB_PORT_FEAT_C_RESET); | 2608 | port1, USB_PORT_FEAT_C_RESET); |
| 2643 | /* FIXME need disconnect() for NOTATTACHED device */ | ||
| 2644 | if (hub_is_superspeed(hub->hdev)) { | 2609 | if (hub_is_superspeed(hub->hdev)) { |
| 2645 | clear_port_feature(hub->hdev, port1, | 2610 | clear_port_feature(hub->hdev, port1, |
| 2646 | USB_PORT_FEAT_C_BH_PORT_RESET); | 2611 | USB_PORT_FEAT_C_BH_PORT_RESET); |
| 2647 | clear_port_feature(hub->hdev, port1, | 2612 | clear_port_feature(hub->hdev, port1, |
| 2648 | USB_PORT_FEAT_C_PORT_LINK_STATE); | 2613 | USB_PORT_FEAT_C_PORT_LINK_STATE); |
| 2614 | clear_port_feature(hub->hdev, port1, | ||
| 2615 | USB_PORT_FEAT_C_CONNECTION); | ||
| 2649 | } | 2616 | } |
| 2650 | if (!warm) | 2617 | if (udev) |
| 2651 | usb_set_device_state(udev, *status | 2618 | usb_set_device_state(udev, *status |
| 2652 | ? USB_STATE_NOTATTACHED | 2619 | ? USB_STATE_NOTATTACHED |
| 2653 | : USB_STATE_DEFAULT); | 2620 | : USB_STATE_DEFAULT); |
| @@ -2660,18 +2627,30 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
| 2660 | struct usb_device *udev, unsigned int delay, bool warm) | 2627 | struct usb_device *udev, unsigned int delay, bool warm) |
| 2661 | { | 2628 | { |
| 2662 | int i, status; | 2629 | int i, status; |
| 2630 | u16 portchange, portstatus; | ||
| 2663 | 2631 | ||
| 2664 | if (!warm) { | 2632 | if (!hub_is_superspeed(hub->hdev)) { |
| 2665 | /* Block EHCI CF initialization during the port reset. | 2633 | if (warm) { |
| 2666 | * Some companion controllers don't like it when they mix. | ||
| 2667 | */ | ||
| 2668 | down_read(&ehci_cf_port_reset_rwsem); | ||
| 2669 | } else { | ||
| 2670 | if (!hub_is_superspeed(hub->hdev)) { | ||
| 2671 | dev_err(hub->intfdev, "only USB3 hub support " | 2634 | dev_err(hub->intfdev, "only USB3 hub support " |
| 2672 | "warm reset\n"); | 2635 | "warm reset\n"); |
| 2673 | return -EINVAL; | 2636 | return -EINVAL; |
| 2674 | } | 2637 | } |
| 2638 | /* Block EHCI CF initialization during the port reset. | ||
| 2639 | * Some companion controllers don't like it when they mix. | ||
| 2640 | */ | ||
| 2641 | down_read(&ehci_cf_port_reset_rwsem); | ||
| 2642 | } else if (!warm) { | ||
| 2643 | /* | ||
| 2644 | * If the caller hasn't explicitly requested a warm reset, | ||
| 2645 | * double check and see if one is needed. | ||
| 2646 | */ | ||
| 2647 | status = hub_port_status(hub, port1, | ||
| 2648 | &portstatus, &portchange); | ||
| 2649 | if (status < 0) | ||
| 2650 | goto done; | ||
| 2651 | |||
| 2652 | if (hub_port_warm_reset_required(hub, portstatus)) | ||
| 2653 | warm = true; | ||
| 2675 | } | 2654 | } |
| 2676 | 2655 | ||
| 2677 | /* Reset the port */ | 2656 | /* Reset the port */ |
| @@ -2692,10 +2671,33 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
| 2692 | status); | 2671 | status); |
| 2693 | } | 2672 | } |
| 2694 | 2673 | ||
| 2695 | /* return on disconnect or reset */ | 2674 | /* Check for disconnect or reset */ |
| 2696 | if (status == 0 || status == -ENOTCONN || status == -ENODEV) { | 2675 | if (status == 0 || status == -ENOTCONN || status == -ENODEV) { |
| 2697 | hub_port_finish_reset(hub, port1, udev, &status, warm); | 2676 | hub_port_finish_reset(hub, port1, udev, &status); |
| 2698 | goto done; | 2677 | |
| 2678 | if (!hub_is_superspeed(hub->hdev)) | ||
| 2679 | goto done; | ||
| 2680 | |||
| 2681 | /* | ||
| 2682 | * If a USB 3.0 device migrates from reset to an error | ||
| 2683 | * state, re-issue the warm reset. | ||
| 2684 | */ | ||
| 2685 | if (hub_port_status(hub, port1, | ||
| 2686 | &portstatus, &portchange) < 0) | ||
| 2687 | goto done; | ||
| 2688 | |||
| 2689 | if (!hub_port_warm_reset_required(hub, portstatus)) | ||
| 2690 | goto done; | ||
| 2691 | |||
| 2692 | /* | ||
| 2693 | * If the port is in SS.Inactive or Compliance Mode, the | ||
| 2694 | * hot or warm reset failed. Try another warm reset. | ||
| 2695 | */ | ||
| 2696 | if (!warm) { | ||
| 2697 | dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", | ||
| 2698 | port1); | ||
| 2699 | warm = true; | ||
| 2700 | } | ||
| 2699 | } | 2701 | } |
| 2700 | 2702 | ||
| 2701 | dev_dbg (hub->intfdev, | 2703 | dev_dbg (hub->intfdev, |
| @@ -2709,7 +2711,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
| 2709 | port1); | 2711 | port1); |
| 2710 | 2712 | ||
| 2711 | done: | 2713 | done: |
| 2712 | if (!warm) | 2714 | if (!hub_is_superspeed(hub->hdev)) |
| 2713 | up_read(&ehci_cf_port_reset_rwsem); | 2715 | up_read(&ehci_cf_port_reset_rwsem); |
| 2714 | 2716 | ||
| 2715 | return status; | 2717 | return status; |
| @@ -2945,9 +2947,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) | |||
| 2945 | 2947 | ||
| 2946 | /* see 7.1.7.6 */ | 2948 | /* see 7.1.7.6 */ |
| 2947 | if (hub_is_superspeed(hub->hdev)) | 2949 | if (hub_is_superspeed(hub->hdev)) |
| 2948 | status = set_port_feature(hub->hdev, | 2950 | status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); |
| 2949 | port1 | (USB_SS_PORT_LS_U3 << 3), | ||
| 2950 | USB_PORT_FEAT_LINK_STATE); | ||
| 2951 | else | 2951 | else |
| 2952 | status = set_port_feature(hub->hdev, port1, | 2952 | status = set_port_feature(hub->hdev, port1, |
| 2953 | USB_PORT_FEAT_SUSPEND); | 2953 | USB_PORT_FEAT_SUSPEND); |
| @@ -3117,9 +3117,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) | |||
| 3117 | 3117 | ||
| 3118 | /* see 7.1.7.7; affects power usage, but not budgeting */ | 3118 | /* see 7.1.7.7; affects power usage, but not budgeting */ |
| 3119 | if (hub_is_superspeed(hub->hdev)) | 3119 | if (hub_is_superspeed(hub->hdev)) |
| 3120 | status = set_port_feature(hub->hdev, | 3120 | status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0); |
| 3121 | port1 | (USB_SS_PORT_LS_U0 << 3), | ||
| 3122 | USB_PORT_FEAT_LINK_STATE); | ||
| 3123 | else | 3121 | else |
| 3124 | status = clear_port_feature(hub->hdev, | 3122 | status = clear_port_feature(hub->hdev, |
| 3125 | port1, USB_PORT_FEAT_SUSPEND); | 3123 | port1, USB_PORT_FEAT_SUSPEND); |
| @@ -4700,12 +4698,21 @@ static void hub_events(void) | |||
| 4700 | */ | 4698 | */ |
| 4701 | if (hub_port_warm_reset_required(hub, portstatus)) { | 4699 | if (hub_port_warm_reset_required(hub, portstatus)) { |
| 4702 | int status; | 4700 | int status; |
| 4701 | struct usb_device *udev = | ||
| 4702 | hub->ports[i - 1]->child; | ||
| 4703 | 4703 | ||
| 4704 | dev_dbg(hub_dev, "warm reset port %d\n", i); | 4704 | dev_dbg(hub_dev, "warm reset port %d\n", i); |
| 4705 | status = hub_port_reset(hub, i, NULL, | 4705 | if (!udev) { |
| 4706 | HUB_BH_RESET_TIME, true); | 4706 | status = hub_port_reset(hub, i, |
| 4707 | if (status < 0) | 4707 | NULL, HUB_BH_RESET_TIME, |
| 4708 | hub_port_disable(hub, i, 1); | 4708 | true); |
| 4709 | if (status < 0) | ||
| 4710 | hub_port_disable(hub, i, 1); | ||
| 4711 | } else { | ||
| 4712 | usb_lock_device(udev); | ||
| 4713 | status = usb_reset_device(udev); | ||
| 4714 | usb_unlock_device(udev); | ||
| 4715 | } | ||
| 4709 | connect_change = 0; | 4716 | connect_change = 0; |
| 4710 | } | 4717 | } |
| 4711 | 4718 | ||
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 59fb5c677dbe..f69720983fc7 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) | |||
| 2706 | { | 2706 | { |
| 2707 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 2707 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
| 2708 | u32 status; | 2708 | u32 status; |
| 2709 | union xhci_trb *trb; | ||
| 2710 | u64 temp_64; | 2709 | u64 temp_64; |
| 2711 | union xhci_trb *event_ring_deq; | 2710 | union xhci_trb *event_ring_deq; |
| 2712 | dma_addr_t deq; | 2711 | dma_addr_t deq; |
| 2713 | 2712 | ||
| 2714 | spin_lock(&xhci->lock); | 2713 | spin_lock(&xhci->lock); |
| 2715 | trb = xhci->event_ring->dequeue; | ||
| 2716 | /* Check if the xHC generated the interrupt, or the irq is shared */ | 2714 | /* Check if the xHC generated the interrupt, or the irq is shared */ |
| 2717 | status = xhci_readl(xhci, &xhci->op_regs->status); | 2715 | status = xhci_readl(xhci, &xhci->op_regs->status); |
| 2718 | if (status == 0xffffffff) | 2716 | if (status == 0xffffffff) |
