All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support
@ 2013-05-23 14:14 Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 1/4] usb: xhci: check usb2 port capabilities before adding hw link PM support Mathias Nyman
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Mathias Nyman @ 2013-05-23 14:14 UTC (permalink / raw)
  To: linux-usb; +Cc: sarah.a.sharp, linux-kernel, Mathias Nyman

This series adds usb2 best effort service latency (BESL) link PM support
to xHCI. BESL is an updated alternative to host initated resume delay
(HIRD) method of describing idle link timeouts.

Additionally this series fixes port capability checking
and replaces magic numbers with port register names.

Changes since v1:
-Use kstrto* and unsigned values in sysfs functions as Alan Stern suggested    

Changes since v2:
- Add Documentation/ABI/testing/sysfs-bus-usb entries for new sysfs attributes
- store l1_timeout value in microseconds instead of xHCI specific values

Mathias Nyman (4):
  usb: xhci: check usb2 port capabilities before adding hw link PM
    support
  usb: xhci: define port register names and use them instead of magic
    numbers
  usb: xhci: add USB2 Link power management BESL support
  usb: add usb2 Link PM variables to sysfs and usb_device

 Documentation/ABI/testing/sysfs-bus-usb |   27 ++++
 drivers/usb/core/sysfs.c                |   54 +++++++
 drivers/usb/host/xhci-ext-caps.h        |    1 +
 drivers/usb/host/xhci-hub.c             |   16 +--
 drivers/usb/host/xhci-mem.c             |   33 ++++-
 drivers/usb/host/xhci.c                 |  237 ++++++++++++++++++++++---------
 drivers/usb/host/xhci.h                 |   29 ++++
 include/linux/usb.h                     |   20 +++
 8 files changed, 337 insertions(+), 80 deletions(-)

-- 
1.7.4.1


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [RFC PATCH v3 1/4] usb: xhci: check usb2 port capabilities before adding hw link PM support
  2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
@ 2013-05-23 14:14 ` Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 2/4] usb: xhci: define port register names and use them instead of magic numbers Mathias Nyman
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Mathias Nyman @ 2013-05-23 14:14 UTC (permalink / raw)
  To: linux-usb; +Cc: sarah.a.sharp, linux-kernel, Mathias Nyman

Hardware link powermanagement in usb2 is a per-port capability.
Previously support for hw lpm was enabled for all ports if any usb2 port supported it.

Now instead cache the capability values and check them for each port individually

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/usb/host/xhci-mem.c |   33 +++++++++++++++++++++++++++++----
 drivers/usb/host/xhci.c     |   27 ++++++++++++++++++++++++++-
 drivers/usb/host/xhci.h     |    3 +++
 3 files changed, 58 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 965b539..5fd97d1 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1851,6 +1851,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 	kfree(xhci->usb3_ports);
 	kfree(xhci->port_array);
 	kfree(xhci->rh_bw);
+	kfree(xhci->ext_caps);
 
 	xhci->page_size = 0;
 	xhci->page_shift = 0;
@@ -2038,7 +2039,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
 }
 
 static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
-		__le32 __iomem *addr, u8 major_revision)
+		__le32 __iomem *addr, u8 major_revision, int max_caps)
 {
 	u32 temp, port_offset, port_count;
 	int i;
@@ -2063,6 +2064,10 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
 		/* WTF? "Valid values are ‘1’ to MaxPorts" */
 		return;
 
+	/* cache usb2 port capabilities */
+	if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
+		xhci->ext_caps[xhci->num_ext_caps++] = temp;
+
 	/* Check the host's USB2 LPM capability */
 	if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
 			(temp & XHCI_L1C)) {
@@ -2120,10 +2125,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
  */
 static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
 {
-	__le32 __iomem *addr;
-	u32 offset;
+	__le32 __iomem *addr, *tmp_addr;
+	u32 offset, tmp_offset;
 	unsigned int num_ports;
 	int i, j, port_index;
+	int cap_count = 0;
 
 	addr = &xhci->cap_regs->hcc_params;
 	offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr));
@@ -2156,13 +2162,32 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
 	 * See section 5.3.6 for offset calculation.
 	 */
 	addr = &xhci->cap_regs->hc_capbase + offset;
+
+	tmp_addr = addr;
+	tmp_offset = offset;
+
+	/* count extended protocol capability entries for later caching */
+	do {
+		u32 cap_id;
+		cap_id = xhci_readl(xhci, tmp_addr);
+		if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
+			cap_count++;
+		tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
+		tmp_addr += tmp_offset;
+	} while (tmp_offset);
+
+	xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
+	if (!xhci->ext_caps)
+		return -ENOMEM;
+
 	while (1) {
 		u32 cap_id;
 
 		cap_id = xhci_readl(xhci, addr);
 		if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
 			xhci_add_in_port(xhci, num_ports, addr,
-					(u8) XHCI_EXT_PORT_MAJOR(cap_id));
+					(u8) XHCI_EXT_PORT_MAJOR(cap_id),
+					cap_count);
 		offset = XHCI_EXT_CAPS_NEXT(cap_id);
 		if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
 				== num_ports)
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index b4aa79d..e540a36 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4023,15 +4023,40 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
 	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;
 			ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
 			if (!ret)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 29c978e..1dbc63f 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1532,6 +1532,9 @@ struct xhci_hcd {
 	unsigned		sw_lpm_support:1;
 	/* support xHCI 1.0 spec USB2 hardware LPM */
 	unsigned		hw_lpm_support:1;
+	/* cached usb2 extened protocol capabilites */
+	u32                     *ext_caps;
+	unsigned int            num_ext_caps;
 	/* Compliance Mode Recovery Data */
 	struct timer_list	comp_mode_recovery_timer;
 	u32			port_status_u0;
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v3 2/4] usb: xhci: define port register names and use them instead of magic numbers
  2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 1/4] usb: xhci: check usb2 port capabilities before adding hw link PM support Mathias Nyman
@ 2013-05-23 14:14 ` Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 3/4] usb: xhci: add USB2 Link power management BESL support Mathias Nyman
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Mathias Nyman @ 2013-05-23 14:14 UTC (permalink / raw)
  To: linux-usb; +Cc: sarah.a.sharp, linux-kernel, Mathias Nyman

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/usb/host/xhci-hub.c |   16 +++++++---------
 drivers/usb/host/xhci.c     |    4 ++--
 drivers/usb/host/xhci.h     |    5 +++++
 3 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 187a3ec..1d35459 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -867,18 +867,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		case USB_PORT_FEAT_U1_TIMEOUT:
 			if (hcd->speed != HCD_USB3)
 				goto error;
-			temp = xhci_readl(xhci, port_array[wIndex] + 1);
+			temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC);
 			temp &= ~PORT_U1_TIMEOUT_MASK;
 			temp |= PORT_U1_TIMEOUT(timeout);
-			xhci_writel(xhci, temp, port_array[wIndex] + 1);
+			xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC);
 			break;
 		case USB_PORT_FEAT_U2_TIMEOUT:
 			if (hcd->speed != HCD_USB3)
 				goto error;
-			temp = xhci_readl(xhci, port_array[wIndex] + 1);
+			temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC);
 			temp &= ~PORT_U2_TIMEOUT_MASK;
 			temp |= PORT_U2_TIMEOUT(timeout);
-			xhci_writel(xhci, temp, port_array[wIndex] + 1);
+			xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC);
 			break;
 		default:
 			goto error;
@@ -1098,10 +1098,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 			__le32 __iomem *addr;
 			u32 tmp;
 
-			/* Add one to the port status register address to get
-			 * the port power control register address.
-			 */
-			addr = port_array[port_index] + 1;
+			/* Get the port power control register address. */
+			addr = port_array[port_index] + PORTPMSC;
 			tmp = xhci_readl(xhci, addr);
 			tmp |= PORT_RWE;
 			xhci_writel(xhci, tmp, addr);
@@ -1193,7 +1191,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
 			/* Add one to the port status register address to get
 			 * the port power control register address.
 			 */
-			addr = port_array[port_index] + 1;
+			addr = port_array[port_index] + PORTPMSC;
 			tmp = xhci_readl(xhci, addr);
 			tmp &= ~PORT_RWE;
 			xhci_writel(xhci, tmp, addr);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index e540a36..317bf08 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3901,7 +3901,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);
@@ -3999,7 +3999,7 @@ 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;
+	pm_addr = port_array[port_num] + PORTPMSC;
 	temp = xhci_readl(xhci, pm_addr);
 
 	xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 1dbc63f..b6cd55e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -132,6 +132,11 @@ struct xhci_cap_regs {
 /* Number of registers per port */
 #define	NUM_PORT_REGS	4
 
+#define PORTSC		0
+#define PORTPMSC	1
+#define PORTLI		2
+#define PORTHLPMC	3
+
 /**
  * struct xhci_op_regs - xHCI Host Controller Operational Registers.
  * @command:		USBCMD - xHC command register
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v3 3/4] usb: xhci: add USB2 Link power management BESL support
  2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 1/4] usb: xhci: check usb2 port capabilities before adding hw link PM support Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 2/4] usb: xhci: define port register names and use them instead of magic numbers Mathias Nyman
@ 2013-05-23 14:14 ` Mathias Nyman
  2013-05-23 14:14 ` [RFC PATCH v3 4/4] usb: add usb2 Link PM variables to sysfs and usb_device Mathias Nyman
  2013-06-04 23:05 ` [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Sarah Sharp
  4 siblings, 0 replies; 8+ messages in thread
From: Mathias Nyman @ 2013-05-23 14:14 UTC (permalink / raw)
  To: linux-usb; +Cc: sarah.a.sharp, linux-kernel, Mathias Nyman

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@linux.intel.com>
---
 drivers/usb/host/xhci-ext-caps.h |    1 +
 drivers/usb/host/xhci.c          |  204 ++++++++++++++++++++++++++------------
 drivers/usb/host/xhci.h          |   21 ++++
 include/linux/usb.h              |    2 +
 4 files changed, 164 insertions(+), 64 deletions(-)

diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h
index 377f424..8d7a132 100644
--- a/drivers/usb/host/xhci-ext-caps.h
+++ 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)
 
 /* command register values to disable interrupts and halt the HC */
 /* start/stop HC execution - do not write unless HC is halted*/
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 317bf08..d1eb393 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3805,6 +3805,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 +3896,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)
 {
@@ -3978,11 +4050,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)
@@ -4000,23 +4073,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_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 = 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_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);
@@ -4058,6 +4181,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;
 			ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
 			if (!ret)
 				udev->usb2_hw_lpm_enabled = 1;
@@ -4388,56 +4514,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 b6cd55e..b971801 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
diff --git a/include/linux/usb.h b/include/linux/usb.h
index a0bee5a..3a1b864 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -468,6 +468,7 @@ struct usb3_lpm_parameters {
  * @wusb: device is Wireless USB
  * @lpm_capable: device supports LPM
  * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
+ * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
  * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled
  * @usb3_lpm_enabled: USB3 hardware LPM enabled
  * @string_langid: language ID for strings
@@ -538,6 +539,7 @@ struct usb_device {
 	unsigned wusb:1;
 	unsigned lpm_capable:1;
 	unsigned usb2_hw_lpm_capable:1;
+	unsigned usb2_hw_lpm_besl_capable:1;
 	unsigned usb2_hw_lpm_enabled:1;
 	unsigned usb3_lpm_enabled:1;
 	int string_langid;
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [RFC PATCH v3 4/4] usb: add usb2 Link PM variables to sysfs and usb_device
  2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
                   ` (2 preceding siblings ...)
  2013-05-23 14:14 ` [RFC PATCH v3 3/4] usb: xhci: add USB2 Link power management BESL support Mathias Nyman
@ 2013-05-23 14:14 ` Mathias Nyman
  2013-06-04 23:05 ` [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Sarah Sharp
  4 siblings, 0 replies; 8+ messages in thread
From: Mathias Nyman @ 2013-05-23 14:14 UTC (permalink / raw)
  To: linux-usb; +Cc: sarah.a.sharp, linux-kernel, Mathias Nyman

Adds abitilty to tune L1 timeout (inactivity timer for usb2 link sleep)
and BESL (best effort service latency)via sysfs.

This also adds a new usb2_lpm_parameters structure with those variables to
struct usb_device.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 Documentation/ABI/testing/sysfs-bus-usb |   27 +++++++++++++++
 drivers/usb/core/sysfs.c                |   54 +++++++++++++++++++++++++++++++
 drivers/usb/host/xhci.c                 |    6 ++-
 include/linux/usb.h                     |   18 ++++++++++
 4 files changed, 103 insertions(+), 2 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index f093e59..9759b8c 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -236,3 +236,30 @@ Description:
 		This attribute is to expose these information to user space.
 		The file will read "hotplug", "wired" and "not used" if the
 		information is available, and "unknown" otherwise.
+
+What:		/sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
+Date:		May 2013
+Contact:	Mathias Nyman <mathias.nyman@linux.intel.com>
+Description:
+		USB 2.0 devices may support hardware link power management (LPM)
+		L1 sleep state. The usb2_lpm_l1_timeout attribute allows
+		tuning the timeout for L1 inactivity timer (LPM timer), e.g.
+		needed inactivity time before host requests the device to go to L1 sleep.
+		Useful for power management tuning.
+		Supported values are 0 - 65535 microseconds.
+
+What:		/sys/bus/usb/devices/.../power/usb2_lpm_besl
+Date:		May 2013
+Contact:	Mathias Nyman <mathias.nyman@linux.intel.com>
+Description:
+		USB 2.0 devices that support hardware link power management (LPM)
+		L1 sleep state now use a best effort service latency value (BESL) to
+		indicate the best effort to resumption of service to the device after the
+		initiation of the resume event.
+		If the device does not have a preferred besl value then the host can select
+		one instead. This usb2_lpm_besl attribute allows to tune the host selected besl
+		value in order to tune power saving and service latency.
+
+		Supported values are 0 - 15.
+		More information on how besl values map to microseconds can be found in
+		USB 2.0 ECN Errata for Link Power Management, section 4.10)
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index aa38db4..d9284b9 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -497,8 +497,62 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
 static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
 			set_usb2_hardware_lpm);
 
+static ssize_t
+show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	return sprintf(buf, "%d\n", udev->l1_params.timeout);
+}
+
+static ssize_t
+set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	u16 timeout;
+
+	if (kstrtou16(buf, 0, &timeout))
+		return -EINVAL;
+
+	udev->l1_params.timeout = timeout;
+
+	return count;
+}
+
+static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR,
+		   show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout);
+
+static ssize_t
+show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr,
+		   char *buf)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	return sprintf(buf, "%d\n", udev->l1_params.besl);
+}
+
+static ssize_t
+set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	u8 besl;
+
+	if (kstrtou8(buf, 0, &besl) || besl > 15)
+		return -EINVAL;
+
+	udev->l1_params.besl = besl;
+
+	return count;
+}
+
+static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR,
+		   show_usb2_lpm_besl, set_usb2_lpm_besl);
+
 static struct attribute *usb2_hardware_lpm_attr[] = {
 	&dev_attr_usb2_hardware_lpm.attr,
+	&dev_attr_usb2_lpm_l1_timeout.attr,
+	&dev_attr_usb2_lpm_besl.attr,
 	NULL,
 };
 static struct attribute_group usb2_hardware_lpm_attr_group = {
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index d1eb393..ba7822f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3907,7 +3907,7 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
 	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;
+	l1 = udev->l1_params.timeout / 256;
 
 	/* device has preferred BESLD */
 	if (field & USB_BESL_DEEP_VALID) {
@@ -4091,7 +4091,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
 			    (field & USB_BESL_BASELINE_VALID))
 				hird = USB_GET_BESL_BASELINE(field);
 			else
-				hird = XHCI_DEFAULT_BESL;
+				hird = udev->l1_params.besl;
 
 			exit_latency = xhci_besl_encoding[hird];
 			spin_unlock_irqrestore(&xhci->lock, flags);
@@ -4181,6 +4181,8 @@ 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;
+			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;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 3a1b864..1f7d63f 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -394,6 +394,22 @@ enum usb_port_connect_type {
 };
 
 /*
+ * USB 2.0 Link Power Management (LPM) parameters.
+ */
+struct usb2_lpm_parameters {
+	/* Best effort service latency indicate how long the host will drive
+	 * resume on an exit from L1.
+	 */
+	unsigned int besl;
+
+	/* Timeout value in microseconds for the L1 inactivity (LPM) timer.
+	 * When the timer counts to zero, the parent hub will initiate a LPM
+	 * transition to L1.
+	 */
+	int timeout;
+};
+
+/*
  * USB 3.0 Link Power Management (LPM) parameters.
  *
  * PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit.
@@ -488,6 +504,7 @@ struct usb3_lpm_parameters {
  *	specific data for the device.
  * @slot_id: Slot ID assigned by xHCI
  * @removable: Device can be physically removed from this port
+ * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
  * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
  * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout.
  * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm()
@@ -568,6 +585,7 @@ struct usb_device {
 	struct wusb_dev *wusb_dev;
 	int slot_id;
 	enum usb_device_removable removable;
+	struct usb2_lpm_parameters l1_params;
 	struct usb3_lpm_parameters u1_params;
 	struct usb3_lpm_parameters u2_params;
 	unsigned lpm_disable_count;
-- 
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support
  2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
                   ` (3 preceding siblings ...)
  2013-05-23 14:14 ` [RFC PATCH v3 4/4] usb: add usb2 Link PM variables to sysfs and usb_device Mathias Nyman
@ 2013-06-04 23:05 ` Sarah Sharp
  2013-06-05 14:51   ` Alan Stern
  4 siblings, 1 reply; 8+ messages in thread
From: Sarah Sharp @ 2013-06-04 23:05 UTC (permalink / raw)
  To: Alan Stern, Greg KH; +Cc: linux-usb, sarah.a.sharp, linux-kernel, Mathias Nyman

Alan, Greg, any more comments on this patchset?

It looks fine to me, and we've been testing it internally within Intel,
so I'll queue it up for 3.11 if you don't have any more objections.

Sarah Sharp

On Thu, May 23, 2013 at 05:14:27PM +0300, Mathias Nyman wrote:
> This series adds usb2 best effort service latency (BESL) link PM support
> to xHCI. BESL is an updated alternative to host initated resume delay
> (HIRD) method of describing idle link timeouts.
> 
> Additionally this series fixes port capability checking
> and replaces magic numbers with port register names.
> 
> Changes since v1:
> -Use kstrto* and unsigned values in sysfs functions as Alan Stern suggested    
> 
> Changes since v2:
> - Add Documentation/ABI/testing/sysfs-bus-usb entries for new sysfs attributes
> - store l1_timeout value in microseconds instead of xHCI specific values
> 
> Mathias Nyman (4):
>   usb: xhci: check usb2 port capabilities before adding hw link PM
>     support
>   usb: xhci: define port register names and use them instead of magic
>     numbers
>   usb: xhci: add USB2 Link power management BESL support
>   usb: add usb2 Link PM variables to sysfs and usb_device
> 
>  Documentation/ABI/testing/sysfs-bus-usb |   27 ++++
>  drivers/usb/core/sysfs.c                |   54 +++++++
>  drivers/usb/host/xhci-ext-caps.h        |    1 +
>  drivers/usb/host/xhci-hub.c             |   16 +--
>  drivers/usb/host/xhci-mem.c             |   33 ++++-
>  drivers/usb/host/xhci.c                 |  237 ++++++++++++++++++++++---------
>  drivers/usb/host/xhci.h                 |   29 ++++
>  include/linux/usb.h                     |   20 +++
>  8 files changed, 337 insertions(+), 80 deletions(-)
> 
> -- 
> 1.7.4.1
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support
  2013-06-04 23:05 ` [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Sarah Sharp
@ 2013-06-05 14:51   ` Alan Stern
  2013-06-05 15:39     ` Greg KH
  0 siblings, 1 reply; 8+ messages in thread
From: Alan Stern @ 2013-06-05 14:51 UTC (permalink / raw)
  To: Sarah Sharp
  Cc: Greg KH, linux-usb, sarah.a.sharp, linux-kernel, Mathias Nyman

On Tue, 4 Jun 2013, Sarah Sharp wrote:

> Alan, Greg, any more comments on this patchset?
> 
> It looks fine to me, and we've been testing it internally within Intel,
> so I'll queue it up for 3.11 if you don't have any more objections.

No objections to any of the parts touching the USB core.

Alan Stern


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support
  2013-06-05 14:51   ` Alan Stern
@ 2013-06-05 15:39     ` Greg KH
  0 siblings, 0 replies; 8+ messages in thread
From: Greg KH @ 2013-06-05 15:39 UTC (permalink / raw)
  To: Alan Stern, Sarah Sharp, linux-usb, sarah.a.sharp, linux-kernel,
	Mathias Nyman

On Wed, Jun 05, 2013 at 10:51:38AM -0400, Alan Stern wrote:
> On Tue, 4 Jun 2013, Sarah Sharp wrote:
> 
> > Alan, Greg, any more comments on this patchset?
> > 
> > It looks fine to me, and we've been testing it internally within Intel,
> > so I'll queue it up for 3.11 if you don't have any more objections.
> 
> No objections to any of the parts touching the USB core.

None from me either.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-06-05 15:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-23 14:14 [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Mathias Nyman
2013-05-23 14:14 ` [RFC PATCH v3 1/4] usb: xhci: check usb2 port capabilities before adding hw link PM support Mathias Nyman
2013-05-23 14:14 ` [RFC PATCH v3 2/4] usb: xhci: define port register names and use them instead of magic numbers Mathias Nyman
2013-05-23 14:14 ` [RFC PATCH v3 3/4] usb: xhci: add USB2 Link power management BESL support Mathias Nyman
2013-05-23 14:14 ` [RFC PATCH v3 4/4] usb: add usb2 Link PM variables to sysfs and usb_device Mathias Nyman
2013-06-04 23:05 ` [RFC PATCH v3 0/4] Add usb2 link powermanagement BESL support Sarah Sharp
2013-06-05 14:51   ` Alan Stern
2013-06-05 15:39     ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.