Commit c8c08fd9 authored by Aapo Vienamo's avatar Aapo Vienamo Committed by Mika Westerberg
Browse files

thunderbolt: debugfs: Implement Gen 4 margining eye selection



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: default avatarAapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent c9077d59
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
@@ -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;
}

+2 −1
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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;
};

+4 −2
Original line number Diff line number Diff line
@@ -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);