|
@@ -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;
|
|
@@ -1111,6 +1111,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.
|
|
@@ -3805,6 +3815,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 */
|
|
@@ -3846,6 +3906,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)
|
|
|
{
|
|
@@ -3901,7 +3983,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);
|
|
@@ -3978,11 +4060,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)
|
|
@@ -3999,40 +4082,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;
|
|
@@ -4363,56 +4526,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)
|