]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
thunderbolt: debugfs: Implement Gen 4 margining eye selection
authorAapo Vienamo <aapo.vienamo@iki.fi>
Thu, 15 Aug 2024 18:45:15 +0000 (21:45 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 1 Nov 2024 05:55:38 +0000 (07:55 +0200)
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
drivers/thunderbolt/sb_regs.h
drivers/thunderbolt/tb.h
drivers/thunderbolt/usb4.c

index 83721ca069a524582147d196c878c1b0a7363cdf..699e1632e3f58a899bdb6999280ce8af5177aba9 100644 (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;
 }
 
index f60b8d90a71a4ac0089a0316105cd4b2e859cdbc..b7e91b99fefe5fd6c4d8f77fc3b84f748edce2de 100644 (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)
index fa7fc9bba70f0998923289176ea30639fe157919..ced9be271620fa90350ca94897a5a9b561ed83d4 100644 (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;
 };
 
index 1fb72ff1268e3971c343f5b9c378ac35108f7eec..985f24b044b39487d4939dc6589ee9df85a81f62 100644 (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);