From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Gil Fine <gil.fine@intel.com>
Cc: andreas.noever@gmail.com, michael.jamet@intel.com,
YehezkelShB@gmail.com, linux-usb@vger.kernel.org,
lukas@wunner.de
Subject: Re: [PATCH 1/7] thunderbolt: Add TMU unidirectional mode
Date: Mon, 29 Nov 2021 10:19:39 +0200 [thread overview]
Message-ID: <YaSNGzpGXIWN4fgh@lahna> (raw)
In-Reply-To: <20211125143821.16558-2-gil.fine@intel.com>
Hi,
On Thu, Nov 25, 2021 at 04:38:15PM +0200, Gil Fine wrote:
> Up until Titan Ridge (Thunderbolt 3) device, routers only supported
> bidirectional mode. In this patch, we add to TMU, a unidirectional mode.
> Unidirectional mode shall be used for enabling of low power state of the link.
Probably should mention here that this is needed to enable CLx-states.
> The Clx enabling is implemented in the next patch of this series.
This line is not necessary.
> Signed-off-by: Gil Fine <gil.fine@intel.com>
> ---
> drivers/thunderbolt/tb.c | 9 +-
> drivers/thunderbolt/tb.h | 24 ++--
> drivers/thunderbolt/tb_regs.h | 2 +
> drivers/thunderbolt/tmu.c | 244 ++++++++++++++++++++++++++++------
> 4 files changed, 231 insertions(+), 48 deletions(-)
>
> diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
> index a231191b06c6..533fe48e85be 100644
> --- a/drivers/thunderbolt/tb.c
> +++ b/drivers/thunderbolt/tb.c
> @@ -221,7 +221,7 @@ static int tb_enable_tmu(struct tb_switch *sw)
> int ret;
>
> /* If it is already enabled in correct mode, don't touch it */
> - if (tb_switch_tmu_is_enabled(sw))
> + if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirect_request))
> return 0;
>
> ret = tb_switch_tmu_disable(sw);
> @@ -669,6 +669,7 @@ static void tb_scan_port(struct tb_port *port)
> tb_switch_lane_bonding_enable(sw);
> /* Set the link configured */
> tb_switch_configure_link(sw);
> + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false);
>
> if (tb_enable_tmu(sw))
> tb_sw_warn(sw, "failed to enable TMU\n");
> @@ -1375,6 +1376,7 @@ static int tb_start(struct tb *tb)
> return ret;
> }
>
> + tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI, false);
> /* Enable TMU if it is off */
> tb_switch_tmu_enable(tb->root_switch);
> /* Full scan to discover devices added before the driver was loaded. */
> @@ -1418,6 +1420,11 @@ static void tb_restore_children(struct tb_switch *sw)
> if (sw->is_unplugged)
> return;
>
> + /*
> + * tb_switch_tmu_configure() was already called when the switch was
> + * added before entering SX/RPM, so no need to call it again
Nit: I suggest using "system sleep" and "runtime suspend" here or
similar.
> + * before enabling TMU.
> + */
> if (tb_enable_tmu(sw))
> tb_sw_warn(sw, "failed to restore TMU configuration\n");
>
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index 3fae40670b72..0205361ff89a 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -89,15 +89,24 @@ enum tb_switch_tmu_rate {
> * @cap: Offset to the TMU capability (%0 if not found)
> * @has_ucap: Does the switch support uni-directional mode
> * @rate: TMU refresh rate related to upstream switch. In case of root
> - * switch this holds the domain rate.
> + * switch this holds the domain rate. Reflects the HW setting.
> * @unidirectional: Is the TMU in uni-directional or bi-directional mode
> - * related to upstream switch. Don't case for root switch.
> + * related to upstream switch. Don't care for root switch.
> + * Reflects the HW setting.
> + * @rate_request: TMU new refresh rate related to upstream switch that is
> + * requested to be set. In case of root switch, this holds
> + * the new domain rate that is requested to be set.
> + * @unidirect_request: Is the new TMU mode: uni-directional or bi-directional
> + * that is requested to be set. Related to upstream switch.
> + * Don't care for root switch.
> */
> struct tb_switch_tmu {
> int cap;
> bool has_ucap;
> enum tb_switch_tmu_rate rate;
> bool unidirectional;
> + bool unidirect_request;
> + enum tb_switch_tmu_rate rate_request;
> };
>
> /**
> @@ -891,13 +900,10 @@ int tb_switch_tmu_init(struct tb_switch *sw);
> int tb_switch_tmu_post_time(struct tb_switch *sw);
> int tb_switch_tmu_disable(struct tb_switch *sw);
> int tb_switch_tmu_enable(struct tb_switch *sw);
> -
> -static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw)
> -{
> - return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI &&
> - !sw->tmu.unidirectional;
> -}
> -
> +bool tb_switch_tmu_hifi_is_enabled(struct tb_switch *sw, bool unidirect);
> +void tb_switch_tmu_configure(struct tb_switch *sw,
> + enum tb_switch_tmu_rate rate,
> + bool unidirectional);
> int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
> int tb_port_add_nfc_credits(struct tb_port *port, int credits);
> int tb_port_clear_counter(struct tb_port *port, int counter);
> diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
> index d8ab6c820451..38bc15680d06 100644
> --- a/drivers/thunderbolt/tb_regs.h
> +++ b/drivers/thunderbolt/tb_regs.h
> @@ -305,6 +305,8 @@ struct tb_regs_port_header {
> /* TMU adapter registers */
> #define TMU_ADP_CS_3 0x03
> #define TMU_ADP_CS_3_UDM BIT(29)
> +#define TMU_ADP_CS_6 0x06
> +#define TMU_ADP_CS_6_DTS BIT(1)
>
> /* Lane adapter registers */
> #define LANE_ADP_CS_0 0x00
> diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c
> index 039c42a06000..1374813bfc5c 100644
> --- a/drivers/thunderbolt/tmu.c
> +++ b/drivers/thunderbolt/tmu.c
> @@ -115,6 +115,11 @@ static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port)
> return tb_port_tmu_set_unidirectional(port, false);
> }
>
> +static inline int tb_port_tmu_unidirectional_enable(struct tb_port *port)
> +{
> + return tb_port_tmu_set_unidirectional(port, true);
> +}
> +
> static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
> {
> int ret;
> @@ -128,6 +133,23 @@ static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
> return val & TMU_ADP_CS_3_UDM;
> }
>
> +static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync)
> +{
> + u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0;
> +
> + return tb_port_tmu_write(port, TMU_ADP_CS_6, TMU_ADP_CS_6_DTS, val);
> +}
> +
> +static int tb_port_tmu_time_sync_disable(struct tb_port *port)
> +{
> + return tb_port_tmu_time_sync(port, true);
> +}
> +
> +static int tb_port_tmu_time_sync_enable(struct tb_port *port)
> +{
> + return tb_port_tmu_time_sync(port, false);
> +}
> +
> static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set)
> {
> int ret;
> @@ -297,6 +319,9 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
> */
> int tb_switch_tmu_disable(struct tb_switch *sw)
> {
> + struct tb_switch *sw_conf, *parent = tb_switch_parent(sw);
Can you drop this line..
> + bool unidirect = tb_switch_tmu_hifi_is_enabled(sw, true);
> + struct tb_port *up, *down;
> int ret;
>
> if (!tb_switch_is_usb4(sw))
> @@ -306,21 +331,28 @@ int tb_switch_tmu_disable(struct tb_switch *sw)
> if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF)
> return 0;
>
> - if (sw->tmu.unidirectional) {
> - struct tb_switch *parent = tb_switch_parent(sw);
> - struct tb_port *up, *down;
> + up = tb_upstream_port(sw);
> + down = tb_port_at(tb_route(sw), parent);
>
> - up = tb_upstream_port(sw);
> - down = tb_port_at(tb_route(sw), parent);
> + if (tb_route(sw)) {
> + sw_conf = unidirect ? parent : sw;
> + tb_switch_tmu_rate_write(sw_conf, TB_SWITCH_TMU_RATE_OFF);
.. and here do
if (unidirect)
tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
else
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
Also add a comment why we need to configure it in the parent when the
TMU mode is unidirectional.
>
> - /* The switch may be unplugged so ignore any errors */
> - tb_port_tmu_unidirectional_disable(up);
> - ret = tb_port_tmu_unidirectional_disable(down);
> + tb_port_tmu_time_sync_disable(up);
> + ret = tb_port_tmu_time_sync_disable(down);
> if (ret)
> return ret;
> - }
>
> - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
> + if (unidirect) {
> + /* The switch may be unplugged so ignore any errors */
> + tb_port_tmu_unidirectional_disable(up);
> + ret = tb_port_tmu_unidirectional_disable(down);
> + if (ret)
> + return ret;
> + }
> + } else {
> + tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
> + }
>
> sw->tmu.unidirectional = false;
> sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
> @@ -329,55 +361,191 @@ int tb_switch_tmu_disable(struct tb_switch *sw)
> return 0;
> }
>
> -/**
> - * tb_switch_tmu_enable() - Enable TMU on a switch
> - * @sw: Switch whose TMU to enable
> - *
> - * Enables TMU of a switch to be in bi-directional, HiFi mode. In this mode
> - * all tunneling should work.
> +/*
> + * This function is called when the previous TMU mode was
> + * TB_SWITCH_TMU_RATE_OFF
> */
> -int tb_switch_tmu_enable(struct tb_switch *sw)
> +static int __tb_switch_tmu_enable_bidir(struct tb_switch *sw)
> {
> + struct tb_switch *parent = tb_switch_parent(sw);
> + struct tb_port *up, *down;
> int ret;
>
> - if (!tb_switch_is_usb4(sw))
> - return 0;
> + up = tb_upstream_port(sw);
> + down = tb_port_at(tb_route(sw), parent);
>
> - if (tb_switch_tmu_is_enabled(sw))
> - return 0;
> + ret = tb_port_tmu_unidirectional_enable(up);
> + if (ret)
> + return ret;
>
> - ret = tb_switch_tmu_set_time_disruption(sw, true);
> + ret = tb_port_tmu_unidirectional_enable(down);
> + if (ret)
> + goto out;
> +
> + ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI);
> + if (ret)
> + goto out;
> +
> + ret = tb_port_tmu_time_sync_enable(up);
> + if (ret)
> + goto out;
> +
> + ret = tb_port_tmu_time_sync_enable(down);
> + if (ret)
> + goto out;
> +
> + return 0;
> +out:
> + /*
> + * In case of any failure in one of the steps, get back to the
> + * TMU configurations in OFF mode. In case of additional failures in
> + * the functions below, ignore them since we already report a failure.
> + */
> + tb_port_tmu_time_sync_disable(up);
> + tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
> + tb_port_tmu_unidirectional_disable(down);
> + tb_port_tmu_unidirectional_disable(up);
Empty line here.
> + return ret;
> +}
> +
> +/*
> + * This function is called when the previous TMU mode was
> + * TB_SWITCH_TMU_RATE_OFF
Add ending '.' to the sentence.
> + */
> +static int __tb_switch_tmu_enable_uni(struct tb_switch *sw)
> +{
> + struct tb_switch *parent = tb_switch_parent(sw);
> + struct tb_port *up, *down;
> + int ret;
> +
> + up = tb_upstream_port(sw);
> + down = tb_port_at(tb_route(sw), parent);
> + ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI);
> if (ret)
> return ret;
>
> - /* Change mode to bi-directional */
> - if (tb_route(sw) && sw->tmu.unidirectional) {
> - struct tb_switch *parent = tb_switch_parent(sw);
> - struct tb_port *up, *down;
> + ret = tb_port_tmu_unidirectional_enable(up);
> + if (ret)
> + goto out;
> +
> + ret = tb_port_tmu_time_sync_enable(up);
> + if (ret)
> + goto out;
>
> - up = tb_upstream_port(sw);
> - down = tb_port_at(tb_route(sw), parent);
> + ret = tb_port_tmu_unidirectional_enable(down);
> + if (ret)
> + goto out;
>
> - ret = tb_port_tmu_unidirectional_disable(down);
> - if (ret)
> - return ret;
> + ret = tb_port_tmu_time_sync_enable(down);
> + if (ret)
> + goto out;
>
> - ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
> - if (ret)
> - return ret;
> + return 0;
> +out:
> + /*
> + * In case of any failure in one of the steps, get back to the
> + * TMU configurations in OFF mode. In case of additional failures in
> + * the functions below, ignore them since we already report a failure.
> + */
> + tb_port_tmu_unidirectional_disable(down);
> + tb_port_tmu_time_sync_disable(up);
> + tb_port_tmu_unidirectional_disable(up);
> + tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
Empty line.
I wonder if it is better to separate these into own function. So you
don't need to duplicate them in error paths.
> + return ret;
> +}
>
> - ret = tb_port_tmu_unidirectional_disable(up);
> - if (ret)
> - return ret;
> +static int tb_switch_tmu_hifi_enable(struct tb_switch *sw)
> +{
> + bool unidirect = sw->tmu.unidirect_request;
> + int ret;
> +
> + if (unidirect && !sw->tmu.has_ucap)
> + return -EOPNOTSUPP;
> +
> + if (!tb_switch_is_usb4(sw))
> + return 0;
> +
> + if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirect_request))
> + return 0;
> +
> + ret = tb_switch_tmu_set_time_disruption(sw, true);
> + if (ret)
> + return ret;
> +
> + if (tb_route(sw)) {
> + /* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */
> + if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) {
> + ret = unidirect ? __tb_switch_tmu_enable_uni(sw)
> + : __tb_switch_tmu_enable_bidir(sw);
> + if (ret)
> + return ret;
I think it is better if you avoid the ternary operator here:
if (unidirect)
ret = __tb_switch_tmu_enable_uni(sw);
else
ret = __tb_switch_tmu_enable_bidir(sw);
if (ret)
return ret;
> + }
> + sw->tmu.unidirectional = unidirect;
> } else {
> + /*
> + * Host-router port configurations are written as
Host router
ditto everywhere.
> + * configurations for down-stream port of the parent of the
downstream
> + * child node - see above.
> + * Here only the host's router rate configuration is written
host routers'
> + */
> ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
> if (ret)
> return ret;
> }
>
> - sw->tmu.unidirectional = false;
> sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
> tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
>
> return tb_switch_tmu_set_time_disruption(sw, false);
> }
> +
> +/**
> + * tb_switch_tmu_enable() - Enable TMU on a switch
> + * @sw: Switch whose TMU to enable
> + *
> + * Enables TMU of a switch to be in unidirectional or bidirectional HiFi mode.
> + * Calling tb_switch_tmu_configure() is required before calling this function,
> + * to select the mode HiFi and directionality (unidirectional/bidirectional).
> + * In both modes all tunneling should work. Unidirectional mode is required for
> + * CLx (Link Low-Power) to work. LowRes mode is not used currently.
> + */
> +int tb_switch_tmu_enable(struct tb_switch *sw)
> +{
> + if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL)
> + return -EOPNOTSUPP;
> +
> + return tb_switch_tmu_hifi_enable(sw);
> +}
> +
> +/**
> + * tb_switch_tmu_configure() - Configure the TMU rate and directionality
> + * @sw: Switch whose mode to change
> + * @rate: Rate to configure Off/LowRes/HiFi
> + * @unidirectional: Unidirectionality selection: Unidirectional or Bidirectional
> + *
> + * Selects the rate of the TMU (Off, LowRes, HiFi), and Directionality
> + * (Unidirectional or Bidirectional)
> + * Shall be called before tb_switch_tmu_enable()
> + */
> +void tb_switch_tmu_configure(struct tb_switch *sw,
> + enum tb_switch_tmu_rate rate, bool unidirectional)
> +{
> + sw->tmu.unidirect_request = unidirectional;
> + sw->tmu.rate_request = rate;
> +}
> +
> +/**
> + * tb_switch_tmu_hifi_is_enabled() - Checks if the specified TMU mode
> + * bidir/uni enabled correctly
> + * @sw: Switch whose TMU mode to check
> + * @unidirect: Select bidirectional or unidirectional mode to check
> + *
> + * Read TMU directionality and rate from HW, and return true,
> + * if matches to bidirectional/unidirectional HiFi mode settings.
> + * Otherwise returns false.
> + */
> +bool tb_switch_tmu_hifi_is_enabled(struct tb_switch *sw, bool unidirect)
> +{
> + return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI &&
> + sw->tmu.unidirectional == unidirect;
> +}
> --
> 2.17.1
next prev parent reply other threads:[~2021-11-29 8:22 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-25 14:38 [PATCH 0/7] thunderbolt: CLx support for USB4 and Titan Ridge Gil Fine
2021-11-25 14:38 ` [PATCH 1/7] thunderbolt: Add TMU unidirectional mode Gil Fine
2021-11-26 11:58 ` Yehezkel Bernat
2021-11-29 8:19 ` Mika Westerberg [this message]
2021-11-25 14:38 ` [PATCH 2/7] thunderbolt: Add CL0s support for USB4 Gil Fine
2021-11-29 8:38 ` Mika Westerberg
2021-11-25 14:38 ` [PATCH 3/7] thunderbolt: Move usb4_switch_wait_for_bit() to switch.c Gil Fine
2021-11-25 14:38 ` [PATCH 4/7] thunderbolt: Enable TMU for Titan Ridge device Gil Fine
2021-11-25 14:38 ` [PATCH 5/7] thunderbolt: Rename Intel VSC capability Gil Fine
2021-11-25 14:38 ` [PATCH 6/7] thunderbolt: Enable CLx for Titan Ridge device Gil Fine
2021-11-29 8:28 ` Mika Westerberg
2021-11-25 14:38 ` [PATCH 7/7] thunderbolt: Add kernel param for CLx disabling Gil Fine
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YaSNGzpGXIWN4fgh@lahna \
--to=mika.westerberg@linux.intel.com \
--cc=YehezkelShB@gmail.com \
--cc=andreas.noever@gmail.com \
--cc=gil.fine@intel.com \
--cc=linux-usb@vger.kernel.org \
--cc=lukas@wunner.de \
--cc=michael.jamet@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox