Blob Blame History Raw
From 13aaf5fbc60b9e8f33734185f327e6c213330776 Mon Sep 17 00:00:00 2001
From: Jack Pham <jackp@codeaurora.org>
Date: Wed, 23 Aug 2017 00:35:30 -0700
Subject: [PATCH 26/26] usb: misc: lvstest: add entry to place port in
 compliance mode
References: FATE#321327
Git-commit: f624ec70b464cb603efb9707234f59b6a502056d
Patch-mainline: v4.14-rc1

Add support for the SuperSpeed Link Layer test case TD.7.34
which requires the operator to place the port into compliance
mode, and to subsequently bring it out via reset. Historically
according to the (now deprecated) USB 3.0 specification a
SuperSpeed host downstream port would automatically transition
to Compliance mode from the Polling state if LFPS polling times
out. However the language in USB 3.1 as well as xHCI 1.1 states
it may be required to explicitly enable this transition. For
such hosts this is done by sending a SET_FEATURE(PORT_LINK_STATE)
with the state set to Compliance to the root hub port.

Similar to the other supported commands, to do this via sysfs:

     echo  > /sys/bus/usb/devices/2-0\:1.0/enable_compliance

According to xHCI 1.1 section 4.19.1.2.4.1, this enables the
transition to compliance mode upon LFPS timeout. Note that this
can only be issued when the port is in disconnected state. And
in order to disable this behavior on subsequent transitions, a
warm reset should be issued. So add another entry to do that:

     echo  > /sys/bus/usb/devices/2-0\:1.0/warm_reset

In general these attributes can also be useful for other USB
SuperSpeed compliance tests such as electrical and eye diagram
testing which require CPn patterns to be transmitted.

Signed-off-by: Jack Pham <jackp@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Oliver Neukum <oneukum@suse.com>
---
 Documentation/ABI/testing/sysfs-bus-usb-lvstest | 13 ++++++++
 drivers/usb/misc/lvstest.c                      | 41 +++++++++++++++++++++++++
 2 files changed, 54 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb-lvstest b/Documentation/ABI/testing/sysfs-bus-usb-lvstest
index 5151290cf8e7..ee0046dc4192 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb-lvstest
+++ b/Documentation/ABI/testing/sysfs-bus-usb-lvstest
@@ -45,3 +45,16 @@ Contact:	Pratyush Anand <pratyush.anand@gmail.com>
 Description:
 		Write to this node to issue "U3 exit" for Link Layer
 		Validation device. It is needed for TD.7.36.
+
+What:		/sys/bus/usb/devices/.../enable_compliance
+Date:		July 2017
+Description:
+		Write to this node to set the port to compliance mode to test
+		with Link Layer Validation device. It is needed for TD.7.34.
+
+What:		/sys/bus/usb/devices/.../warm_reset
+Date:		July 2017
+Description:
+		Write to this node to issue "Warm Reset" for Link Layer Validation
+		device. It may be needed to properly reset an xHCI 1.1 host port if
+		compliance mode needed to be explicitly enabled.
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 2142132a1f82..ddddd6387f66 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -178,6 +178,25 @@ static ssize_t hot_reset_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(hot_reset);
 
+static ssize_t warm_reset_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	int ret;
+
+	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
+			USB_PORT_FEAT_BH_PORT_RESET);
+	if (ret < 0) {
+		dev_err(dev, "can't issue warm reset %d\n", ret);
+		return ret;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(warm_reset);
+
 static ssize_t u2_timeout_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t count)
 {
@@ -274,13 +293,35 @@ static ssize_t get_dev_desc_store(struct device *dev,
 }
 static DEVICE_ATTR_WO(get_dev_desc);
 
+static ssize_t enable_compliance_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct usb_interface *intf = to_usb_interface(dev);
+	struct usb_device *hdev = interface_to_usbdev(intf);
+	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	int ret;
+
+	ret = lvs_rh_set_port_feature(hdev,
+			lvs->portnum | USB_SS_PORT_LS_COMP_MOD << 3,
+			USB_PORT_FEAT_LINK_STATE);
+	if (ret < 0) {
+		dev_err(dev, "can't enable compliance mode %d\n", ret);
+		return ret;
+	}
+
+	return count;
+}
+static DEVICE_ATTR_WO(enable_compliance);
+
 static struct attribute *lvs_attributes[] = {
 	&dev_attr_get_dev_desc.attr,
 	&dev_attr_u1_timeout.attr,
 	&dev_attr_u2_timeout.attr,
 	&dev_attr_hot_reset.attr,
+	&dev_attr_warm_reset.attr,
 	&dev_attr_u3_entry.attr,
 	&dev_attr_u3_exit.attr,
+	&dev_attr_enable_compliance.attr,
 	NULL
 };
 
-- 
2.12.3