From 5480dfc275aa7e5f892505ce082d71635d6a22f8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 9 Jan 2019 17:25:43 +0200 Subject: thunderbolt: Set sleep bit when suspending switch Thunderbolt 2 devices and beyond link controller needs to be notified when a switch is going to be suspended by setting bit 31 in LC_SX_CTRL register. Add this functionality to the software connection manager. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/lc.c | 44 +++++++++++++++++++++++++++++++++++++++++++ drivers/thunderbolt/switch.c | 6 ++---- drivers/thunderbolt/tb.h | 1 + drivers/thunderbolt/tb_regs.h | 2 ++ 4 files changed, 49 insertions(+), 4 deletions(-) (limited to 'drivers/thunderbolt') diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c index a5dddf176546..ae1e92611c3e 100644 --- a/drivers/thunderbolt/lc.c +++ b/drivers/thunderbolt/lc.c @@ -133,3 +133,47 @@ void tb_lc_unconfigure_link(struct tb_switch *sw) tb_lc_configure_lane(up, false); tb_lc_configure_lane(down, false); } + +/** + * tb_lc_set_sleep() - Inform LC that the switch is going to sleep + * @sw: Switch to set sleep + * + * Let the switch link controllers know that the switch is going to + * sleep. + */ +int tb_lc_set_sleep(struct tb_switch *sw) +{ + int start, size, nlc, ret, i; + u32 desc; + + if (sw->generation < 2) + return 0; + + ret = read_lc_desc(sw, &desc); + if (ret) + return ret; + + /* Figure out number of link controllers */ + nlc = desc & TB_LC_DESC_NLC_MASK; + start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT; + size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT; + + /* For each link controller set sleep bit */ + for (i = 0; i < nlc; i++) { + unsigned int offset = sw->cap_lc + start + i * size; + u32 ctrl; + + ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, + offset + TB_LC_SX_CTRL, 1); + if (ret) + return ret; + + ctrl |= TB_LC_SX_CTRL_SLP; + ret = tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, + offset + TB_LC_SX_CTRL, 1); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index dd218dc4781b..b3f93ebe6e39 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1586,10 +1586,8 @@ void tb_switch_suspend(struct tb_switch *sw) if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) tb_switch_suspend(sw->ports[i].remote->sw); } - /* - * TODO: invoke tb_cfg_prepare_to_sleep here? does not seem to have any - * effect? - */ + + tb_lc_set_sleep(sw); } struct tb_sw_lookup { diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 69e0534224d8..985a48a67a43 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -467,6 +467,7 @@ int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid); int tb_lc_configure_link(struct tb_switch *sw); void tb_lc_unconfigure_link(struct tb_switch *sw); +int tb_lc_set_sleep(struct tb_switch *sw); static inline int tb_route_length(u64 route) { diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index e0f867dad5cf..1ab6e0fb31c0 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -239,6 +239,7 @@ struct tb_regs_hop { /* Common link controller registers */ #define TB_LC_DESC 0x02 +#define TB_LC_DESC_NLC_MASK GENMASK(3, 0) #define TB_LC_DESC_SIZE_SHIFT 8 #define TB_LC_DESC_SIZE_MASK GENMASK(15, 8) #define TB_LC_DESC_PORT_SIZE_SHIFT 16 @@ -250,5 +251,6 @@ struct tb_regs_hop { #define TB_LC_SX_CTRL_L1C BIT(16) #define TB_LC_SX_CTRL_L2C BIT(20) #define TB_LC_SX_CTRL_UPSTREAM BIT(30) +#define TB_LC_SX_CTRL_SLP BIT(31) #endif -- cgit v1.2.3