diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index ea80354c410a..43de2938d1e4 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -38,6 +38,7 @@ #define COUNTER_SET_LEN 3 #define HW_MARGINING_DWORDS 3 +#define SW_MARGINING_DWORDS 4 /* * USB4 spec doesn't specify dwell range, the range of 100 ms to 500 ms @@ -496,7 +497,7 @@ struct tb_margining { unsigned int gen; bool asym_rx; u32 caps[3]; - u32 results[3]; + u32 results[5]; enum usb4_margining_lane lanes; unsigned int min_ber_level; unsigned int max_ber_level; @@ -614,6 +615,11 @@ supports_optional_voltage_offset_range(const struct tb_margining *margining) return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT; } +static bool supports_extended_error_counter(const struct tb_margining *margining) +{ + return margining->caps[0] & USB4_MARGIN_CAP_0_EXT_ERR_COUNTER; +} + static ssize_t margining_ber_level_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -703,6 +709,8 @@ static int margining_caps_show(struct seq_file *s, void *not_used) ber_level_show(s, margining->max_ber_level); } else { seq_puts(s, "# hardware margining: no\n"); + seq_printf(s, "# extended error counter: %s\n", + str_yes_no(supports_extended_error_counter(margining))); } seq_printf(s, "# all lanes simultaneously: %s\n", @@ -1157,10 +1165,16 @@ static int margining_run_sw(struct tb_margining *margining, { u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL; int ret, i; + u32 dwords; if (params->error_counter != USB4_MARGIN_SW_ERROR_COUNTER_START) goto out_stop_or_clear; + if (supports_extended_error_counter(margining)) + dwords = SW_MARGINING_DWORDS; + else + dwords = 1; + ret = usb4_port_sw_margin(margining->port, margining->target, margining->index, params, margining->results); if (ret) @@ -1170,7 +1184,8 @@ static int margining_run_sw(struct tb_margining *margining, u32 errors = 0; ret = usb4_port_sw_margin_errors(margining->port, margining->target, - margining->index, &margining->results[1]); + margining->index, &margining->results[1], + dwords); if (ret) { tb_port_warn(margining->port, "failed to read margining error counters\n"); break; @@ -1450,18 +1465,30 @@ static int margining_results_show(struct seq_file *s, void *not_used) u32 lane_errors, result; seq_printf(s, "0x%08x\n", margining->results[1]); + if (supports_extended_error_counter(margining)) { + seq_printf(s, "0x%08x\n", margining->results[2]); + seq_printf(s, "0x%08x\n", margining->results[3]); + seq_printf(s, "0x%08x\n", margining->results[4]); + } + result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); if (result == USB4_MARGINING_LANE_RX0 || result == USB4_MARGINING_LANE_ALL) { lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, margining->results[1]); + if (supports_extended_error_counter(margining)) + lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK, + margining->results[2]); seq_printf(s, "# lane 0 errors: %u\n", lane_errors); } if (result == USB4_MARGINING_LANE_RX1 || result == USB4_MARGINING_LANE_ALL) { lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, margining->results[1]); + if (supports_extended_error_counter(margining)) + lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK, + margining->results[3]); seq_printf(s, "# lane 1 errors: %u\n", lane_errors); } if (margining->asym_rx && @@ -1469,6 +1496,9 @@ static int margining_results_show(struct seq_file *s, void *not_used) result == USB4_MARGINING_LANE_ALL)) { lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK, margining->results[1]); + if (supports_extended_error_counter(margining)) + lane_errors |= FIELD_PREP(USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK, + margining->results[4]); seq_printf(s, "# lane 2 errors: %u\n", lane_errors); } } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 5391502a4b87..b801f3a694be 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -59,6 +59,7 @@ enum usb4_sb_opcode { #define USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK GENMASK(18, 13) #define USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT BIT(19) #define USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK GENMASK(26, 20) +#define USB4_MARGIN_CAP_0_EXT_ERR_COUNTER BIT(28) #define USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK GENMASK(7, 0) #define USB4_MARGIN_CAP_1_TIME_DESTR BIT(8) #define USB4_MARGIN_CAP_1_TIME_INDP_MASK GENMASK(10, 9) @@ -108,5 +109,6 @@ enum usb4_sb_opcode { #define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0) #define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4) #define USB4_MARGIN_SW_ERR_COUNTER_LANE_2_MASK GENMASK(11, 8) +#define USB4_MARGIN_SW_EXT_ERR_COUNTER_MASK GENMASK(15, 4) #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index c112954ce3fd..e86845762e22 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1442,7 +1442,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, u8 index, const struct usb4_port_margining_params *params, u32 *results); int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, - u8 index, u32 *errors); + u8 index, u32 *errors, size_t dwords); 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 5fd4fe070f25..fe1b061f7055 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1826,13 +1826,14 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, * @target: Sideband target * @index: Retimer index if target is %USB4_SB_TARGET_RETIMER * @errors: Error metadata is copied here. + * @dwords: Number of dwords to read error counter values * * This reads back the software margining error counters from the port. * * Return: %0 on success, negative errno otherwise. */ int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, - u8 index, u32 *errors) + u8 index, u32 *errors, size_t dwords) { int ret; @@ -1842,7 +1843,7 @@ int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, return ret; return usb4_port_sb_read(port, target, index, USB4_SB_METADATA, errors, - sizeof(*errors)); + sizeof(*errors) * dwords); } static inline int usb4_port_retimer_op(struct tb_port *port, u8 index,