2563 lines · 64.3 KB · C
RawDownload
// 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 = kzalloc(PAGE_SIZE, GFP_KERNEL);	if (!buf)		return ERR_PTR(-ENOMEM); 	nbytes = min_t(size_t, *count, PAGE_SIZE);	if (copy_from_user(buf, user_buf, nbytes)) {		kfree(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);	kfree(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, &reg, 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);	kfree(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);	kfree(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.