Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: xhci: add USB2 Link power management BESL support

usb 2.0 devices with link power managment (LPM) can describe their idle link
timeouts either in BESL or HIRD format, so far xHCI has only supported HIRD but
later xHCI errata add BESL support as well

BESL timeouts need to inform exit latency changes with an evaluate
context command the same way USB 3.0 link PM code does.
The same xhci_change_max_exit_latency() function is used as with USB3
but code is pulled out from #ifdef CONFIG_PM as USB2.0 BESL LPM
funcionality does not depend on CONFIG_PM.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>

authored by

Mathias Nyman and committed by
Sarah Sharp
a558ccdc b6e76371

+164 -64
+1
drivers/usb/host/xhci-ext-caps.h
··· 71 71 72 72 /* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ 73 73 #define XHCI_HLC (1 << 19) 74 + #define XHCI_BLC (1 << 19) 74 75 75 76 /* command register values to disable interrupts and halt the HC */ 76 77 /* start/stop HC execution - do not write unless HC is halted*/
+140 -64
drivers/usb/host/xhci.c
··· 3815 3815 return raw_port; 3816 3816 } 3817 3817 3818 + /* 3819 + * Issue an Evaluate Context command to change the Maximum Exit Latency in the 3820 + * slot context. If that succeeds, store the new MEL in the xhci_virt_device. 3821 + */ 3822 + static int xhci_change_max_exit_latency(struct xhci_hcd *xhci, 3823 + struct usb_device *udev, u16 max_exit_latency) 3824 + { 3825 + struct xhci_virt_device *virt_dev; 3826 + struct xhci_command *command; 3827 + struct xhci_input_control_ctx *ctrl_ctx; 3828 + struct xhci_slot_ctx *slot_ctx; 3829 + unsigned long flags; 3830 + int ret; 3831 + 3832 + spin_lock_irqsave(&xhci->lock, flags); 3833 + if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) { 3834 + spin_unlock_irqrestore(&xhci->lock, flags); 3835 + return 0; 3836 + } 3837 + 3838 + /* Attempt to issue an Evaluate Context command to change the MEL. */ 3839 + virt_dev = xhci->devs[udev->slot_id]; 3840 + command = xhci->lpm_command; 3841 + xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx); 3842 + spin_unlock_irqrestore(&xhci->lock, flags); 3843 + 3844 + ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx); 3845 + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); 3846 + slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx); 3847 + slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT)); 3848 + slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency); 3849 + 3850 + xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n"); 3851 + xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id); 3852 + xhci_dbg_ctx(xhci, command->in_ctx, 0); 3853 + 3854 + /* Issue and wait for the evaluate context command. */ 3855 + ret = xhci_configure_endpoint(xhci, udev, command, 3856 + true, true); 3857 + xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id); 3858 + xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0); 3859 + 3860 + if (!ret) { 3861 + spin_lock_irqsave(&xhci->lock, flags); 3862 + virt_dev->current_mel = max_exit_latency; 3863 + spin_unlock_irqrestore(&xhci->lock, flags); 3864 + } 3865 + return ret; 3866 + } 3867 + 3818 3868 #ifdef CONFIG_PM_RUNTIME 3819 3869 3820 3870 /* BESL to HIRD Encoding array for USB2 LPM */ ··· 3904 3854 besl = 15; 3905 3855 3906 3856 return besl; 3857 + } 3858 + 3859 + /* Calculate BESLD, L1 timeout and HIRDM for USB2 PORTHLPMC */ 3860 + static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev) 3861 + { 3862 + u32 field; 3863 + int l1; 3864 + int besld = 0; 3865 + int hirdm = 0; 3866 + 3867 + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); 3868 + 3869 + /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */ 3870 + l1 = XHCI_L1_TIMEOUT / 256; 3871 + 3872 + /* device has preferred BESLD */ 3873 + if (field & USB_BESL_DEEP_VALID) { 3874 + besld = USB_GET_BESL_DEEP(field); 3875 + hirdm = 1; 3876 + } 3877 + 3878 + return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm); 3907 3879 } 3908 3880 3909 3881 static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, ··· 4060 3988 { 4061 3989 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 4062 3990 __le32 __iomem **port_array; 4063 - __le32 __iomem *pm_addr; 4064 - u32 temp; 3991 + __le32 __iomem *pm_addr, *hlpm_addr; 3992 + u32 pm_val, hlpm_val, field; 4065 3993 unsigned int port_num; 4066 3994 unsigned long flags; 4067 - int hird; 3995 + int hird, exit_latency; 3996 + int ret; 4068 3997 4069 3998 if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || 4070 3999 !udev->lpm_capable) ··· 4083 4010 port_array = xhci->usb2_ports; 4084 4011 port_num = udev->portnum - 1; 4085 4012 pm_addr = port_array[port_num] + PORTPMSC; 4086 - temp = xhci_readl(xhci, pm_addr); 4013 + pm_val = xhci_readl(xhci, pm_addr); 4014 + hlpm_addr = port_array[port_num] + PORTHLPMC; 4015 + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); 4087 4016 4088 4017 xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", 4089 4018 enable ? "enable" : "disable", port_num); 4090 4019 4091 - hird = xhci_calculate_hird_besl(xhci, udev); 4092 - 4093 4020 if (enable) { 4094 - temp &= ~PORT_HIRD_MASK; 4095 - temp |= PORT_HIRD(hird) | PORT_RWE; 4096 - xhci_writel(xhci, temp, pm_addr); 4097 - temp = xhci_readl(xhci, pm_addr); 4098 - temp |= PORT_HLE; 4099 - xhci_writel(xhci, temp, pm_addr); 4021 + /* Host supports BESL timeout instead of HIRD */ 4022 + if (udev->usb2_hw_lpm_besl_capable) { 4023 + /* if device doesn't have a preferred BESL value use a 4024 + * default one which works with mixed HIRD and BESL 4025 + * systems. See XHCI_DEFAULT_BESL definition in xhci.h 4026 + */ 4027 + if ((field & USB_BESL_SUPPORT) && 4028 + (field & USB_BESL_BASELINE_VALID)) 4029 + hird = USB_GET_BESL_BASELINE(field); 4030 + else 4031 + hird = XHCI_DEFAULT_BESL; 4032 + 4033 + exit_latency = xhci_besl_encoding[hird]; 4034 + spin_unlock_irqrestore(&xhci->lock, flags); 4035 + 4036 + /* USB 3.0 code dedicate one xhci->lpm_command->in_ctx 4037 + * input context for link powermanagement evaluate 4038 + * context commands. It is protected by hcd->bandwidth 4039 + * mutex and is shared by all devices. We need to set 4040 + * the max ext latency in USB 2 BESL LPM as well, so 4041 + * use the same mutex and xhci_change_max_exit_latency() 4042 + */ 4043 + mutex_lock(hcd->bandwidth_mutex); 4044 + ret = xhci_change_max_exit_latency(xhci, udev, 4045 + exit_latency); 4046 + mutex_unlock(hcd->bandwidth_mutex); 4047 + 4048 + if (ret < 0) 4049 + return ret; 4050 + spin_lock_irqsave(&xhci->lock, flags); 4051 + 4052 + hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); 4053 + xhci_writel(xhci, hlpm_val, hlpm_addr); 4054 + /* flush write */ 4055 + xhci_readl(xhci, hlpm_addr); 4056 + } else { 4057 + hird = xhci_calculate_hird_besl(xhci, udev); 4058 + } 4059 + 4060 + pm_val &= ~PORT_HIRD_MASK; 4061 + pm_val |= PORT_HIRD(hird) | PORT_RWE; 4062 + xhci_writel(xhci, pm_val, pm_addr); 4063 + pm_val = xhci_readl(xhci, pm_addr); 4064 + pm_val |= PORT_HLE; 4065 + xhci_writel(xhci, pm_val, pm_addr); 4066 + /* flush write */ 4067 + xhci_readl(xhci, pm_addr); 4100 4068 } else { 4101 - temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); 4102 - xhci_writel(xhci, temp, pm_addr); 4069 + pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); 4070 + xhci_writel(xhci, pm_val, pm_addr); 4071 + /* flush write */ 4072 + xhci_readl(xhci, pm_addr); 4073 + if (udev->usb2_hw_lpm_besl_capable) { 4074 + spin_unlock_irqrestore(&xhci->lock, flags); 4075 + mutex_lock(hcd->bandwidth_mutex); 4076 + xhci_change_max_exit_latency(xhci, udev, 0); 4077 + mutex_unlock(hcd->bandwidth_mutex); 4078 + return 0; 4079 + } 4103 4080 } 4104 4081 4105 4082 spin_unlock_irqrestore(&xhci->lock, flags); ··· 4191 4068 if (xhci->hw_lpm_support == 1 && 4192 4069 xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { 4193 4070 udev->usb2_hw_lpm_capable = 1; 4071 + if (xhci_check_usb2_port_capability(xhci, portnum, 4072 + XHCI_BLC)) 4073 + udev->usb2_hw_lpm_besl_capable = 1; 4194 4074 ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); 4195 4075 if (!ret) 4196 4076 udev->usb2_hw_lpm_enabled = 1; ··· 4522 4396 return timeout; 4523 4397 } 4524 4398 return timeout; 4525 - } 4526 - 4527 - /* 4528 - * Issue an Evaluate Context command to change the Maximum Exit Latency in the 4529 - * slot context. If that succeeds, store the new MEL in the xhci_virt_device. 4530 - */ 4531 - static int xhci_change_max_exit_latency(struct xhci_hcd *xhci, 4532 - struct usb_device *udev, u16 max_exit_latency) 4533 - { 4534 - struct xhci_virt_device *virt_dev; 4535 - struct xhci_command *command; 4536 - struct xhci_input_control_ctx *ctrl_ctx; 4537 - struct xhci_slot_ctx *slot_ctx; 4538 - unsigned long flags; 4539 - int ret; 4540 - 4541 - spin_lock_irqsave(&xhci->lock, flags); 4542 - if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) { 4543 - spin_unlock_irqrestore(&xhci->lock, flags); 4544 - return 0; 4545 - } 4546 - 4547 - /* Attempt to issue an Evaluate Context command to change the MEL. */ 4548 - virt_dev = xhci->devs[udev->slot_id]; 4549 - command = xhci->lpm_command; 4550 - xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx); 4551 - spin_unlock_irqrestore(&xhci->lock, flags); 4552 - 4553 - ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx); 4554 - ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); 4555 - slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx); 4556 - slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT)); 4557 - slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency); 4558 - 4559 - xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n"); 4560 - xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id); 4561 - xhci_dbg_ctx(xhci, command->in_ctx, 0); 4562 - 4563 - /* Issue and wait for the evaluate context command. */ 4564 - ret = xhci_configure_endpoint(xhci, udev, command, 4565 - true, true); 4566 - xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id); 4567 - xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0); 4568 - 4569 - if (!ret) { 4570 - spin_lock_irqsave(&xhci->lock, flags); 4571 - virt_dev->current_mel = max_exit_latency; 4572 - spin_unlock_irqrestore(&xhci->lock, flags); 4573 - } 4574 - return ret; 4575 4399 } 4576 4400 4577 4401 static int calculate_max_exit_latency(struct usb_device *udev,
+21
drivers/usb/host/xhci.h
··· 386 386 #define PORT_L1DS(p) (((p) & 0xff) << 8) 387 387 #define PORT_HLE (1 << 16) 388 388 389 + 390 + /* USB2 Protocol PORTHLPMC */ 391 + #define PORT_HIRDM(p)((p) & 3) 392 + #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) 393 + #define PORT_BESLD(p)(((p) & 0xf) << 10) 394 + 395 + /* use 512 microseconds as USB2 LPM L1 default timeout. */ 396 + #define XHCI_L1_TIMEOUT 512 397 + 398 + /* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume latency. 399 + * Safe to use with mixed HIRD and BESL systems (host and device) and is used 400 + * by other operating systems. 401 + * 402 + * XHCI 1.0 errata 8/14/12 Table 13 notes: 403 + * "Software should choose xHC BESL/BESLD field values that do not violate a 404 + * device's resume latency requirements, 405 + * e.g. not program values > '4' if BLC = '1' and a HIRD device is attached, 406 + * or not program values < '4' if BLC = '0' and a BESL device is attached. 407 + */ 408 + #define XHCI_DEFAULT_BESL 4 409 + 389 410 /** 390 411 * struct xhci_intr_reg - Interrupt Register Set 391 412 * @irq_pending: IMAN - Interrupt Management Register. Used to enable
+2
include/linux/usb.h
··· 468 468 * @wusb: device is Wireless USB 469 469 * @lpm_capable: device supports LPM 470 470 * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM 471 + * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM 471 472 * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled 472 473 * @usb3_lpm_enabled: USB3 hardware LPM enabled 473 474 * @string_langid: language ID for strings ··· 539 538 unsigned wusb:1; 540 539 unsigned lpm_capable:1; 541 540 unsigned usb2_hw_lpm_capable:1; 541 + unsigned usb2_hw_lpm_besl_capable:1; 542 542 unsigned usb2_hw_lpm_enabled:1; 543 543 unsigned usb3_lpm_enabled:1; 544 544 int string_langid;