aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-09-23 17:19:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-09-26 18:51:10 -0400
commit65580b4321eb36f16ae8b5987bfa1bb948fc5112 (patch)
tree12735f923ad670c405efb5ab34c188dc06ad15f7 /drivers/usb/host/xhci.c
parent9574323c39d1f8359a04843075d89c9f32d8b7e6 (diff)
xHCI: set USB2 hardware LPM
If the device pass the USB2 software LPM and the host supports hardware LPM, enable hardware LPM for the device to let the host decide when to put the link into lower power state. If hardware LPM is enabled for a port and driver wants to put it into suspend, it must first disable hardware LPM, resume the port into U0, and then suspend the port. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r--drivers/usb/host/xhci.c74
1 files changed, 73 insertions, 1 deletions
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
3707int 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
3702int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) 3761int 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
3782int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
3783 struct usb_device *udev, int enable)
3784{
3785 return 0;
3786}
3787
3716int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) 3788int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
3717{ 3789{
3718 return 0; 3790 return 0;