mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 02:24:24 +02:00
thunderbolt: Changes for v6.11 merge window
This includes following USB4/Thunderbolt changes for the v6.11 merge window: - Add receiver lane margining support for retimers - Add sideband register access to debugfs - Minor cleanups. All these have been in linux-next with no reported issues. -----BEGIN PGP SIGNATURE----- iQJUBAABCgA+FiEEVTdhRGBbNzLrSUBaAP2fSd+ZWKAFAmZ6kBUgHG1pa2Eud2Vz dGVyYmVyZ0BsaW51eC5pbnRlbC5jb20ACgkQAP2fSd+ZWKDYqQ//a0P3e8qOShr0 tSnfNnL5fXtGALnbJL+5l+OXeRWvAEw7qdZOHDjTw5HPk0WUJKWcJXAX0egaY9CB tPfQYo6JmVZG9d6ZiyzbEq5JJtCCj9zchqeyGQfVnWDQdRIRwIiaryDUdZNWvso1 at4HWS3ySUPSMlQlxrQW1R/i9gN14+q2ePEMvrqkWIovFs4GQaukF+UhM6bEnoEH FJFlQMkCz/IyR2DUG6mDDMD029jxZ2qFmDb0LGFLyo51iHYfrXQXAA3l8PKCuM/6 UB4MeA0gECzWOeo/LmpMbiG2D2WMtPVUQu3I+pLVLlRLTFvBpY2MbBmz/o0iG4sY Ci1tW3wHaceh87SA69T0f5BzW2Wklh6rprYn6sGA32jx7c2Wp2caARitfw++fgM0 Ygt9GrePP3Angz5Tz8ACLrgHPN28rW+oGTt4Y5pVDP38E49ywOS7EkwxHHhPM/Uf FflnWlcABoB9WL5J2cSuRl8pD+FO9VL2k7mjtr3wzqKKUyajngGeMZ8lE8IiwuNA Bd+14ozo9H5kApYGwXkRuo2zRbucBHGv4X7ZLRoH9nTPaErE+ATnru1yW5ZJigrz wPmasVTaSWyQ5EYO9wQqkLHrpASa2cLOSNzXbZQMJ0rti+wlFlJbTolWO3/yXfnQ NrJfnyRJmOy1EzcYJUWKZ666sirzIr0= =w1fy -----END PGP SIGNATURE----- Merge tag 'thunderbolt-for-v6.11-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt into usb-next Mika writes: thunderbolt: Changes for v6.11 merge window This includes following USB4/Thunderbolt changes for the v6.11 merge window: - Add receiver lane margining support for retimers - Add sideband register access to debugfs - Minor cleanups. All these have been in linux-next with no reported issues. * tag 'thunderbolt-for-v6.11-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/westeri/thunderbolt: thunderbolt: debugfs: Use FIELD_GET() thunderbolt: Add receiver lane margining support for retimers thunderbolt: Make margining functions accept target and retimer index thunderbolt: Split out margining from USB4 port thunderbolt: Add sideband register access to debugfs thunderbolt: Make usb4_port_sb_read/write() available outside of usb4.c thunderbolt: Move usb4_port_margining_caps() declaration into correct place thunderbolt: Mention Thunderbolt/USB4 debugging tools in Kconfig
This commit is contained in:
commit
6a6aad7489
|
|
@ -22,20 +22,25 @@ config USB4_DEBUGFS_WRITE
|
|||
bool "Enable write by debugfs to configuration spaces (DANGEROUS)"
|
||||
help
|
||||
Enables writing to device configuration registers through
|
||||
debugfs interface.
|
||||
debugfs interface. You can use tools such as Thunderbolt/USB4
|
||||
debugging tools to access these registers. For more
|
||||
information see:
|
||||
|
||||
https://github.com/intel/tbtools
|
||||
|
||||
Only enable this if you know what you are doing! Never enable
|
||||
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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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,35 +372,29 @@ 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;
|
||||
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);
|
||||
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;
|
||||
|
|
@ -437,12 +444,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);
|
||||
}
|
||||
|
|
@ -485,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
|
||||
|
|
@ -520,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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -22,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" */
|
||||
|
|
@ -35,6 +40,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 */
|
||||
|
|
@ -42,30 +51,21 @@ enum usb4_sb_opcode {
|
|||
#define USB4_MARGIN_CAP_0_MODES_SW BIT(1)
|
||||
#define USB4_MARGIN_CAP_0_2_LANES BIT(2)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_MASK GENMASK(4, 3)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_INDP_SHIFT 3
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_MIN 0x0
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_HL 0x1
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_BOTH 0x2
|
||||
#define USB4_MARGIN_CAP_0_TIME BIT(5)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6)
|
||||
#define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_SHIFT 6
|
||||
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13)
|
||||
#define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_SHIFT 13
|
||||
#define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8)
|
||||
#define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9)
|
||||
#define USB4_MARGIN_CAP_1_TIME_INDP_SHIFT 9
|
||||
#define USB4_MARGIN_CAP_1_TIME_MIN 0x0
|
||||
#define USB4_MARGIN_CAP_1_TIME_LR 0x1
|
||||
#define USB4_MARGIN_CAP_1_TIME_BOTH 0x2
|
||||
#define USB4_MARGIN_CAP_1_TIME_STEPS_MASK GENMASK(15, 11)
|
||||
#define USB4_MARGIN_CAP_1_TIME_STEPS_SHIFT 11
|
||||
#define USB4_MARGIN_CAP_1_TIME_OFFSET_MASK GENMASK(20, 16)
|
||||
#define USB4_MARGIN_CAP_1_TIME_OFFSET_SHIFT 16
|
||||
#define USB4_MARGIN_CAP_1_MIN_BER_MASK GENMASK(25, 21)
|
||||
#define USB4_MARGIN_CAP_1_MIN_BER_SHIFT 21
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_MASK GENMASK(30, 26)
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
|
||||
#define USB4_MARGIN_CAP_1_MAX_BER_SHIFT 26
|
||||
|
||||
/* USB4_SB_OPCODE_RUN_HW_LANE_MARGINING */
|
||||
#define USB4_MARGIN_HW_TIME BIT(3)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1327,26 +1331,43 @@ 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_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,
|
||||
/**
|
||||
* 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, 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);
|
||||
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_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);
|
||||
|
|
@ -1445,6 +1466,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) { }
|
||||
|
|
@ -1454,6 +1477,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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -1610,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
|
||||
|
|
@ -1640,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;
|
||||
|
|
@ -1656,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
|
||||
|
|
@ -1683,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;
|
||||
|
|
@ -1697,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,
|
||||
|
|
@ -1776,47 +1805,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 +1825,32 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
|
@ -1863,8 +1875,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 +1901,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 +1924,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 +2004,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 +2016,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 +2043,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 +2052,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user