mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-22 23:13:01 +02:00
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:
parent
c9077d59ad
commit
c8c08fd9c2
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user