thunderbolt: Add TMU uni-directional mode
Up until Titan Ridge (Thunderbolt 3) device routers only supported bi-directional mode. In this patch we add to TMU a uni-directional mode. The uni-directional mode is needed for enabling of low power state of the link (CLx). Signed-off-by: Gil Fine <gil.fine@intel.com> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Родитель
3cc1c6de45
Коммит
a28ec0e165
|
@ -221,7 +221,7 @@ static int tb_enable_tmu(struct tb_switch *sw)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* If it is already enabled in correct mode, don't touch it */
|
/* 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.unidirectional_request))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = tb_switch_tmu_disable(sw);
|
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);
|
tb_switch_lane_bonding_enable(sw);
|
||||||
/* Set the link configured */
|
/* Set the link configured */
|
||||||
tb_switch_configure_link(sw);
|
tb_switch_configure_link(sw);
|
||||||
|
tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false);
|
||||||
|
|
||||||
if (tb_enable_tmu(sw))
|
if (tb_enable_tmu(sw))
|
||||||
tb_sw_warn(sw, "failed to enable TMU\n");
|
tb_sw_warn(sw, "failed to enable TMU\n");
|
||||||
|
@ -1375,6 +1376,7 @@ static int tb_start(struct tb *tb)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI, false);
|
||||||
/* Enable TMU if it is off */
|
/* Enable TMU if it is off */
|
||||||
tb_switch_tmu_enable(tb->root_switch);
|
tb_switch_tmu_enable(tb->root_switch);
|
||||||
/* Full scan to discover devices added before the driver was loaded. */
|
/* 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)
|
if (sw->is_unplugged)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tb_switch_tmu_configure() was already called when the switch was
|
||||||
|
* added before entering system sleep or runtime suspend,
|
||||||
|
* so no need to call it again before enabling TMU.
|
||||||
|
*/
|
||||||
if (tb_enable_tmu(sw))
|
if (tb_enable_tmu(sw))
|
||||||
tb_sw_warn(sw, "failed to restore TMU configuration\n");
|
tb_sw_warn(sw, "failed to restore TMU configuration\n");
|
||||||
|
|
||||||
|
|
|
@ -89,15 +89,24 @@ enum tb_switch_tmu_rate {
|
||||||
* @cap: Offset to the TMU capability (%0 if not found)
|
* @cap: Offset to the TMU capability (%0 if not found)
|
||||||
* @has_ucap: Does the switch support uni-directional mode
|
* @has_ucap: Does the switch support uni-directional mode
|
||||||
* @rate: TMU refresh rate related to upstream switch. In case of root
|
* @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
|
* @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.
|
||||||
|
* @unidirectional_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.
|
||||||
|
* @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.
|
||||||
*/
|
*/
|
||||||
struct tb_switch_tmu {
|
struct tb_switch_tmu {
|
||||||
int cap;
|
int cap;
|
||||||
bool has_ucap;
|
bool has_ucap;
|
||||||
enum tb_switch_tmu_rate rate;
|
enum tb_switch_tmu_rate rate;
|
||||||
bool unidirectional;
|
bool unidirectional;
|
||||||
|
bool unidirectional_request;
|
||||||
|
enum tb_switch_tmu_rate rate_request;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -891,11 +900,22 @@ int tb_switch_tmu_init(struct tb_switch *sw);
|
||||||
int tb_switch_tmu_post_time(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_disable(struct tb_switch *sw);
|
||||||
int tb_switch_tmu_enable(struct tb_switch *sw);
|
int tb_switch_tmu_enable(struct tb_switch *sw);
|
||||||
|
void tb_switch_tmu_configure(struct tb_switch *sw,
|
||||||
static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw)
|
enum tb_switch_tmu_rate rate,
|
||||||
|
bool unidirectional);
|
||||||
|
/**
|
||||||
|
* tb_switch_tmu_hifi_is_enabled() - Checks if the specified TMU mode is enabled
|
||||||
|
* @sw: Router whose TMU mode to check
|
||||||
|
* @unidirectional: If uni-directional (bi-directional otherwise)
|
||||||
|
*
|
||||||
|
* Return true if hardware TMU configuration matches the one passed in
|
||||||
|
* as parameter. That is HiFi and either uni-directional or bi-directional.
|
||||||
|
*/
|
||||||
|
static inline bool tb_switch_tmu_hifi_is_enabled(const struct tb_switch *sw,
|
||||||
|
bool unidirectional)
|
||||||
{
|
{
|
||||||
return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI &&
|
return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI &&
|
||||||
!sw->tmu.unidirectional;
|
sw->tmu.unidirectional == unidirectional;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
|
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
|
||||||
|
|
|
@ -246,6 +246,7 @@ enum usb4_switch_op {
|
||||||
#define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16
|
#define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16
|
||||||
#define TMU_RTR_CS_22 0x16
|
#define TMU_RTR_CS_22 0x16
|
||||||
#define TMU_RTR_CS_24 0x18
|
#define TMU_RTR_CS_24 0x18
|
||||||
|
#define TMU_RTR_CS_25 0x19
|
||||||
|
|
||||||
enum tb_port_type {
|
enum tb_port_type {
|
||||||
TB_TYPE_INACTIVE = 0x000000,
|
TB_TYPE_INACTIVE = 0x000000,
|
||||||
|
@ -305,6 +306,8 @@ struct tb_regs_port_header {
|
||||||
/* TMU adapter registers */
|
/* TMU adapter registers */
|
||||||
#define TMU_ADP_CS_3 0x03
|
#define TMU_ADP_CS_3 0x03
|
||||||
#define TMU_ADP_CS_3_UDM BIT(29)
|
#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 */
|
/* Lane adapter registers */
|
||||||
#define LANE_ADP_CS_0 0x00
|
#define LANE_ADP_CS_0 0x00
|
||||||
|
|
|
@ -115,6 +115,11 @@ static inline int tb_port_tmu_unidirectional_disable(struct tb_port *port)
|
||||||
return tb_port_tmu_set_unidirectional(port, false);
|
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)
|
static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -128,6 +133,23 @@ static bool tb_port_tmu_is_unidirectional(struct tb_port *port)
|
||||||
return val & TMU_ADP_CS_3_UDM;
|
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)
|
static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -207,6 +229,7 @@ int tb_switch_tmu_init(struct tb_switch *sw)
|
||||||
*/
|
*/
|
||||||
int tb_switch_tmu_post_time(struct tb_switch *sw)
|
int tb_switch_tmu_post_time(struct tb_switch *sw)
|
||||||
{
|
{
|
||||||
|
unsigned int post_time_high_offset, post_time_high = 0;
|
||||||
unsigned int post_local_time_offset, post_time_offset;
|
unsigned int post_local_time_offset, post_time_offset;
|
||||||
struct tb_switch *root_switch = sw->tb->root_switch;
|
struct tb_switch *root_switch = sw->tb->root_switch;
|
||||||
u64 hi, mid, lo, local_time, post_time;
|
u64 hi, mid, lo, local_time, post_time;
|
||||||
|
@ -247,6 +270,7 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
|
||||||
|
|
||||||
post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22;
|
post_local_time_offset = sw->tmu.cap + TMU_RTR_CS_22;
|
||||||
post_time_offset = sw->tmu.cap + TMU_RTR_CS_24;
|
post_time_offset = sw->tmu.cap + TMU_RTR_CS_24;
|
||||||
|
post_time_high_offset = sw->tmu.cap + TMU_RTR_CS_25;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the Grandmaster time to the Post Local Time registers
|
* Write the Grandmaster time to the Post Local Time registers
|
||||||
|
@ -258,17 +282,24 @@ int tb_switch_tmu_post_time(struct tb_switch *sw)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Have the new switch update its local time (by writing 1 to
|
* Have the new switch update its local time by:
|
||||||
* the post_time registers) and wait for the completion of the
|
* 1) writing 0x1 to the Post Time Low register and 0xffffffff to
|
||||||
* same (post_time register becomes 0). This means the time has
|
* Post Time High register.
|
||||||
* been converged properly.
|
* 2) write 0 to Post Time High register and then wait for
|
||||||
|
* the completion of the post_time register becomes 0.
|
||||||
|
* This means the time has been converged properly.
|
||||||
*/
|
*/
|
||||||
post_time = 1;
|
post_time = 0xffffffff00000001ULL;
|
||||||
|
|
||||||
ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2);
|
ret = tb_sw_write(sw, &post_time, TB_CFG_SWITCH, post_time_offset, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
ret = tb_sw_write(sw, &post_time_high, TB_CFG_SWITCH,
|
||||||
|
post_time_high_offset, 1);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
usleep_range(5, 10);
|
usleep_range(5, 10);
|
||||||
ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH,
|
ret = tb_sw_read(sw, &post_time, TB_CFG_SWITCH,
|
||||||
|
@ -297,8 +328,6 @@ out:
|
||||||
*/
|
*/
|
||||||
int tb_switch_tmu_disable(struct tb_switch *sw)
|
int tb_switch_tmu_disable(struct tb_switch *sw)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!tb_switch_is_usb4(sw))
|
if (!tb_switch_is_usb4(sw))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -306,21 +335,42 @@ int tb_switch_tmu_disable(struct tb_switch *sw)
|
||||||
if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF)
|
if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (sw->tmu.unidirectional) {
|
|
||||||
|
if (tb_route(sw)) {
|
||||||
|
bool unidirectional = tb_switch_tmu_hifi_is_enabled(sw, true);
|
||||||
struct tb_switch *parent = tb_switch_parent(sw);
|
struct tb_switch *parent = tb_switch_parent(sw);
|
||||||
struct tb_port *up, *down;
|
struct tb_port *down, *up;
|
||||||
|
int ret;
|
||||||
|
|
||||||
up = tb_upstream_port(sw);
|
|
||||||
down = tb_port_at(tb_route(sw), parent);
|
down = tb_port_at(tb_route(sw), parent);
|
||||||
|
up = tb_upstream_port(sw);
|
||||||
|
/*
|
||||||
|
* In case of uni-directional time sync, TMU handshake is
|
||||||
|
* initiated by upstream router. In case of bi-directional
|
||||||
|
* time sync, TMU handshake is initiated by downstream router.
|
||||||
|
* Therefore, we change the rate to off in the respective
|
||||||
|
* router.
|
||||||
|
*/
|
||||||
|
if (unidirectional)
|
||||||
|
tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
|
||||||
|
else
|
||||||
|
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
|
||||||
|
|
||||||
|
tb_port_tmu_time_sync_disable(up);
|
||||||
|
ret = tb_port_tmu_time_sync_disable(down);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (unidirectional) {
|
||||||
/* The switch may be unplugged so ignore any errors */
|
/* The switch may be unplugged so ignore any errors */
|
||||||
tb_port_tmu_unidirectional_disable(up);
|
tb_port_tmu_unidirectional_disable(up);
|
||||||
ret = tb_port_tmu_unidirectional_disable(down);
|
ret = tb_port_tmu_unidirectional_disable(down);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
|
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
sw->tmu.unidirectional = false;
|
sw->tmu.unidirectional = false;
|
||||||
sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
|
sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF;
|
||||||
|
@ -329,55 +379,187 @@ int tb_switch_tmu_disable(struct tb_switch *sw)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional)
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
int tb_switch_tmu_enable(struct tb_switch *sw)
|
|
||||||
{
|
{
|
||||||
|
struct tb_switch *parent = tb_switch_parent(sw);
|
||||||
|
struct tb_port *down, *up;
|
||||||
|
|
||||||
|
down = tb_port_at(tb_route(sw), parent);
|
||||||
|
up = tb_upstream_port(sw);
|
||||||
|
/*
|
||||||
|
* In case of any failure in one of the steps when setting
|
||||||
|
* bi-directional or uni-directional TMU mode, get back to the TMU
|
||||||
|
* configurations in off mode. In case of additional failures in
|
||||||
|
* the functions below, ignore them since the caller shall already
|
||||||
|
* report a failure.
|
||||||
|
*/
|
||||||
|
tb_port_tmu_time_sync_disable(down);
|
||||||
|
tb_port_tmu_time_sync_disable(up);
|
||||||
|
if (unidirectional)
|
||||||
|
tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF);
|
||||||
|
else
|
||||||
|
tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF);
|
||||||
|
|
||||||
|
tb_port_tmu_unidirectional_disable(down);
|
||||||
|
tb_port_tmu_unidirectional_disable(up);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when the previous TMU mode was
|
||||||
|
* TB_SWITCH_TMU_RATE_OFF.
|
||||||
|
*/
|
||||||
|
static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw)
|
||||||
|
{
|
||||||
|
struct tb_switch *parent = tb_switch_parent(sw);
|
||||||
|
struct tb_port *up, *down;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
up = tb_upstream_port(sw);
|
||||||
|
down = tb_port_at(tb_route(sw), parent);
|
||||||
|
|
||||||
|
ret = tb_port_tmu_unidirectional_disable(up);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = tb_port_tmu_unidirectional_disable(down);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = tb_switch_tmu_rate_write(sw, 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:
|
||||||
|
__tb_switch_tmu_off(sw, false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when the previous TMU mode was
|
||||||
|
* TB_SWITCH_TMU_RATE_OFF.
|
||||||
|
*/
|
||||||
|
static int __tb_switch_tmu_enable_unidirectional(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;
|
||||||
|
|
||||||
|
ret = tb_port_tmu_unidirectional_enable(up);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = tb_port_tmu_time_sync_enable(up);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = tb_port_tmu_unidirectional_enable(down);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = tb_port_tmu_time_sync_enable(down);
|
||||||
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
__tb_switch_tmu_off(sw, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tb_switch_tmu_hifi_enable(struct tb_switch *sw)
|
||||||
|
{
|
||||||
|
bool unidirectional = sw->tmu.unidirectional_request;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (unidirectional && !sw->tmu.has_ucap)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!tb_switch_is_usb4(sw))
|
if (!tb_switch_is_usb4(sw))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (tb_switch_tmu_is_enabled(sw))
|
if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = tb_switch_tmu_set_time_disruption(sw, true);
|
ret = tb_switch_tmu_set_time_disruption(sw, true);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Change mode to bi-directional */
|
if (tb_route(sw)) {
|
||||||
if (tb_route(sw) && sw->tmu.unidirectional) {
|
/* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */
|
||||||
struct tb_switch *parent = tb_switch_parent(sw);
|
if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) {
|
||||||
struct tb_port *up, *down;
|
if (unidirectional)
|
||||||
|
ret = __tb_switch_tmu_enable_unidirectional(sw);
|
||||||
up = tb_upstream_port(sw);
|
else
|
||||||
down = tb_port_at(tb_route(sw), parent);
|
ret = __tb_switch_tmu_enable_bidirectional(sw);
|
||||||
|
|
||||||
ret = tb_port_tmu_unidirectional_disable(down);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = tb_port_tmu_unidirectional_disable(up);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
sw->tmu.unidirectional = unidirectional;
|
||||||
} else {
|
} else {
|
||||||
|
/*
|
||||||
|
* Host router port configurations are written as
|
||||||
|
* part of configurations for downstream port of the parent
|
||||||
|
* of the child node - see above.
|
||||||
|
* Here only the host router' rate configuration is written.
|
||||||
|
*/
|
||||||
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
|
ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
sw->tmu.unidirectional = false;
|
|
||||||
sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
|
sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI;
|
||||||
tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw));
|
|
||||||
|
|
||||||
|
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);
|
return tb_switch_tmu_set_time_disruption(sw, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tb_switch_tmu_enable() - Enable TMU on a router
|
||||||
|
* @sw: Router whose TMU to enable
|
||||||
|
*
|
||||||
|
* Enables TMU of a router to be in uni-directional or bi-directional HiFi mode.
|
||||||
|
* Calling tb_switch_tmu_configure() is required before calling this function,
|
||||||
|
* to select the mode HiFi and directionality (uni-directional/bi-directional).
|
||||||
|
* In both modes all tunneling should work. Uni-directional mode is required for
|
||||||
|
* CLx (Link Low-Power) to work.
|
||||||
|
*/
|
||||||
|
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: Router whose mode to change
|
||||||
|
* @rate: Rate to configure Off/LowRes/HiFi
|
||||||
|
* @unidirectional: If uni-directional (bi-directional otherwise)
|
||||||
|
*
|
||||||
|
* Selects the rate of the TMU and directionality (uni-directional or
|
||||||
|
* bi-directional). Must 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.unidirectional_request = unidirectional;
|
||||||
|
sw->tmu.rate_request = rate;
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче