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: Aapo Vienamo <aapo.vienamo@iki.fi>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Aapo Vienamo 2024-08-15 21:45:15 +03:00 committed by Mika Westerberg
parent c9077d59ad
commit c8c08fd9c2
4 changed files with 65 additions and 3 deletions

View File

@ -436,6 +436,8 @@ out:
* @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;
}

View File

@ -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)

View File

@ -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;
};

View File

@ -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);