[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