linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining
@ 2024-10-23 10:11 Mika Westerberg
  2024-10-23 10:11 ` [PATCH 1/8] thunderbolt: Don't hardcode margining capabilities size Mika Westerberg
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

Hi all,

This series from Aapo adds support for USB4 v2 Gen 4 link symmetric and
asymmetric receiver lane margining. Useful for instance on manufacturing
floor to check stability of the electrical signaling.

Aapo Vienamo (8):
  thunderbolt: Don't hardcode margining capabilities size
  thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities
  thunderbolt: debugfs: Implement Gen 4 margining eye selection
  thunderbolt: debugfs: Replace "both lanes" with "all lanes"
  thunderbolt: debugfs: Replace margining lane numbers with an enum
  thunderbolt: debugfs: Refactor hardware margining result parsing
  thunderbolt: debugfs: Don't hardcode margining results size
  thunderbolt: debugfs: Implement asymmetric lane margining

 drivers/thunderbolt/debugfs.c | 504 +++++++++++++++++++++++++---------
 drivers/thunderbolt/sb_regs.h |  32 ++-
 drivers/thunderbolt/tb.h      |  16 +-
 drivers/thunderbolt/usb4.c    |  18 +-
 4 files changed, 412 insertions(+), 158 deletions(-)

-- 
2.45.2


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

* [PATCH 1/8] thunderbolt: Don't hardcode margining capabilities size
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 2/8] thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities Mika Westerberg
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Use or pass ARRAY_SIZE() of the capabilities array instead of hardcoding
it. USB4 Gen 4 introduces an additional data word, which requires
expanding the capabilities array.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 13 ++++++-------
 drivers/thunderbolt/tb.h      |  2 +-
 drivers/thunderbolt/usb4.c    |  5 +++--
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 350310bd0fee..972f1948725f 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -7,6 +7,7 @@
  *	    Mika Westerberg <mika.westerberg@linux.intel.com>
  */
 
+#include <linux/array_size.h>
 #include <linux/bitfield.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
@@ -570,16 +571,13 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 {
 	struct tb_margining *margining = s->private;
 	struct tb *tb = margining->port->sw->tb;
-	u32 cap0, cap1;
 
 	if (mutex_lock_interruptible(&tb->lock))
 		return -ERESTARTSYS;
 
 	/* Dump the raw caps first */
-	cap0 = margining->caps[0];
-	seq_printf(s, "0x%08x\n", cap0);
-	cap1 = margining->caps[1];
-	seq_printf(s, "0x%08x\n", cap1);
+	for (int i = 0; i < ARRAY_SIZE(margining->caps); i++)
+		seq_printf(s, "0x%08x\n", margining->caps[i]);
 
 	seq_printf(s, "# software margining: %s\n",
 		   supports_software(margining) ? "yes" : "no");
@@ -623,7 +621,7 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 	if (supports_time(margining)) {
 		seq_puts(s, "# time margining: yes\n");
 		seq_printf(s, "# time margining is destructive: %s\n",
-			   cap1 & USB4_MARGIN_CAP_1_TIME_DESTR ? "yes" : "no");
+			   str_yes_no(margining->caps[1] & USB4_MARGIN_CAP_1_TIME_DESTR));
 
 		switch (independent_time_margins(margining)) {
 		case USB4_MARGIN_CAP_1_TIME_MIN:
@@ -1401,7 +1399,8 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	margining->index = index;
 	margining->dev = dev;
 
-	ret = usb4_port_margining_caps(port, target, index, margining->caps);
+	ret = usb4_port_margining_caps(port, target, index, margining->caps,
+				       ARRAY_SIZE(margining->caps));
 	if (ret) {
 		kfree(margining);
 		return NULL;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 6737188f2581..fa7fc9bba70f 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1388,7 +1388,7 @@ struct usb4_port_margining_params {
 };
 
 int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
-			     u8 index, u32 *caps);
+			     u8 index, u32 *caps, size_t ncaps);
 int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
 			u8 index, const struct usb4_port_margining_params *params,
 			u32 *results);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 0a9b4aeb3fa1..1fb72ff1268e 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1631,11 +1631,12 @@ int usb4_port_asym_start(struct tb_port *port)
  * @target: Sideband target
  * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
  * @caps: Array with at least two elements to hold the results
+ * @ncaps: Number of elements in the caps array
  *
  * Reads the USB4 port lane margining capabilities into @caps.
  */
 int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
-			     u8 index, u32 *caps)
+			     u8 index, u32 *caps, size_t ncaps)
 {
 	int ret;
 
@@ -1645,7 +1646,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
 		return ret;
 
 	return usb4_port_sb_read(port, target, index, USB4_SB_DATA, caps,
-				 sizeof(*caps) * 2);
+				 sizeof(*caps) * ncaps);
 }
 
 /**
-- 
2.45.2


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

* [PATCH 2/8] thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
  2024-10-23 10:11 ` [PATCH 1/8] thunderbolt: Don't hardcode margining capabilities size Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 3/8] thunderbolt: debugfs: Implement Gen 4 margining eye selection Mika Westerberg
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Parse the Gen 4 specific capabilities. Change the return types of
independent_voltage_margins() and independent_time_margins() to enums
that distinguish between the Gen 2/3 and Gen 4 margins since they behave
differently between generations.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 143 ++++++++++++++++++++++++++++------
 drivers/thunderbolt/sb_regs.h |  11 +++
 2 files changed, 131 insertions(+), 23 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 972f1948725f..83721ca069a5 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -44,6 +44,24 @@
 #define MAX_DWELL_TIME		500 /* ms */
 #define DWELL_SAMPLE_INTERVAL	10
 
+enum usb4_margin_cap_voltage_indp {
+	USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN,
+	USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL,
+	USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH,
+	USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN,
+	USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH,
+	USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN,
+};
+
+enum usb4_margin_cap_time_indp {
+	USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN,
+	USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR,
+	USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH,
+	USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN,
+	USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH,
+	USB4_MARGIN_CAP_TIME_INDP_UNKNOWN,
+};
+
 /* Sideband registers and their sizes as defined in the USB4 spec */
 struct sb_reg {
 	unsigned int reg;
@@ -396,6 +414,7 @@ static ssize_t retimer_sb_regs_write(struct file *file,
  * @target: Sideband target
  * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
  * @dev: Pointer to the device that is the target (USB4 port or retimer)
+ * @gen: Link generation
  * @caps: Port lane margining capabilities
  * @results: Last lane margining results
  * @lanes: %0, %1 or %7 (all)
@@ -423,7 +442,8 @@ struct tb_margining {
 	enum usb4_sb_target target;
 	u8 index;
 	struct device *dev;
-	u32 caps[2];
+	unsigned int gen;
+	u32 caps[3];
 	u32 results[2];
 	unsigned int lanes;
 	unsigned int min_ber_level;
@@ -464,12 +484,16 @@ static int margining_modify_error_counter(struct tb_margining *margining,
 
 static bool supports_software(const struct tb_margining *margining)
 {
-	return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
+	if (margining->gen < 4)
+		return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
+	return margining->caps[2] & USB4_MARGIN_CAP_2_MODES_SW;
 }
 
 static bool supports_hardware(const struct tb_margining *margining)
 {
-	return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW;
+	if (margining->gen < 4)
+		return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW;
+	return margining->caps[2] & USB4_MARGIN_CAP_2_MODES_HW;
 }
 
 static bool both_lanes(const struct tb_margining *margining)
@@ -477,22 +501,58 @@ static bool both_lanes(const struct tb_margining *margining)
 	return margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES;
 }
 
-static unsigned int
+static enum usb4_margin_cap_voltage_indp
 independent_voltage_margins(const struct tb_margining *margining)
 {
-	return FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK, margining->caps[0]);
+	if (margining->gen < 4) {
+		switch (FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK, margining->caps[0])) {
+		case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
+			return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN;
+		case USB4_MARGIN_CAP_0_VOLTAGE_HL:
+			return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL;
+		case USB4_MARGIN_CAP_1_TIME_BOTH:
+			return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH;
+		}
+	} else {
+		switch (FIELD_GET(USB4_MARGIN_CAP_2_VOLTAGE_INDP_MASK, margining->caps[2])) {
+		case USB4_MARGIN_CAP_2_VOLTAGE_MIN:
+			return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN;
+		case USB4_MARGIN_CAP_2_VOLTAGE_BOTH:
+			return USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH;
+		}
+	}
+	return USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN;
 }
 
 static bool supports_time(const struct tb_margining *margining)
 {
-	return margining->caps[0] & USB4_MARGIN_CAP_0_TIME;
+	if (margining->gen < 4)
+		return margining->caps[0] & USB4_MARGIN_CAP_0_TIME;
+	return margining->caps[2] & USB4_MARGIN_CAP_2_TIME;
 }
 
 /* Only applicable if supports_time() returns true */
-static unsigned int
+static enum usb4_margin_cap_time_indp
 independent_time_margins(const struct tb_margining *margining)
 {
-	return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]);
+	if (margining->gen < 4) {
+		switch (FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1])) {
+		case USB4_MARGIN_CAP_1_TIME_MIN:
+			return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN;
+		case USB4_MARGIN_CAP_1_TIME_LR:
+			return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR;
+		case USB4_MARGIN_CAP_1_TIME_BOTH:
+			return USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH;
+		}
+	} else {
+		switch (FIELD_GET(USB4_MARGIN_CAP_2_TIME_INDP_MASK, margining->caps[2])) {
+		case USB4_MARGIN_CAP_2_TIME_MIN:
+			return USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN;
+		case USB4_MARGIN_CAP_2_TIME_BOTH:
+			return USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH;
+		}
+	}
+	return USB4_MARGIN_CAP_TIME_INDP_UNKNOWN;
 }
 
 static bool
@@ -571,6 +631,7 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 {
 	struct tb_margining *margining = s->private;
 	struct tb *tb = margining->port->sw->tb;
+	int ret = 0;
 
 	if (mutex_lock_interruptible(&tb->lock))
 		return -ERESTARTSYS;
@@ -607,15 +668,26 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 	}
 
 	switch (independent_voltage_margins(margining)) {
-	case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN:
 		seq_puts(s, "# returns minimum between high and low voltage margins\n");
 		break;
-	case USB4_MARGIN_CAP_0_VOLTAGE_HL:
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL:
 		seq_puts(s, "# returns high or low voltage margin\n");
 		break;
-	case USB4_MARGIN_CAP_0_VOLTAGE_BOTH:
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH:
 		seq_puts(s, "# returns both high and low margins\n");
 		break;
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN:
+		seq_puts(s, "# returns minimum between high and low voltage margins in both lower and upper eye\n");
+		break;
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH:
+		seq_puts(s, "# returns both high and low margins of both upper and lower eye\n");
+		break;
+	case USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN:
+		tb_port_warn(margining->port,
+			     "failed to parse independent voltage margining capabilities\n");
+		ret = -EIO;
+		goto out;
 	}
 
 	if (supports_time(margining)) {
@@ -624,15 +696,26 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 			   str_yes_no(margining->caps[1] & USB4_MARGIN_CAP_1_TIME_DESTR));
 
 		switch (independent_time_margins(margining)) {
-		case USB4_MARGIN_CAP_1_TIME_MIN:
+		case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN:
 			seq_puts(s, "# returns minimum between left and right time margins\n");
 			break;
-		case USB4_MARGIN_CAP_1_TIME_LR:
+		case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR:
 			seq_puts(s, "# returns left or right margin\n");
 			break;
-		case USB4_MARGIN_CAP_1_TIME_BOTH:
+		case USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH:
 			seq_puts(s, "# returns both left and right margins\n");
 			break;
+		case USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN:
+			seq_puts(s, "# returns minimum between left and right time margins in both lower and upper eye\n");
+			break;
+		case USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH:
+			seq_puts(s, "# returns both left and right margins of both upper and lower eye\n");
+			break;
+		case USB4_MARGIN_CAP_TIME_INDP_UNKNOWN:
+			tb_port_warn(margining->port,
+				     "failed to parse independent time margining capabilities\n");
+			ret = -EIO;
+			goto out;
 		}
 
 		seq_printf(s, "# time margin steps: %u\n",
@@ -643,8 +726,9 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 		seq_puts(s, "# time margining: no\n");
 	}
 
+out:
 	mutex_unlock(&tb->lock);
-	return 0;
+	return ret;
 }
 DEBUGFS_ATTR_RO(margining_caps);
 
@@ -1390,6 +1474,12 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	unsigned int val;
 	int ret;
 
+	ret = tb_port_get_link_generation(port);
+	if (ret < 0) {
+		tb_port_warn(port, "failed to read link generation\n");
+		return NULL;
+	}
+
 	margining = kzalloc(sizeof(*margining), GFP_KERNEL);
 	if (!margining)
 		return NULL;
@@ -1398,6 +1488,7 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	margining->target = target;
 	margining->index = index;
 	margining->dev = dev;
+	margining->gen = ret;
 
 	ret = usb4_port_margining_caps(port, target, index, margining->caps,
 				       ARRAY_SIZE(margining->caps));
@@ -1410,10 +1501,17 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	if (supports_software(margining))
 		margining->software = true;
 
-	val = FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK, margining->caps[0]);
-	margining->voltage_steps = val;
-	val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]);
-	margining->max_voltage_offset = 74 + val * 2;
+	if (margining->gen < 4) {
+		val = FIELD_GET(USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK, margining->caps[0]);
+		margining->voltage_steps = val;
+		val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]);
+		margining->max_voltage_offset = 74 + val * 2;
+	} else {
+		val = FIELD_GET(USB4_MARGIN_CAP_2_VOLTAGE_STEPS_MASK, margining->caps[2]);
+		margining->voltage_steps = val;
+		val = FIELD_GET(USB4_MARGIN_CAP_2_MAX_VOLTAGE_OFFSET_MASK, margining->caps[2]);
+		margining->max_voltage_offset = 74 + val * 2;
+	}
 
 	if (supports_optional_voltage_offset_range(margining)) {
 		val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK,
@@ -1455,11 +1553,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	debugfs_create_file("results", 0600, dir, margining,
 			    &margining_results_fops);
 	debugfs_create_file("test", 0600, dir, margining, &margining_test_fops);
-	if (independent_voltage_margins(margining) == USB4_MARGIN_CAP_0_VOLTAGE_HL ||
+	if (independent_voltage_margins(margining) == USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL ||
 	    (supports_time(margining) &&
-	     independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
-		debugfs_create_file("margin", 0600, dir, margining,
-				    &margining_margin_fops);
+	     independent_time_margins(margining) == USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR))
+		debugfs_create_file("margin", 0600, dir, margining, &margining_margin_fops);
 
 	margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR;
 	margining->dwell_time = MIN_DWELL_TIME;
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index dbcad25ead50..f60b8d90a71a 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -69,6 +69,17 @@ enum usb4_sb_opcode {
 #define USB4_MARGIN_CAP_1_TIME_OFFSET_MASK	GENMASK(20, 16)
 #define USB4_MARGIN_CAP_1_MIN_BER_MASK		GENMASK(25, 21)
 #define USB4_MARGIN_CAP_1_MAX_BER_MASK		GENMASK(30, 26)
+#define USB4_MARGIN_CAP_2_MODES_HW		BIT(0)
+#define USB4_MARGIN_CAP_2_MODES_SW		BIT(1)
+#define USB4_MARGIN_CAP_2_TIME			BIT(2)
+#define USB4_MARGIN_CAP_2_MAX_VOLTAGE_OFFSET_MASK GENMASK(8, 3)
+#define USB4_MARGIN_CAP_2_VOLTAGE_STEPS_MASK	GENMASK(15, 9)
+#define USB4_MARGIN_CAP_2_VOLTAGE_INDP_MASK	GENMASK(17, 16)
+#define USB4_MARGIN_CAP_2_VOLTAGE_MIN		0x0
+#define USB4_MARGIN_CAP_2_VOLTAGE_BOTH		0x1
+#define USB4_MARGIN_CAP_2_TIME_INDP_MASK	GENMASK(19, 18)
+#define USB4_MARGIN_CAP_2_TIME_MIN		0x0
+#define USB4_MARGIN_CAP_2_TIME_BOTH		0x1
 
 /* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */
 #define USB4_MARGIN_HW_TIME			BIT(3)
-- 
2.45.2


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

* [PATCH 3/8] thunderbolt: debugfs: Implement Gen 4 margining eye selection
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
  2024-10-23 10:11 ` [PATCH 1/8] thunderbolt: Don't hardcode margining capabilities size Mika Westerberg
  2024-10-23 10:11 ` [PATCH 2/8] thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 4/8] thunderbolt: debugfs: Replace "both lanes" with "all lanes" Mika Westerberg
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Add a debugfs knob for USB4 Gen 4 margining eye selection. Gen 4 uses
3-level pulse amplitude modulation (PAM3) which changes how margining
measurements are made because PAM3 has two eyes per lane from which
the margins can be measured.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 58 +++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/sb_regs.h |  3 +-
 drivers/thunderbolt/tb.h      |  1 +
 drivers/thunderbolt/usb4.c    |  6 ++--
 4 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 83721ca069a5..699e1632e3f5 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -436,6 +436,8 @@ static ssize_t retimer_sb_regs_write(struct file *file,
  * @time: %true if time margining is used instead of voltage
  * @right_high: %false if left/low margin test is performed, %true if
  *		right/high
+ * @upper_eye: %false if the lower PAM3 eye is used, %true if the upper
+ *	       eye is used
  */
 struct tb_margining {
 	struct tb_port *port;
@@ -462,6 +464,7 @@ struct tb_margining {
 	bool software;
 	bool time;
 	bool right_high;
+	bool upper_eye;
 };
 
 static int margining_modify_error_counter(struct tb_margining *margining,
@@ -1162,6 +1165,7 @@ static int margining_run_write(void *data, u64 val)
 			.time = margining->time,
 			.voltage_time_offset = margining->voltage_time_offset,
 			.right_high = margining->right_high,
+			.upper_eye = margining->upper_eye,
 			.optional_voltage_offset_range = margining->optional_voltage_offset_range,
 		};
 
@@ -1177,6 +1181,7 @@ static int margining_run_write(void *data, u64 val)
 			.lanes = margining->lanes,
 			.time = margining->time,
 			.right_high = margining->right_high,
+			.upper_eye = margining->upper_eye,
 			.optional_voltage_offset_range = margining->optional_voltage_offset_range,
 		};
 
@@ -1464,6 +1469,55 @@ static int margining_margin_show(struct seq_file *s, void *not_used)
 }
 DEBUGFS_ATTR_RW(margining_margin);
 
+static ssize_t margining_eye_write(struct file *file,
+				   const char __user *user_buf,
+				   size_t count, loff_t *ppos)
+{
+	struct seq_file *s = file->private_data;
+	struct tb_port *port = s->private;
+	struct usb4_port *usb4 = port->usb4;
+	struct tb *tb = port->sw->tb;
+	int ret = 0;
+	char *buf;
+
+	buf = validate_and_copy_from_user(user_buf, &count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	buf[count - 1] = '\0';
+
+	scoped_cond_guard(mutex_intr, ret = -ERESTARTSYS, &tb->lock) {
+		if (!strcmp(buf, "lower"))
+			usb4->margining->upper_eye = false;
+		else if (!strcmp(buf, "upper"))
+			usb4->margining->upper_eye = true;
+		else
+			ret = -EINVAL;
+	}
+
+	free_page((unsigned long)buf);
+	return ret ? ret : count;
+}
+
+static int margining_eye_show(struct seq_file *s, void *not_used)
+{
+	struct tb_port *port = s->private;
+	struct usb4_port *usb4 = port->usb4;
+	struct tb *tb = port->sw->tb;
+
+	scoped_guard(mutex_intr, &tb->lock) {
+		if (usb4->margining->upper_eye)
+			seq_puts(s, "lower [upper]\n");
+		else
+			seq_puts(s, "[lower] upper\n");
+
+		return 0;
+	}
+
+	return -ERESTARTSYS;
+}
+DEBUGFS_ATTR_RW(margining_eye);
+
 static struct tb_margining *margining_alloc(struct tb_port *port,
 					    struct device *dev,
 					    enum usb4_sb_target target,
@@ -1573,6 +1627,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 		debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining,
 				    &margining_dwell_time_fops);
 	}
+
+	if (margining->gen >= 4)
+		debugfs_create_file("eye", 0600, dir, port, &margining_eye_fops);
+
 	return margining;
 }
 
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index f60b8d90a71a..b7e91b99fefe 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -83,7 +83,7 @@ enum usb4_sb_opcode {
 
 /* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */
 #define USB4_MARGIN_HW_TIME			BIT(3)
-#define USB4_MARGIN_HW_RH			BIT(4)
+#define USB4_MARGIN_HW_RHU			BIT(4)
 #define USB4_MARGIN_HW_BER_MASK			GENMASK(9, 5)
 #define USB4_MARGIN_HW_BER_SHIFT		5
 #define USB4_MARGIN_HW_OPT_VOLTAGE		BIT(10)
@@ -106,6 +106,7 @@ enum usb4_sb_opcode {
 #define USB4_MARGIN_SW_OPT_VOLTAGE		BIT(5)
 #define USB4_MARGIN_SW_VT_MASK			GENMASK(12, 6)
 #define USB4_MARGIN_SW_COUNTER_MASK		GENMASK(14, 13)
+#define USB4_MARGIN_SW_UPPER_EYE		BIT(15)
 
 #define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK	GENMASK(3, 0)
 #define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK	GENMASK(7, 4)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index fa7fc9bba70f..ced9be271620 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1384,6 +1384,7 @@ struct usb4_port_margining_params {
 	u32 voltage_time_offset;
 	bool optional_voltage_offset_range;
 	bool right_high;
+	bool upper_eye;
 	bool time;
 };
 
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 1fb72ff1268e..985f24b044b3 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1673,8 +1673,8 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
 	val = params->lanes;
 	if (params->time)
 		val |= USB4_MARGIN_HW_TIME;
-	if (params->right_high)
-		val |= USB4_MARGIN_HW_RH;
+	if (params->right_high || params->upper_eye)
+		val |= USB4_MARGIN_HW_RHU;
 	if (params->ber_level)
 		val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level);
 	if (params->optional_voltage_offset_range)
@@ -1723,6 +1723,8 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
 		val |= USB4_MARGIN_SW_OPT_VOLTAGE;
 	if (params->right_high)
 		val |= USB4_MARGIN_SW_RH;
+	if (params->upper_eye)
+		val |= USB4_MARGIN_SW_UPPER_EYE;
 	val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter);
 	val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset);
 
-- 
2.45.2


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

* [PATCH 4/8] thunderbolt: debugfs: Replace "both lanes" with "all lanes"
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (2 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 3/8] thunderbolt: debugfs: Implement Gen 4 margining eye selection Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 5/8] thunderbolt: debugfs: Replace margining lane numbers with an enum Mika Westerberg
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

With USB4 Gen 4, the link can be configured into an asymmetric mode,
where there are three receivers and only one transmitter. The USB4
specification also uses the "all lanes" nomenclature instead of "both
lanes".

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 12 ++++++------
 drivers/thunderbolt/sb_regs.h |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 699e1632e3f5..5f9f8babeae2 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -499,9 +499,9 @@ static bool supports_hardware(const struct tb_margining *margining)
 	return margining->caps[2] & USB4_MARGIN_CAP_2_MODES_HW;
 }
 
-static bool both_lanes(const struct tb_margining *margining)
+static bool all_lanes(const struct tb_margining *margining)
 {
-	return margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES;
+	return margining->caps[0] & USB4_MARGIN_CAP_0_ALL_LANES;
 }
 
 static enum usb4_margin_cap_voltage_indp
@@ -655,8 +655,8 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 		seq_puts(s, "# hardware margining: no\n");
 	}
 
-	seq_printf(s, "# both lanes simultaneously: %s\n",
-		  both_lanes(margining) ? "yes" : "no");
+	seq_printf(s, "# all lanes simultaneously: %s\n",
+		  str_yes_no(all_lanes(margining)));
 	seq_printf(s, "# voltage margin steps: %u\n",
 		   margining->voltage_steps);
 	seq_printf(s, "# maximum voltage offset: %u mV\n",
@@ -762,7 +762,7 @@ margining_lanes_write(struct file *file, const char __user *user_buf,
 		margining->lanes = 1;
 	} else if (!strcmp(buf, "all")) {
 		/* Needs to be supported */
-		if (both_lanes(margining))
+		if (all_lanes(margining))
 			margining->lanes = 7;
 		else
 			ret = -EINVAL;
@@ -787,7 +787,7 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
 		return -ERESTARTSYS;
 
 	lanes = margining->lanes;
-	if (both_lanes(margining)) {
+	if (all_lanes(margining)) {
 		if (!lanes)
 			seq_puts(s, "[0] 1 all\n");
 		else if (lanes == 1)
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index b7e91b99fefe..a3652b9cb246 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -49,7 +49,7 @@ enum usb4_sb_opcode {
 /* USB4_SB_OPCODE_READ_LANE_MARGINING_CAP */
 #define USB4_MARGIN_CAP_0_MODES_HW		BIT(0)
 #define USB4_MARGIN_CAP_0_MODES_SW		BIT(1)
-#define USB4_MARGIN_CAP_0_2_LANES		BIT(2)
+#define USB4_MARGIN_CAP_0_ALL_LANES		BIT(2)
 #define USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK	GENMASK(4, 3)
 #define USB4_MARGIN_CAP_0_VOLTAGE_MIN		0x0
 #define USB4_MARGIN_CAP_0_VOLTAGE_HL		0x1
-- 
2.45.2


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

* [PATCH 5/8] thunderbolt: debugfs: Replace margining lane numbers with an enum
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (3 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 4/8] thunderbolt: debugfs: Replace "both lanes" with "all lanes" Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 6/8] thunderbolt: debugfs: Refactor hardware margining result parsing Mika Westerberg
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Replace the raw values and macros with an enum and use it consistently.
No functional changes.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 46 +++++++++++++++++++----------------
 drivers/thunderbolt/sb_regs.h |  3 ---
 drivers/thunderbolt/tb.h      | 10 ++++++--
 3 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 5f9f8babeae2..3404237d167b 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -447,7 +447,7 @@ struct tb_margining {
 	unsigned int gen;
 	u32 caps[3];
 	u32 results[2];
-	unsigned int lanes;
+	enum usb4_margining_lane lanes;
 	unsigned int min_ber_level;
 	unsigned int max_ber_level;
 	unsigned int ber_level;
@@ -757,13 +757,13 @@ margining_lanes_write(struct file *file, const char __user *user_buf,
 	}
 
 	if (!strcmp(buf, "0")) {
-		margining->lanes = 0;
+		margining->lanes = USB4_MARGINING_LANE_RX0;
 	} else if (!strcmp(buf, "1")) {
-		margining->lanes = 1;
+		margining->lanes = USB4_MARGINING_LANE_RX1;
 	} else if (!strcmp(buf, "all")) {
 		/* Needs to be supported */
 		if (all_lanes(margining))
-			margining->lanes = 7;
+			margining->lanes = USB4_MARGINING_LANE_ALL;
 		else
 			ret = -EINVAL;
 	} else {
@@ -781,21 +781,21 @@ static int margining_lanes_show(struct seq_file *s, void *not_used)
 {
 	struct tb_margining *margining = s->private;
 	struct tb *tb = margining->port->sw->tb;
-	unsigned int lanes;
+	enum usb4_margining_lane lanes;
 
 	if (mutex_lock_interruptible(&tb->lock))
 		return -ERESTARTSYS;
 
 	lanes = margining->lanes;
 	if (all_lanes(margining)) {
-		if (!lanes)
+		if (lanes == USB4_MARGINING_LANE_RX0)
 			seq_puts(s, "[0] 1 all\n");
-		else if (lanes == 1)
+		else if (lanes == USB4_MARGINING_LANE_RX1)
 			seq_puts(s, "0 [1] all\n");
 		else
 			seq_puts(s, "0 1 [all]\n");
 	} else {
-		if (!lanes)
+		if (lanes == USB4_MARGINING_LANE_RX0)
 			seq_puts(s, "[0] 1\n");
 		else
 			seq_puts(s, "0 [1]\n");
@@ -1089,13 +1089,13 @@ static int margining_run_sw(struct tb_margining *margining,
 		if (ret)
 			break;
 
-		if (margining->lanes == USB4_MARGIN_SW_LANE_0)
+		if (margining->lanes == USB4_MARGINING_LANE_RX0)
 			errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
 					   margining->results[1]);
-		else if (margining->lanes == USB4_MARGIN_SW_LANE_1)
+		else if (margining->lanes == USB4_MARGINING_LANE_RX1)
 			errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
 					   margining->results[1]);
-		else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES)
+		else if (margining->lanes == USB4_MARGINING_LANE_ALL)
 			errors = margining->results[1];
 
 		/* Any errors stop the test */
@@ -1225,7 +1225,7 @@ static ssize_t margining_results_write(struct file *file,
 	if (margining->software) {
 		/* Clear the error counters */
 		margining_modify_error_counter(margining,
-					       USB4_MARGIN_SW_ALL_LANES,
+					       USB4_MARGINING_LANE_ALL,
 					       USB4_MARGIN_SW_ERROR_COUNTER_CLEAR);
 	}
 
@@ -1278,7 +1278,8 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 		seq_printf(s, "0x%08x\n", margining->results[1]);
 
 		if (margining->time) {
-			if (!margining->lanes || margining->lanes == 7) {
+			if (margining->lanes == USB4_MARGINING_LANE_RX0 ||
+			    margining->lanes == USB4_MARGINING_LANE_ALL) {
 				val = margining->results[1];
 				seq_puts(s, "# lane 0 right time margin: ");
 				time_margin_show(s, margining, val);
@@ -1287,7 +1288,8 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 				seq_puts(s, "# lane 0 left time margin: ");
 				time_margin_show(s, margining, val);
 			}
-			if (margining->lanes == 1 || margining->lanes == 7) {
+			if (margining->lanes == USB4_MARGINING_LANE_RX1 ||
+			    margining->lanes == USB4_MARGINING_LANE_ALL) {
 				val = margining->results[1] >>
 					USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
 				seq_puts(s, "# lane 1 right time margin: ");
@@ -1298,7 +1300,8 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 				time_margin_show(s, margining, val);
 			}
 		} else {
-			if (!margining->lanes || margining->lanes == 7) {
+			if (margining->lanes == USB4_MARGINING_LANE_RX0 ||
+			    margining->lanes == USB4_MARGINING_LANE_ALL) {
 				val = margining->results[1];
 				seq_puts(s, "# lane 0 high voltage margin: ");
 				voltage_margin_show(s, margining, val);
@@ -1307,7 +1310,8 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 				seq_puts(s, "# lane 0 low voltage margin: ");
 				voltage_margin_show(s, margining, val);
 			}
-			if (margining->lanes == 1 || margining->lanes == 7) {
+			if (margining->lanes == USB4_MARGINING_LANE_RX1 ||
+			    margining->lanes == USB4_MARGINING_LANE_ALL) {
 				val = margining->results[1] >>
 					USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
 				seq_puts(s, "# lane 1 high voltage margin: ");
@@ -1322,16 +1326,16 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 		u32 lane_errors, result;
 
 		seq_printf(s, "0x%08x\n", margining->results[1]);
-		result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]);
 
-		if (result == USB4_MARGIN_SW_LANE_0 ||
-		    result == USB4_MARGIN_SW_ALL_LANES) {
+		result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]);
+		if (result == USB4_MARGINING_LANE_RX0 ||
+		    result == USB4_MARGINING_LANE_ALL) {
 			lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK,
 						margining->results[1]);
 			seq_printf(s, "# lane 0 errors: %u\n", lane_errors);
 		}
-		if (result == USB4_MARGIN_SW_LANE_1 ||
-		    result == USB4_MARGIN_SW_ALL_LANES) {
+		if (result == USB4_MARGINING_LANE_RX1 ||
+		    result == USB4_MARGINING_LANE_ALL) {
 			lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
 						margining->results[1]);
 			seq_printf(s, "# lane 1 errors: %u\n", lane_errors);
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index a3652b9cb246..91c6333d08c8 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -98,9 +98,6 @@ enum usb4_sb_opcode {
 
 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
 #define USB4_MARGIN_SW_LANES_MASK		GENMASK(2, 0)
-#define USB4_MARGIN_SW_LANE_0			0x0
-#define USB4_MARGIN_SW_LANE_1			0x1
-#define USB4_MARGIN_SW_ALL_LANES		0x7
 #define USB4_MARGIN_SW_TIME			BIT(3)
 #define USB4_MARGIN_SW_RH			BIT(4)
 #define USB4_MARGIN_SW_OPT_VOLTAGE		BIT(5)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index ced9be271620..fb2e1f089169 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1367,11 +1367,17 @@ enum usb4_margin_sw_error_counter {
 	USB4_MARGIN_SW_ERROR_COUNTER_STOP,
 };
 
+enum usb4_margining_lane {
+	USB4_MARGINING_LANE_RX0 = 0,
+	USB4_MARGINING_LANE_RX1 = 1,
+	USB4_MARGINING_LANE_ALL = 7,
+};
+
 /**
  * struct usb4_port_margining_params - USB4 margining parameters
  * @error_counter: Error counter operation for software margining
  * @ber_level: Current BER level contour value
- * @lanes: %0, %1 or %7 (all)
+ * @lanes: Lanes to enable for the margining operation
  * @voltage_time_offset: Offset for voltage / time for software margining
  * @optional_voltage_offset_range: Enable optional extended voltage range
  * @right_high: %false if left/low margin test is performed, %true if right/high
@@ -1380,7 +1386,7 @@ enum usb4_margin_sw_error_counter {
 struct usb4_port_margining_params {
 	enum usb4_margin_sw_error_counter error_counter;
 	u32 ber_level;
-	u32 lanes;
+	enum usb4_margining_lane lanes;
 	u32 voltage_time_offset;
 	bool optional_voltage_offset_range;
 	bool right_high;
-- 
2.45.2


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

* [PATCH 6/8] thunderbolt: debugfs: Refactor hardware margining result parsing
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (4 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 5/8] thunderbolt: debugfs: Replace margining lane numbers with an enum Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 7/8] thunderbolt: debugfs: Don't hardcode margining results size Mika Westerberg
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Make the result parsing and formatting code less repetitive in
preparation for adding another result for Gen 4 asymmetric link support.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 99 ++++++++++++++++++-----------------
 drivers/thunderbolt/sb_regs.h | 12 ++---
 2 files changed, 56 insertions(+), 55 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 3404237d167b..2f9756c201b6 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -1238,10 +1238,10 @@ static void voltage_margin_show(struct seq_file *s,
 {
 	unsigned int tmp, voltage;
 
-	tmp = FIELD_GET(USB4_MARGIN_HW_RES_1_MARGIN_MASK, val);
+	tmp = FIELD_GET(USB4_MARGIN_HW_RES_MARGIN_MASK, val);
 	voltage = tmp * margining->max_voltage_offset / margining->voltage_steps;
 	seq_printf(s, "%u mV (%u)", voltage, tmp);
-	if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
+	if (val & USB4_MARGIN_HW_RES_EXCEEDS)
 		seq_puts(s, " exceeds maximum");
 	seq_puts(s, "\n");
 	if (margining->optional_voltage_offset_range)
@@ -1253,14 +1253,53 @@ static void time_margin_show(struct seq_file *s,
 {
 	unsigned int tmp, interval;
 
-	tmp = FIELD_GET(USB4_MARGIN_HW_RES_1_MARGIN_MASK, val);
+	tmp = FIELD_GET(USB4_MARGIN_HW_RES_MARGIN_MASK, val);
 	interval = tmp * margining->max_time_offset / margining->time_steps;
 	seq_printf(s, "%u mUI (%u)", interval, tmp);
-	if (val & USB4_MARGIN_HW_RES_1_EXCEEDS)
+	if (val & USB4_MARGIN_HW_RES_EXCEEDS)
 		seq_puts(s, " exceeds maximum");
 	seq_puts(s, "\n");
 }
 
+static u8 margining_hw_result_val(const u32 *results,
+				  enum usb4_margining_lane lane,
+				  bool right_high)
+{
+	u32 val;
+
+	if (lane == USB4_MARGINING_LANE_RX0)
+		val = results[1];
+	else if (lane == USB4_MARGINING_LANE_RX1)
+		val = results[1] >> USB4_MARGIN_HW_RES_LANE_SHIFT;
+	else
+		val = 0;
+
+	return right_high ? val : val >> USB4_MARGIN_HW_RES_LL_SHIFT;
+}
+
+static void margining_hw_result_format(struct seq_file *s,
+				       const struct tb_margining *margining,
+				       enum usb4_margining_lane lane)
+{
+	u8 val;
+
+	if (margining->time) {
+		val = margining_hw_result_val(margining->results, lane, true);
+		seq_printf(s, "# lane %u right time margin: ", lane);
+		time_margin_show(s, margining, val);
+		val = margining_hw_result_val(margining->results, lane, false);
+		seq_printf(s, "# lane %u left time margin: ", lane);
+		time_margin_show(s, margining, val);
+	} else {
+		val = margining_hw_result_val(margining->results, lane, true);
+		seq_printf(s, "# lane %u high voltage margin: ", lane);
+		voltage_margin_show(s, margining, val);
+		val = margining_hw_result_val(margining->results, lane, false);
+		seq_printf(s, "# lane %u low voltage margin: ", lane);
+		voltage_margin_show(s, margining, val);
+	}
+}
+
 static int margining_results_show(struct seq_file *s, void *not_used)
 {
 	struct tb_margining *margining = s->private;
@@ -1273,54 +1312,16 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 	seq_printf(s, "0x%08x\n", margining->results[0]);
 	/* Only the hardware margining has two result dwords */
 	if (!margining->software) {
-		unsigned int val;
-
 		seq_printf(s, "0x%08x\n", margining->results[1]);
 
-		if (margining->time) {
-			if (margining->lanes == USB4_MARGINING_LANE_RX0 ||
-			    margining->lanes == USB4_MARGINING_LANE_ALL) {
-				val = margining->results[1];
-				seq_puts(s, "# lane 0 right time margin: ");
-				time_margin_show(s, margining, val);
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT;
-				seq_puts(s, "# lane 0 left time margin: ");
-				time_margin_show(s, margining, val);
-			}
-			if (margining->lanes == USB4_MARGINING_LANE_RX1 ||
-			    margining->lanes == USB4_MARGINING_LANE_ALL) {
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
-				seq_puts(s, "# lane 1 right time margin: ");
-				time_margin_show(s, margining, val);
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT;
-				seq_puts(s, "# lane 1 left time margin: ");
-				time_margin_show(s, margining, val);
-			}
+		if (margining->lanes == USB4_MARGINING_LANE_ALL) {
+			margining_hw_result_format(s, margining,
+						   USB4_MARGINING_LANE_RX0);
+			margining_hw_result_format(s, margining,
+						   USB4_MARGINING_LANE_RX1);
 		} else {
-			if (margining->lanes == USB4_MARGINING_LANE_RX0 ||
-			    margining->lanes == USB4_MARGINING_LANE_ALL) {
-				val = margining->results[1];
-				seq_puts(s, "# lane 0 high voltage margin: ");
-				voltage_margin_show(s, margining, val);
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT;
-				seq_puts(s, "# lane 0 low voltage margin: ");
-				voltage_margin_show(s, margining, val);
-			}
-			if (margining->lanes == USB4_MARGINING_LANE_RX1 ||
-			    margining->lanes == USB4_MARGINING_LANE_ALL) {
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT;
-				seq_puts(s, "# lane 1 high voltage margin: ");
-				voltage_margin_show(s, margining, val);
-				val = margining->results[1] >>
-					USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT;
-				seq_puts(s, "# lane 1 low voltage margin: ");
-				voltage_margin_show(s, margining, val);
-			}
+			margining_hw_result_format(s, margining,
+						   margining->lanes);
 		}
 	} else {
 		u32 lane_errors, result;
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index 91c6333d08c8..7b5521ea0f74 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -89,12 +89,12 @@ enum usb4_sb_opcode {
 #define USB4_MARGIN_HW_OPT_VOLTAGE		BIT(10)
 
 /* Applicable to all margin values */
-#define USB4_MARGIN_HW_RES_1_MARGIN_MASK	GENMASK(6, 0)
-#define USB4_MARGIN_HW_RES_1_EXCEEDS		BIT(7)
-/* Different lane margin shifts */
-#define USB4_MARGIN_HW_RES_1_L0_LL_MARGIN_SHIFT	8
-#define USB4_MARGIN_HW_RES_1_L1_RH_MARGIN_SHIFT	16
-#define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT	24
+#define USB4_MARGIN_HW_RES_MARGIN_MASK		GENMASK(6, 0)
+#define USB4_MARGIN_HW_RES_EXCEEDS		BIT(7)
+
+/* Shifts for parsing the lane results */
+#define USB4_MARGIN_HW_RES_LANE_SHIFT		16
+#define USB4_MARGIN_HW_RES_LL_SHIFT		8
 
 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */
 #define USB4_MARGIN_SW_LANES_MASK		GENMASK(2, 0)
-- 
2.45.2


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

* [PATCH 7/8] thunderbolt: debugfs: Don't hardcode margining results size
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (5 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 6/8] thunderbolt: debugfs: Refactor hardware margining result parsing Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-10-23 10:11 ` [PATCH 8/8] thunderbolt: debugfs: Implement asymmetric lane margining Mika Westerberg
  2024-11-01  6:00 ` [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 " Mika Westerberg
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Use ARRAY_SIZE() when available or pass in the array size derived from
it. This is in preparation for adding another result data word for
supporting Gen 4 asymmetric links with an additional lane.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 8 ++++----
 drivers/thunderbolt/tb.h      | 2 +-
 drivers/thunderbolt/usb4.c    | 7 ++++---
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 2f9756c201b6..9899d88b7371 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -1191,7 +1191,7 @@ static int margining_run_write(void *data, u64 val)
 			    margining->lanes);
 
 		ret = usb4_port_hw_margin(port, margining->target, margining->index, &params,
-					  margining->results);
+					  margining->results, ARRAY_SIZE(margining->results));
 	}
 
 	if (down_sw)
@@ -1219,8 +1219,7 @@ static ssize_t margining_results_write(struct file *file,
 		return -ERESTARTSYS;
 
 	/* Just clear the results */
-	margining->results[0] = 0;
-	margining->results[1] = 0;
+	memset(margining->results, 0, sizeof(margining->results));
 
 	if (margining->software) {
 		/* Clear the error counters */
@@ -1312,7 +1311,8 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 	seq_printf(s, "0x%08x\n", margining->results[0]);
 	/* Only the hardware margining has two result dwords */
 	if (!margining->software) {
-		seq_printf(s, "0x%08x\n", margining->results[1]);
+		for (int i = 1; i < ARRAY_SIZE(margining->results); i++)
+			seq_printf(s, "0x%08x\n", margining->results[i]);
 
 		if (margining->lanes == USB4_MARGINING_LANE_ALL) {
 			margining_hw_result_format(s, margining,
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index fb2e1f089169..0954b8bafada 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1398,7 +1398,7 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
 			     u8 index, u32 *caps, size_t ncaps);
 int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
 			u8 index, const struct usb4_port_margining_params *params,
-			u32 *results);
+			u32 *results, size_t nresults);
 int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
 			u8 index, const struct usb4_port_margining_params *params,
 			u32 *results);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 985f24b044b3..05985b18834e 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1655,14 +1655,15 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
  * @target: Sideband target
  * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
  * @params: Parameters for USB4 hardware margining
- * @results: Array with at least two elements to hold the results
+ * @results: Array to hold the results
+ * @nresults: Number of elements in the results array
  *
  * Runs hardware lane margining on USB4 port and returns the result in
  * @results.
  */
 int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
 			u8 index, const struct usb4_port_margining_params *params,
-			u32 *results)
+			u32 *results, size_t nresults)
 {
 	u32 val;
 	int ret;
@@ -1691,7 +1692,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
 		return ret;
 
 	return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results,
-				 sizeof(*results) * 2);
+				 sizeof(*results) * nresults);
 }
 
 /**
-- 
2.45.2


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

* [PATCH 8/8] thunderbolt: debugfs: Implement asymmetric lane margining
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (6 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 7/8] thunderbolt: debugfs: Don't hardcode margining results size Mika Westerberg
@ 2024-10-23 10:11 ` Mika Westerberg
  2024-11-01  6:00 ` [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 " Mika Westerberg
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-10-23 10:11 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo, Mika Westerberg

From: Aapo Vienamo <aapo.vienamo@iki.fi>

Add support for the RX2 receiver which is used as the third receiver in
asymmetric links. This requires expanding the results array for the
additional third data word of the hardware margining results.

Signed-off-by: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/thunderbolt/debugfs.c | 157 +++++++++++++++++++++++++---------
 drivers/thunderbolt/sb_regs.h |   1 +
 drivers/thunderbolt/tb.h      |   1 +
 3 files changed, 117 insertions(+), 42 deletions(-)

diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 9899d88b7371..a1d0d8a33f20 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -415,6 +415,7 @@ static ssize_t retimer_sb_regs_write(struct file *file,
  * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
  * @dev: Pointer to the device that is the target (USB4 port or retimer)
  * @gen: Link generation
+ * @asym_rx: %true% if @port supports asymmetric link with 3 Rx
  * @caps: Port lane margining capabilities
  * @results: Last lane margining results
  * @lanes: %0, %1 or %7 (all)
@@ -445,8 +446,9 @@ struct tb_margining {
 	u8 index;
 	struct device *dev;
 	unsigned int gen;
+	bool asym_rx;
 	u32 caps[3];
-	u32 results[2];
+	u32 results[3];
 	enum usb4_margining_lane lanes;
 	unsigned int min_ber_level;
 	unsigned int max_ber_level;
@@ -735,14 +737,37 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
 }
 DEBUGFS_ATTR_RO(margining_caps);
 
+static const struct {
+	enum usb4_margining_lane lane;
+	const char *name;
+} lane_names[] = {
+	{
+		.lane = USB4_MARGINING_LANE_RX0,
+		.name = "0",
+	},
+	{
+		.lane = USB4_MARGINING_LANE_RX1,
+		.name = "1",
+	},
+	{
+		.lane = USB4_MARGINING_LANE_RX2,
+		.name = "2",
+	},
+	{
+		.lane = USB4_MARGINING_LANE_ALL,
+		.name = "all",
+	},
+};
+
 static ssize_t
 margining_lanes_write(struct file *file, const char __user *user_buf,
 		      size_t count, loff_t *ppos)
 {
 	struct seq_file *s = file->private_data;
 	struct tb_margining *margining = s->private;
-	struct tb *tb = margining->port->sw->tb;
-	int ret = 0;
+	struct tb_port *port = margining->port;
+	struct tb *tb = port->sw->tb;
+	int lane = -1;
 	char *buf;
 
 	buf = validate_and_copy_from_user(user_buf, &count);
@@ -751,57 +776,60 @@ margining_lanes_write(struct file *file, const char __user *user_buf,
 
 	buf[count - 1] = '\0';
 
-	if (mutex_lock_interruptible(&tb->lock)) {
-		ret = -ERESTARTSYS;
-		goto out_free;
+	for (int i = 0; i < ARRAY_SIZE(lane_names); i++) {
+		if (!strcmp(buf, lane_names[i].name)) {
+			lane = lane_names[i].lane;
+			break;
+		}
 	}
 
-	if (!strcmp(buf, "0")) {
-		margining->lanes = USB4_MARGINING_LANE_RX0;
-	} else if (!strcmp(buf, "1")) {
-		margining->lanes = USB4_MARGINING_LANE_RX1;
-	} else if (!strcmp(buf, "all")) {
-		/* Needs to be supported */
-		if (all_lanes(margining))
-			margining->lanes = USB4_MARGINING_LANE_ALL;
-		else
-			ret = -EINVAL;
-	} else {
-		ret = -EINVAL;
-	}
+	free_page((unsigned long)buf);
 
-	mutex_unlock(&tb->lock);
+	if (lane == -1)
+		return -EINVAL;
 
-out_free:
-	free_page((unsigned long)buf);
-	return ret < 0 ? ret : count;
+	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
+		if (lane == USB4_MARGINING_LANE_ALL && !all_lanes(margining))
+			return -EINVAL;
+		/*
+		 * Enabling on RX2 requires that it is supported by the
+		 * USB4 port.
+		 */
+		if (lane == USB4_MARGINING_LANE_RX2 && !margining->asym_rx)
+			return -EINVAL;
+
+		margining->lanes = lane;
+	}
+
+	return count;
 }
 
 static int margining_lanes_show(struct seq_file *s, void *not_used)
 {
 	struct tb_margining *margining = s->private;
-	struct tb *tb = margining->port->sw->tb;
-	enum usb4_margining_lane lanes;
-
-	if (mutex_lock_interruptible(&tb->lock))
-		return -ERESTARTSYS;
+	struct tb_port *port = margining->port;
+	struct tb *tb = port->sw->tb;
 
-	lanes = margining->lanes;
-	if (all_lanes(margining)) {
-		if (lanes == USB4_MARGINING_LANE_RX0)
-			seq_puts(s, "[0] 1 all\n");
-		else if (lanes == USB4_MARGINING_LANE_RX1)
-			seq_puts(s, "0 [1] all\n");
-		else
-			seq_puts(s, "0 1 [all]\n");
-	} else {
-		if (lanes == USB4_MARGINING_LANE_RX0)
-			seq_puts(s, "[0] 1\n");
-		else
-			seq_puts(s, "0 [1]\n");
+	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) {
+		for (int i = 0; i < ARRAY_SIZE(lane_names); i++) {
+			if (lane_names[i].lane == USB4_MARGINING_LANE_ALL &&
+			    !all_lanes(margining))
+				continue;
+			if (lane_names[i].lane == USB4_MARGINING_LANE_RX2 &&
+			    !margining->asym_rx)
+				continue;
+
+			if (i != 0)
+				seq_putc(s, ' ');
+
+			if (lane_names[i].lane == margining->lanes)
+				seq_printf(s, "[%s]", lane_names[i].name);
+			else
+				seq_printf(s, "%s", lane_names[i].name);
+		}
+		seq_puts(s, "\n");
 	}
 
-	mutex_unlock(&tb->lock);
 	return 0;
 }
 DEBUGFS_ATTR_RW(margining_lanes);
@@ -1095,6 +1123,9 @@ static int margining_run_sw(struct tb_margining *margining,
 		else if (margining->lanes == USB4_MARGINING_LANE_RX1)
 			errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK,
 					   margining->results[1]);
+		else if (margining->lanes == USB4_MARGINING_LANE_RX2)
+			errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK,
+					   margining->results[1]);
 		else if (margining->lanes == USB4_MARGINING_LANE_ALL)
 			errors = margining->results[1];
 
@@ -1115,6 +1146,31 @@ static int margining_run_sw(struct tb_margining *margining,
 	return ret;
 }
 
+static int validate_margining(struct tb_margining *margining)
+{
+	/*
+	 * For running on RX2 the link must be asymmetric with 3
+	 * receivers. Because this is can change dynamically, check it
+	 * here before we start the margining and report back error if
+	 * expectations are not met.
+	 */
+	if (margining->lanes == USB4_MARGINING_LANE_RX2) {
+		int ret;
+
+		ret = tb_port_get_link_width(margining->port);
+		if (ret < 0)
+			return ret;
+		if (ret != TB_LINK_WIDTH_ASYM_RX) {
+			tb_port_warn(margining->port, "link is %s expected %s",
+				     tb_width_name(ret),
+				     tb_width_name(TB_LINK_WIDTH_ASYM_RX));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
 static int margining_run_write(void *data, u64 val)
 {
 	struct tb_margining *margining = data;
@@ -1135,6 +1191,10 @@ static int margining_run_write(void *data, u64 val)
 		goto out_rpm_put;
 	}
 
+	ret = validate_margining(margining);
+	if (ret)
+		goto out_unlock;
+
 	if (tb_is_upstream_port(port))
 		down_sw = sw;
 	else if (port->remote)
@@ -1270,6 +1330,8 @@ static u8 margining_hw_result_val(const u32 *results,
 		val = results[1];
 	else if (lane == USB4_MARGINING_LANE_RX1)
 		val = results[1] >> USB4_MARGIN_HW_RES_LANE_SHIFT;
+	else if (lane == USB4_MARGINING_LANE_RX2)
+		val = results[2];
 	else
 		val = 0;
 
@@ -1319,6 +1381,9 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 						   USB4_MARGINING_LANE_RX0);
 			margining_hw_result_format(s, margining,
 						   USB4_MARGINING_LANE_RX1);
+			if (margining->asym_rx)
+				margining_hw_result_format(s, margining,
+						USB4_MARGINING_LANE_RX2);
 		} else {
 			margining_hw_result_format(s, margining,
 						   margining->lanes);
@@ -1341,6 +1406,13 @@ static int margining_results_show(struct seq_file *s, void *not_used)
 						margining->results[1]);
 			seq_printf(s, "# lane 1 errors: %u\n", lane_errors);
 		}
+		if (margining->asym_rx &&
+		    (result == USB4_MARGINING_LANE_RX2 ||
+		     result == USB4_MARGINING_LANE_ALL)) {
+			lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK,
+						margining->results[1]);
+			seq_printf(s, "# lane 2 errors: %u\n", lane_errors);
+		}
 	}
 
 	mutex_unlock(&tb->lock);
@@ -1548,6 +1620,7 @@ static struct tb_margining *margining_alloc(struct tb_port *port,
 	margining->index = index;
 	margining->dev = dev;
 	margining->gen = ret;
+	margining->asym_rx = tb_port_width_supported(port, TB_LINK_WIDTH_ASYM_RX);
 
 	ret = usb4_port_margining_caps(port, target, index, margining->caps,
 				       ARRAY_SIZE(margining->caps));
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index 7b5521ea0f74..5391502a4b87 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -107,5 +107,6 @@ enum usb4_sb_opcode {
 
 #define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK	GENMASK(3, 0)
 #define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK	GENMASK(7, 4)
+#define USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK	GENMASK(11, 8)
 
 #endif
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 0954b8bafada..ddbf0cd78377 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1370,6 +1370,7 @@ enum usb4_margin_sw_error_counter {
 enum usb4_margining_lane {
 	USB4_MARGINING_LANE_RX0 = 0,
 	USB4_MARGINING_LANE_RX1 = 1,
+	USB4_MARGINING_LANE_RX2 = 2,
 	USB4_MARGINING_LANE_ALL = 7,
 };
 
-- 
2.45.2


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

* Re: [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining
  2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
                   ` (7 preceding siblings ...)
  2024-10-23 10:11 ` [PATCH 8/8] thunderbolt: debugfs: Implement asymmetric lane margining Mika Westerberg
@ 2024-11-01  6:00 ` Mika Westerberg
  8 siblings, 0 replies; 10+ messages in thread
From: Mika Westerberg @ 2024-11-01  6:00 UTC (permalink / raw)
  To: linux-usb
  Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
	Aapo Vienamo

On Wed, Oct 23, 2024 at 01:11:03PM +0300, Mika Westerberg wrote:
> Hi all,
> 
> This series from Aapo adds support for USB4 v2 Gen 4 link symmetric and
> asymmetric receiver lane margining. Useful for instance on manufacturing
> floor to check stability of the electrical signaling.
> 
> Aapo Vienamo (8):
>   thunderbolt: Don't hardcode margining capabilities size
>   thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities
>   thunderbolt: debugfs: Implement Gen 4 margining eye selection
>   thunderbolt: debugfs: Replace "both lanes" with "all lanes"
>   thunderbolt: debugfs: Replace margining lane numbers with an enum
>   thunderbolt: debugfs: Refactor hardware margining result parsing
>   thunderbolt: debugfs: Don't hardcode margining results size
>   thunderbolt: debugfs: Implement asymmetric lane margining

All applied to thunderbolt.git/next.

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

end of thread, other threads:[~2024-11-01  6:01 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-23 10:11 [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 lane margining Mika Westerberg
2024-10-23 10:11 ` [PATCH 1/8] thunderbolt: Don't hardcode margining capabilities size Mika Westerberg
2024-10-23 10:11 ` [PATCH 2/8] thunderbolt: debugfs: Add USB4 Gen 4 margining capabilities Mika Westerberg
2024-10-23 10:11 ` [PATCH 3/8] thunderbolt: debugfs: Implement Gen 4 margining eye selection Mika Westerberg
2024-10-23 10:11 ` [PATCH 4/8] thunderbolt: debugfs: Replace "both lanes" with "all lanes" Mika Westerberg
2024-10-23 10:11 ` [PATCH 5/8] thunderbolt: debugfs: Replace margining lane numbers with an enum Mika Westerberg
2024-10-23 10:11 ` [PATCH 6/8] thunderbolt: debugfs: Refactor hardware margining result parsing Mika Westerberg
2024-10-23 10:11 ` [PATCH 7/8] thunderbolt: debugfs: Don't hardcode margining results size Mika Westerberg
2024-10-23 10:11 ` [PATCH 8/8] thunderbolt: debugfs: Implement asymmetric lane margining Mika Westerberg
2024-11-01  6:00 ` [PATCH 0/8] thunderbolt: Add support for USB4 v2 Gen 4 " Mika Westerberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).