From patchwork Mon Aug 19 11:40:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mika Westerberg X-Patchwork-Id: 820591 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8F00515B140 for ; Mon, 19 Aug 2024 11:40:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067661; cv=none; b=dq0Gg4IxkfHKerWLo5+jIS8XFw7OSUTtEWl5XVfpKUv4i3gjSwq9hAnk9nywN/7FJGcqVpOJta1pXov4bY36+p4XfrRKL0lqSALnOlojO9bgMowCIXPYaiiHkPvKbGl9l69XLujyJs+LKy1rRfIdF6vtqDV5ewPv9jxfK8uPXjk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067661; c=relaxed/simple; bh=3VP9VUTlBI4F7r6su3b2T9/XTB2ROE0dlfAhwIwdWkE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=essgxXfKUIerf5KCBWEhVj6aGq/bMwb+jJDOeyYmVuv7ZYUaLJlWUoRM0BhTGuNhagXl10Q3rlUFXYtRU1gDasaJRDLhAOqDAtIC23WTh88MXDJXXaxU5WPKcyW1VqdoxlER74W06psM+Y9ca9U5jyE+dkm+vmUc2SZ0tqxUCws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=XfR7OX2N; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="XfR7OX2N" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1724067659; x=1755603659; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=3VP9VUTlBI4F7r6su3b2T9/XTB2ROE0dlfAhwIwdWkE=; b=XfR7OX2NY/u/aBtiV2Xhxwm+VYrYIyDSu60BexpWGs1+HSUwMeUxtPuC xnEVy5dQCk9Ts7aPzLd/1ljPKtsruFtmhKthP+YDuR3A7n+7GPQYZv0na jbo5hc1QX4FvdcMBgJhZqlBd9tMTTbLdT7A3guNngb2L9gxceJQkNfhGN VE1dm9Sf4ahq6bWo6OUqwGac6gZJ01Zgudz7gM6SptnHtNefFyFWw+7sL PcxFLfo04pv6ZJmwOXLvTx/otgqwS/zUC+AP++vvji/798gpZyukdNcMr /spbTx9PxCvWwJcX98fLkTYyzFw5ZhvknNMUNv6SfTMT/ETNKwk3otNh0 A==; X-CSE-ConnectionGUID: ubGgmHPgRdGjMxqE3cOe3g== X-CSE-MsgGUID: HFo7emMyQxGEp+iiuRY9Nw== X-IronPort-AV: E=McAfee;i="6700,10204,11168"; a="13092951" X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="13092951" Received: from fmviesa001.fm.intel.com ([10.60.135.141]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Aug 2024 04:40:59 -0700 X-CSE-ConnectionGUID: OOR600oLSBeAHnzia1n15A== X-CSE-MsgGUID: oszOZdrHQkK9Yknnhkrz4A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="91118710" Received: from black.fi.intel.com ([10.237.72.28]) by fmviesa001.fm.intel.com with ESMTP; 19 Aug 2024 04:40:56 -0700 Received: by black.fi.intel.com (Postfix, from userid 1001) id A1B55562; Mon, 19 Aug 2024 14:40:54 +0300 (EEST) From: Mika Westerberg To: Yehezkel Bernat , Michael Jamet , Lukas Wunner , Andreas Noever Cc: linux-usb@vger.kernel.org, Aapo Vienamo , Rene Sapiens , R Kannappan , Dan Carpenter , Mika Westerberg Subject: [PATCH v2 1/4] thunderbolt: Add missing usb4_port_sb_read() to usb4_port_sw_margin() Date: Mon, 19 Aug 2024 14:40:51 +0300 Message-ID: <20240819114054.4139941-2-mika.westerberg@linux.intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> References: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Aapo Vienamo Synchronize the operation completion by reading back the software margining operation completion metadata into margining->results. Signed-off-by: Aapo Vienamo Co-developed-by: R Kannappan Signed-off-by: R Kannappan Co-developed-by: Rene Sapiens Signed-off-by: Rene Sapiens Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 5 ++--- drivers/thunderbolt/tb.h | 2 +- drivers/thunderbolt/usb4.c | 13 ++++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 9ed4bb2e8d05..a0d07887990e 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -785,9 +785,8 @@ static int margining_run_write(void *data, u64 val) margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); ret = usb4_port_sw_margin(port, margining->target, margining->index, - margining->lanes, margining->time, - margining->right_high, - USB4_MARGIN_SW_COUNTER_CLEAR); + margining->lanes, margining->time, margining->right_high, + USB4_MARGIN_SW_COUNTER_CLEAR, &margining->results[0]); if (ret) goto out_clx; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index b47f7873c847..321db4076573 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1360,7 +1360,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, 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); + bool right_high, u32 counter, u32 *results); int usb4_port_sw_margin_errors(struct tb_port *port, enum usb4_sb_target target, u8 index, u32 *errors); diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 4d83b65afb5b..5505aa95c2ea 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1703,6 +1703,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * @timing: Perform timing margining instead of voltage * @right_high: Use Right/high margin instead of left/low * @counter: What to do with the error counter + * @results: Data word for the operation completion data * * Runs software lane margining on USB4 port. Read back the error * counters by calling usb4_port_sw_margin_errors(). Returns %0 in @@ -1710,7 +1711,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, */ 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) + bool right_high, u32 counter, u32 *results) { u32 val; int ret; @@ -1728,8 +1729,14 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, if (ret) return ret; - return usb4_port_sb_op(port, target, index, - USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); + ret = usb4_port_sb_op(port, target, index, + USB4_SB_OPCODE_RUN_SW_LANE_MARGINING, 2500); + if (ret) + return ret; + + return usb4_port_sb_read(port, target, index, USB4_SB_DATA, results, + sizeof(*results)); + } /** From patchwork Mon Aug 19 11:40:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mika Westerberg X-Patchwork-Id: 821295 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D9F9B166F01 for ; Mon, 19 Aug 2024 11:41:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067662; cv=none; b=QdtTieIlKS/tvbWsl1PCZ6+Vevs9UwNbRxDXBETqjdSMkfI6MIlEZu1dZt7eR3nLGotuF3+xQN9yERqIpVmvTcFl0j9qyrkiJk9YroVDCQig2BBy/uAre5YPR7JgXJ7uK0BUjxCzPFxFNoRKCoED2DWnO2JLROLZKrJgvsCsROE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067662; c=relaxed/simple; bh=kgLalTb/dWZbWi5S0NdM0Z5qxZySAA65KQNmc8+cfL0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Xvg7DFWbIK0Rt30m79I1lpcytZ6qNC0zO16Iv1gUAk9ePtNyAjXt8q/vsB12bKJR7iHNmwmolJ443MoLcWcPsgwNnbkViFRvFnMnqPII+0ieCpEw9RlKnPnkPpZqh4/+Pm+TnaWnmjLKc5iJp6ppCcUvT3oPyFE02mUVjekPToo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Li2qcVhF; arc=none smtp.client-ip=198.175.65.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Li2qcVhF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1724067662; x=1755603662; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=kgLalTb/dWZbWi5S0NdM0Z5qxZySAA65KQNmc8+cfL0=; b=Li2qcVhFiHHm6FNxNxUMxHlSCsq4wZ7r/WEOzafeUSmlv+a5VmIffeuy CvigAZ5ajUR2i8W90bOrYSMs6B1B/dm4ZOIdfssQJf9p8XwOdC7gDOQfy rggu+DHVb8jhw/oPREBW02MPQTg3XSxn8rvV5P9hLExfGkwHH0cT4AgpA oUZXNYw1wgfQVLw1PDm7WAV1a5jfSH7cJKEkK5n9CkK6KQL3zN+wYCuGw Di9c6dxIWFSo9r3rTCnQICZs2sLGNs394iygIywFISrsm9npDZkQ1p3Ez AmYlaVKaKFxTxirvPTxcloJa/MMO0rWrcvSk6IxNerD0WBdpl/3QAQ8vM w==; X-CSE-ConnectionGUID: 3VV/J48+Sda3B6C+gfwoYQ== X-CSE-MsgGUID: Cy3wx+8SRCK7RptTeVeSTQ== X-IronPort-AV: E=McAfee;i="6700,10204,11168"; a="33688823" X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="33688823" Received: from fmviesa002.fm.intel.com ([10.60.135.142]) by orvoesa104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Aug 2024 04:40:59 -0700 X-CSE-ConnectionGUID: a++PGJDGTKaQqeS2itP5DQ== X-CSE-MsgGUID: fVdasaSWR/CY7hXNulPYoQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="83566110" Received: from black.fi.intel.com ([10.237.72.28]) by fmviesa002.fm.intel.com with ESMTP; 19 Aug 2024 04:40:55 -0700 Received: by black.fi.intel.com (Postfix, from userid 1001) id 9818E2AA; Mon, 19 Aug 2024 14:40:54 +0300 (EEST) From: Mika Westerberg To: Yehezkel Bernat , Michael Jamet , Lukas Wunner , Andreas Noever Cc: linux-usb@vger.kernel.org, Aapo Vienamo , Rene Sapiens , R Kannappan , Dan Carpenter , Mika Westerberg Subject: [PATCH v2 2/4] thunderbolt: Consolidate margining parameters into a structure Date: Mon, 19 Aug 2024 14:40:52 +0300 Message-ID: <20240819114054.4139941-3-mika.westerberg@linux.intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> References: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Rene Sapiens Consolidate the hardware and software margining parameters into a single structure to reduce the number of parameters passed to the margining functions. Signed-off-by: Rene Sapiens Co-developed-by: Aapo Vienamo Signed-off-by: Aapo Vienamo Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 32 +++++++++++++++++------- drivers/thunderbolt/sb_regs.h | 5 ---- drivers/thunderbolt/tb.h | 38 ++++++++++++++++++++++++++--- drivers/thunderbolt/usb4.c | 46 ++++++++++++++++------------------- 4 files changed, 78 insertions(+), 43 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index a0d07887990e..5d1588baea6a 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -780,13 +780,20 @@ static int margining_run_write(void *data, u64 val) } if (margining->software) { + struct usb4_port_margining_params params = { + .error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, + .lanes = margining->lanes, + .time = margining->time, + .right_high = margining->right_high, + }; + tb_port_dbg(port, "running software %s lane margining for %s lanes %u\n", margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); - ret = usb4_port_sw_margin(port, margining->target, margining->index, - margining->lanes, margining->time, margining->right_high, - USB4_MARGIN_SW_COUNTER_CLEAR, &margining->results[0]); + + ret = usb4_port_sw_margin(port, margining->target, margining->index, ¶ms, + &margining->results[0]); if (ret) goto out_clx; @@ -794,16 +801,23 @@ static int margining_run_write(void *data, u64 val) margining->index, &margining->results[0]); } else { + struct usb4_port_margining_params params = { + .ber_level = margining->ber_level, + .lanes = margining->lanes, + .time = margining->time, + .right_high = margining->right_high, + }; + + /* Clear the results */ + margining->results[0] = 0; + margining->results[1] = 0; + tb_port_dbg(port, "running hardware %s lane margining for %s lanes %u\n", margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); - /* Clear the results */ - margining->results[0] = 0; - margining->results[1] = 0; - ret = usb4_port_hw_margin(port, margining->target, margining->index, - margining->lanes, margining->ber_level, - margining->time, margining->right_high, + + ret = usb4_port_hw_margin(port, margining->target, margining->index, ¶ms, margining->results); } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 2a88edfc97b2..86e80aa297f7 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -85,10 +85,5 @@ enum usb4_sb_opcode { #define USB4_MARGIN_SW_TIME BIT(3) #define USB4_MARGIN_SW_RH BIT(4) #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) -#define USB4_MARGIN_SW_COUNTER_SHIFT 13 -#define USB4_MARGIN_SW_COUNTER_NOP 0x0 -#define USB4_MARGIN_SW_COUNTER_CLEAR 0x1 -#define USB4_MARGIN_SW_COUNTER_START 0x2 -#define USB4_MARGIN_SW_COUNTER_STOP 0x3 #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 321db4076573..89ea66f885a4 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1353,14 +1353,44 @@ int usb4_port_sb_read(struct tb_port *port, enum usb4_sb_target target, u8 index int usb4_port_sb_write(struct tb_port *port, enum usb4_sb_target target, u8 index, u8 reg, const void *buf, u8 size); +/** + * enum usb4_margin_sw_error_counter - Software margining error counter operation + * @USB4_MARGIN_SW_ERROR_COUNTER_NOP: No change in counter setup + * @USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: Set the error counter to 0, enable counter + * @USB4_MARGIN_SW_ERROR_COUNTER_START: Start counter, count from last value + * @USB4_MARGIN_SW_ERROR_COUNTER_STOP: Stop counter, do not clear value + */ +enum usb4_margin_sw_error_counter { + USB4_MARGIN_SW_ERROR_COUNTER_NOP, + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, + USB4_MARGIN_SW_ERROR_COUNTER_START, + USB4_MARGIN_SW_ERROR_COUNTER_STOP, +}; + +/** + * struct usb4_port_margining_params - USB4 margining parameters + * @error_counter: Error counter operation for software margining + * @ber_level: Current BER level contour value + * @lanes: %0, %1 or %7 (all) + * @right_high: %false if left/low margin test is performed, %true if right/high + * @time: %true if time margining is used instead of voltage + */ +struct usb4_port_margining_params { + enum usb4_margin_sw_error_counter error_counter; + u32 ber_level; + u32 lanes; + bool right_high; + bool time; +}; + 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); + u8 index, const struct usb4_port_margining_params *params, + 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, u32 *results); + 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); diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 5505aa95c2ea..cb51cafcf20c 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1653,31 +1653,29 @@ int usb4_port_margining_caps(struct tb_port *port, enum usb4_sb_target target, * @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 - * @timing: Perform timing margining instead of voltage - * @right_high: Use Right/high margin instead of left/low + * @params: Parameters for USB4 hardware margining * @results: Array with at least two elements to hold the results * * Runs hardware lane margining on USB4 port and returns the result in * @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) + u8 index, const struct usb4_port_margining_params *params, + u32 *results) { u32 val; int ret; - val = lanes; - if (timing) + if (WARN_ON_ONCE(!params)) + return -EINVAL; + + val = params->lanes; + if (params->time) val |= USB4_MARGIN_HW_TIME; - if (right_high) + if (params->right_high) val |= USB4_MARGIN_HW_RH; - if (ber_level) - val |= (ber_level << USB4_MARGIN_HW_BER_SHIFT) & - USB4_MARGIN_HW_BER_MASK; + if (params->ber_level) + val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level); ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val)); @@ -1698,11 +1696,7 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * @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 - * @right_high: Use Right/high margin instead of left/low - * @counter: What to do with the error counter + * @params: Parameters for USB4 software margining * @results: Data word for the operation completion data * * Runs software lane margining on USB4 port. Read back the error @@ -1710,19 +1704,21 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, * success and negative errno otherwise. */ 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 *results) + u8 index, const struct usb4_port_margining_params *params, + u32 *results) { u32 val; int ret; - val = lanes; - if (timing) + if (WARN_ON_ONCE(!params)) + return -EINVAL; + + val = params->lanes; + if (params->time) val |= USB4_MARGIN_SW_TIME; - if (right_high) + if (params->right_high) val |= USB4_MARGIN_SW_RH; - val |= (counter << USB4_MARGIN_SW_COUNTER_SHIFT) & - USB4_MARGIN_SW_COUNTER_MASK; + val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val)); From patchwork Mon Aug 19 11:40:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mika Westerberg X-Patchwork-Id: 820590 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6AA21166F36 for ; Mon, 19 Aug 2024 11:41:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067663; cv=none; b=iL5gn7grXdRX8rvBt40VFgp7vx1bAnk2lo9rdxmouUD6N+6s4G0fzhbadFZmwnH4XKMexUv5LoheQXL4oePTwNimxJQcZLKQd0NGFYlrWF/KYbt5J2RkZefHzOKIbEbBe36BMLmpZyGaiCREhsBx91GA+x0uX1gPDh3WwSekAME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067663; c=relaxed/simple; bh=XI/0mqGiBldhh3acDlJ2+tv/KCxgYfKX6cF69vCRyBw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mzaiAiZRICbc4yYWn2PL8GEx4TrjEejAjrQRQRuUwYBtllPKazWPGC0CcL+rdImunllDWP2J5YM4MJ/PNiWZ26mUR1x5p9NzHrooyOuOqCpBQLc2PcgJX+HdQNQ4VenhF7qsWq3TXUbmlWKGTQb/x52f2f03YlZfnAaJ84dAbuI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=MAiL8QUg; arc=none smtp.client-ip=198.175.65.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="MAiL8QUg" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1724067662; x=1755603662; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=XI/0mqGiBldhh3acDlJ2+tv/KCxgYfKX6cF69vCRyBw=; b=MAiL8QUgKQn78QC05S/YIygTKXtqUZL78cbQaRgBWU1R4PX+B4dN7rWR HwFb0PzvUKwFkCz0NkBfaiwBq2dodgZ+LjpJgfGh30rKhXZoRxIQfNkZE z8WFmvW8ScfovLcTZCc0US7KTB8/Kdf4kbOvVfQbwo5KJ7JKAULPGg5z0 9Z9AXlc+/kHgkud/JXGbb74/JoYeYswkBJ5VYqWZ1m+POUQe9YlDOAPRf ux0/Q096Hbm/q3LC1b25aW6kEc3x4LAlIGvUXPMLysxMupfvkUINflAOj 5x7KgE7kOP/9Fa3YiRbD90XCPY5+c+rbfFEuDx9jMbIORIdo8uBXvNZQ+ w==; X-CSE-ConnectionGUID: i0nvXPVhTUCBKOhL96lsYg== X-CSE-MsgGUID: AJLPSidLSZaCGS5K9NfXXQ== X-IronPort-AV: E=McAfee;i="6700,10204,11168"; a="33688828" X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="33688828" Received: from fmviesa002.fm.intel.com ([10.60.135.142]) by orvoesa104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Aug 2024 04:40:59 -0700 X-CSE-ConnectionGUID: JBss5WKzQAqUQr6gcuOHlA== X-CSE-MsgGUID: 8xO4a1VsSwqsYFpZRksYqQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="83566112" Received: from black.fi.intel.com ([10.237.72.28]) by fmviesa002.fm.intel.com with ESMTP; 19 Aug 2024 04:40:55 -0700 Received: by black.fi.intel.com (Postfix, from userid 1001) id AB5A551C; Mon, 19 Aug 2024 14:40:54 +0300 (EEST) From: Mika Westerberg To: Yehezkel Bernat , Michael Jamet , Lukas Wunner , Andreas Noever Cc: linux-usb@vger.kernel.org, Aapo Vienamo , Rene Sapiens , R Kannappan , Dan Carpenter , Mika Westerberg Subject: [PATCH v2 3/4] thunderbolt: Add optional voltage offset range for receiver lane margining Date: Mon, 19 Aug 2024 14:40:53 +0300 Message-ID: <20240819114054.4139941-4-mika.westerberg@linux.intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> References: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Rene Sapiens Add optional extended voltage offset range support for software and hardware margining as defined by the USB4 specification. If supported, it can be enabled like below: # cd /sys/kernel/debug/thunderbolt/ROUTER/portX/margining/ # echo Y > optional_voltage_offset Signed-off-by: Rene Sapiens Co-developed-by: R Kannappan Signed-off-by: R Kannappan Co-developed-by: Aapo Vienamo Signed-off-by: Aapo Vienamo Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 74 +++++++++++++++++++++++++++++++++++ drivers/thunderbolt/sb_regs.h | 5 +++ drivers/thunderbolt/tb.h | 2 + drivers/thunderbolt/usb4.c | 4 ++ 4 files changed, 85 insertions(+) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 5d1588baea6a..9defa69ef3a7 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -394,8 +394,12 @@ static ssize_t retimer_sb_regs_write(struct file *file, * @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) + * @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 @@ -414,8 +418,11 @@ struct tb_margining { unsigned int ber_level; unsigned int voltage_steps; unsigned int max_voltage_offset; + unsigned int voltage_steps_optional_range; + unsigned int max_voltage_offset_optional_range; unsigned int time_steps; unsigned int max_time_offset; + bool optional_voltage_offset_range; bool software; bool time; bool right_high; @@ -454,6 +461,12 @@ independent_time_margins(const struct tb_margining *margining) return FIELD_GET(USB4_MARGIN_CAP_1_TIME_INDP_MASK, margining->caps[1]); } +static bool +supports_optional_voltage_offset_range(const struct tb_margining *margining) +{ + return margining->caps[0] & USB4_MARGIN_CAP_0_OPT_VOLTAGE_SUPPORT; +} + static ssize_t margining_ber_level_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -553,6 +566,14 @@ static int margining_caps_show(struct seq_file *s, void *not_used) margining->voltage_steps); seq_printf(s, "# maximum voltage offset: %u mV\n", margining->max_voltage_offset); + seq_printf(s, "# optional voltage offset range support: %s\n", + str_yes_no(supports_optional_voltage_offset_range(margining))); + if (supports_optional_voltage_offset_range(margining)) { + seq_printf(s, "# voltage margin steps, optional range: %u\n", + margining->voltage_steps_optional_range); + seq_printf(s, "# maximum voltage offset, optional range: %u mV\n", + margining->max_voltage_offset_optional_range); + } switch (independent_voltage_margins(margining)) { case USB4_MARGIN_CAP_0_VOLTAGE_MIN: @@ -667,6 +688,42 @@ static int margining_lanes_show(struct seq_file *s, void *not_used) } DEBUGFS_ATTR_RW(margining_lanes); +static ssize_t +margining_optional_voltage_offset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + bool val; + int ret; + + ret = kstrtobool_from_user(user_buf, count, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + margining->optional_voltage_offset_range = val; + } + + return count; +} + +static int margining_optional_voltage_offset_show(struct seq_file *s, + void *not_used) +{ + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + seq_printf(s, "%u\n", margining->optional_voltage_offset_range); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_optional_voltage_offset); + static ssize_t margining_mode_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -785,6 +842,7 @@ static int margining_run_write(void *data, u64 val) .lanes = margining->lanes, .time = margining->time, .right_high = margining->right_high, + .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; tb_port_dbg(port, @@ -806,6 +864,7 @@ static int margining_run_write(void *data, u64 val) .lanes = margining->lanes, .time = margining->time, .right_high = margining->right_high, + .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; /* Clear the results */ @@ -865,6 +924,8 @@ static void voltage_margin_show(struct seq_file *s, if (val & USB4_MARGIN_HW_RES_1_EXCEEDS) seq_puts(s, " exceeds maximum"); seq_puts(s, "\n"); + if (margining->optional_voltage_offset_range) + seq_puts(s, " optional voltage offset range enabled\n"); } static void time_margin_show(struct seq_file *s, @@ -1104,6 +1165,15 @@ static struct tb_margining *margining_alloc(struct tb_port *port, val = FIELD_GET(USB4_MARGIN_CAP_0_MAX_VOLTAGE_OFFSET_MASK, margining->caps[0]); margining->max_voltage_offset = 74 + val * 2; + if (supports_optional_voltage_offset_range(margining)) { + val = FIELD_GET(USB4_MARGIN_CAP_0_VOLT_STEPS_OPT_MASK, + margining->caps[0]); + margining->voltage_steps_optional_range = val; + val = FIELD_GET(USB4_MARGIN_CAP_1_MAX_VOLT_OFS_OPT_MASK, + margining->caps[1]); + margining->max_voltage_offset_optional_range = 74 + val * 2; + } + if (supports_time(margining)) { val = FIELD_GET(USB4_MARGIN_CAP_1_TIME_STEPS_MASK, margining->caps[1]); margining->time_steps = val; @@ -1140,6 +1210,10 @@ static struct tb_margining *margining_alloc(struct tb_port *port, independent_time_margins(margining) == USB4_MARGIN_CAP_1_TIME_LR)) debugfs_create_file("margin", 0600, dir, margining, &margining_margin_fops); + + if (supports_optional_voltage_offset_range(margining)) + debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining, + &margining_optional_voltage_offset_fops); return margining; } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 86e80aa297f7..8bff0740222c 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -57,6 +57,9 @@ enum usb4_sb_opcode { #define USB4_MARGIN_CAP_0_TIME BIT(5) #define USB4_MARGIN_CAP_0_VOLTAGE_STEPS_MASK GENMASK(12, 6) #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_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) #define USB4_MARGIN_CAP_1_TIME_MIN 0x0 @@ -72,6 +75,7 @@ enum usb4_sb_opcode { #define USB4_MARGIN_HW_RH BIT(4) #define USB4_MARGIN_HW_BER_MASK GENMASK(9, 5) #define USB4_MARGIN_HW_BER_SHIFT 5 +#define USB4_MARGIN_HW_OPT_VOLTAGE BIT(10) /* Applicable to all margin values */ #define USB4_MARGIN_HW_RES_1_MARGIN_MASK GENMASK(6, 0) @@ -84,6 +88,7 @@ enum usb4_sb_opcode { /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */ #define USB4_MARGIN_SW_TIME BIT(3) #define USB4_MARGIN_SW_RH BIT(4) +#define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5) #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 89ea66f885a4..262c333924b8 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1372,6 +1372,7 @@ enum usb4_margin_sw_error_counter { * @error_counter: Error counter operation for software margining * @ber_level: Current BER level contour value * @lanes: %0, %1 or %7 (all) + * @optional_voltage_offset_range: Enable optional extended voltage range * @right_high: %false if left/low margin test is performed, %true if right/high * @time: %true if time margining is used instead of voltage */ @@ -1379,6 +1380,7 @@ struct usb4_port_margining_params { enum usb4_margin_sw_error_counter error_counter; u32 ber_level; u32 lanes; + bool optional_voltage_offset_range; bool right_high; bool time; }; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index cb51cafcf20c..a2f81e17ad8d 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1676,6 +1676,8 @@ int usb4_port_hw_margin(struct tb_port *port, enum usb4_sb_target target, val |= USB4_MARGIN_HW_RH; if (params->ber_level) val |= FIELD_PREP(USB4_MARGIN_HW_BER_MASK, params->ber_level); + if (params->optional_voltage_offset_range) + val |= USB4_MARGIN_HW_OPT_VOLTAGE; ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val)); @@ -1716,6 +1718,8 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, val = params->lanes; if (params->time) val |= USB4_MARGIN_SW_TIME; + if (params->optional_voltage_offset_range) + val |= USB4_MARGIN_SW_OPT_VOLTAGE; if (params->right_high) val |= USB4_MARGIN_SW_RH; val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); From patchwork Mon Aug 19 11:40:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mika Westerberg X-Patchwork-Id: 821294 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9D37E166F3F for ; Mon, 19 Aug 2024 11:41:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067664; cv=none; b=oJ7ak1Ae2jUJHF8udfFnmUj2AMAHNFrSDIG/eCCDmxJA8hoH6AjIvhMMz87Hxddtv5vDrPgWVVXANprGbfDkNveKixF1ujz3khbet5SrG9mpawUOur18UGy7fAjk70nBleQHaBEh1LqUAJU1x4gZP8JjdbTmS/ZadXN39HOqxXU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724067664; c=relaxed/simple; bh=cLAPe3F3bdIqxPk8iGfLAbChAAB+JzuT3yBe/n0kAiI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ALiReoCkuh6s5X2TpmoCFkckeBjy1BLJ8UPlBiLBJ/M18qqcIFGOAecqCH7bJItWG4h7ayEFHFOsWy2ZxpnIPJdDGsNtMeecIsZ8Kz6mm/V/JIhoO1B2/PZqlpWQCacuzbmgKYp/Ehrn2ri/mVo1ctx3H8VG4f80F3KG+YfY3wE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=none smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=IjB2ZW3l; arc=none smtp.client-ip=198.175.65.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="IjB2ZW3l" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1724067662; x=1755603662; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=cLAPe3F3bdIqxPk8iGfLAbChAAB+JzuT3yBe/n0kAiI=; b=IjB2ZW3lLsrPW0gusi6pkbX1lrdz7r3UONQLJJtSFxTD5xOKaRn4olQ+ RTw68QW4FT4py07YnhVcEBBeUmxPHWJZSyBqP5G/f7pV1NSesGkyS/yav L54BCBKoW+tnsRRtiu/iuW1gkIz0eBHc16fcPqlT0E6S+ArWxc2GofqdZ NCm6A6rrvt4ZUP3bodQ4vC7HlL8GAUGSWOuYBA2goCfTe7GYCxiQWrg2w KZKpOie9o57pH1fNnuxjJhreZxbi9qWyF68+n4q/nWtBZv0DEdhlZtWz8 HP3eZbz/kyF8hpLmN4z2cHlubCda/sPBI/KN6omhPkdwenwhvxl0MmYvC w==; X-CSE-ConnectionGUID: oUsZt3sqRTuXdRQPBhGBQw== X-CSE-MsgGUID: 8R0rLe/5QiiUeTMU/Qx/xw== X-IronPort-AV: E=McAfee;i="6700,10204,11168"; a="33688831" X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="33688831" Received: from fmviesa002.fm.intel.com ([10.60.135.142]) by orvoesa104.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Aug 2024 04:40:59 -0700 X-CSE-ConnectionGUID: e/uSW8zATXWyCi9fGflzLw== X-CSE-MsgGUID: xWBAV5u8Qj++WdsRFwxpsg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,159,1719903600"; d="scan'208";a="83566114" Received: from black.fi.intel.com ([10.237.72.28]) by fmviesa002.fm.intel.com with ESMTP; 19 Aug 2024 04:40:55 -0700 Received: by black.fi.intel.com (Postfix, from userid 1001) id B903D644; Mon, 19 Aug 2024 14:40:54 +0300 (EEST) From: Mika Westerberg To: Yehezkel Bernat , Michael Jamet , Lukas Wunner , Andreas Noever Cc: linux-usb@vger.kernel.org, Aapo Vienamo , Rene Sapiens , R Kannappan , Dan Carpenter , Mika Westerberg Subject: [PATCH v2 4/4] thunderbolt: Improve software receiver lane margining Date: Mon, 19 Aug 2024 14:40:54 +0300 Message-ID: <20240819114054.4139941-5-mika.westerberg@linux.intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> References: <20240819114054.4139941-1-mika.westerberg@linux.intel.com> Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: R Kannappan USB4 specification defines the metadata needed to perform software margining, as well as the necessary steps which include waiting for dwell time. - Add dwell_time attribute to set the wait time while performing margining and checking for link errors. - Add error_counter attribute to configure error counter prior to margining test. - Add voltage_time_offset attribute to set the voltage or time offset steps before performing the software margining test. - Perform software margining test for dwell duration, break if there are link errors, stop the clocks and provide results. Below is a minimalistic example how this can be used. Note these values are just examples. The exact values in practice depend on host specific capabilities and the type of measurement to be performed. # cd /sys/kernel/debug/thunderbolt/ROUTER/portX/margining/ # echo software > mode # echo 400 > dwell_time # echo 1 > run As usual the results attribute contains the results of a succesfull run. Signed-off-by: R Kannappan Co-developed-by: Rene Sapiens Signed-off-by: Rene Sapiens Co-developed-by: Aapo Vienamo Signed-off-by: Aapo Vienamo Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 295 ++++++++++++++++++++++++++++++++-- drivers/thunderbolt/sb_regs.h | 8 + drivers/thunderbolt/tb.h | 2 + drivers/thunderbolt/usb4.c | 1 + 4 files changed, 290 insertions(+), 16 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 9defa69ef3a7..350310bd0fee 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -34,6 +35,14 @@ #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 + /* Sideband registers and their sizes as defined in the USB4 spec */ struct sb_reg { unsigned int reg; @@ -399,6 +408,9 @@ static ssize_t retimer_sb_regs_write(struct file *file, * 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 @@ -422,12 +434,33 @@ struct tb_margining { unsigned int max_voltage_offset_optional_range; unsigned int time_steps; unsigned int max_time_offset; + unsigned int voltage_time_offset; + unsigned int dwell_time; + enum usb4_margin_sw_error_counter error_counter; bool optional_voltage_offset_range; bool software; bool time; bool right_high; }; +static int margining_modify_error_counter(struct tb_margining *margining, + u32 lanes, enum usb4_margin_sw_error_counter error_counter) +{ + struct usb4_port_margining_params params = { 0 }; + struct tb_port *port = margining->port; + u32 result; + + if (error_counter != USB4_MARGIN_SW_ERROR_COUNTER_CLEAR && + error_counter != USB4_MARGIN_SW_ERROR_COUNTER_STOP) + return -EOPNOTSUPP; + + params.error_counter = error_counter; + params.lanes = lanes; + + return usb4_port_sw_margin(port, margining->target, margining->index, + ¶ms, &result); +} + static bool supports_software(const struct tb_margining *margining) { return margining->caps[0] & USB4_MARGIN_CAP_0_MODES_SW; @@ -689,9 +722,165 @@ static int margining_lanes_show(struct seq_file *s, void *not_used) DEBUGFS_ATTR_RW(margining_lanes); static ssize_t -margining_optional_voltage_offset_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +margining_voltage_time_offset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + unsigned int max_margin; + unsigned int val; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 10, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + if (margining->time) + max_margin = margining->time_steps; + else + if (margining->optional_voltage_offset_range) + max_margin = margining->voltage_steps_optional_range; + else + max_margin = margining->voltage_steps; + + margining->voltage_time_offset = clamp(val, 0, max_margin); + } + + return count; +} + +static int margining_voltage_time_offset_show(struct seq_file *s, + void *not_used) +{ + const struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + seq_printf(s, "%d\n", margining->voltage_time_offset); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_voltage_time_offset); + +static ssize_t +margining_error_counter_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + enum usb4_margin_sw_error_counter error_counter; + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + char *buf; + + buf = validate_and_copy_from_user(user_buf, &count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + buf[count - 1] = '\0'; + + if (!strcmp(buf, "nop")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_NOP; + else if (!strcmp(buf, "clear")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; + else if (!strcmp(buf, "start")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_START; + else if (!strcmp(buf, "stop")) + error_counter = USB4_MARGIN_SW_ERROR_COUNTER_STOP; + else + return -EINVAL; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + margining->error_counter = error_counter; + } + + return count; +} + +static int margining_error_counter_show(struct seq_file *s, void *not_used) +{ + const struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + switch (margining->error_counter) { + case USB4_MARGIN_SW_ERROR_COUNTER_NOP: + seq_puts(s, "[nop] clear start stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_CLEAR: + seq_puts(s, "nop [clear] start stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_START: + seq_puts(s, "nop clear [start] stop\n"); + break; + case USB4_MARGIN_SW_ERROR_COUNTER_STOP: + seq_puts(s, "nop clear start [stop]\n"); + break; + } + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_error_counter); + +static ssize_t +margining_dwell_time_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + unsigned int val; + int ret; + + ret = kstrtouint_from_user(user_buf, count, 10, &val); + if (ret) + return ret; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + margining->dwell_time = clamp(val, MIN_DWELL_TIME, MAX_DWELL_TIME); + } + + return count; +} + +static int margining_dwell_time_show(struct seq_file *s, void *not_used) +{ + struct tb_margining *margining = s->private; + struct tb *tb = margining->port->sw->tb; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &tb->lock) { + if (!margining->software) + return -EOPNOTSUPP; + + seq_printf(s, "%d\n", margining->dwell_time); + } + + return 0; +} +DEBUGFS_ATTR_RW(margining_dwell_time); + +static ssize_t +margining_optional_voltage_offset_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct seq_file *s = file->private_data; struct tb_margining *margining = s->private; @@ -796,6 +985,51 @@ static int margining_mode_show(struct seq_file *s, void *not_used) } DEBUGFS_ATTR_RW(margining_mode); +static int margining_run_sw(struct tb_margining *margining, + struct usb4_port_margining_params *params) +{ + u32 nsamples = margining->dwell_time / DWELL_SAMPLE_INTERVAL; + int ret, i; + + ret = usb4_port_sw_margin(margining->port, margining->target, margining->index, + params, margining->results); + if (ret) + goto out_stop; + + for (i = 0; i <= nsamples; i++) { + u32 errors = 0; + + ret = usb4_port_sw_margin_errors(margining->port, margining->target, + margining->index, &margining->results[1]); + if (ret) + break; + + if (margining->lanes == USB4_MARGIN_SW_LANE_0) + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, + margining->results[1]); + else if (margining->lanes == USB4_MARGIN_SW_LANE_1) + errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, + margining->results[1]); + else if (margining->lanes == USB4_MARGIN_SW_ALL_LANES) + errors = margining->results[1]; + + /* Any errors stop the test */ + if (errors) + break; + + fsleep(DWELL_SAMPLE_INTERVAL * USEC_PER_MSEC); + } + +out_stop: + /* + * Stop the counters but don't clear them to allow the + * different error counter configurations. + */ + margining_modify_error_counter(margining, margining->lanes, + USB4_MARGIN_SW_ERROR_COUNTER_STOP); + return ret; +} + static int margining_run_write(void *data, u64 val) { struct tb_margining *margining = data; @@ -836,11 +1070,15 @@ static int margining_run_write(void *data, u64 val) clx = ret; } + /* Clear the results */ + memset(margining->results, 0, sizeof(margining->results)); + if (margining->software) { struct usb4_port_margining_params params = { .error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR, .lanes = margining->lanes, .time = margining->time, + .voltage_time_offset = margining->voltage_time_offset, .right_high = margining->right_high, .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; @@ -850,14 +1088,7 @@ static int margining_run_write(void *data, u64 val) margining->time ? "time" : "voltage", dev_name(dev), margining->lanes); - ret = usb4_port_sw_margin(port, margining->target, margining->index, ¶ms, - &margining->results[0]); - if (ret) - goto out_clx; - - ret = usb4_port_sw_margin_errors(port, margining->target, - margining->index, - &margining->results[0]); + ret = margining_run_sw(margining, ¶ms); } else { struct usb4_port_margining_params params = { .ber_level = margining->ber_level, @@ -867,10 +1098,6 @@ static int margining_run_write(void *data, u64 val) .optional_voltage_offset_range = margining->optional_voltage_offset_range, }; - /* Clear the results */ - margining->results[0] = 0; - margining->results[1] = 0; - tb_port_dbg(port, "running hardware %s lane margining for %s lanes %u\n", margining->time ? "time" : "voltage", dev_name(dev), @@ -880,7 +1107,6 @@ static int margining_run_write(void *data, u64 val) margining->results); } -out_clx: if (down_sw) tb_switch_clx_enable(down_sw, clx); out_unlock: @@ -909,6 +1135,13 @@ static ssize_t margining_results_write(struct file *file, margining->results[0] = 0; margining->results[1] = 0; + if (margining->software) { + /* Clear the error counters */ + margining_modify_error_counter(margining, + USB4_MARGIN_SW_ALL_LANES, + USB4_MARGIN_SW_ERROR_COUNTER_CLEAR); + } + mutex_unlock(&tb->lock); return count; } @@ -998,6 +1231,24 @@ static int margining_results_show(struct seq_file *s, void *not_used) voltage_margin_show(s, margining, val); } } + } else { + u32 lane_errors, result; + + seq_printf(s, "0x%08x\n", margining->results[1]); + result = FIELD_GET(USB4_MARGIN_SW_LANES_MASK, margining->results[0]); + + if (result == USB4_MARGIN_SW_LANE_0 || + result == USB4_MARGIN_SW_ALL_LANES) { + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK, + margining->results[1]); + seq_printf(s, "# lane 0 errors: %u\n", lane_errors); + } + if (result == USB4_MARGIN_SW_LANE_1 || + result == USB4_MARGIN_SW_ALL_LANES) { + lane_errors = FIELD_GET(USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK, + margining->results[1]); + seq_printf(s, "# lane 1 errors: %u\n", lane_errors); + } } mutex_unlock(&tb->lock); @@ -1211,9 +1462,21 @@ static struct tb_margining *margining_alloc(struct tb_port *port, debugfs_create_file("margin", 0600, dir, margining, &margining_margin_fops); + margining->error_counter = USB4_MARGIN_SW_ERROR_COUNTER_CLEAR; + margining->dwell_time = MIN_DWELL_TIME; + if (supports_optional_voltage_offset_range(margining)) debugfs_create_file("optional_voltage_offset", DEBUGFS_MODE, dir, margining, &margining_optional_voltage_offset_fops); + + if (supports_software(margining)) { + debugfs_create_file("voltage_time_offset", DEBUGFS_MODE, dir, margining, + &margining_voltage_time_offset_fops); + debugfs_create_file("error_counter", DEBUGFS_MODE, dir, margining, + &margining_error_counter_fops); + debugfs_create_file("dwell_time", DEBUGFS_MODE, dir, margining, + &margining_dwell_time_fops); + } return margining; } diff --git a/drivers/thunderbolt/sb_regs.h b/drivers/thunderbolt/sb_regs.h index 8bff0740222c..dbcad25ead50 100644 --- a/drivers/thunderbolt/sb_regs.h +++ b/drivers/thunderbolt/sb_regs.h @@ -86,9 +86,17 @@ enum usb4_sb_opcode { #define USB4_MARGIN_HW_RES_1_L1_LL_MARGIN_SHIFT 24 /* USB4_SB_OPCODE_RUN_SW_LANE_MARGINING */ +#define USB4_MARGIN_SW_LANES_MASK GENMASK(2, 0) +#define USB4_MARGIN_SW_LANE_0 0x0 +#define USB4_MARGIN_SW_LANE_1 0x1 +#define USB4_MARGIN_SW_ALL_LANES 0x7 #define USB4_MARGIN_SW_TIME BIT(3) #define USB4_MARGIN_SW_RH BIT(4) #define USB4_MARGIN_SW_OPT_VOLTAGE BIT(5) +#define USB4_MARGIN_SW_VT_MASK GENMASK(12, 6) #define USB4_MARGIN_SW_COUNTER_MASK GENMASK(14, 13) +#define USB4_MARGIN_SW_ERR_COUNTER_LANE_0_MASK GENMASK(3, 0) +#define USB4_MARGIN_SW_ERR_COUNTER_LANE_1_MASK GENMASK(7, 4) + #endif diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 262c333924b8..6737188f2581 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1372,6 +1372,7 @@ enum usb4_margin_sw_error_counter { * @error_counter: Error counter operation for software margining * @ber_level: Current BER level contour value * @lanes: %0, %1 or %7 (all) + * @voltage_time_offset: Offset for voltage / time for software margining * @optional_voltage_offset_range: Enable optional extended voltage range * @right_high: %false if left/low margin test is performed, %true if right/high * @time: %true if time margining is used instead of voltage @@ -1380,6 +1381,7 @@ struct usb4_port_margining_params { enum usb4_margin_sw_error_counter error_counter; u32 ber_level; u32 lanes; + u32 voltage_time_offset; bool optional_voltage_offset_range; bool right_high; bool time; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index a2f81e17ad8d..0a9b4aeb3fa1 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -1723,6 +1723,7 @@ int usb4_port_sw_margin(struct tb_port *port, enum usb4_sb_target target, if (params->right_high) val |= USB4_MARGIN_SW_RH; val |= FIELD_PREP(USB4_MARGIN_SW_COUNTER_MASK, params->error_counter); + val |= FIELD_PREP(USB4_MARGIN_SW_VT_MASK, params->voltage_time_offset); ret = usb4_port_sb_write(port, target, index, USB4_SB_METADATA, &val, sizeof(val));