diff options
-rw-r--r-- | drivers/usb/core/driver.c | 14 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 9 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 74 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 4 | ||||
-rw-r--r-- | include/linux/usb.h | 4 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 1 |
9 files changed, 120 insertions, 1 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 14b83f2a4e87..adf5ca8a2396 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) | |||
1700 | return 0; | 1700 | return 0; |
1701 | } | 1701 | } |
1702 | 1702 | ||
1703 | int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) | ||
1704 | { | ||
1705 | struct usb_hcd *hcd = bus_to_hcd(udev->bus); | ||
1706 | int ret = -EPERM; | ||
1707 | |||
1708 | if (hcd->driver->set_usb2_hw_lpm) { | ||
1709 | ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); | ||
1710 | if (!ret) | ||
1711 | udev->usb2_hw_lpm_enabled = enable; | ||
1712 | } | ||
1713 | |||
1714 | return ret; | ||
1715 | } | ||
1716 | |||
1703 | #endif /* CONFIG_USB_SUSPEND */ | 1717 | #endif /* CONFIG_USB_SUSPEND */ |
1704 | 1718 | ||
1705 | struct bus_type usb_bus_type = { | 1719 | struct bus_type usb_bus_type = { |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4ffc3d1bd9e7..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) | |||
2392 | } | 2392 | } |
2393 | } | 2393 | } |
2394 | 2394 | ||
2395 | /* disable USB2 hardware LPM */ | ||
2396 | if (udev->usb2_hw_lpm_enabled == 1) | ||
2397 | usb_set_usb2_hardware_lpm(udev, 0); | ||
2398 | |||
2395 | /* see 7.1.7.6 */ | 2399 | /* see 7.1.7.6 */ |
2396 | if (hub_is_superspeed(hub->hdev)) | 2400 | if (hub_is_superspeed(hub->hdev)) |
2397 | status = set_port_feature(hub->hdev, | 2401 | status = set_port_feature(hub->hdev, |
@@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) | |||
2603 | if (status < 0) { | 2607 | if (status < 0) { |
2604 | dev_dbg(&udev->dev, "can't resume, status %d\n", status); | 2608 | dev_dbg(&udev->dev, "can't resume, status %d\n", status); |
2605 | hub_port_logical_disconnect(hub, port1); | 2609 | hub_port_logical_disconnect(hub, port1); |
2610 | } else { | ||
2611 | /* Try to enable USB2 hardware LPM */ | ||
2612 | if (udev->usb2_hw_lpm_capable == 1) | ||
2613 | usb_set_usb2_hardware_lpm(udev, 1); | ||
2606 | } | 2614 | } |
2615 | |||
2607 | return status; | 2616 | return status; |
2608 | } | 2617 | } |
2609 | 2618 | ||
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0d023cd2c149..3888778582c4 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
@@ -82,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); | |||
82 | extern int usb_runtime_suspend(struct device *dev); | 82 | extern int usb_runtime_suspend(struct device *dev); |
83 | extern int usb_runtime_resume(struct device *dev); | 83 | extern int usb_runtime_resume(struct device *dev); |
84 | extern int usb_runtime_idle(struct device *dev); | 84 | extern int usb_runtime_idle(struct device *dev); |
85 | extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); | ||
85 | 86 | ||
86 | #else | 87 | #else |
87 | 88 | ||
@@ -96,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) | |||
96 | return 0; | 97 | return 0; |
97 | } | 98 | } |
98 | 99 | ||
100 | static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) | ||
101 | { | ||
102 | return 0; | ||
103 | } | ||
99 | #endif | 104 | #endif |
100 | 105 | ||
101 | extern struct bus_type usb_bus_type; | 106 | extern struct bus_type usb_bus_type; |
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d7be6d7324d3..9f844d45c667 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -574,10 +574,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
574 | switch (wValue) { | 574 | switch (wValue) { |
575 | case USB_PORT_FEAT_SUSPEND: | 575 | case USB_PORT_FEAT_SUSPEND: |
576 | temp = xhci_readl(xhci, port_array[wIndex]); | 576 | temp = xhci_readl(xhci, port_array[wIndex]); |
577 | if ((temp & PORT_PLS_MASK) != XDEV_U0) { | ||
578 | /* Resume the port to U0 first */ | ||
579 | xhci_set_link_state(xhci, port_array, wIndex, | ||
580 | XDEV_U0); | ||
581 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
582 | msleep(10); | ||
583 | spin_lock_irqsave(&xhci->lock, flags); | ||
584 | } | ||
577 | /* In spec software should not attempt to suspend | 585 | /* In spec software should not attempt to suspend |
578 | * a port unless the port reports that it is in the | 586 | * a port unless the port reports that it is in the |
579 | * enabled (PED = ‘1’,PLS < ‘3’) state. | 587 | * enabled (PED = ‘1’,PLS < ‘3’) state. |
580 | */ | 588 | */ |
589 | temp = xhci_readl(xhci, port_array[wIndex]); | ||
581 | if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) | 590 | if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) |
582 | || (temp & PORT_PLS_MASK) >= XDEV_U3) { | 591 | || (temp & PORT_PLS_MASK) >= XDEV_U3) { |
583 | xhci_warn(xhci, "USB core suspending device " | 592 | xhci_warn(xhci, "USB core suspending device " |
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 213a7d73b118..e66e2b03fbbe 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c | |||
@@ -349,6 +349,7 @@ static const struct hc_driver xhci_pci_hc_driver = { | |||
349 | * call back when device connected and addressed | 349 | * call back when device connected and addressed |
350 | */ | 350 | */ |
351 | .update_device = xhci_update_device, | 351 | .update_device = xhci_update_device, |
352 | .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, | ||
352 | }; | 353 | }; |
353 | 354 | ||
354 | /*-------------------------------------------------------------------------*/ | 355 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b0649a4bd315..4648cc0c5721 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c | |||
@@ -3286,6 +3286,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) | |||
3286 | del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); | 3286 | del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); |
3287 | } | 3287 | } |
3288 | 3288 | ||
3289 | if (udev->usb2_hw_lpm_enabled) { | ||
3290 | xhci_set_usb2_hardware_lpm(hcd, udev, 0); | ||
3291 | udev->usb2_hw_lpm_enabled = 0; | ||
3292 | } | ||
3293 | |||
3289 | spin_lock_irqsave(&xhci->lock, flags); | 3294 | spin_lock_irqsave(&xhci->lock, flags); |
3290 | /* Don't disable the slot if the host controller is dead. */ | 3295 | /* Don't disable the slot if the host controller is dead. */ |
3291 | state = xhci_readl(xhci, &xhci->op_regs->status); | 3296 | state = xhci_readl(xhci, &xhci->op_regs->status); |
@@ -3699,20 +3704,87 @@ finish: | |||
3699 | return ret; | 3704 | return ret; |
3700 | } | 3705 | } |
3701 | 3706 | ||
3707 | int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, | ||
3708 | struct usb_device *udev, int enable) | ||
3709 | { | ||
3710 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | ||
3711 | __le32 __iomem **port_array; | ||
3712 | __le32 __iomem *pm_addr; | ||
3713 | u32 temp; | ||
3714 | unsigned int port_num; | ||
3715 | unsigned long flags; | ||
3716 | int u2del, hird; | ||
3717 | |||
3718 | if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || | ||
3719 | !udev->lpm_capable) | ||
3720 | return -EPERM; | ||
3721 | |||
3722 | if (!udev->parent || udev->parent->parent || | ||
3723 | udev->descriptor.bDeviceClass == USB_CLASS_HUB) | ||
3724 | return -EPERM; | ||
3725 | |||
3726 | if (udev->usb2_hw_lpm_capable != 1) | ||
3727 | return -EPERM; | ||
3728 | |||
3729 | spin_lock_irqsave(&xhci->lock, flags); | ||
3730 | |||
3731 | port_array = xhci->usb2_ports; | ||
3732 | port_num = udev->portnum - 1; | ||
3733 | pm_addr = port_array[port_num] + 1; | ||
3734 | temp = xhci_readl(xhci, pm_addr); | ||
3735 | |||
3736 | xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", | ||
3737 | enable ? "enable" : "disable", port_num); | ||
3738 | |||
3739 | u2del = HCS_U2_LATENCY(xhci->hcs_params3); | ||
3740 | if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) | ||
3741 | hird = xhci_calculate_hird_besl(u2del, 1); | ||
3742 | else | ||
3743 | hird = xhci_calculate_hird_besl(u2del, 0); | ||
3744 | |||
3745 | if (enable) { | ||
3746 | temp &= ~PORT_HIRD_MASK; | ||
3747 | temp |= PORT_HIRD(hird) | PORT_RWE; | ||
3748 | xhci_writel(xhci, temp, pm_addr); | ||
3749 | temp = xhci_readl(xhci, pm_addr); | ||
3750 | temp |= PORT_HLE; | ||
3751 | xhci_writel(xhci, temp, pm_addr); | ||
3752 | } else { | ||
3753 | temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); | ||
3754 | xhci_writel(xhci, temp, pm_addr); | ||
3755 | } | ||
3756 | |||
3757 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
3758 | return 0; | ||
3759 | } | ||
3760 | |||
3702 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) | 3761 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) |
3703 | { | 3762 | { |
3704 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); | 3763 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
3705 | int ret; | 3764 | int ret; |
3706 | 3765 | ||
3707 | ret = xhci_usb2_software_lpm_test(hcd, udev); | 3766 | ret = xhci_usb2_software_lpm_test(hcd, udev); |
3708 | if (!ret) | 3767 | if (!ret) { |
3709 | xhci_dbg(xhci, "software LPM test succeed\n"); | 3768 | xhci_dbg(xhci, "software LPM test succeed\n"); |
3769 | if (xhci->hw_lpm_support == 1) { | ||
3770 | udev->usb2_hw_lpm_capable = 1; | ||
3771 | ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); | ||
3772 | if (!ret) | ||
3773 | udev->usb2_hw_lpm_enabled = 1; | ||
3774 | } | ||
3775 | } | ||
3710 | 3776 | ||
3711 | return 0; | 3777 | return 0; |
3712 | } | 3778 | } |
3713 | 3779 | ||
3714 | #else | 3780 | #else |
3715 | 3781 | ||
3782 | int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, | ||
3783 | struct usb_device *udev, int enable) | ||
3784 | { | ||
3785 | return 0; | ||
3786 | } | ||
3787 | |||
3716 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) | 3788 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) |
3717 | { | 3789 | { |
3718 | return 0; | 3790 | return 0; |
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b24c4fce457e..e738466703a5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h | |||
@@ -367,7 +367,9 @@ struct xhci_op_regs { | |||
367 | #define PORT_L1S_SUCCESS 1 | 367 | #define PORT_L1S_SUCCESS 1 |
368 | #define PORT_RWE (1 << 3) | 368 | #define PORT_RWE (1 << 3) |
369 | #define PORT_HIRD(p) (((p) & 0xf) << 4) | 369 | #define PORT_HIRD(p) (((p) & 0xf) << 4) |
370 | #define PORT_HIRD_MASK (0xf << 4) | ||
370 | #define PORT_L1DS(p) (((p) & 0xff) << 8) | 371 | #define PORT_L1DS(p) (((p) & 0xff) << 8) |
372 | #define PORT_HLE (1 << 16) | ||
371 | 373 | ||
372 | /** | 374 | /** |
373 | * struct xhci_intr_reg - Interrupt Register Set | 375 | * struct xhci_intr_reg - Interrupt Register Set |
@@ -1677,6 +1679,8 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, | |||
1677 | gfp_t mem_flags); | 1679 | gfp_t mem_flags); |
1678 | int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); | 1680 | int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); |
1679 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); | 1681 | int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); |
1682 | int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, | ||
1683 | struct usb_device *udev, int enable); | ||
1680 | int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, | 1684 | int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, |
1681 | struct usb_tt *tt, gfp_t mem_flags); | 1685 | struct usb_tt *tt, gfp_t mem_flags); |
1682 | int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); | 1686 | int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 1d00d9bc5d65..6f49a1b39fa6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -411,6 +411,8 @@ struct usb_tt; | |||
411 | * @authenticated: Crypto authentication passed | 411 | * @authenticated: Crypto authentication passed |
412 | * @wusb: device is Wireless USB | 412 | * @wusb: device is Wireless USB |
413 | * @lpm_capable: device supports LPM | 413 | * @lpm_capable: device supports LPM |
414 | * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM | ||
415 | * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled | ||
414 | * @string_langid: language ID for strings | 416 | * @string_langid: language ID for strings |
415 | * @product: iProduct string, if present (static) | 417 | * @product: iProduct string, if present (static) |
416 | * @manufacturer: iManufacturer string, if present (static) | 418 | * @manufacturer: iManufacturer string, if present (static) |
@@ -474,6 +476,8 @@ struct usb_device { | |||
474 | unsigned authenticated:1; | 476 | unsigned authenticated:1; |
475 | unsigned wusb:1; | 477 | unsigned wusb:1; |
476 | unsigned lpm_capable:1; | 478 | unsigned lpm_capable:1; |
479 | unsigned usb2_hw_lpm_capable:1; | ||
480 | unsigned usb2_hw_lpm_enabled:1; | ||
477 | int string_langid; | 481 | int string_langid; |
478 | 482 | ||
479 | /* static strings from the device */ | 483 | /* static strings from the device */ |
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 0097136ba45d..a4cd6c58870a 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h | |||
@@ -343,6 +343,7 @@ struct hc_driver { | |||
343 | * address is set | 343 | * address is set |
344 | */ | 344 | */ |
345 | int (*update_device)(struct usb_hcd *, struct usb_device *); | 345 | int (*update_device)(struct usb_hcd *, struct usb_device *); |
346 | int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); | ||
346 | }; | 347 | }; |
347 | 348 | ||
348 | extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); | 349 | extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); |