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:
Greg Kroah-Hartman 2024-06-25 14:56:49 +02:00
commit 6a6aad7489
6 changed files with 671 additions and 297 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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);
}
/**