]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
thunderbolt: debugfs: Add write capability to path config space
authorGil Fine <gil.fine@linux.intel.com>
Mon, 11 Nov 2024 20:52:11 +0000 (22:52 +0200)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Fri, 3 Jan 2025 09:50:02 +0000 (11:50 +0200)
Currently debugfs interface allows writing of router, adapter and
counters config spaces but not for paths. However, it can be useful
during debugging to modify path config space so for this reason add this
support to the debugfs interface as well.

Signed-off-by: Gil Fine <gil.fine@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
drivers/thunderbolt/debugfs.c

index a1d0d8a33f208a23cabe54ec84d481e794e09bdd..fa61127b2c47cf9fe40d609c8adb5c9067fa0f2c 100644 (file)
@@ -168,6 +168,13 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len,
         * offset relative_offset cap_id vs_cap_id value\n
         * v[0]   v[1]            v[2]   v[3]      v[4]
         *
+        * For Path configuration space:
+        * Short format is: offset value\n
+        *                  v[0]   v[1]
+        * Long format as produced from the read side:
+        * offset relative_offset in_hop_id value\n
+        * v[0]   v[1]            v[2]      v[3]
+        *
         * For Counter configuration space:
         * Short format is: offset\n
         *                  v[0]
@@ -191,14 +198,33 @@ static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len,
 }
 
 #if IS_ENABLED(CONFIG_USB4_DEBUGFS_WRITE)
+/*
+ * Path registers need to be written in double word pairs and they both must be
+ * read before written. This writes one double word in patch config space
+ * following the spec flow.
+ */
+static int path_write_one(struct tb_port *port, u32 val, u32 offset)
+{
+       u32 index = offset % PATH_LEN;
+       u32 offs = offset - index;
+       u32 data[PATH_LEN];
+       int ret;
+
+       ret = tb_port_read(port, data, TB_CFG_HOPS, offs, PATH_LEN);
+       if (ret)
+               return ret;
+       data[index] = val;
+       return tb_port_write(port, data, TB_CFG_HOPS, offs, PATH_LEN);
+}
+
 static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port,
-                         const char __user *user_buf, size_t count,
-                         loff_t *ppos)
+                         enum tb_cfg_space space, const char __user *user_buf,
+                         size_t count, loff_t *ppos)
 {
+       int long_fmt_len, ret = 0;
        struct tb *tb = sw->tb;
        char *line, *buf;
        u32 val, offset;
-       int ret = 0;
 
        buf = validate_and_copy_from_user(user_buf, &count);
        if (IS_ERR(buf))
@@ -214,12 +240,21 @@ static ssize_t regs_write(struct tb_switch *sw, struct tb_port *port,
        /* User did hardware changes behind the driver's back */
        add_taint(TAINT_USER, LOCKDEP_STILL_OK);
 
+       if (space == TB_CFG_HOPS)
+               long_fmt_len = 4;
+       else
+               long_fmt_len = 5;
+
        line = buf;
-       while (parse_line(&line, &offset, &val, 2, 5)) {
-               if (port)
-                       ret = tb_port_write(port, &val, TB_CFG_PORT, offset, 1);
-               else
+       while (parse_line(&line, &offset, &val, 2, long_fmt_len)) {
+               if (port) {
+                       if (space == TB_CFG_HOPS)
+                               ret = path_write_one(port, val, offset);
+                       else
+                               ret = tb_port_write(port, &val, space, offset, 1);
+               } else {
                        ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1);
+               }
                if (ret)
                        break;
        }
@@ -240,7 +275,16 @@ static ssize_t port_regs_write(struct file *file, const char __user *user_buf,
        struct seq_file *s = file->private_data;
        struct tb_port *port = s->private;
 
-       return regs_write(port->sw, port, user_buf, count, ppos);
+       return regs_write(port->sw, port, TB_CFG_PORT, user_buf, count, ppos);
+}
+
+static ssize_t path_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;
+
+       return regs_write(port->sw, port, TB_CFG_HOPS, user_buf, count, ppos);
 }
 
 static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
@@ -249,7 +293,7 @@ static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
        struct seq_file *s = file->private_data;
        struct tb_switch *sw = s->private;
 
-       return regs_write(sw, NULL, user_buf, count, ppos);
+       return regs_write(sw, NULL, TB_CFG_SWITCH, user_buf, count, ppos);
 }
 
 static bool parse_sb_line(char **line, u8 *reg, u8 *data, size_t data_size,
@@ -401,6 +445,7 @@ out:
 #define DEBUGFS_MODE           0600
 #else
 #define port_regs_write                NULL
+#define path_write             NULL
 #define switch_regs_write      NULL
 #define port_sb_regs_write     NULL
 #define retimer_sb_regs_write  NULL
@@ -2243,7 +2288,7 @@ out_rpm_put:
 
        return ret;
 }
-DEBUGFS_ATTR_RO(path);
+DEBUGFS_ATTR_RW(path);
 
 static int counter_set_regs_show(struct tb_port *port, struct seq_file *s,
                                 int counter)