[Quantal][SRU][PATCH 2/2] usb: xhci: add USB2 Link power management BESL support
Shawn Wang
shawn.wang at canonical.com
Thu Sep 26 09:32:23 UTC 2013
Hi Tim,
The original commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09
add usb2_hw_lpm_besl_capable in struct
usb3_lpm_parameters. (include/linux/usb.h)
However, the XHCI_HLC and XHCI_BLC are the same.
We skip the below change, use usb2_hw_lpm_capable to instead of
usb2_hw_lpm_besl_capable.
If add dcf06a036848b4e8e6c8220f2e00b9adf6f84918, should we change the
struct usb3_lpm_parameters?
Some realtek webcam modules need the backport to fix preview issues.
a558ccdcc71c7770c5e80c926a31cfe8a3892a09
+++ b/drivers/usb/host/xhci-ext-caps.h
@@ -71,6 +71,7 @@
/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
#define XHCI_HLC (1 << 19)
+#define XHCI_BLC (1 << 19)
@@ -4068,6 +4191,9 @@ int xhci_update_device(struct usb_hcd *hcd, struct
usb_device *udev)
if (xhci->hw_lpm_support == 1 &&
xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
udev->usb2_hw_lpm_capable = 1;
+ if (xhci_check_usb2_port_capability(xhci, portnum,
+ XHCI_BLC))
+ udev->usb2_hw_lpm_besl_capable = 1;
dcf06a036848b4e8e6c8220f2e00b9adf6f84918
/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
#define XHCI_HLC (1 << 19)
-#define XHCI_BLC (1 << 19)
+#define XHCI_BLC (1 << 20)
Regards,
Shawn
On Wed, Sep 25, 2013 at 10:52 PM, Tim Gardner <tim.gardner at canonical.com>wrote:
> On 09/25/2013 07:27 AM, Gavin Guo wrote:
> > From: Mathias Nyman <mathias.nyman at linux.intel.com>
> >
> > BugLink: https://bugs.launchpad.net/bugs/1229576
> >
> > 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 at linux.intel.com>
> > Signed-off-by: Sarah Sharp <sarah.a.sharp at linux.intel.com>
> > (backported from commit a558ccdcc71c7770c5e80c926a31cfe8a3892a09)
> >
> > Signed-off-by: Shawn Wang <shawn.wang at canonical.com>
> > Signed-off-by: Gavin Guo <gavin.guo at canonical.com>
> >
> > Conflicts:
> > drivers/usb/host/xhci.c
> > ---
> > drivers/usb/host/xhci.c | 201
> ++++++++++++++++++++++++++++++++---------------
> > drivers/usb/host/xhci.h | 21 +++++
> > 2 files changed, 158 insertions(+), 64 deletions(-)
> >
> > diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> > index c3b0f2d..fb2aaaf 100644
> > --- a/drivers/usb/host/xhci.c
> > +++ b/drivers/usb/host/xhci.c
> > @@ -3783,6 +3783,56 @@ int xhci_address_device(struct usb_hcd *hcd,
> struct usb_device *udev)
> > return 0;
> > }
> >
> > +/*
> > + * Issue an Evaluate Context command to change the Maximum Exit Latency
> in the
> > + * slot context. If that succeeds, store the new MEL in the
> xhci_virt_device.
> > + */
> > +static int xhci_change_max_exit_latency(struct xhci_hcd *xhci,
> > + struct usb_device *udev, u16 max_exit_latency)
> > +{
> > + struct xhci_virt_device *virt_dev;
> > + struct xhci_command *command;
> > + struct xhci_input_control_ctx *ctrl_ctx;
> > + struct xhci_slot_ctx *slot_ctx;
> > + unsigned long flags;
> > + int ret;
> > +
> > + spin_lock_irqsave(&xhci->lock, flags);
> > + if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) {
> > + spin_unlock_irqrestore(&xhci->lock, flags);
> > + return 0;
> > + }
> > +
> > + /* Attempt to issue an Evaluate Context command to change the MEL.
> */
> > + virt_dev = xhci->devs[udev->slot_id];
> > + command = xhci->lpm_command;
> > + xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
> > + spin_unlock_irqrestore(&xhci->lock, flags);
> > +
> > + ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
> > + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
> > + slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx);
> > + slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT));
> > + slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency);
> > +
> > + xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n");
> > + xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id);
> > + xhci_dbg_ctx(xhci, command->in_ctx, 0);
> > +
> > + /* Issue and wait for the evaluate context command. */
> > + ret = xhci_configure_endpoint(xhci, udev, command,
> > + true, true);
> > + xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id);
> > + xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0);
> > +
> > + if (!ret) {
> > + spin_lock_irqsave(&xhci->lock, flags);
> > + virt_dev->current_mel = max_exit_latency;
> > + spin_unlock_irqrestore(&xhci->lock, flags);
> > + }
> > + return ret;
> > +}
> > +
> > #ifdef CONFIG_USB_SUSPEND
> >
> > /* BESL to HIRD Encoding array for USB2 LPM */
> > @@ -3824,6 +3874,28 @@ static int xhci_calculate_hird_besl(struct
> xhci_hcd *xhci,
> > return besl;
> > }
> >
> > +/* Calculate BESLD, L1 timeout and HIRDM for USB2 PORTHLPMC */
> > +static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
> > +{
> > + u32 field;
> > + int l1;
> > + int besld = 0;
> > + int hirdm = 0;
> > +
> > + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
> > +
> > + /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */
> > + l1 = XHCI_L1_TIMEOUT / 256;
> > +
> > + /* device has preferred BESLD */
> > + if (field & USB_BESL_DEEP_VALID) {
> > + besld = USB_GET_BESL_DEEP(field);
> > + hirdm = 1;
> > + }
> > +
> > + return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm);
> > +}
> > +
> > static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
> > struct usb_device *udev)
> > {
> > @@ -3955,11 +4027,12 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd
> *hcd,
> > {
> > struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> > __le32 __iomem **port_array;
> > - __le32 __iomem *pm_addr;
> > - u32 temp;
> > + __le32 __iomem *pm_addr, *hlpm_addr;
> > + u32 pm_val, hlpm_val, field;
> > unsigned int port_num;
> > unsigned long flags;
> > - int hird;
> > + int hird, exit_latency;
> > + int ret;
> >
> > if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
> > !udev->lpm_capable)
> > @@ -3977,23 +4050,73 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd
> *hcd,
> > port_array = xhci->usb2_ports;
> > port_num = udev->portnum - 1;
> > pm_addr = port_array[port_num] + PORTPMSC;
> > - temp = xhci_readl(xhci, pm_addr);
> > + pm_val = xhci_readl(xhci, pm_addr);
> > + hlpm_addr = port_array[port_num] + PORTHLPMC;
> > + field = le32_to_cpu(udev->bos->ext_cap->bmAttributes);
> >
> > xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
> > enable ? "enable" : "disable", port_num);
> >
> > - hird = xhci_calculate_hird_besl(xhci, udev);
> > -
> > if (enable) {
> > - temp &= ~PORT_HIRD_MASK;
> > - temp |= PORT_HIRD(hird) | PORT_RWE;
> > - xhci_writel(xhci, temp, pm_addr);
> > - temp = xhci_readl(xhci, pm_addr);
> > - temp |= PORT_HLE;
> > - xhci_writel(xhci, temp, pm_addr);
> > + /* Host supports BESL timeout instead of HIRD */
> > + if (udev->usb2_hw_lpm_capable) {
> > + /* if device doesn't have a preferred BESL value
> use a
> > + * default one which works with mixed HIRD and BESL
> > + * systems. See XHCI_DEFAULT_BESL definition in
> xhci.h
> > + */
> > + if ((field & USB_BESL_SUPPORT) &&
> > + (field & USB_BESL_BASELINE_VALID))
> > + hird = USB_GET_BESL_BASELINE(field);
> > + else
> > + hird = XHCI_DEFAULT_BESL;
> > +
> > + exit_latency = xhci_besl_encoding[hird];
> > + spin_unlock_irqrestore(&xhci->lock, flags);
> > +
> > + /* USB 3.0 code dedicate one
> xhci->lpm_command->in_ctx
> > + * input context for link powermanagement evaluate
> > + * context commands. It is protected by
> hcd->bandwidth
> > + * mutex and is shared by all devices. We need to
> set
> > + * the max ext latency in USB 2 BESL LPM as well,
> so
> > + * use the same mutex and
> xhci_change_max_exit_latency()
> > + */
> > + mutex_lock(hcd->bandwidth_mutex);
> > + ret = xhci_change_max_exit_latency(xhci, udev,
> > + exit_latency);
> > + mutex_unlock(hcd->bandwidth_mutex);
> > +
> > + if (ret < 0)
> > + return ret;
> > + spin_lock_irqsave(&xhci->lock, flags);
> > +
> > + hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev);
> > + xhci_writel(xhci, hlpm_val, hlpm_addr);
> > + /* flush write */
> > + xhci_readl(xhci, hlpm_addr);
> > + } else {
> > + hird = xhci_calculate_hird_besl(xhci, udev);
> > + }
> > +
> > + pm_val &= ~PORT_HIRD_MASK;
> > + pm_val |= PORT_HIRD(hird) | PORT_RWE;
> > + xhci_writel(xhci, pm_val, pm_addr);
> > + pm_val = xhci_readl(xhci, pm_addr);
> > + pm_val |= PORT_HLE;
> > + xhci_writel(xhci, pm_val, pm_addr);
> > + /* flush write */
> > + xhci_readl(xhci, pm_addr);
> > } else {
> > - temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
> > - xhci_writel(xhci, temp, pm_addr);
> > + pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
> > + xhci_writel(xhci, pm_val, pm_addr);
> > + /* flush write */
> > + xhci_readl(xhci, pm_addr);
> > + if (udev->usb2_hw_lpm_capable) {
> > + spin_unlock_irqrestore(&xhci->lock, flags);
> > + mutex_lock(hcd->bandwidth_mutex);
> > + xhci_change_max_exit_latency(xhci, udev, 0);
> > + mutex_unlock(hcd->bandwidth_mutex);
> > + return 0;
> > + }
> > }
> >
> > spin_unlock_irqrestore(&xhci->lock, flags);
> > @@ -4340,56 +4463,6 @@ static u16 xhci_calculate_lpm_timeout(struct
> usb_hcd *hcd,
> > return timeout;
> > }
> >
> > -/*
> > - * Issue an Evaluate Context command to change the Maximum Exit Latency
> in the
> > - * slot context. If that succeeds, store the new MEL in the
> xhci_virt_device.
> > - */
> > -static int xhci_change_max_exit_latency(struct xhci_hcd *xhci,
> > - struct usb_device *udev, u16 max_exit_latency)
> > -{
> > - struct xhci_virt_device *virt_dev;
> > - struct xhci_command *command;
> > - struct xhci_input_control_ctx *ctrl_ctx;
> > - struct xhci_slot_ctx *slot_ctx;
> > - unsigned long flags;
> > - int ret;
> > -
> > - spin_lock_irqsave(&xhci->lock, flags);
> > - if (max_exit_latency == xhci->devs[udev->slot_id]->current_mel) {
> > - spin_unlock_irqrestore(&xhci->lock, flags);
> > - return 0;
> > - }
> > -
> > - /* Attempt to issue an Evaluate Context command to change the MEL.
> */
> > - virt_dev = xhci->devs[udev->slot_id];
> > - command = xhci->lpm_command;
> > - xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
> > - spin_unlock_irqrestore(&xhci->lock, flags);
> > -
> > - ctrl_ctx = xhci_get_input_control_ctx(xhci, command->in_ctx);
> > - ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
> > - slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx);
> > - slot_ctx->dev_info2 &= cpu_to_le32(~((u32) MAX_EXIT));
> > - slot_ctx->dev_info2 |= cpu_to_le32(max_exit_latency);
> > -
> > - xhci_dbg(xhci, "Set up evaluate context for LPM MEL change.\n");
> > - xhci_dbg(xhci, "Slot %u Input Context:\n", udev->slot_id);
> > - xhci_dbg_ctx(xhci, command->in_ctx, 0);
> > -
> > - /* Issue and wait for the evaluate context command. */
> > - ret = xhci_configure_endpoint(xhci, udev, command,
> > - true, true);
> > - xhci_dbg(xhci, "Slot %u Output Context:\n", udev->slot_id);
> > - xhci_dbg_ctx(xhci, virt_dev->out_ctx, 0);
> > -
> > - if (!ret) {
> > - spin_lock_irqsave(&xhci->lock, flags);
> > - virt_dev->current_mel = max_exit_latency;
> > - spin_unlock_irqrestore(&xhci->lock, flags);
> > - }
> > - return ret;
> > -}
> > -
> > static int calculate_max_exit_latency(struct usb_device *udev,
> > enum usb3_link_state state_changed,
> > u16 hub_encoded_timeout)
> > diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> > index 79c324a..5897c13 100644
> > --- a/drivers/usb/host/xhci.h
> > +++ b/drivers/usb/host/xhci.h
> > @@ -386,6 +386,27 @@ struct xhci_op_regs {
> > #define PORT_L1DS(p) (((p) & 0xff) << 8)
> > #define PORT_HLE (1 << 16)
> >
> > +
> > +/* USB2 Protocol PORTHLPMC */
> > +#define PORT_HIRDM(p)((p) & 3)
> > +#define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2)
> > +#define PORT_BESLD(p)(((p) & 0xf) << 10)
> > +
> > +/* use 512 microseconds as USB2 LPM L1 default timeout. */
> > +#define XHCI_L1_TIMEOUT 512
> > +
> > +/* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume
> latency.
> > + * Safe to use with mixed HIRD and BESL systems (host and device) and
> is used
> > + * by other operating systems.
> > + *
> > + * XHCI 1.0 errata 8/14/12 Table 13 notes:
> > + * "Software should choose xHC BESL/BESLD field values that do not
> violate a
> > + * device's resume latency requirements,
> > + * e.g. not program values > '4' if BLC = '1' and a HIRD device is
> attached,
> > + * or not program values < '4' if BLC = '0' and a BESL device is
> attached.
> > + */
> > +#define XHCI_DEFAULT_BESL 4
> > +
> > /**
> > * struct xhci_intr_reg - Interrupt Register Set
> > * @irq_pending: IMAN - Interrupt Management Register. Used to
> enable
> >
>
> Hmm, might you also need dcf06a036848b4e8e6c8220f2e00b9adf6f84918 ?
>
> --
> Tim Gardner tim.gardner at canonical.com
>
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20130926/8d610a5e/attachment.html>
More information about the kernel-team
mailing list