Merge 3.10-rc5 into usb-next
[deliverable/linux.git] / drivers / usb / host / xhci.c
index d8f640b12dd9d950e842892858a617b7fa97247e..77113c1fcb9630fd99907f507f2f177e4fd0e894 100644 (file)
@@ -218,7 +218,7 @@ static int xhci_setup_msi(struct xhci_hcd *xhci)
                return ret;
        }
 
-       ret = request_irq(pdev->irq, (irq_handler_t)xhci_msi_irq,
+       ret = request_irq(pdev->irq, xhci_msi_irq,
                                0, "xhci_hcd", xhci_to_hcd(xhci));
        if (ret) {
                xhci_dbg(xhci, "disable MSI interrupt\n");
@@ -290,7 +290,7 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
 
        for (i = 0; i < xhci->msix_count; i++) {
                ret = request_irq(xhci->msix_entries[i].vector,
-                               (irq_handler_t)xhci_msi_irq,
+                               xhci_msi_irq,
                                0, "xhci_hcd", xhci_to_hcd(xhci));
                if (ret)
                        goto disable_msix;
@@ -1121,6 +1121,16 @@ unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc)
        return index;
 }
 
+/* The reverse operation to xhci_get_endpoint_index. Calculate the USB endpoint
+ * address from the XHCI endpoint index.
+ */
+unsigned int xhci_get_endpoint_address(unsigned int ep_index)
+{
+       unsigned int number = DIV_ROUND_UP(ep_index, 2);
+       unsigned int direction = ep_index % 2 ? USB_DIR_OUT : USB_DIR_IN;
+       return direction | number;
+}
+
 /* Find the flag for this endpoint (for use in the control context).  Use the
  * endpoint index to create a bitmask.  The slot context is bit 0, endpoint 0 is
  * bit 1, etc.
@@ -3815,6 +3825,56 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1)
        return raw_port;
 }
 
+/*
+ * 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_PM_RUNTIME
 
 /* BESL to HIRD Encoding array for USB2 LPM */
@@ -3856,6 +3916,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 = udev->l1_params.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)
 {
@@ -3911,7 +3993,7 @@ static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
         * Check device's USB 2.0 extension descriptor to determine whether
         * HIRD or BESL shoule be used. See USB2.0 LPM errata.
         */
-       pm_addr = port_array[port_num] + 1;
+       pm_addr = port_array[port_num] + PORTPMSC;
        hird = xhci_calculate_hird_besl(xhci, udev);
        temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
        xhci_writel(xhci, temp, pm_addr);
@@ -3988,11 +4070,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)
@@ -4009,40 +4092,120 @@ 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] + 1;
-       temp = xhci_readl(xhci, pm_addr);
+       pm_addr = port_array[port_num] + PORTPMSC;
+       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_besl_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 = udev->l1_params.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_besl_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);
        return 0;
 }
 
+/* check if a usb2 port supports a given extened capability protocol
+ * only USB2 ports extended protocol capability values are cached.
+ * Return 1 if capability is supported
+ */
+static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
+                                          unsigned capability)
+{
+       u32 port_offset, port_count;
+       int i;
+
+       for (i = 0; i < xhci->num_ext_caps; i++) {
+               if (xhci->ext_caps[i] & capability) {
+                       /* port offsets starts at 1 */
+                       port_offset = XHCI_EXT_PORT_OFF(xhci->ext_caps[i]) - 1;
+                       port_count = XHCI_EXT_PORT_COUNT(xhci->ext_caps[i]);
+                       if (port >= port_offset &&
+                           port < port_offset + port_count)
+                               return 1;
+               }
+       }
+       return 0;
+}
+
 int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        int             ret;
+       int             portnum = udev->portnum - 1;
 
        ret = xhci_usb2_software_lpm_test(hcd, udev);
        if (!ret) {
                xhci_dbg(xhci, "software LPM test succeed\n");
-               if (xhci->hw_lpm_support == 1) {
+               if (xhci->hw_lpm_support == 1 &&
+                   xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
                        udev->usb2_hw_lpm_capable = 1;
+                       udev->l1_params.timeout = XHCI_L1_TIMEOUT;
+                       udev->l1_params.besl = XHCI_DEFAULT_BESL;
+                       if (xhci_check_usb2_port_capability(xhci, portnum,
+                                                           XHCI_BLC))
+                               udev->usb2_hw_lpm_besl_capable = 1;
                        ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
                        if (!ret)
                                udev->usb2_hw_lpm_enabled = 1;
@@ -4373,56 +4536,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)
This page took 0.03232 seconds and 5 git commands to generate.