xhci: Add infrastructure for host-specific LPM policies.
[deliverable/linux.git] / drivers / usb / host / xhci.c
index e1963d4a430f2952c8383b5a25fff8debc6c0014..518d002d54ccebb75df4e9e1810fc656cca3a6e1 100644 (file)
@@ -106,6 +106,9 @@ int xhci_halt(struct xhci_hcd *xhci)
                        STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
        if (!ret)
                xhci->xhc_state |= XHCI_STATE_HALTED;
+       else
+               xhci_warn(xhci, "Host not halted after %u microseconds.\n",
+                               XHCI_MAX_HALT_USEC);
        return ret;
 }
 
@@ -149,7 +152,7 @@ int xhci_reset(struct xhci_hcd *xhci)
 {
        u32 command;
        u32 state;
-       int ret;
+       int ret, i;
 
        state = xhci_readl(xhci, &xhci->op_regs->status);
        if ((state & STS_HALT) == 0) {
@@ -172,7 +175,15 @@ int xhci_reset(struct xhci_hcd *xhci)
         * xHCI cannot write to any doorbells or operational registers other
         * than status until the "Controller Not Ready" flag is cleared.
         */
-       return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
+       ret = handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
+
+       for (i = 0; i < 2; ++i) {
+               xhci->bus_state[i].port_c_suspend = 0;
+               xhci->bus_state[i].suspended_ports = 0;
+               xhci->bus_state[i].resuming_ports = 0;
+       }
+
+       return ret;
 }
 
 #ifdef CONFIG_PCI
@@ -664,11 +675,11 @@ static void xhci_save_registers(struct xhci_hcd *xhci)
        xhci->s3.dev_nt = xhci_readl(xhci, &xhci->op_regs->dev_notification);
        xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
        xhci->s3.config_reg = xhci_readl(xhci, &xhci->op_regs->config_reg);
-       xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
-       xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
        xhci->s3.erst_size = xhci_readl(xhci, &xhci->ir_set->erst_size);
        xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base);
        xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
+       xhci->s3.irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
+       xhci->s3.irq_control = xhci_readl(xhci, &xhci->ir_set->irq_control);
 }
 
 static void xhci_restore_registers(struct xhci_hcd *xhci)
@@ -677,10 +688,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci)
        xhci_writel(xhci, xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
        xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
        xhci_writel(xhci, xhci->s3.config_reg, &xhci->op_regs->config_reg);
-       xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
-       xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
        xhci_writel(xhci, xhci->s3.erst_size, &xhci->ir_set->erst_size);
        xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base);
+       xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue);
+       xhci_writel(xhci, xhci->s3.irq_pending, &xhci->ir_set->irq_pending);
+       xhci_writel(xhci, xhci->s3.irq_control, &xhci->ir_set->irq_control);
 }
 
 static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
@@ -2434,7 +2446,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
                                udev->slot_id, must_succeed);
        else
                ret = xhci_queue_evaluate_context(xhci, in_ctx->dma,
-                               udev->slot_id);
+                               udev->slot_id, must_succeed);
        if (ret < 0) {
                if (command)
                        list_del(&command->cmd_list);
@@ -3825,6 +3837,325 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
        return 0;
 }
 
+/*---------------------- USB 3.0 Link PM functions ------------------------*/
+
+static u16 xhci_get_timeout_no_hub_lpm(struct usb_device *udev,
+               enum usb3_link_state state)
+{
+       unsigned long long sel;
+       unsigned long long pel;
+       unsigned int max_sel_pel;
+       char *state_name;
+
+       switch (state) {
+       case USB3_LPM_U1:
+               /* Convert SEL and PEL stored in nanoseconds to microseconds */
+               sel = DIV_ROUND_UP(udev->u1_params.sel, 1000);
+               pel = DIV_ROUND_UP(udev->u1_params.pel, 1000);
+               max_sel_pel = USB3_LPM_MAX_U1_SEL_PEL;
+               state_name = "U1";
+               break;
+       case USB3_LPM_U2:
+               sel = DIV_ROUND_UP(udev->u2_params.sel, 1000);
+               pel = DIV_ROUND_UP(udev->u2_params.pel, 1000);
+               max_sel_pel = USB3_LPM_MAX_U2_SEL_PEL;
+               state_name = "U2";
+               break;
+       default:
+               dev_warn(&udev->dev, "%s: Can't get timeout for non-U1 or U2 state.\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       if (sel <= max_sel_pel && pel <= max_sel_pel)
+               return USB3_LPM_DEVICE_INITIATED;
+
+       if (sel > max_sel_pel)
+               dev_dbg(&udev->dev, "Device-initiated %s disabled "
+                               "due to long SEL %llu ms\n",
+                               state_name, sel);
+       else
+               dev_dbg(&udev->dev, "Device-initiated %s disabled "
+                               "due to long PEL %llu\n ms",
+                               state_name, pel);
+       return USB3_LPM_DISABLED;
+}
+
+static u16 xhci_call_host_update_timeout_for_endpoint(struct xhci_hcd *xhci,
+               struct usb_device *udev,
+               struct usb_endpoint_descriptor *desc,
+               enum usb3_link_state state,
+               u16 *timeout)
+{
+       return USB3_LPM_DISABLED;
+}
+
+static int xhci_update_timeout_for_endpoint(struct xhci_hcd *xhci,
+               struct usb_device *udev,
+               struct usb_endpoint_descriptor *desc,
+               enum usb3_link_state state,
+               u16 *timeout)
+{
+       u16 alt_timeout;
+
+       alt_timeout = xhci_call_host_update_timeout_for_endpoint(xhci, udev,
+               desc, state, timeout);
+
+       /* If we found we can't enable hub-initiated LPM, or
+        * the U1 or U2 exit latency was too high to allow
+        * device-initiated LPM as well, just stop searching.
+        */
+       if (alt_timeout == USB3_LPM_DISABLED ||
+                       alt_timeout == USB3_LPM_DEVICE_INITIATED) {
+               *timeout = alt_timeout;
+               return -E2BIG;
+       }
+       if (alt_timeout > *timeout)
+               *timeout = alt_timeout;
+       return 0;
+}
+
+static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci,
+               struct usb_device *udev,
+               struct usb_host_interface *alt,
+               enum usb3_link_state state,
+               u16 *timeout)
+{
+       int j;
+
+       for (j = 0; j < alt->desc.bNumEndpoints; j++) {
+               if (xhci_update_timeout_for_endpoint(xhci, udev,
+                                       &alt->endpoint[j].desc, state, timeout))
+                       return -E2BIG;
+               continue;
+       }
+       return 0;
+}
+
+static int xhci_check_tier_policy(struct xhci_hcd *xhci,
+               struct usb_device *udev,
+               enum usb3_link_state state)
+{
+       return -EINVAL;
+}
+
+/* Returns the U1 or U2 timeout that should be enabled.
+ * If the tier check or timeout setting functions return with a non-zero exit
+ * code, that means the timeout value has been finalized and we shouldn't look
+ * at any more endpoints.
+ */
+static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd,
+                       struct usb_device *udev, enum usb3_link_state state)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct usb_host_config *config;
+       char *state_name;
+       int i;
+       u16 timeout = USB3_LPM_DISABLED;
+
+       if (state == USB3_LPM_U1)
+               state_name = "U1";
+       else if (state == USB3_LPM_U2)
+               state_name = "U2";
+       else {
+               dev_warn(&udev->dev, "Can't enable unknown link state %i\n",
+                               state);
+               return timeout;
+       }
+
+       if (xhci_check_tier_policy(xhci, udev, state) < 0)
+               return timeout;
+
+       /* Gather some information about the currently installed configuration
+        * and alternate interface settings.
+        */
+       if (xhci_update_timeout_for_endpoint(xhci, udev, &udev->ep0.desc,
+                       state, &timeout))
+               return timeout;
+
+       config = udev->actconfig;
+       if (!config)
+               return timeout;
+
+       for (i = 0; i < USB_MAXINTERFACES; i++) {
+               struct usb_driver *driver;
+               struct usb_interface *intf = config->interface[i];
+
+               if (!intf)
+                       continue;
+
+               /* Check if any currently bound drivers want hub-initiated LPM
+                * disabled.
+                */
+               if (intf->dev.driver) {
+                       driver = to_usb_driver(intf->dev.driver);
+                       if (driver && driver->disable_hub_initiated_lpm) {
+                               dev_dbg(&udev->dev, "Hub-initiated %s disabled "
+                                               "at request of driver %s\n",
+                                               state_name, driver->name);
+                               return xhci_get_timeout_no_hub_lpm(udev, state);
+                       }
+               }
+
+               /* Not sure how this could happen... */
+               if (!intf->cur_altsetting)
+                       continue;
+
+               if (xhci_update_timeout_for_interface(xhci, udev,
+                                       intf->cur_altsetting,
+                                       state, &timeout))
+                       return timeout;
+       }
+       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)
+{
+       unsigned long long u1_mel_us = 0;
+       unsigned long long u2_mel_us = 0;
+       unsigned long long mel_us = 0;
+       bool disabling_u1;
+       bool disabling_u2;
+       bool enabling_u1;
+       bool enabling_u2;
+
+       disabling_u1 = (state_changed == USB3_LPM_U1 &&
+                       hub_encoded_timeout == USB3_LPM_DISABLED);
+       disabling_u2 = (state_changed == USB3_LPM_U2 &&
+                       hub_encoded_timeout == USB3_LPM_DISABLED);
+
+       enabling_u1 = (state_changed == USB3_LPM_U1 &&
+                       hub_encoded_timeout != USB3_LPM_DISABLED);
+       enabling_u2 = (state_changed == USB3_LPM_U2 &&
+                       hub_encoded_timeout != USB3_LPM_DISABLED);
+
+       /* If U1 was already enabled and we're not disabling it,
+        * or we're going to enable U1, account for the U1 max exit latency.
+        */
+       if ((udev->u1_params.timeout != USB3_LPM_DISABLED && !disabling_u1) ||
+                       enabling_u1)
+               u1_mel_us = DIV_ROUND_UP(udev->u1_params.mel, 1000);
+       if ((udev->u2_params.timeout != USB3_LPM_DISABLED && !disabling_u2) ||
+                       enabling_u2)
+               u2_mel_us = DIV_ROUND_UP(udev->u2_params.mel, 1000);
+
+       if (u1_mel_us > u2_mel_us)
+               mel_us = u1_mel_us;
+       else
+               mel_us = u2_mel_us;
+       /* xHCI host controller max exit latency field is only 16 bits wide. */
+       if (mel_us > MAX_EXIT) {
+               dev_warn(&udev->dev, "Link PM max exit latency of %lluus "
+                               "is too big.\n", mel_us);
+               return -E2BIG;
+       }
+       return mel_us;
+}
+
+/* Returns the USB3 hub-encoded value for the U1/U2 timeout. */
+int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
+                       struct usb_device *udev, enum usb3_link_state state)
+{
+       struct xhci_hcd *xhci;
+       u16 hub_encoded_timeout;
+       int mel;
+       int ret;
+
+       xhci = hcd_to_xhci(hcd);
+       /* The LPM timeout values are pretty host-controller specific, so don't
+        * enable hub-initiated timeouts unless the vendor has provided
+        * information about their timeout algorithm.
+        */
+       if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
+                       !xhci->devs[udev->slot_id])
+               return USB3_LPM_DISABLED;
+
+       hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state);
+       mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout);
+       if (mel < 0) {
+               /* Max Exit Latency is too big, disable LPM. */
+               hub_encoded_timeout = USB3_LPM_DISABLED;
+               mel = 0;
+       }
+
+       ret = xhci_change_max_exit_latency(xhci, udev, mel);
+       if (ret)
+               return ret;
+       return hub_encoded_timeout;
+}
+
+int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
+                       struct usb_device *udev, enum usb3_link_state state)
+{
+       struct xhci_hcd *xhci;
+       u16 mel;
+       int ret;
+
+       xhci = hcd_to_xhci(hcd);
+       if (!xhci || !(xhci->quirks & XHCI_LPM_SUPPORT) ||
+                       !xhci->devs[udev->slot_id])
+               return 0;
+
+       mel = calculate_max_exit_latency(udev, state, USB3_LPM_DISABLED);
+       ret = xhci_change_max_exit_latency(xhci, udev, mel);
+       if (ret)
+               return ret;
+       return 0;
+}
+/*-------------------------------------------------------------------------*/
+
 int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -4086,7 +4417,6 @@ static int __init xhci_hcd_init(void)
        BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8);
        /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */
        BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
-       BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
        return 0;
 unreg_pci:
        xhci_unregister_pci();
This page took 0.030856 seconds and 5 git commands to generate.