// SPDX-License-Identifier: GPL-2.0/* * Debugfs interface * * Copyright (C) 2020, Intel Corporation * Authors: Gil Fine <gil.fine@intel.com> * Mika Westerberg <mika.westerberg@linux.intel.com> */ #include <linux/array_size.h>#include <linux/bitfield.h>#include <linux/debugfs.h>#include <linux/delay.h>#include <linux/pm_runtime.h>#include <linux/string_choices.h>#include <linux/uaccess.h> #include "tb.h"#include "sb_regs.h" #define PORT_CAP_V1_PCIE_LEN 1#define PORT_CAP_V2_PCIE_LEN 2#define PORT_CAP_POWER_LEN 2#define PORT_CAP_LANE_LEN 3#define PORT_CAP_USB3_LEN 5#define PORT_CAP_DP_V1_LEN 9#define PORT_CAP_DP_V2_LEN 14#define PORT_CAP_TMU_V1_LEN 8#define PORT_CAP_TMU_V2_LEN 10#define PORT_CAP_BASIC_LEN 9#define PORT_CAP_USB4_LEN 20 #define SWITCH_CAP_TMU_LEN 26#define SWITCH_CAP_BASIC_LEN 27 #define PATH_LEN 2 #define COUNTER_SET_LEN 3 /* * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms * probed to give good results. */#define MIN_DWELL_TIME 100 /* ms */#define MAX_DWELL_TIME 500 /* ms */#define DWELL_SAMPLE_INTERVAL 10 enum usb4_margin_cap_voltage_indp { USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH, USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN,}; enum usb4_margin_cap_time_indp { USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN, USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR, USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH, USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN, USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH, USB4_MARGIN_CAP_TIME_INDP_UNKNOWN,}; /* 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) \{ \ return single_open(file, __space ## _show, inode->i_private); \} \ \static const struct file_operations __space ## _fops = { \ .owner = THIS_MODULE, \ .open = __space ## _open, \ .release = single_release, \ .read = seq_read, \ .write = __write, \ .llseek = seq_lseek, \} #define DEBUGFS_ATTR_RO(__space) \ DEBUGFS_ATTR(__space, NULL) #define DEBUGFS_ATTR_RW(__space) \ DEBUGFS_ATTR(__space, __space ## _write) static struct dentry *tb_debugfs_root; static void *validate_and_copy_from_user(const void __user *user_buf, size_t *count){ size_t nbytes; void *buf; if (!*count) return ERR_PTR(-EINVAL); if (!access_ok(user_buf, *count)) return ERR_PTR(-EFAULT); buf = (void *)get_zeroed_page(GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); nbytes = min_t(size_t, *count, PAGE_SIZE); if (copy_from_user(buf, user_buf, nbytes)) { free_page((unsigned long)buf); return ERR_PTR(-EFAULT); } *count = nbytes; return buf;} static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len, int long_fmt_len){ char *token; u32 v[5]; int ret; token = strsep(line, "\n"); if (!token) return false; /* * For Adapter/Router configuration space: * Short format is: offset value\n * v[0] v[1] * Long format as produced from the read side: * 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] * Long format as produced from the read side: * offset relative_offset counter_id value\n * v[0] v[1] v[2] v[3] */ ret = sscanf(token, "%i %i %i %i %i", &v[0], &v[1], &v[2], &v[3], &v[4]); /* In case of Counters, clear counter, "val" content is NA */ if (ret == short_fmt_len) { *offs = v[0]; *val = v[short_fmt_len - 1]; return true; } else if (ret == long_fmt_len) { *offs = v[0]; *val = v[long_fmt_len - 1]; return true; } return false;} #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 path 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, 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; 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; } /* 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, 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; } mutex_unlock(&tb->lock); out: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); free_page((unsigned long)buf); return ret < 0 ? ret : count;} static ssize_t port_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; 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, size_t count, loff_t *ppos){ struct seq_file *s = file->private_data; struct tb_switch *sw = s->private; 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, 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_reg->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; } 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: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); free_page((unsigned long)buf); 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; } 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: pm_runtime_mark_last_busy(&rt->dev); pm_runtime_put_autosuspend(&rt->dev); free_page((unsigned long)buf); return ret < 0 ? ret : count;}#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#define DEBUGFS_MODE 0400#endif #if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)/** * 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) * @gen: Link generation * @asym_rx: %true% if @port supports asymmetric link with 3 Rx * @caps: Port lane margining capabilities * @results: Last lane margining results * @lanes: %0, %1 or %7 (all) * @min_ber_level: Minimum supported BER level contour value * @max_ber_level: Maximum supported BER level contour value * @ber_level: Current BER level contour value * @voltage_steps: Number of mandatory voltage steps * @max_voltage_offset: Maximum mandatory voltage offset (in mV) * @voltage_steps_optional_range: Number of voltage steps for optional range * @max_voltage_offset_optional_range: Maximum voltage offset for the optional * range (in mV). * @time_steps: Number of time margin steps * @max_time_offset: Maximum time margin offset (in mUI) * @voltage_time_offset: Offset for voltage / time for software margining * @dwell_time: Dwell time for software margining (in ms) * @error_counter: Error counter operation for software margining * @optional_voltage_offset_range: Enable optional extended voltage range * @software: %true if software margining is used instead of hardware * @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; enum usb4_sb_target target; u8 index; struct device *dev; unsigned int gen; bool asym_rx; u32 caps[3]; u32 results[3]; enum usb4_margining_lane lanes; unsigned int min_ber_level; unsigned int max_ber_level;Lines 1–500 of 2563. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Debugfs interface * * Copyright (C) 2020, Intel Corporation * Authors: Gil Fine <gil.fine@intel.com> * Mika Westerberg <mika.westerberg@linux.intel.com> */ #include <linux/array_size.h> #include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/pm_runtime.h> #include <linux/string_choices.h> #include <linux/uaccess.h> #include "tb.h" #include "sb_regs.h" #define PORT_CAP_V1_PCIE_LEN 1 #define PORT_CAP_V2_PCIE_LEN 2 #define PORT_CAP_POWER_LEN 2 #define PORT_CAP_LANE_LEN 3 #define PORT_CAP_USB3_LEN 5 #define PORT_CAP_DP_V1_LEN 9 #define PORT_CAP_DP_V2_LEN 14 #define PORT_CAP_TMU_V1_LEN 8 #define PORT_CAP_TMU_V2_LEN 10 #define PORT_CAP_BASIC_LEN 9 #define PORT_CAP_USB4_LEN 20 #define SWITCH_CAP_TMU_LEN 26 #define SWITCH_CAP_BASIC_LEN 27 #define PATH_LEN 2 #define COUNTER_SET_LEN 3 /* * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms * probed to give good results. */ #define MIN_DWELL_TIME 100 /* ms */ #define MAX_DWELL_TIME 500 /* ms */ #define DWELL_SAMPLE_INTERVAL 10 enum usb4_margin_cap_voltage_indp { USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_MIN, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_HL, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_2_3_BOTH, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_MIN, USB4_MARGIN_CAP_VOLTAGE_INDP_GEN_4_BOTH, USB4_MARGIN_CAP_VOLTAGE_INDP_UNKNOWN, }; enum usb4_margin_cap_time_indp { USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_MIN, USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_LR, USB4_MARGIN_CAP_TIME_INDP_GEN_2_3_BOTH, USB4_MARGIN_CAP_TIME_INDP_GEN_4_MIN, USB4_MARGIN_CAP_TIME_INDP_GEN_4_BOTH, USB4_MARGIN_CAP_TIME_INDP_UNKNOWN, }; /* 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) \ { \ return single_open(file, __space ## _show, inode->i_private); \ } \ \ static const struct file_operations __space ## _fops = { \ .owner = THIS_MODULE, \ .open = __space ## _open, \ .release = single_release, \ .read = seq_read, \ .write = __write, \ .llseek = seq_lseek, \ } #define DEBUGFS_ATTR_RO(__space) \ DEBUGFS_ATTR(__space, NULL) #define DEBUGFS_ATTR_RW(__space) \ DEBUGFS_ATTR(__space, __space ## _write) static struct dentry *tb_debugfs_root; static void *validate_and_copy_from_user(const void __user *user_buf, size_t *count) { size_t nbytes; void *buf; if (!*count) return ERR_PTR(-EINVAL); if (!access_ok(user_buf, *count)) return ERR_PTR(-EFAULT); buf = (void *)get_zeroed_page(GFP_KERNEL); if (!buf) return ERR_PTR(-ENOMEM); nbytes = min_t(size_t, *count, PAGE_SIZE); if (copy_from_user(buf, user_buf, nbytes)) { free_page((unsigned long)buf); return ERR_PTR(-EFAULT); } *count = nbytes; return buf; } static bool parse_line(char **line, u32 *offs, u32 *val, int short_fmt_len, int long_fmt_len) { char *token; u32 v[5]; int ret; token = strsep(line, "\n"); if (!token) return false; /* * For Adapter/Router configuration space: * Short format is: offset value\n * v[0] v[1] * Long format as produced from the read side: * 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] * Long format as produced from the read side: * offset relative_offset counter_id value\n * v[0] v[1] v[2] v[3] */ ret = sscanf(token, "%i %i %i %i %i", &v[0], &v[1], &v[2], &v[3], &v[4]); /* In case of Counters, clear counter, "val" content is NA */ if (ret == short_fmt_len) { *offs = v[0]; *val = v[short_fmt_len - 1]; return true; } else if (ret == long_fmt_len) { *offs = v[0]; *val = v[long_fmt_len - 1]; return true; } return false; } #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 path 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, 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; 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; } /* 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, 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; } mutex_unlock(&tb->lock); out: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); free_page((unsigned long)buf); return ret < 0 ? ret : count; } static ssize_t port_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; 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, size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct tb_switch *sw = s->private; 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, 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_reg->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; } 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: pm_runtime_mark_last_busy(&sw->dev); pm_runtime_put_autosuspend(&sw->dev); free_page((unsigned long)buf); 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; } 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: pm_runtime_mark_last_busy(&rt->dev); pm_runtime_put_autosuspend(&rt->dev); free_page((unsigned long)buf); return ret < 0 ? ret : count; } #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 #define DEBUGFS_MODE 0400 #endif #if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING) /** * 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) * @gen: Link generation * @asym_rx: %true% if @port supports asymmetric link with 3 Rx * @caps: Port lane margining capabilities * @results: Last lane margining results * @lanes: %0, %1 or %7 (all) * @min_ber_level: Minimum supported BER level contour value * @max_ber_level: Maximum supported BER level contour value * @ber_level: Current BER level contour value * @voltage_steps: Number of mandatory voltage steps * @max_voltage_offset: Maximum mandatory voltage offset (in mV) * @voltage_steps_optional_range: Number of voltage steps for optional range * @max_voltage_offset_optional_range: Maximum voltage offset for the optional * range (in mV). * @time_steps: Number of time margin steps * @max_time_offset: Maximum time margin offset (in mUI) * @voltage_time_offset: Offset for voltage / time for software margining * @dwell_time: Dwell time for software margining (in ms) * @error_counter: Error counter operation for software margining * @optional_voltage_offset_range: Enable optional extended voltage range * @software: %true if software margining is used instead of hardware * @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; enum usb4_sb_target target; u8 index; struct device *dev; unsigned int gen; bool asym_rx; u32 caps[3]; u32 results[3]; enum usb4_margining_lane lanes; unsigned int min_ber_level; unsigned int max_ber_level;