* [PATCH v2 1/6] thunderbolt: Move usb4_port_margining_caps() declaration into correct place
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 2/6] thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c Mika Westerberg
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
It is supposed to be close with the other lane margining functions so
move it there. No functional changes.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/tb.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 18aae4ccaed5..ac9368c7a513 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1327,12 +1327,12 @@ int usb4_port_router_offline(struct tb_port *port);
int usb4_port_router_online(struct tb_port *port);
int usb4_port_enumerate_retimers(struct tb_port *port);
bool usb4_port_clx_supported(struct tb_port *port);
-int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
bool usb4_port_asym_supported(struct tb_port *port);
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
int usb4_port_asym_start(struct tb_port *port);
+int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
unsigned int ber_level, bool timing, bool right_high,
u32 *results);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 2/6] thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 1/6] thunderbolt: Move usb4_port_margining_caps() declaration into correct place Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 3/6] thunderbolt: Add sideband register access to debugfs Mika Westerberg
` (5 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
We need to call these from other files too so make them available
outside of usb4.c.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/retimer.c | 8 +--
drivers/thunderbolt/tb.h | 21 +++++--
drivers/thunderbolt/usb4.c | 112 ++++++++++++++--------------------
3 files changed, 66 insertions(+), 75 deletions(-)
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index 6eaaa5074ce8..9b66bff98f7e 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -372,16 +372,16 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
u32 vendor, device;
int ret;
- ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor,
- sizeof(vendor));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_VENDOR_ID, &vendor, sizeof(vendor));
if (ret) {
if (ret != -ENODEV)
tb_port_warn(port, "failed read retimer VendorId: %d\n", ret);
return ret;
}
- ret = usb4_port_retimer_read(port, index, USB4_SB_PRODUCT_ID, &device,
- sizeof(device));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_PRODUCT_ID, &device, sizeof(device));
if (ret) {
if (ret != -ENODEV)
tb_port_warn(port, "failed read retimer ProductId: %d\n", ret);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index ac9368c7a513..ab3366fcb0a3 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1332,6 +1332,23 @@ bool usb4_port_asym_supported(struct tb_port *port);
int usb4_port_asym_set_link_width(struct tb_port *port, enum tb_link_width width);
int usb4_port_asym_start(struct tb_port *port);
+/**
+ * enum tb_sb_target - Sideband transaction target
+ * @USB4_SB_TARGET_ROUTER: Target is the router itself
+ * @USB4_SB_TARGET_PARTNER: Target is partner
+ * @USB4_SB_TARGET_RETIMER: Target is retimer
+ */
+enum usb4_sb_target {
+ USB4_SB_TARGET_ROUTER,
+ USB4_SB_TARGET_PARTNER,
+ USB4_SB_TARGET_RETIMER,
+};
+
+int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index,
+ u8 reg, void *buf, u8 size);
+int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u8 reg, const void *buf, u8 size);
+
int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
unsigned int ber_level, bool timing, bool right_high,
@@ -1342,10 +1359,6 @@ int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
-int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
- u8 size);
-int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
- const void *buf, u8 size);
int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index de480bf2a53d..140e0da3a8de 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -17,12 +17,6 @@
#define USB4_DATA_RETRIES 3
#define USB4_DATA_DWORDS 16
-enum usb4_sb_target {
- USB4_SB_TARGET_ROUTER,
- USB4_SB_TARGET_PARTNER,
- USB4_SB_TARGET_RETIMER,
-};
-
#define USB4_NVM_READ_OFFSET_MASK GENMASK(23, 2)
#define USB4_NVM_READ_OFFSET_SHIFT 2
#define USB4_NVM_READ_LENGTH_MASK GENMASK(27, 24)
@@ -1289,8 +1283,20 @@ static int usb4_port_write_data(struct tb_port *port, const void *data,
dwords);
}
-static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target,
- u8 index, u8 reg, void *buf, u8 size)
+/**
+ * usb4_port_sb_read() - Read from sideband register
+ * @port: USB4 port to read
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
+ * @reg: Sideband register index
+ * @buf: Buffer where the sideband data is copied
+ * @size: Size of @buf
+ *
+ * Reads data from sideband register @reg and copies it into @buf.
+ * Returns %0 in case of success and negative errno in case of failure.
+ */
+int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index,
+ u8 reg, void *buf, u8 size)
{
size_t dwords = DIV_ROUND_UP(size, 4);
int ret;
@@ -1329,8 +1335,20 @@ static int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target,
return buf ? usb4_port_read_data(port, buf, dwords) : 0;
}
-static int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
- u8 index, u8 reg, const void *buf, u8 size)
+/**
+ * usb4_port_sb_write() - Write to sideband register
+ * @port: USB4 port to write
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
+ * @reg: Sideband register index
+ * @buf: Data to write
+ * @size: Size of @buf
+ *
+ * Writes @buf to sideband register @reg. Returns %0 in case of success
+ * and negative errno in case of failure.
+ */
+int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u8 reg, const void *buf, u8 size)
{
size_t dwords = DIV_ROUND_UP(size, 4);
int ret;
@@ -1776,47 +1794,6 @@ int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index)
USB4_SB_OPCODE_UNSET_INBOUND_SBTX, 500);
}
-/**
- * usb4_port_retimer_read() - Read from retimer sideband registers
- * @port: USB4 port
- * @index: Retimer index
- * @reg: Sideband register to read
- * @buf: Data from @reg is stored here
- * @size: Number of bytes to read
- *
- * Function reads retimer sideband registers starting from @reg. The
- * retimer is connected to @port at @index. Returns %0 in case of
- * success, and read data is copied to @buf. If there is no retimer
- * present at given @index returns %-ENODEV. In any other failure
- * returns negative errno.
- */
-int usb4_port_retimer_read(struct tb_port *port, u8 index, u8 reg, void *buf,
- u8 size)
-{
- return usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index, reg, buf,
- size);
-}
-
-/**
- * usb4_port_retimer_write() - Write to retimer sideband registers
- * @port: USB4 port
- * @index: Retimer index
- * @reg: Sideband register to write
- * @buf: Data that is written starting from @reg
- * @size: Number of bytes to write
- *
- * Writes retimer sideband registers starting from @reg. The retimer is
- * connected to @port at @index. Returns %0 in case of success. If there
- * is no retimer present at given @index returns %-ENODEV. In any other
- * failure returns negative errno.
- */
-int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
- const void *buf, u8 size)
-{
- return usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index, reg, buf,
- size);
-}
-
/**
* usb4_port_retimer_is_last() - Is the retimer last on-board retimer
* @port: USB4 port
@@ -1837,8 +1814,8 @@ int usb4_port_retimer_is_last(struct tb_port *port, u8 index)
if (ret)
return ret;
- ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata,
- sizeof(metadata));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata, sizeof(metadata));
return ret ? ret : metadata & 1;
}
@@ -1863,8 +1840,8 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index)
if (ret)
return ret;
- ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA, &metadata,
- sizeof(metadata));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata, sizeof(metadata));
return ret ? ret : metadata & USB4_NVM_SECTOR_SIZE_MASK;
}
@@ -1889,8 +1866,8 @@ int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
metadata = (dwaddress << USB4_NVM_SET_OFFSET_SHIFT) &
USB4_NVM_SET_OFFSET_MASK;
- ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
- sizeof(metadata));
+ ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata, sizeof(metadata));
if (ret)
return ret;
@@ -1912,8 +1889,8 @@ static int usb4_port_retimer_nvm_write_next_block(void *data,
u8 index = info->index;
int ret;
- ret = usb4_port_retimer_write(port, index, USB4_SB_DATA,
- buf, dwords * 4);
+ ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_DATA, buf, dwords * 4);
if (ret)
return ret;
@@ -1992,8 +1969,8 @@ int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index,
u32 metadata, val;
int ret;
- ret = usb4_port_retimer_read(port, index, USB4_SB_OPCODE, &val,
- sizeof(val));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_OPCODE, &val, sizeof(val));
if (ret)
return ret;
@@ -2004,8 +1981,9 @@ int usb4_port_retimer_nvm_authenticate_status(struct tb_port *port, u8 index,
return 0;
case -EAGAIN:
- ret = usb4_port_retimer_read(port, index, USB4_SB_METADATA,
- &metadata, sizeof(metadata));
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata,
+ sizeof(metadata));
if (ret)
return ret;
@@ -2030,8 +2008,8 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
if (dwords < USB4_DATA_DWORDS)
metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT;
- ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
- sizeof(metadata));
+ ret = usb4_port_sb_write(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata, sizeof(metadata));
if (ret)
return ret;
@@ -2039,8 +2017,8 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
if (ret)
return ret;
- return usb4_port_retimer_read(port, index, USB4_SB_DATA, buf,
- dwords * 4);
+ return usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_DATA, buf, dwords * 4);
}
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 3/6] thunderbolt: Add sideband register access to debugfs
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 1/6] thunderbolt: Move usb4_port_margining_caps() declaration into correct place Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 2/6] thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 4/6] thunderbolt: Split out margining from USB4 port Mika Westerberg
` (4 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
This makes it possible to read and write USB4 port and retimer sideband
registers through debugfs which is useful for debugging and manufacturing
purposes. We add "sb_regs" debugfs attribute under each USB4 port and
retimer that is used to access the sideband.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/debugfs.c | 293 ++++++++++++++++++++++++++++++++++
drivers/thunderbolt/retimer.c | 2 +
drivers/thunderbolt/sb_regs.h | 8 +
drivers/thunderbolt/tb.h | 4 +
4 files changed, 307 insertions(+)
diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 70b52aac3d97..5196ece2ded7 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -33,6 +33,44 @@
#define COUNTER_SET_LEN 3
+/* Sideband registers and their sizes as defined in the USB4 spec */
+struct sb_reg {
+ unsigned int reg;
+ unsigned int size;
+};
+
+#define SB_MAX_SIZE 64
+
+/* Sideband registers for router */
+static const struct sb_reg port_sb_regs[] = {
+ { USB4_SB_VENDOR_ID, 4 },
+ { USB4_SB_PRODUCT_ID, 4 },
+ { USB4_SB_DEBUG_CONF, 4 },
+ { USB4_SB_DEBUG, 54 },
+ { USB4_SB_LRD_TUNING, 4 },
+ { USB4_SB_OPCODE, 4 },
+ { USB4_SB_METADATA, 4 },
+ { USB4_SB_LINK_CONF, 3 },
+ { USB4_SB_GEN23_TXFFE, 4 },
+ { USB4_SB_GEN4_TXFFE, 4 },
+ { USB4_SB_VERSION, 4 },
+ { USB4_SB_DATA, 64 },
+};
+
+/* Sideband registers for retimer */
+static const struct sb_reg retimer_sb_regs[] = {
+ { USB4_SB_VENDOR_ID, 4 },
+ { USB4_SB_PRODUCT_ID, 4 },
+ { USB4_SB_FW_VERSION, 4 },
+ { USB4_SB_LRD_TUNING, 4 },
+ { USB4_SB_OPCODE, 4 },
+ { USB4_SB_METADATA, 4 },
+ { USB4_SB_GEN23_TXFFE, 4 },
+ { USB4_SB_GEN4_TXFFE, 4 },
+ { USB4_SB_VERSION, 4 },
+ { USB4_SB_DATA, 64 },
+};
+
#define DEBUGFS_ATTR(__space, __write) \
static int __space ## _open(struct inode *inode, struct file *file) \
{ \
@@ -184,10 +222,157 @@ static ssize_t switch_regs_write(struct file *file, const char __user *user_buf,
return regs_write(sw, NULL, user_buf, count, ppos);
}
+
+static bool parse_sb_line(char **line, u8 *reg, u8 *data, size_t data_size,
+ size_t *bytes_read)
+{
+ char *field, *token;
+ int i;
+
+ token = strsep(line, "\n");
+ if (!token)
+ return false;
+
+ /* Parse the register first */
+ field = strsep(&token, " ");
+ if (!field)
+ return false;
+ if (kstrtou8(field, 0, reg))
+ return false;
+
+ /* Then the values for the register, up to data_size */
+ for (i = 0; i < data_size; i++) {
+ field = strsep(&token, " ");
+ if (!field)
+ break;
+ if (kstrtou8(field, 0, &data[i]))
+ return false;
+ }
+
+ *bytes_read = i;
+ return true;
+}
+
+static ssize_t sb_regs_write(struct tb_port *port, const struct sb_reg *sb_regs,
+ size_t size, enum usb4_sb_target target, u8 index,
+ char *buf, size_t count, loff_t *ppos)
+{
+ u8 reg, data[SB_MAX_SIZE];
+ size_t bytes_read;
+ char *line = buf;
+
+ /* User did hardware changes behind the driver's back */
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+
+ /*
+ * For sideband registers we accept:
+ * reg b0 b1 b2...\n
+ *
+ * Here "reg" is the byte offset of the sideband register and "b0"..
+ * are the byte values. There can be less byte values than the register
+ * size. The leftovers will not be overwritten.
+ */
+ while (parse_sb_line(&line, ®, data, ARRAY_SIZE(data), &bytes_read)) {
+ const struct sb_reg *sb_reg;
+ int ret;
+
+ /* At least one byte must be passed */
+ if (bytes_read < 1)
+ return -EINVAL;
+
+ /* Find the register */
+ sb_reg = NULL;
+ for (int i = 0; i < size; i++) {
+ if (sb_regs[i].reg == reg) {
+ sb_reg = &sb_regs[i];
+ break;
+ }
+ }
+
+ if (!sb_reg)
+ return -EINVAL;
+
+ if (bytes_read > sb_regs->size)
+ return -E2BIG;
+
+ ret = usb4_port_sb_write(port, target, index, sb_reg->reg, data,
+ bytes_read);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t port_sb_regs_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 tb_switch *sw = port->sw;
+ struct tb *tb = sw->tb;
+ char *buf;
+ int ret;
+
+ buf = validate_and_copy_from_user(user_buf, &count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ pm_runtime_get_sync(&sw->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm_put;
+ }
+
+ ret = sb_regs_write(port, port_sb_regs, ARRAY_SIZE(port_sb_regs),
+ USB4_SB_TARGET_ROUTER, 0, buf, count, ppos);
+
+ mutex_unlock(&tb->lock);
+out_rpm_put:
+ pm_runtime_mark_last_busy(&sw->dev);
+ pm_runtime_put_autosuspend(&sw->dev);
+
+ return ret < 0 ? ret : count;
+}
+
+static ssize_t retimer_sb_regs_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct tb_retimer *rt = s->private;
+ struct tb *tb = rt->tb;
+ char *buf;
+ int ret;
+
+ buf = validate_and_copy_from_user(user_buf, &count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ pm_runtime_get_sync(&rt->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm_put;
+ }
+
+ ret = sb_regs_write(rt->port, retimer_sb_regs, ARRAY_SIZE(retimer_sb_regs),
+ USB4_SB_TARGET_RETIMER, rt->index, buf, count, ppos);
+
+ mutex_unlock(&tb->lock);
+out_rpm_put:
+ pm_runtime_mark_last_busy(&rt->dev);
+ pm_runtime_put_autosuspend(&rt->dev);
+
+ return ret < 0 ? ret : count;
+}
#define DEBUGFS_MODE 0600
#else
#define port_regs_write NULL
#define switch_regs_write NULL
+#define port_sb_regs_write NULL
+#define retimer_sb_regs_write NULL
#define DEBUGFS_MODE 0400
#endif
@@ -1505,6 +1690,60 @@ static int counters_show(struct seq_file *s, void *not_used)
}
DEBUGFS_ATTR_RW(counters);
+static int sb_regs_show(struct tb_port *port, const struct sb_reg *sb_regs,
+ size_t size, enum usb4_sb_target target, u8 index,
+ struct seq_file *s)
+{
+ int ret, i;
+
+ seq_puts(s, "# register value\n");
+
+ for (i = 0; i < size; i++) {
+ const struct sb_reg *regs = &sb_regs[i];
+ u8 data[64];
+ int j;
+
+ memset(data, 0, sizeof(data));
+ ret = usb4_port_sb_read(port, target, index, regs->reg, data,
+ regs->size);
+ if (ret)
+ return ret;
+
+ seq_printf(s, "0x%02x", regs->reg);
+ for (j = 0; j < regs->size; j++)
+ seq_printf(s, " 0x%02x", data[j]);
+ seq_puts(s, "\n");
+ }
+
+ return 0;
+}
+
+static int port_sb_regs_show(struct seq_file *s, void *not_used)
+{
+ struct tb_port *port = s->private;
+ struct tb_switch *sw = port->sw;
+ struct tb *tb = sw->tb;
+ int ret;
+
+ pm_runtime_get_sync(&sw->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm_put;
+ }
+
+ ret = sb_regs_show(port, port_sb_regs, ARRAY_SIZE(port_sb_regs),
+ USB4_SB_TARGET_ROUTER, 0, s);
+
+ mutex_unlock(&tb->lock);
+out_rpm_put:
+ pm_runtime_mark_last_busy(&sw->dev);
+ pm_runtime_put_autosuspend(&sw->dev);
+
+ return ret;
+}
+DEBUGFS_ATTR_RW(port_sb_regs);
+
/**
* tb_switch_debugfs_init() - Add debugfs entries for router
* @sw: Pointer to the router
@@ -1539,6 +1778,9 @@ void tb_switch_debugfs_init(struct tb_switch *sw)
if (port->config.counters_support)
debugfs_create_file("counters", 0600, debugfs_dir, port,
&counters_fops);
+ if (port->usb4)
+ debugfs_create_file("sb_regs", DEBUGFS_MODE, debugfs_dir,
+ port, &port_sb_regs_fops);
}
margining_switch_init(sw);
@@ -1590,6 +1832,57 @@ void tb_service_debugfs_remove(struct tb_service *svc)
svc->debugfs_dir = NULL;
}
+static int retimer_sb_regs_show(struct seq_file *s, void *not_used)
+{
+ struct tb_retimer *rt = s->private;
+ struct tb *tb = rt->tb;
+ int ret;
+
+ pm_runtime_get_sync(&rt->dev);
+
+ if (mutex_lock_interruptible(&tb->lock)) {
+ ret = -ERESTARTSYS;
+ goto out_rpm_put;
+ }
+
+ ret = sb_regs_show(rt->port, retimer_sb_regs, ARRAY_SIZE(retimer_sb_regs),
+ USB4_SB_TARGET_RETIMER, rt->index, s);
+
+ mutex_unlock(&tb->lock);
+out_rpm_put:
+ pm_runtime_mark_last_busy(&rt->dev);
+ pm_runtime_put_autosuspend(&rt->dev);
+
+ return ret;
+}
+DEBUGFS_ATTR_RW(retimer_sb_regs);
+
+/**
+ * tb_retimer_debugfs_init() - Add debugfs directory for retimer
+ * @rt: Pointer to retimer structure
+ *
+ * Adds and populates retimer debugfs directory.
+ */
+void tb_retimer_debugfs_init(struct tb_retimer *rt)
+{
+ struct dentry *debugfs_dir;
+
+ debugfs_dir = debugfs_create_dir(dev_name(&rt->dev), tb_debugfs_root);
+ debugfs_create_file("sb_regs", DEBUGFS_MODE, debugfs_dir, rt,
+ &retimer_sb_regs_fops);
+}
+
+/**
+ * tb_retimer_debugfs_remove() - Remove retimer debugfs directory
+ * @rt: Pointer to retimer structure
+ *
+ * Removes the retimer debugfs directory along with its contents.
+ */
+void tb_retimer_debugfs_remove(struct tb_retimer *rt)
+{
+ debugfs_lookup_and_remove(dev_name(&rt->dev), tb_debugfs_root);
+}
+
void tb_debugfs_init(void)
{
tb_debugfs_root = debugfs_create_dir("thunderbolt", NULL);
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index 9b66bff98f7e..8ce1dc7bbfad 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -437,12 +437,14 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
pm_runtime_mark_last_busy(&rt->dev);
pm_runtime_use_autosuspend(&rt->dev);
+ tb_retimer_debugfs_init(rt);
return 0;
}
static void tb_retimer_remove(struct tb_retimer *rt)
{
dev_info(&rt->dev, "retimer disconnected\n");
+ tb_retimer_debugfs_remove(rt);
tb_nvm_free(rt->nvm);
device_unregister(&rt->dev);
}
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index f37a4320f10a..b2a6add82161 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -12,6 +12,10 @@
#define USB4_SB_VENDOR_ID 0x00
#define USB4_SB_PRODUCT_ID 0x01
+#define USB4_SB_FW_VERSION 0x02
+#define USB4_SB_DEBUG_CONF 0x05
+#define USB4_SB_DEBUG 0x06
+#define USB4_SB_LRD_TUNING 0x07
#define USB4_SB_OPCODE 0x08
enum usb4_sb_opcode {
@@ -35,6 +39,10 @@ enum usb4_sb_opcode {
#define USB4_SB_METADATA 0x09
#define USB4_SB_METADATA_NVM_AUTH_WRITE_MASK GENMASK(5, 0)
+#define USB4_SB_LINK_CONF 0x0c
+#define USB4_SB_GEN23_TXFFE 0x0d
+#define USB4_SB_GEN4_TXFFE 0x0e
+#define USB4_SB_VERSION 0x0f
#define USB4_SB_DATA 0x12
/* USB4_SB_OPCODE_READ_LANE_MARGINING_CAP */
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index ab3366fcb0a3..1a53d18223d9 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1458,6 +1458,8 @@ void tb_xdomain_debugfs_init(struct tb_xdomain *xd);
void tb_xdomain_debugfs_remove(struct tb_xdomain *xd);
void tb_service_debugfs_init(struct tb_service *svc);
void tb_service_debugfs_remove(struct tb_service *svc);
+void tb_retimer_debugfs_init(struct tb_retimer *rt);
+void tb_retimer_debugfs_remove(struct tb_retimer *rt);
#else
static inline void tb_debugfs_init(void) { }
static inline void tb_debugfs_exit(void) { }
@@ -1467,6 +1469,8 @@ static inline void tb_xdomain_debugfs_init(struct tb_xdomain *xd) { }
static inline void tb_xdomain_debugfs_remove(struct tb_xdomain *xd) { }
static inline void tb_service_debugfs_init(struct tb_service *svc) { }
static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
+static inline void tb_retimer_debugfs_init(struct tb_retimer *rt) { }
+static inline void tb_retimer_debugfs_remove(struct tb_retimer *rt) { }
#endif
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 4/6] thunderbolt: Split out margining from USB4 port
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
` (2 preceding siblings ...)
2024-06-14 12:15 ` [PATCH v2 3/6] thunderbolt: Add sideband register access to debugfs Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 5/6] thunderbolt: Make margining functions accept target and retimer index Mika Westerberg
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
We are going to expand lane margining support for retimers too so split
out the generic margining functionality out of being specific to USB4
ports.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/debugfs.c | 234 ++++++++++++++++------------------
1 file changed, 112 insertions(+), 122 deletions(-)
diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 5196ece2ded7..0e871c7ae9c0 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -379,6 +379,7 @@ static ssize_t retimer_sb_regs_write(struct file *file,
#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)
/**
* struct tb_margining - Lane margining support
+ * @port: USB4 port through which the margining operations are run
* @caps: Port lane margining capabilities
* @results: Last lane margining results
* @lanes: %0, %1 or %7 (all)
@@ -395,6 +396,7 @@ static ssize_t retimer_sb_regs_write(struct file *file,
* right/high
*/
struct tb_margining {
+ struct tb_port *port;
u32 caps[2];
u32 results[2];
unsigned int lanes;
@@ -410,36 +412,38 @@ struct tb_margining {
bool right_high;
};
-static bool supports_software(const struct usb4_port *usb4)
+static bool supports_software(const struct tb_margining *margining)
{
- return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
+ return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW;
}
-static bool supports_hardware(const struct usb4_port *usb4)
+static bool supports_hardware(const struct tb_margining *margining)
{
- return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW;
+ return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_HW;
}
-static bool both_lanes(const struct usb4_port *usb4)
+static bool both_lanes(const struct tb_margining *margining)
{
- return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES;
+ return margining->caps[0] & USB4_MARGIN_CAP_0_2_LANES;
}
-static unsigned int independent_voltage_margins(const struct usb4_port *usb4)
+static unsigned int
+independent_voltage_margins(const struct tb_margining *margining)
{
- return (usb4->margining->caps[0] & USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK) >>
+ return (margining->caps[0] & USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK) >>
USB4_MARGIN_CAP_0_VOLTAGE_INDP_SHIFT;
}
-static bool supports_time(const struct usb4_port *usb4)
+static bool supports_time(const struct tb_margining *margining)
{
- return usb4->margining->caps[0] & USB4_MARGIN_CAP_0_TIME;
+ return margining->caps[0] & USB4_MARGIN_CAP_0_TIME;
}
/* Only applicable if supports_time() returns true */
-static unsigned int independent_time_margins(const struct usb4_port *usb4)
+static unsigned int
+independent_time_margins(const struct tb_margining *margining)
{
- return (usb4->margining->caps[1] & USB4_MARGIN_CAP_1_TIME_INDP_MASK) >>
+ return (margining->caps[1] & USB4_MARGIN_CAP_1_TIME_INDP_MASK) >>
USB4_MARGIN_CAP_1_TIME_INDP_SHIFT;
}
@@ -448,9 +452,8 @@ margining_ber_level_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
unsigned int val;
int ret = 0;
char *buf;
@@ -458,7 +461,7 @@ margining_ber_level_write(struct file *file, const char __user *user_buf,
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- if (usb4->margining->software) {
+ if (margining->software) {
ret = -EINVAL;
goto out_unlock;
}
@@ -475,13 +478,13 @@ margining_ber_level_write(struct file *file, const char __user *user_buf,
if (ret)
goto out_free;
- if (val < usb4->margining->min_ber_level ||
- val > usb4->margining->max_ber_level) {
+ if (val < margining->min_ber_level ||
+ val > margining->max_ber_level) {
ret = -EINVAL;
goto out_free;
}
- usb4->margining->ber_level = val;
+ margining->ber_level = val;
out_free:
free_page((unsigned long)buf);
@@ -501,52 +504,50 @@ static void ber_level_show(struct seq_file *s, unsigned int val)
static int margining_ber_level_show(struct seq_file *s, void *not_used)
{
- struct tb_port *port = s->private;
- struct usb4_port *usb4 = port->usb4;
+ const struct tb_margining *margining = s->private;
- if (usb4->margining->software)
+ if (margining->software)
return -EINVAL;
- ber_level_show(s, usb4->margining->ber_level);
+ ber_level_show(s, margining->ber_level);
return 0;
}
DEBUGFS_ATTR_RW(margining_ber_level);
static int margining_caps_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
u32 cap0, cap1;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
/* Dump the raw caps first */
- cap0 = usb4->margining->caps[0];
+ cap0 = margining->caps[0];
seq_printf(s, "0x%08x\n", cap0);
- cap1 = usb4->margining->caps[1];
+ cap1 = margining->caps[1];
seq_printf(s, "0x%08x\n", cap1);
seq_printf(s, "# software margining: %s\n",
- supports_software(usb4) ? "yes" : "no");
- if (supports_hardware(usb4)) {
+ supports_software(margining) ? "yes" : "no");
+ if (supports_hardware(margining)) {
seq_puts(s, "# hardware margining: yes\n");
seq_puts(s, "# minimum BER level contour: ");
- ber_level_show(s, usb4->margining->min_ber_level);
+ ber_level_show(s, margining->min_ber_level);
seq_puts(s, "# maximum BER level contour: ");
- ber_level_show(s, usb4->margining->max_ber_level);
+ ber_level_show(s, margining->max_ber_level);
} else {
seq_puts(s, "# hardware margining: no\n");
}
seq_printf(s, "# both lanes simultaneously: %s\n",
- both_lanes(usb4) ? "yes" : "no");
+ both_lanes(margining) ? "yes" : "no");
seq_printf(s, "# voltage margin steps: %u\n",
- usb4->margining->voltage_steps);
+ margining->voltage_steps);
seq_printf(s, "# maximum voltage offset: %u mV\n",
- usb4->margining->max_voltage_offset);
+ margining->max_voltage_offset);
- switch (independent_voltage_margins(usb4)) {
+ switch (independent_voltage_margins(margining)) {
case USB4_MARGIN_CAP_0_VOLTAGE_MIN:
seq_puts(s, "# returns minimum between high and low voltage margins\n");
break;
@@ -558,12 +559,12 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
break;
}
- if (supports_time(usb4)) {
+ if (supports_time(margining)) {
seq_puts(s, "# time margining: yes\n");
seq_printf(s, "# time margining is destructive: %s\n",
cap1 & USB4_MARGIN_CAP_1_TIME_DESTR ? "yes" : "no");
- switch (independent_time_margins(usb4)) {
+ switch (independent_time_margins(margining)) {
case USB4_MARGIN_CAP_1_TIME_MIN:
seq_puts(s, "# returns minimum between left and right time margins\n");
break;
@@ -576,9 +577,9 @@ static int margining_caps_show(struct seq_file *s, void *not_used)
}
seq_printf(s, "# time margin steps: %u\n",
- usb4->margining->time_steps);
+ margining->time_steps);
seq_printf(s, "# maximum time offset: %u mUI\n",
- usb4->margining->max_time_offset);
+ margining->max_time_offset);
} else {
seq_puts(s, "# time margining: no\n");
}
@@ -593,9 +594,8 @@ margining_lanes_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
int ret = 0;
char *buf;
@@ -611,13 +611,13 @@ margining_lanes_write(struct file *file, const char __user *user_buf,
}
if (!strcmp(buf, "0")) {
- usb4->margining->lanes = 0;
+ margining->lanes = 0;
} else if (!strcmp(buf, "1")) {
- usb4->margining->lanes = 1;
+ margining->lanes = 1;
} else if (!strcmp(buf, "all")) {
/* Needs to be supported */
- if (both_lanes(usb4))
- usb4->margining->lanes = 7;
+ if (both_lanes(margining))
+ margining->lanes = 7;
else
ret = -EINVAL;
} else {
@@ -633,16 +633,15 @@ margining_lanes_write(struct file *file, const char __user *user_buf,
static int margining_lanes_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
unsigned int lanes;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- lanes = usb4->margining->lanes;
- if (both_lanes(usb4)) {
+ lanes = margining->lanes;
+ if (both_lanes(margining)) {
if (!lanes)
seq_puts(s, "[0] 1 all\n");
else if (lanes == 1)
@@ -666,9 +665,8 @@ static ssize_t margining_mode_write(struct file *file,
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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
int ret = 0;
char *buf;
@@ -684,13 +682,13 @@ static ssize_t margining_mode_write(struct file *file,
}
if (!strcmp(buf, "software")) {
- if (supports_software(usb4))
- usb4->margining->software = true;
+ if (supports_software(margining))
+ margining->software = true;
else
ret = -EINVAL;
} else if (!strcmp(buf, "hardware")) {
- if (supports_hardware(usb4))
- usb4->margining->software = false;
+ if (supports_hardware(margining))
+ margining->software = false;
else
ret = -EINVAL;
} else {
@@ -706,23 +704,22 @@ static ssize_t margining_mode_write(struct file *file,
static int margining_mode_show(struct seq_file *s, void *not_used)
{
- const struct tb_port *port = s->private;
- const struct usb4_port *usb4 = port->usb4;
- struct tb *tb = port->sw->tb;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
const char *space = "";
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- if (supports_software(usb4)) {
- if (usb4->margining->software)
+ if (supports_software(margining)) {
+ if (margining->software)
seq_puts(s, "[software]");
else
seq_puts(s, "software");
space = " ";
}
- if (supports_hardware(usb4)) {
- if (usb4->margining->software)
+ if (supports_hardware(margining)) {
+ if (margining->software)
seq_printf(s, "%shardware", space);
else
seq_printf(s, "%s[hardware]", space);
@@ -737,10 +734,9 @@ DEBUGFS_ATTR_RW(margining_mode);
static int margining_run_write(void *data, u64 val)
{
- struct tb_port *port = data;
- struct usb4_port *usb4 = port->usb4;
+ struct tb_margining *margining = data;
+ struct tb_port *port = margining->port;
struct tb_switch *sw = port->sw;
- struct tb_margining *margining;
struct tb_switch *down_sw;
struct tb *tb = sw->tb;
int ret, clx;
@@ -775,8 +771,6 @@ static int margining_run_write(void *data, u64 val)
clx = ret;
}
- margining = usb4->margining;
-
if (margining->software) {
tb_port_dbg(port, "running software %s lane margining for lanes %u\n",
margining->time ? "time" : "voltage", margining->lanes);
@@ -817,16 +811,15 @@ static ssize_t margining_results_write(struct file *file,
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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
/* Just clear the results */
- usb4->margining->results[0] = 0;
- usb4->margining->results[1] = 0;
+ margining->results[0] = 0;
+ margining->results[1] = 0;
mutex_unlock(&tb->lock);
return count;
@@ -860,15 +853,12 @@ static void time_margin_show(struct seq_file *s,
static int margining_results_show(struct seq_file *s, void *not_used)
{
- struct tb_port *port = s->private;
- struct usb4_port *usb4 = port->usb4;
- struct tb_margining *margining;
- struct tb *tb = port->sw->tb;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- margining = usb4->margining;
/* Dump the raw results first */
seq_printf(s, "0x%08x\n", margining->results[0]);
/* Only the hardware margining has two result dwords */
@@ -930,9 +920,8 @@ static ssize_t margining_test_write(struct file *file,
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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
int ret = 0;
char *buf;
@@ -947,10 +936,10 @@ static ssize_t margining_test_write(struct file *file,
goto out_free;
}
- if (!strcmp(buf, "time") && supports_time(usb4))
- usb4->margining->time = true;
+ if (!strcmp(buf, "time") && supports_time(margining))
+ margining->time = true;
else if (!strcmp(buf, "voltage"))
- usb4->margining->time = false;
+ margining->time = false;
else
ret = -EINVAL;
@@ -963,15 +952,14 @@ static ssize_t margining_test_write(struct file *file,
static int margining_test_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- if (supports_time(usb4)) {
- if (usb4->margining->time)
+ if (supports_time(margining)) {
+ if (margining->time)
seq_puts(s, "voltage [time]\n");
else
seq_puts(s, "[voltage] time\n");
@@ -989,9 +977,8 @@ static ssize_t margining_margin_write(struct file *file,
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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
int ret = 0;
char *buf;
@@ -1006,18 +993,18 @@ static ssize_t margining_margin_write(struct file *file,
goto out_free;
}
- if (usb4->margining->time) {
+ if (margining->time) {
if (!strcmp(buf, "left"))
- usb4->margining->right_high = false;
+ margining->right_high = false;
else if (!strcmp(buf, "right"))
- usb4->margining->right_high = true;
+ margining->right_high = true;
else
ret = -EINVAL;
} else {
if (!strcmp(buf, "low"))
- usb4->margining->right_high = false;
+ margining->right_high = false;
else if (!strcmp(buf, "high"))
- usb4->margining->right_high = true;
+ margining->right_high = true;
else
ret = -EINVAL;
}
@@ -1031,20 +1018,19 @@ static ssize_t margining_margin_write(struct file *file,
static int margining_margin_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;
+ struct tb_margining *margining = s->private;
+ struct tb *tb = margining->port->sw->tb;
if (mutex_lock_interruptible(&tb->lock))
return -ERESTARTSYS;
- if (usb4->margining->time) {
- if (usb4->margining->right_high)
+ if (margining->time) {
+ if (margining->right_high)
seq_puts(s, "left [right]\n");
else
seq_puts(s, "[left] right\n");
} else {
- if (usb4->margining->right_high)
+ if (margining->right_high)
seq_puts(s, "low [high]\n");
else
seq_puts(s, "[low] high\n");
@@ -1075,16 +1061,16 @@ static void margining_port_init(struct tb_port *port)
if (!margining)
return;
+ margining->port = port;
+
ret = usb4_port_margining_caps(port, margining->caps);
if (ret) {
kfree(margining);
return;
}
- usb4->margining = margining;
-
/* Set the initial mode */
- if (supports_software(usb4))
+ if (supports_software(margining))
margining->software = true;
val = (margining->caps[0] & USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK) >>
@@ -1094,7 +1080,7 @@ static void margining_port_init(struct tb_port *port)
USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_SHIFT;
margining->max_voltage_offset = 74 + val * 2;
- if (supports_time(usb4)) {
+ if (supports_time(margining)) {
val = (margining->caps[1] & USB4_MARGIN_CAP_1_TIME_STEPS_MASK) >>
USB4_MARGIN_CAP_1_TIME_STEPS_SHIFT;
margining->time_steps = val;
@@ -1108,7 +1094,7 @@ static void margining_port_init(struct tb_port *port)
}
dir = debugfs_create_dir("margining", parent);
- if (supports_hardware(usb4)) {
+ if (supports_hardware(margining)) {
val = (margining->caps[1] & USB4_MARGIN_CAP_1_MIN_BER_MASK) >>
USB4_MARGIN_CAP_1_MIN_BER_SHIFT;
margining->min_ber_level = val;
@@ -1119,19 +1105,23 @@ static void margining_port_init(struct tb_port *port)
/* Set the default to minimum */
margining->ber_level = margining->min_ber_level;
- debugfs_create_file("ber_level_contour", 0400, dir, port,
+ debugfs_create_file("ber_level_contour", 0400, dir, margining,
&margining_ber_level_fops);
}
- debugfs_create_file("caps", 0400, dir, port, &margining_caps_fops);
- debugfs_create_file("lanes", 0600, dir, port, &margining_lanes_fops);
- debugfs_create_file("mode", 0600, dir, port, &margining_mode_fops);
- debugfs_create_file("run", 0600, dir, port, &margining_run_fops);
- debugfs_create_file("results", 0600, dir, port, &margining_results_fops);
- debugfs_create_file("test", 0600, dir, port, &margining_test_fops);
- if (independent_voltage_margins(usb4) == USB4_MARGIN_CAP_0_VOLTAGE_HL ||
- (supports_time(usb4) &&
- independent_time_margins(usb4) == USB4_MARGIN_CAP_1_TIME_LR))
- debugfs_create_file("margin", 0600, dir, port, &margining_margin_fops);
+ debugfs_create_file("caps", 0400, dir, margining, &margining_caps_fops);
+ debugfs_create_file("lanes", 0600, dir, margining, &margining_lanes_fops);
+ debugfs_create_file("mode", 0600, dir, margining, &margining_mode_fops);
+ debugfs_create_file("run", 0600, dir, margining, &margining_run_fops);
+ debugfs_create_file("results", 0600, dir, margining,
+ &margining_results_fops);
+ debugfs_create_file("test", 0600, dir, margining, &margining_test_fops);
+ if (independent_voltage_margins(margining) == USB4_MARGIN_CAP_0_VOLTAGE_HL ||
+ (supports_time(margining) &&
+ independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
+ debugfs_create_file("margin", 0600, dir, margining,
+ &margining_margin_fops);
+
+ usb4->margining = margining;
}
static void margining_port_remove(struct tb_port *port)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 5/6] thunderbolt: Make margining functions accept target and retimer index
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
` (3 preceding siblings ...)
2024-06-14 12:15 ` [PATCH v2 4/6] thunderbolt: Split out margining from USB4 port Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:15 ` [PATCH v2 6/6] thunderbolt: Add receiver lane margining support for retimers Mika Westerberg
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
In order to add lane margining support for retimers make the margining
functions take sideband target and retimer index as parameters. This
makes it possible to access both router and retimer sideband using the
same functions.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/debugfs.c | 16 ++++++-----
drivers/thunderbolt/tb.h | 15 ++++++-----
drivers/thunderbolt/usb4.c | 51 +++++++++++++++++++++--------------
3 files changed, 50 insertions(+), 32 deletions(-)
diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 0e871c7ae9c0..87b2c91a4656 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -774,22 +774,25 @@ static int margining_run_write(void *data, u64 val)
if (margining->software) {
tb_port_dbg(port, "running software %s lane margining for lanes %u\n",
margining->time ? "time" : "voltage", margining->lanes);
- ret = usb4_port_sw_margin(port, margining->lanes, margining->time,
+ ret = usb4_port_sw_margin(port, USB4_SB_TARGET_ROUTER, 0,
+ margining->lanes, margining->time,
margining->right_high,
USB4_MARGIN_SW_COUNTER_CLEAR);
if (ret)
goto out_clx;
- ret = usb4_port_sw_margin_errors(port, &margining->results[0]);
+ ret = usb4_port_sw_margin_errors(port, USB4_SB_TARGET_ROUTER, 0,
+ &margining->results[0]);
} else {
tb_port_dbg(port, "running hardware %s lane margining for lanes %u\n",
margining->time ? "time" : "voltage", margining->lanes);
/* Clear the results */
margining->results[0] = 0;
margining->results[1] = 0;
- ret = usb4_port_hw_margin(port, margining->lanes,
- margining->ber_level, margining->time,
- margining->right_high, margining->results);
+ ret = usb4_port_hw_margin(port, USB4_SB_TARGET_ROUTER, 0,
+ margining->lanes, margining->ber_level,
+ margining->time, margining->right_high,
+ margining->results);
}
out_clx:
@@ -1063,7 +1066,8 @@ static void margining_port_init(struct tb_port *port)
margining->port = port;
- ret = usb4_port_margining_caps(port, margining->caps);
+ ret = usb4_port_margining_caps(port, USB4_SB_TARGET_ROUTER, 0,
+ margining->caps);
if (ret) {
kfree(margining);
return;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 1a53d18223d9..2f3de3d3c2e0 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -1349,13 +1349,16 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index
int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target,
u8 index, u8 reg, const void *buf, u8 size);
-int usb4_port_margining_caps(struct tb_port *port, u32 *caps);
-int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
- unsigned int ber_level, bool timing, bool right_high,
- u32 *results);
-int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
+int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u32 *caps);
+int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, unsigned int lanes, unsigned int ber_level,
+ bool timing, bool right_high, u32 *results);
+int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter);
-int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors);
+int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u32 *errors);
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index 140e0da3a8de..d952de07493f 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1628,26 +1628,31 @@ int usb4_port_asym_start(struct tb_port *port)
/**
* usb4_port_margining_caps() - Read USB4 port marginig capabilities
* @port: USB4 port
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @caps: Array with at least two elements to hold the results
*
* Reads the USB4 port lane margining capabilities into @caps.
*/
-int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
+int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u32 *caps)
{
int ret;
- ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+ ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_READ_LANE_MARGINING_CAP, 500);
if (ret)
return ret;
- return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
- USB4_SB_DATA, caps, sizeof(*caps) * 2);
+ return usb4_port_sb_read(port, target, index, USB4_SB_DATA, caps,
+ sizeof(*caps) * 2);
}
/**
* usb4_port_hw_margin() - Run hardware lane margining on port
* @port: USB4 port
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @ber_level: BER level contour value
@@ -1658,9 +1663,9 @@ int usb4_port_margining_caps(struct tb_port *port, u32 *caps)
* Runs hardware lane margining on USB4 port and returns the result in
* @results.
*/
-int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
- unsigned int ber_level, bool timing, bool right_high,
- u32 *results)
+int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, unsigned int lanes, unsigned int ber_level,
+ bool timing, bool right_high, u32 *results)
{
u32 val;
int ret;
@@ -1674,23 +1679,25 @@ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) &
USB4_MARGIN_HW_BER_MASK;
- ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
- USB4_SB_METADATA, &val, sizeof(val));
+ ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
+ sizeof(val));
if (ret)
return ret;
- ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+ ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_HW_LANE_MARGINING, 2500);
if (ret)
return ret;
- return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
- USB4_SB_DATA, results, sizeof(*results) * 2);
+ return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results,
+ sizeof(*results) * 2);
}
/**
* usb4_port_sw_margin() - Run software lane margining on port
* @port: USB4 port
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @lanes: Which lanes to run (must match the port capabilities). Can be
* %0, %1 or %7.
* @timing: Perform timing margining instead of voltage
@@ -1701,7 +1708,8 @@ int usb4_port_hw_margin(struct tb_port *port, unsigned int lanes,
* counters by calling usb4_port_sw_margin_errors(). Returns %0 in
* success and negative errno otherwise.
*/
-int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
+int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, unsigned int lanes, bool timing,
bool right_high, u32 counter)
{
u32 val;
@@ -1715,34 +1723,37 @@ int usb4_port_sw_margin(struct tb_port *port, unsigned int lanes, bool timing,
val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) &
USB4_MARGIN_SW_COUNTER_MASK;
- ret = usb4_port_sb_write(port, USB4_SB_TARGET_ROUTER, 0,
- USB4_SB_METADATA, &val, sizeof(val));
+ ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val,
+ sizeof(val));
if (ret)
return ret;
- return usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+ return usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500);
}
/**
* usb4_port_sw_margin_errors() - Read the software margining error counters
* @port: USB4 port
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
* @errors: Error metadata is copied here.
*
* This reads back the software margining error counters from the port.
* Returns %0 in success and negative errno otherwise.
*/
-int usb4_port_sw_margin_errors(struct tb_port *port, u32 *errors)
+int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
+ u8 index, u32 *errors)
{
int ret;
- ret = usb4_port_sb_op(port, USB4_SB_TARGET_ROUTER, 0,
+ ret = usb4_port_sb_op(port, target, index,
USB4_SB_OPCODE_READ_SW_MARGIN_ERR, 150);
if (ret)
return ret;
- return usb4_port_sb_read(port, USB4_SB_TARGET_ROUTER, 0,
- USB4_SB_METADATA, errors, sizeof(*errors));
+ return usb4_port_sb_read(port, target, index, USB4_SB_METADATA, errors,
+ sizeof(*errors));
}
static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 6/6] thunderbolt: Add receiver lane margining support for retimers
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
` (4 preceding siblings ...)
2024-06-14 12:15 ` [PATCH v2 5/6] thunderbolt: Make margining functions accept target and retimer index Mika Westerberg
@ 2024-06-14 12:15 ` Mika Westerberg
2024-06-14 12:19 ` [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Greg KH
2024-06-17 9:58 ` Mika Westerberg
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:15 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH, Mika Westerberg
Retimers support lane margining as well so make this available through
debugfs in the same way as we do for the USB4 ports. When this is
enabled we also expose retimers on the other side of the cable because
typically margining is implemented only on direction towards the cable.
However, for the retimers on the other side of the cable we do not allow
NVM upgrade to avoid confusing the existing userspace (the same retimer
may now appear twice with different name) and is probably not a good
idea anyway.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
drivers/thunderbolt/Kconfig | 11 +++--
drivers/thunderbolt/debugfs.c | 92 +++++++++++++++++++++++++----------
drivers/thunderbolt/retimer.c | 43 +++++++++-------
drivers/thunderbolt/sb_regs.h | 1 +
drivers/thunderbolt/tb.h | 5 ++
drivers/thunderbolt/usb4.c | 24 +++++++++
6 files changed, 128 insertions(+), 48 deletions(-)
diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig
index 3e01f41e9d66..0abdb69ee9f4 100644
--- a/drivers/thunderbolt/Kconfig
+++ b/drivers/thunderbolt/Kconfig
@@ -32,14 +32,15 @@ config USB4_DEBUGFS_WRITE
this for production systems or distro kernels.
config USB4_DEBUGFS_MARGINING
- bool "Expose receiver lane margining operations under USB4 ports (DANGEROUS)"
+ bool "Expose receiver lane margining operations under USB4 ports and retimers (DANGEROUS)"
depends on DEBUG_FS
depends on USB4_DEBUGFS_WRITE
help
- Enables hardware and software based receiver lane margining support
- under each USB4 port. Used for electrical quality and robustness
- validation during manufacturing. Should not be enabled by distro
- kernels.
+ Enables hardware and software based receiver lane margining
+ support under each USB4 port and retimer, including retimers
+ on the other side of the cable. Used for electrical quality
+ and robustness validation during manufacturing. Should not be
+ enabled by distro kernels.
config USB4_KUNIT_TEST
bool "KUnit tests" if !KUNIT_ALL_TESTS
diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c
index 87b2c91a4656..c2a735421609 100644
--- a/drivers/thunderbolt/debugfs.c
+++ b/drivers/thunderbolt/debugfs.c
@@ -380,6 +380,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
/**
* struct tb_margining - Lane margining support
* @port: USB4 port through which the margining operations are run
+ * @target: Sideband target
+ * @index: Retimer index if taget is %USB4_SB_TARGET_RETIMER
+ * @dev: Pointer to the device that is the target (USB4 port or retimer)
* @caps: Port lane margining capabilities
* @results: Last lane margining results
* @lanes: %0, %1 or %7 (all)
@@ -397,6 +400,9 @@ static ssize_t retimer_sb_regs_write(struct file *file,
*/
struct tb_margining {
struct tb_port *port;
+ enum usb4_sb_target target;
+ u8 index;
+ struct device *dev;
u32 caps[2];
u32 results[2];
unsigned int lanes;
@@ -736,6 +742,7 @@ static int margining_run_write(void *data, u64 val)
{
struct tb_margining *margining = data;
struct tb_port *port = margining->port;
+ struct device *dev = margining->dev;
struct tb_switch *sw = port->sw;
struct tb_switch *down_sw;
struct tb *tb = sw->tb;
@@ -744,7 +751,7 @@ static int margining_run_write(void *data, u64 val)
if (val != 1)
return -EINVAL;
- pm_runtime_get_sync(&sw->dev);
+ pm_runtime_get_sync(dev);
if (mutex_lock_interruptible(&tb->lock)) {
ret = -ERESTARTSYS;
@@ -772,24 +779,29 @@ static int margining_run_write(void *data, u64 val)
}
if (margining->software) {
- tb_port_dbg(port, "running software %s lane margining for lanes %u\n",
- margining->time ? "time" : "voltage", margining->lanes);
- ret = usb4_port_sw_margin(port, USB4_SB_TARGET_ROUTER, 0,
+ tb_port_dbg(port,
+ "running software %s lane margining for %s lanes %u\n",
+ margining->time ? "time" : "voltage", dev_name(dev),
+ margining->lanes);
+ ret = usb4_port_sw_margin(port, margining->target, margining->index,
margining->lanes, margining->time,
margining->right_high,
USB4_MARGIN_SW_COUNTER_CLEAR);
if (ret)
goto out_clx;
- ret = usb4_port_sw_margin_errors(port, USB4_SB_TARGET_ROUTER, 0,
+ ret = usb4_port_sw_margin_errors(port, margining->target,
+ margining->index,
&margining->results[0]);
} else {
- tb_port_dbg(port, "running hardware %s lane margining for lanes %u\n",
- margining->time ? "time" : "voltage", margining->lanes);
+ tb_port_dbg(port,
+ "running hardware %s lane margining for %s lanes %u\n",
+ margining->time ? "time" : "voltage", dev_name(dev),
+ margining->lanes);
/* Clear the results */
margining->results[0] = 0;
margining->results[1] = 0;
- ret = usb4_port_hw_margin(port, USB4_SB_TARGET_ROUTER, 0,
+ ret = usb4_port_hw_margin(port, margining->target, margining->index,
margining->lanes, margining->ber_level,
margining->time, margining->right_high,
margining->results);
@@ -801,8 +813,8 @@ static int margining_run_write(void *data, u64 val)
out_unlock:
mutex_unlock(&tb->lock);
out_rpm_put:
- pm_runtime_mark_last_busy(&sw->dev);
- pm_runtime_put_autosuspend(&sw->dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
@@ -1044,33 +1056,29 @@ static int margining_margin_show(struct seq_file *s, void *not_used)
}
DEBUGFS_ATTR_RW(margining_margin);
-static void margining_port_init(struct tb_port *port)
+static struct tb_margining *margining_alloc(struct tb_port *port,
+ struct device *dev,
+ enum usb4_sb_target target,
+ u8 index, struct dentry *parent)
{
struct tb_margining *margining;
- struct dentry *dir, *parent;
- struct usb4_port *usb4;
- char dir_name[10];
+ struct dentry *dir;
unsigned int val;
int ret;
- usb4 = port->usb4;
- if (!usb4)
- return;
-
- snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
- parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
-
margining = kzalloc(sizeof(*margining), GFP_KERNEL);
if (!margining)
- return;
+ return NULL;
margining->port = port;
+ margining->target = target;
+ margining->index = index;
+ margining->dev = dev;
- ret = usb4_port_margining_caps(port, USB4_SB_TARGET_ROUTER, 0,
- margining->caps);
+ ret = usb4_port_margining_caps(port, target, index, margining->caps);
if (ret) {
kfree(margining);
- return;
+ return NULL;
}
/* Set the initial mode */
@@ -1124,8 +1132,22 @@ static void margining_port_init(struct tb_port *port)
independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR))
debugfs_create_file("margin", 0600, dir, margining,
&margining_margin_fops);
+ return margining;
+}
- usb4->margining = margining;
+static void margining_port_init(struct tb_port *port)
+{
+ struct dentry *parent;
+ char dir_name[10];
+
+ if (!port->usb4)
+ return;
+
+ snprintf(dir_name, sizeof(dir_name), "port%d", port->port);
+ parent = debugfs_lookup(dir_name, port->sw->debugfs_dir);
+ port->usb4->margining = margining_alloc(port, &port->usb4->dev,
+ USB4_SB_TARGET_ROUTER, 0,
+ parent);
}
static void margining_port_remove(struct tb_port *port)
@@ -1199,11 +1221,27 @@ static void margining_xdomain_remove(struct tb_xdomain *xd)
downstream = tb_port_at(xd->route, parent_sw);
margining_port_remove(downstream);
}
+
+static void margining_retimer_init(struct tb_retimer *rt, struct dentry *debugfs_dir)
+{
+ rt->margining = margining_alloc(rt->port, &rt->dev,
+ USB4_SB_TARGET_RETIMER, rt->index,
+ debugfs_dir);
+}
+
+static void margining_retimer_remove(struct tb_retimer *rt)
+{
+ kfree(rt->margining);
+ rt->margining = NULL;
+}
#else
static inline void margining_switch_init(struct tb_switch *sw) { }
static inline void margining_switch_remove(struct tb_switch *sw) { }
static inline void margining_xdomain_init(struct tb_xdomain *xd) { }
static inline void margining_xdomain_remove(struct tb_xdomain *xd) { }
+static inline void margining_retimer_init(struct tb_retimer *rt,
+ struct dentry *debugfs_dir) { }
+static inline void margining_retimer_remove(struct tb_retimer *rt) { }
#endif
static int port_clear_all_counters(struct tb_port *port)
@@ -1864,6 +1902,7 @@ void tb_retimer_debugfs_init(struct tb_retimer *rt)
debugfs_dir = debugfs_create_dir(dev_name(&rt->dev), tb_debugfs_root);
debugfs_create_file("sb_regs", DEBUGFS_MODE, debugfs_dir, rt,
&retimer_sb_regs_fops);
+ margining_retimer_init(rt, debugfs_dir);
}
/**
@@ -1875,6 +1914,7 @@ void tb_retimer_debugfs_init(struct tb_retimer *rt)
void tb_retimer_debugfs_remove(struct tb_retimer *rt)
{
debugfs_lookup_and_remove(dev_name(&rt->dev), tb_debugfs_root);
+ margining_retimer_remove(rt);
}
void tb_debugfs_init(void)
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index 8ce1dc7bbfad..721319329afa 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -14,7 +14,11 @@
#include "sb_regs.h"
#include "tb.h"
+#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)
#define TB_MAX_RETIMER_INDEX 6
+#else
+#define TB_MAX_RETIMER_INDEX 2
+#endif
/**
* tb_retimer_nvm_read() - Read contents of retimer NVM
@@ -319,6 +323,8 @@ static ssize_t nvm_version_show(struct device *dev,
if (!rt->nvm)
ret = -EAGAIN;
+ else if (rt->no_nvm_upgrade)
+ ret = -EOPNOTSUPP;
else
ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
@@ -366,7 +372,8 @@ const struct device_type tb_retimer_type = {
.release = tb_retimer_release,
};
-static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
+static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status,
+ bool on_board)
{
struct tb_retimer *rt;
u32 vendor, device;
@@ -388,13 +395,6 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
return ret;
}
- /*
- * Check that it supports NVM operations. If not then don't add
- * the device at all.
- */
- ret = usb4_port_retimer_nvm_sector_size(port, index);
- if (ret < 0)
- return ret;
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
if (!rt)
@@ -407,6 +407,13 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status)
rt->port = port;
rt->tb = port->sw->tb;
+ /*
+ * Only support NVM upgrade for on-board retimers. The retimers
+ * on the other side of the connection.
+ */
+ if (!on_board || usb4_port_retimer_nvm_sector_size(port, index) <= 0)
+ rt->no_nvm_upgrade = true;
+
rt->dev.parent = &port->usb4->dev;
rt->dev.bus = &tb_bus_type;
rt->dev.type = &tb_retimer_type;
@@ -487,7 +494,7 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index)
int tb_retimer_scan(struct tb_port *port, bool add)
{
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
- int ret, i, last_idx = 0;
+ int ret, i, max, last_idx = 0;
/*
* Send broadcast RT to make sure retimer indices facing this
@@ -522,26 +529,28 @@ int tb_retimer_scan(struct tb_port *port, bool add)
break;
}
- tb_retimer_unset_inbound_sbtx(port);
-
- if (!last_idx)
- return 0;
-
- /* Add on-board retimers if they do not exist already */
+ max = i;
ret = 0;
- for (i = 1; i <= last_idx; i++) {
+
+ /* Add retimers if they do not exist already */
+ for (i = 1; i <= max; i++) {
struct tb_retimer *rt;
+ /* Skip cable retimers */
+ if (usb4_port_retimer_is_cable(port, i))
+ continue;
+
rt = tb_port_find_retimer(port, i);
if (rt) {
put_device(&rt->dev);
} else if (add) {
- ret = tb_retimer_add(port, i, status[i]);
+ ret = tb_retimer_add(port, i, status[i], i <= last_idx);
if (ret && ret != -EOPNOTSUPP)
break;
}
}
+ tb_retimer_unset_inbound_sbtx(port);
return ret;
}
diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h
index b2a6add82161..a29ef3c19422 100644
--- a/drivers/thunderbolt/sb_regs.h
+++ b/drivers/thunderbolt/sb_regs.h
@@ -26,6 +26,7 @@ enum usb4_sb_opcode {
USB4_SB_OPCODE_SET_INBOUND_SBTX = 0x5055534c, /* "LSUP" */
USB4_SB_OPCODE_UNSET_INBOUND_SBTX = 0x50555355, /* "USUP" */
USB4_SB_OPCODE_QUERY_LAST_RETIMER = 0x5453414c, /* "LAST" */
+ USB4_SB_OPCODE_QUERY_CABLE_RETIMER = 0x524c4243, /* "CBLR" */
USB4_SB_OPCODE_GET_NVM_SECTOR_SIZE = 0x53534e47, /* "GNSS" */
USB4_SB_OPCODE_NVM_SET_OFFSET = 0x53504f42, /* "BOPS" */
USB4_SB_OPCODE_NVM_BLOCK_WRITE = 0x574b4c42, /* "BLKW" */
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 2f3de3d3c2e0..b47f7873c847 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -329,6 +329,7 @@ struct usb4_port {
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
* @no_nvm_upgrade: Prevent NVM upgrade of this retimer
* @auth_status: Status of last NVM authentication
+ * @margining: Pointer to margining structure if enabled
*/
struct tb_retimer {
struct device dev;
@@ -340,6 +341,9 @@ struct tb_retimer {
struct tb_nvm *nvm;
bool no_nvm_upgrade;
u32 auth_status;
+#ifdef CONFIG_USB4_DEBUGFS_MARGINING
+ struct tb_margining *margining;
+#endif
};
/**
@@ -1363,6 +1367,7 @@ int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target,
int usb4_port_retimer_set_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_unset_inbound_sbtx(struct tb_port *port, u8 index);
int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
+int usb4_port_retimer_is_cable(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
unsigned int address);
diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c
index d952de07493f..4d83b65afb5b 100644
--- a/drivers/thunderbolt/usb4.c
+++ b/drivers/thunderbolt/usb4.c
@@ -1830,6 +1830,30 @@ int usb4_port_retimer_is_last(struct tb_port *port, u8 index)
return ret ? ret : metadata & 1;
}
+/**
+ * usb4_port_retimer_is_cable() - Is the retimer cable retimer
+ * @port: USB4 port
+ * @index: Retimer index
+ *
+ * If the retimer at @index is last cable retimer this function returns
+ * %1 and %0 if it is on-board retimer. In case a retimer is not present
+ * at @index returns %-ENODEV. Otherwise returns negative errno.
+ */
+int usb4_port_retimer_is_cable(struct tb_port *port, u8 index)
+{
+ u32 metadata;
+ int ret;
+
+ ret = usb4_port_retimer_op(port, index, USB4_SB_OPCODE_QUERY_CABLE_RETIMER,
+ 500);
+ if (ret)
+ return ret;
+
+ ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index,
+ USB4_SB_METADATA, &metadata, sizeof(metadata));
+ return ret ? ret : metadata & 1;
+}
+
/**
* usb4_port_retimer_nvm_sector_size() - Read retimer NVM sector size
* @port: USB4 port
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
` (5 preceding siblings ...)
2024-06-14 12:15 ` [PATCH v2 6/6] thunderbolt: Add receiver lane margining support for retimers Mika Westerberg
@ 2024-06-14 12:19 ` Greg KH
2024-06-14 12:37 ` Mika Westerberg
2024-06-17 9:58 ` Mika Westerberg
7 siblings, 1 reply; 11+ messages in thread
From: Greg KH @ 2024-06-14 12:19 UTC (permalink / raw)
To: Mika Westerberg
Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
linux-usb
On Fri, Jun 14, 2024 at 03:15:06PM +0300, Mika Westerberg wrote:
> Hi all,
>
> This series adds USB4 link sideband access through debugfs. This can be
> used to run port operations etc. from userspace usable for example in
> manufacturing. The other feature is receiver lane margining support for
> retimers with similar use case.
>
> Mika Westerberg (6):
> thunderbolt: Move usb4_port_margining_caps() declaration into correct place
> thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c
> thunderbolt: Add sideband register access to debugfs
> thunderbolt: Split out margining from USB4 port
> thunderbolt: Make margining functions accept target and retimer index
> thunderbolt: Add receiver lane margining support for retimers
>
> drivers/thunderbolt/Kconfig | 11 +-
> drivers/thunderbolt/debugfs.c | 623 ++++++++++++++++++++++++++--------
> drivers/thunderbolt/retimer.c | 53 +--
> drivers/thunderbolt/sb_regs.h | 9 +
> drivers/thunderbolt/tb.h | 45 ++-
> drivers/thunderbolt/usb4.c | 183 +++++-----
> 6 files changed, 655 insertions(+), 269 deletions(-)
No hint as to what changed from v1?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support
2024-06-14 12:19 ` [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Greg KH
@ 2024-06-14 12:37 ` Mika Westerberg
2024-06-14 12:55 ` Greg KH
0 siblings, 1 reply; 11+ messages in thread
From: Mika Westerberg @ 2024-06-14 12:37 UTC (permalink / raw)
To: Greg KH
Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
linux-usb
On Fri, Jun 14, 2024 at 02:19:58PM +0200, Greg KH wrote:
> On Fri, Jun 14, 2024 at 03:15:06PM +0300, Mika Westerberg wrote:
> > Hi all,
> >
> > This series adds USB4 link sideband access through debugfs. This can be
> > used to run port operations etc. from userspace usable for example in
> > manufacturing. The other feature is receiver lane margining support for
> > retimers with similar use case.
> >
> > Mika Westerberg (6):
> > thunderbolt: Move usb4_port_margining_caps() declaration into correct place
> > thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c
> > thunderbolt: Add sideband register access to debugfs
> > thunderbolt: Split out margining from USB4 port
> > thunderbolt: Make margining functions accept target and retimer index
> > thunderbolt: Add receiver lane margining support for retimers
> >
> > drivers/thunderbolt/Kconfig | 11 +-
> > drivers/thunderbolt/debugfs.c | 623 ++++++++++++++++++++++++++--------
> > drivers/thunderbolt/retimer.c | 53 +--
> > drivers/thunderbolt/sb_regs.h | 9 +
> > drivers/thunderbolt/tb.h | 45 ++-
> > drivers/thunderbolt/usb4.c | 183 +++++-----
> > 6 files changed, 655 insertions(+), 269 deletions(-)
>
> No hint as to what changed from v1?
Oh, crap. I added it but added for the v1 cover letter by accident :(
Sorry about that. The changelog is below:
Previous version can be found:
https://lore.kernel.org/linux-usb/20240610100627.3521887-1-mika.westerberg@linux.intel.com/
Changes from previous version:
- Do not store debugfs dentry pointer, instead look it up when needed.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support
2024-06-14 12:37 ` Mika Westerberg
@ 2024-06-14 12:55 ` Greg KH
0 siblings, 0 replies; 11+ messages in thread
From: Greg KH @ 2024-06-14 12:55 UTC (permalink / raw)
To: Mika Westerberg
Cc: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever,
linux-usb
On Fri, Jun 14, 2024 at 03:37:53PM +0300, Mika Westerberg wrote:
> On Fri, Jun 14, 2024 at 02:19:58PM +0200, Greg KH wrote:
> > On Fri, Jun 14, 2024 at 03:15:06PM +0300, Mika Westerberg wrote:
> > > Hi all,
> > >
> > > This series adds USB4 link sideband access through debugfs. This can be
> > > used to run port operations etc. from userspace usable for example in
> > > manufacturing. The other feature is receiver lane margining support for
> > > retimers with similar use case.
> > >
> > > Mika Westerberg (6):
> > > thunderbolt: Move usb4_port_margining_caps() declaration into correct place
> > > thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c
> > > thunderbolt: Add sideband register access to debugfs
> > > thunderbolt: Split out margining from USB4 port
> > > thunderbolt: Make margining functions accept target and retimer index
> > > thunderbolt: Add receiver lane margining support for retimers
> > >
> > > drivers/thunderbolt/Kconfig | 11 +-
> > > drivers/thunderbolt/debugfs.c | 623 ++++++++++++++++++++++++++--------
> > > drivers/thunderbolt/retimer.c | 53 +--
> > > drivers/thunderbolt/sb_regs.h | 9 +
> > > drivers/thunderbolt/tb.h | 45 ++-
> > > drivers/thunderbolt/usb4.c | 183 +++++-----
> > > 6 files changed, 655 insertions(+), 269 deletions(-)
> >
> > No hint as to what changed from v1?
>
> Oh, crap. I added it but added for the v1 cover letter by accident :(
> Sorry about that. The changelog is below:
>
> Previous version can be found:
>
> https://lore.kernel.org/linux-usb/20240610100627.3521887-1-mika.westerberg@linux.intel.com/
>
> Changes from previous version:
>
> - Do not store debugfs dentry pointer, instead look it up when needed.
Great, thanks for the change, looks good!
greg k-h
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support
2024-06-14 12:15 [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Mika Westerberg
` (6 preceding siblings ...)
2024-06-14 12:19 ` [PATCH v2 0/6] thunderbolt: Sideband access and retimer lane margining support Greg KH
@ 2024-06-17 9:58 ` Mika Westerberg
7 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2024-06-17 9:58 UTC (permalink / raw)
To: Yehezkel Bernat, Michael Jamet, Lukas Wunner, Andreas Noever
Cc: linux-usb, Greg KH
On Fri, Jun 14, 2024 at 03:15:06PM +0300, Mika Westerberg wrote:
> Hi all,
>
> This series adds USB4 link sideband access through debugfs. This can be
> used to run port operations etc. from userspace usable for example in
> manufacturing. The other feature is receiver lane margining support for
> retimers with similar use case.
>
> Mika Westerberg (6):
> thunderbolt: Move usb4_port_margining_caps() declaration into correct place
> thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c
> thunderbolt: Add sideband register access to debugfs
> thunderbolt: Split out margining from USB4 port
> thunderbolt: Make margining functions accept target and retimer index
> thunderbolt: Add receiver lane margining support for retimers
Applied to thunderbolt.git/next.
^ permalink raw reply [flat|nested] 11+ messages in thread