xref: /openbmc/linux/drivers/thunderbolt/lc.c (revision c845428b7a9157523103100806bc8130d64769c8)
1a9be5582SMika Westerberg // SPDX-License-Identifier: GPL-2.0
2a9be5582SMika Westerberg /*
3a9be5582SMika Westerberg  * Thunderbolt link controller support
4a9be5582SMika Westerberg  *
5a9be5582SMika Westerberg  * Copyright (C) 2019, Intel Corporation
6a9be5582SMika Westerberg  * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
7a9be5582SMika Westerberg  */
8a9be5582SMika Westerberg 
9*6c24584aSSanath S #include <linux/delay.h>
10*6c24584aSSanath S 
11a9be5582SMika Westerberg #include "tb.h"
12a9be5582SMika Westerberg 
13a9be5582SMika Westerberg /**
14a9be5582SMika Westerberg  * tb_lc_read_uuid() - Read switch UUID from link controller common register
15a9be5582SMika Westerberg  * @sw: Switch whose UUID is read
16a9be5582SMika Westerberg  * @uuid: UUID is placed here
17a9be5582SMika Westerberg  */
tb_lc_read_uuid(struct tb_switch * sw,u32 * uuid)18a9be5582SMika Westerberg int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid)
19a9be5582SMika Westerberg {
20a9be5582SMika Westerberg 	if (!sw->cap_lc)
21a9be5582SMika Westerberg 		return -EINVAL;
22a9be5582SMika Westerberg 	return tb_sw_read(sw, uuid, TB_CFG_SWITCH, sw->cap_lc + TB_LC_FUSE, 4);
23a9be5582SMika Westerberg }
24e879a709SMika Westerberg 
read_lc_desc(struct tb_switch * sw,u32 * desc)25e879a709SMika Westerberg static int read_lc_desc(struct tb_switch *sw, u32 *desc)
26e879a709SMika Westerberg {
27e879a709SMika Westerberg 	if (!sw->cap_lc)
28e879a709SMika Westerberg 		return -EINVAL;
29e879a709SMika Westerberg 	return tb_sw_read(sw, desc, TB_CFG_SWITCH, sw->cap_lc + TB_LC_DESC, 1);
30e879a709SMika Westerberg }
31e879a709SMika Westerberg 
find_port_lc_cap(struct tb_port * port)32e879a709SMika Westerberg static int find_port_lc_cap(struct tb_port *port)
33e879a709SMika Westerberg {
34e879a709SMika Westerberg 	struct tb_switch *sw = port->sw;
35e879a709SMika Westerberg 	int start, phys, ret, size;
36e879a709SMika Westerberg 	u32 desc;
37e879a709SMika Westerberg 
38e879a709SMika Westerberg 	ret = read_lc_desc(sw, &desc);
39e879a709SMika Westerberg 	if (ret)
40e879a709SMika Westerberg 		return ret;
41e879a709SMika Westerberg 
42e879a709SMika Westerberg 	/* Start of port LC registers */
43e879a709SMika Westerberg 	start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
44e879a709SMika Westerberg 	size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
45e879a709SMika Westerberg 	phys = tb_phy_port_from_link(port->port);
46e879a709SMika Westerberg 
47e879a709SMika Westerberg 	return sw->cap_lc + start + phys * size;
48e879a709SMika Westerberg }
49e879a709SMika Westerberg 
50*6c24584aSSanath S /**
51*6c24584aSSanath S  * tb_lc_reset_port() - Trigger downstream port reset through LC
52*6c24584aSSanath S  * @port: Port that is reset
53*6c24584aSSanath S  *
54*6c24584aSSanath S  * Triggers downstream port reset through link controller registers.
55*6c24584aSSanath S  * Returns %0 in case of success negative errno otherwise. Only supports
56*6c24584aSSanath S  * non-USB4 routers with link controller (that's Thunderbolt 2 and
57*6c24584aSSanath S  * Thunderbolt 3).
58*6c24584aSSanath S  */
tb_lc_reset_port(struct tb_port * port)59*6c24584aSSanath S int tb_lc_reset_port(struct tb_port *port)
60*6c24584aSSanath S {
61*6c24584aSSanath S 	struct tb_switch *sw = port->sw;
62*6c24584aSSanath S 	int cap, ret;
63*6c24584aSSanath S 	u32 mode;
64*6c24584aSSanath S 
65*6c24584aSSanath S 	if (sw->generation < 2)
66*6c24584aSSanath S 		return -EINVAL;
67*6c24584aSSanath S 
68*6c24584aSSanath S 	cap = find_port_lc_cap(port);
69*6c24584aSSanath S 	if (cap < 0)
70*6c24584aSSanath S 		return cap;
71*6c24584aSSanath S 
72*6c24584aSSanath S 	ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
73*6c24584aSSanath S 	if (ret)
74*6c24584aSSanath S 		return ret;
75*6c24584aSSanath S 
76*6c24584aSSanath S 	mode |= TB_LC_PORT_MODE_DPR;
77*6c24584aSSanath S 
78*6c24584aSSanath S 	ret = tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
79*6c24584aSSanath S 	if (ret)
80*6c24584aSSanath S 		return ret;
81*6c24584aSSanath S 
82*6c24584aSSanath S 	fsleep(10000);
83*6c24584aSSanath S 
84*6c24584aSSanath S 	ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
85*6c24584aSSanath S 	if (ret)
86*6c24584aSSanath S 		return ret;
87*6c24584aSSanath S 
88*6c24584aSSanath S 	mode &= ~TB_LC_PORT_MODE_DPR;
89*6c24584aSSanath S 
90*6c24584aSSanath S 	return tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1);
91*6c24584aSSanath S }
92*6c24584aSSanath S 
tb_lc_set_port_configured(struct tb_port * port,bool configured)93e28178bfSMika Westerberg static int tb_lc_set_port_configured(struct tb_port *port, bool configured)
94e879a709SMika Westerberg {
95e879a709SMika Westerberg 	bool upstream = tb_is_upstream_port(port);
96e879a709SMika Westerberg 	struct tb_switch *sw = port->sw;
97e879a709SMika Westerberg 	u32 ctrl, lane;
98e879a709SMika Westerberg 	int cap, ret;
99e879a709SMika Westerberg 
100e879a709SMika Westerberg 	if (sw->generation < 2)
101e879a709SMika Westerberg 		return 0;
102e879a709SMika Westerberg 
103e879a709SMika Westerberg 	cap = find_port_lc_cap(port);
104e879a709SMika Westerberg 	if (cap < 0)
105e879a709SMika Westerberg 		return cap;
106e879a709SMika Westerberg 
107e879a709SMika Westerberg 	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
108e879a709SMika Westerberg 	if (ret)
109e879a709SMika Westerberg 		return ret;
110e879a709SMika Westerberg 
111e879a709SMika Westerberg 	/* Resolve correct lane */
112e879a709SMika Westerberg 	if (port->port % 2)
113e879a709SMika Westerberg 		lane = TB_LC_SX_CTRL_L1C;
114e879a709SMika Westerberg 	else
115e879a709SMika Westerberg 		lane = TB_LC_SX_CTRL_L2C;
116e879a709SMika Westerberg 
117e28178bfSMika Westerberg 	if (configured) {
118e879a709SMika Westerberg 		ctrl |= lane;
119e879a709SMika Westerberg 		if (upstream)
120e879a709SMika Westerberg 			ctrl |= TB_LC_SX_CTRL_UPSTREAM;
121e879a709SMika Westerberg 	} else {
122e879a709SMika Westerberg 		ctrl &= ~lane;
123e879a709SMika Westerberg 		if (upstream)
124e879a709SMika Westerberg 			ctrl &= ~TB_LC_SX_CTRL_UPSTREAM;
125e879a709SMika Westerberg 	}
126e879a709SMika Westerberg 
127e879a709SMika Westerberg 	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
128e879a709SMika Westerberg }
129e879a709SMika Westerberg 
130e879a709SMika Westerberg /**
131e28178bfSMika Westerberg  * tb_lc_configure_port() - Let LC know about configured port
132e28178bfSMika Westerberg  * @port: Port that is set as configured
133e879a709SMika Westerberg  *
134e28178bfSMika Westerberg  * Sets the port configured for power management purposes.
135e879a709SMika Westerberg  */
tb_lc_configure_port(struct tb_port * port)136e28178bfSMika Westerberg int tb_lc_configure_port(struct tb_port *port)
137e879a709SMika Westerberg {
138e28178bfSMika Westerberg 	return tb_lc_set_port_configured(port, true);
139e879a709SMika Westerberg }
140e879a709SMika Westerberg 
141e879a709SMika Westerberg /**
142e28178bfSMika Westerberg  * tb_lc_unconfigure_port() - Let LC know about unconfigured port
143e28178bfSMika Westerberg  * @port: Port that is set as configured
144e879a709SMika Westerberg  *
145e28178bfSMika Westerberg  * Sets the port unconfigured for power management purposes.
146e879a709SMika Westerberg  */
tb_lc_unconfigure_port(struct tb_port * port)147e28178bfSMika Westerberg void tb_lc_unconfigure_port(struct tb_port *port)
148e879a709SMika Westerberg {
149e28178bfSMika Westerberg 	tb_lc_set_port_configured(port, false);
150e879a709SMika Westerberg }
1515480dfc2SMika Westerberg 
tb_lc_set_xdomain_configured(struct tb_port * port,bool configure)152284652a4SMika Westerberg static int tb_lc_set_xdomain_configured(struct tb_port *port, bool configure)
153284652a4SMika Westerberg {
154284652a4SMika Westerberg 	struct tb_switch *sw = port->sw;
155284652a4SMika Westerberg 	u32 ctrl, lane;
156284652a4SMika Westerberg 	int cap, ret;
157284652a4SMika Westerberg 
158284652a4SMika Westerberg 	if (sw->generation < 2)
159284652a4SMika Westerberg 		return 0;
160284652a4SMika Westerberg 
161284652a4SMika Westerberg 	cap = find_port_lc_cap(port);
162284652a4SMika Westerberg 	if (cap < 0)
163284652a4SMika Westerberg 		return cap;
164284652a4SMika Westerberg 
165284652a4SMika Westerberg 	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
166284652a4SMika Westerberg 	if (ret)
167284652a4SMika Westerberg 		return ret;
168284652a4SMika Westerberg 
169284652a4SMika Westerberg 	/* Resolve correct lane */
170284652a4SMika Westerberg 	if (port->port % 2)
171284652a4SMika Westerberg 		lane = TB_LC_SX_CTRL_L1D;
172284652a4SMika Westerberg 	else
173284652a4SMika Westerberg 		lane = TB_LC_SX_CTRL_L2D;
174284652a4SMika Westerberg 
175284652a4SMika Westerberg 	if (configure)
176284652a4SMika Westerberg 		ctrl |= lane;
177284652a4SMika Westerberg 	else
178284652a4SMika Westerberg 		ctrl &= ~lane;
179284652a4SMika Westerberg 
180284652a4SMika Westerberg 	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
181284652a4SMika Westerberg }
182284652a4SMika Westerberg 
183284652a4SMika Westerberg /**
184284652a4SMika Westerberg  * tb_lc_configure_xdomain() - Inform LC that the link is XDomain
185284652a4SMika Westerberg  * @port: Switch downstream port connected to another host
186284652a4SMika Westerberg  *
187284652a4SMika Westerberg  * Sets the lane configured for XDomain accordingly so that the LC knows
188284652a4SMika Westerberg  * about this. Returns %0 in success and negative errno in failure.
189284652a4SMika Westerberg  */
tb_lc_configure_xdomain(struct tb_port * port)190284652a4SMika Westerberg int tb_lc_configure_xdomain(struct tb_port *port)
191284652a4SMika Westerberg {
192284652a4SMika Westerberg 	return tb_lc_set_xdomain_configured(port, true);
193284652a4SMika Westerberg }
194284652a4SMika Westerberg 
195284652a4SMika Westerberg /**
196284652a4SMika Westerberg  * tb_lc_unconfigure_xdomain() - Unconfigure XDomain from port
197284652a4SMika Westerberg  * @port: Switch downstream port that was connected to another host
198284652a4SMika Westerberg  *
199284652a4SMika Westerberg  * Unsets the lane XDomain configuration.
200284652a4SMika Westerberg  */
tb_lc_unconfigure_xdomain(struct tb_port * port)201284652a4SMika Westerberg void tb_lc_unconfigure_xdomain(struct tb_port *port)
202284652a4SMika Westerberg {
203284652a4SMika Westerberg 	tb_lc_set_xdomain_configured(port, false);
204284652a4SMika Westerberg }
205284652a4SMika Westerberg 
206fdb0887cSMika Westerberg /**
207fdb0887cSMika Westerberg  * tb_lc_start_lane_initialization() - Start lane initialization
208fdb0887cSMika Westerberg  * @port: Device router lane 0 adapter
209fdb0887cSMika Westerberg  *
210fdb0887cSMika Westerberg  * Starts lane initialization for @port after the router resumed from
211fdb0887cSMika Westerberg  * sleep. Should be called for those downstream lane adapters that were
212fdb0887cSMika Westerberg  * not connected (tb_lc_configure_port() was not called) before sleep.
213fdb0887cSMika Westerberg  *
214fdb0887cSMika Westerberg  * Returns %0 in success and negative errno in case of failure.
215fdb0887cSMika Westerberg  */
tb_lc_start_lane_initialization(struct tb_port * port)216fdb0887cSMika Westerberg int tb_lc_start_lane_initialization(struct tb_port *port)
217fdb0887cSMika Westerberg {
218fdb0887cSMika Westerberg 	struct tb_switch *sw = port->sw;
219fdb0887cSMika Westerberg 	int ret, cap;
220fdb0887cSMika Westerberg 	u32 ctrl;
221fdb0887cSMika Westerberg 
222fdb0887cSMika Westerberg 	if (!tb_route(sw))
223fdb0887cSMika Westerberg 		return 0;
224fdb0887cSMika Westerberg 
225fdb0887cSMika Westerberg 	if (sw->generation < 2)
226fdb0887cSMika Westerberg 		return 0;
227fdb0887cSMika Westerberg 
228fdb0887cSMika Westerberg 	cap = find_port_lc_cap(port);
229fdb0887cSMika Westerberg 	if (cap < 0)
230fdb0887cSMika Westerberg 		return cap;
231fdb0887cSMika Westerberg 
232fdb0887cSMika Westerberg 	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
233fdb0887cSMika Westerberg 	if (ret)
234fdb0887cSMika Westerberg 		return ret;
235fdb0887cSMika Westerberg 
236fdb0887cSMika Westerberg 	ctrl |= TB_LC_SX_CTRL_SLI;
237fdb0887cSMika Westerberg 
238fdb0887cSMika Westerberg 	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
239fdb0887cSMika Westerberg }
240fdb0887cSMika Westerberg 
24143f977bcSGil Fine /**
24243f977bcSGil Fine  * tb_lc_is_clx_supported() - Check whether CLx is supported by the lane adapter
24343f977bcSGil Fine  * @port: Lane adapter
24443f977bcSGil Fine  *
24543f977bcSGil Fine  * TB_LC_LINK_ATTR_CPS bit reflects if the link supports CLx including
24643f977bcSGil Fine  * active cables (if connected on the link).
24743f977bcSGil Fine  */
tb_lc_is_clx_supported(struct tb_port * port)24843f977bcSGil Fine bool tb_lc_is_clx_supported(struct tb_port *port)
24943f977bcSGil Fine {
25043f977bcSGil Fine 	struct tb_switch *sw = port->sw;
25143f977bcSGil Fine 	int cap, ret;
25243f977bcSGil Fine 	u32 val;
25343f977bcSGil Fine 
25443f977bcSGil Fine 	cap = find_port_lc_cap(port);
25543f977bcSGil Fine 	if (cap < 0)
25643f977bcSGil Fine 		return false;
25743f977bcSGil Fine 
25843f977bcSGil Fine 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_ATTR, 1);
25943f977bcSGil Fine 	if (ret)
26043f977bcSGil Fine 		return false;
26143f977bcSGil Fine 
26243f977bcSGil Fine 	return !!(val & TB_LC_LINK_ATTR_CPS);
26343f977bcSGil Fine }
26443f977bcSGil Fine 
26530a4eca6SMika Westerberg /**
26630a4eca6SMika Westerberg  * tb_lc_is_usb_plugged() - Is there USB device connected to port
26730a4eca6SMika Westerberg  * @port: Device router lane 0 adapter
26830a4eca6SMika Westerberg  *
26930a4eca6SMika Westerberg  * Returns true if the @port has USB type-C device connected.
27030a4eca6SMika Westerberg  */
tb_lc_is_usb_plugged(struct tb_port * port)27130a4eca6SMika Westerberg bool tb_lc_is_usb_plugged(struct tb_port *port)
27230a4eca6SMika Westerberg {
27330a4eca6SMika Westerberg 	struct tb_switch *sw = port->sw;
27430a4eca6SMika Westerberg 	int cap, ret;
27530a4eca6SMika Westerberg 	u32 val;
27630a4eca6SMika Westerberg 
27730a4eca6SMika Westerberg 	if (sw->generation != 3)
27830a4eca6SMika Westerberg 		return false;
27930a4eca6SMika Westerberg 
28030a4eca6SMika Westerberg 	cap = find_port_lc_cap(port);
28130a4eca6SMika Westerberg 	if (cap < 0)
28230a4eca6SMika Westerberg 		return false;
28330a4eca6SMika Westerberg 
28430a4eca6SMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_CS_42, 1);
28530a4eca6SMika Westerberg 	if (ret)
28630a4eca6SMika Westerberg 		return false;
28730a4eca6SMika Westerberg 
28830a4eca6SMika Westerberg 	return !!(val & TB_LC_CS_42_USB_PLUGGED);
28930a4eca6SMika Westerberg }
29030a4eca6SMika Westerberg 
29130a4eca6SMika Westerberg /**
29230a4eca6SMika Westerberg  * tb_lc_is_xhci_connected() - Is the internal xHCI connected
29330a4eca6SMika Westerberg  * @port: Device router lane 0 adapter
29430a4eca6SMika Westerberg  *
29530a4eca6SMika Westerberg  * Returns true if the internal xHCI has been connected to @port.
29630a4eca6SMika Westerberg  */
tb_lc_is_xhci_connected(struct tb_port * port)29730a4eca6SMika Westerberg bool tb_lc_is_xhci_connected(struct tb_port *port)
29830a4eca6SMika Westerberg {
29930a4eca6SMika Westerberg 	struct tb_switch *sw = port->sw;
30030a4eca6SMika Westerberg 	int cap, ret;
30130a4eca6SMika Westerberg 	u32 val;
30230a4eca6SMika Westerberg 
30330a4eca6SMika Westerberg 	if (sw->generation != 3)
30430a4eca6SMika Westerberg 		return false;
30530a4eca6SMika Westerberg 
30630a4eca6SMika Westerberg 	cap = find_port_lc_cap(port);
30730a4eca6SMika Westerberg 	if (cap < 0)
30830a4eca6SMika Westerberg 		return false;
30930a4eca6SMika Westerberg 
31030a4eca6SMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
31130a4eca6SMika Westerberg 	if (ret)
31230a4eca6SMika Westerberg 		return false;
31330a4eca6SMika Westerberg 
31430a4eca6SMika Westerberg 	return !!(val & TB_LC_LINK_REQ_XHCI_CONNECT);
31530a4eca6SMika Westerberg }
31630a4eca6SMika Westerberg 
__tb_lc_xhci_connect(struct tb_port * port,bool connect)31730a4eca6SMika Westerberg static int __tb_lc_xhci_connect(struct tb_port *port, bool connect)
31830a4eca6SMika Westerberg {
31930a4eca6SMika Westerberg 	struct tb_switch *sw = port->sw;
32030a4eca6SMika Westerberg 	int cap, ret;
32130a4eca6SMika Westerberg 	u32 val;
32230a4eca6SMika Westerberg 
32330a4eca6SMika Westerberg 	if (sw->generation != 3)
32430a4eca6SMika Westerberg 		return -EINVAL;
32530a4eca6SMika Westerberg 
32630a4eca6SMika Westerberg 	cap = find_port_lc_cap(port);
32730a4eca6SMika Westerberg 	if (cap < 0)
32830a4eca6SMika Westerberg 		return cap;
32930a4eca6SMika Westerberg 
33030a4eca6SMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
33130a4eca6SMika Westerberg 	if (ret)
33230a4eca6SMika Westerberg 		return ret;
33330a4eca6SMika Westerberg 
33430a4eca6SMika Westerberg 	if (connect)
33530a4eca6SMika Westerberg 		val |= TB_LC_LINK_REQ_XHCI_CONNECT;
33630a4eca6SMika Westerberg 	else
33730a4eca6SMika Westerberg 		val &= ~TB_LC_LINK_REQ_XHCI_CONNECT;
33830a4eca6SMika Westerberg 
33930a4eca6SMika Westerberg 	return tb_sw_write(sw, &val, TB_CFG_SWITCH, cap + TB_LC_LINK_REQ, 1);
34030a4eca6SMika Westerberg }
34130a4eca6SMika Westerberg 
34230a4eca6SMika Westerberg /**
34330a4eca6SMika Westerberg  * tb_lc_xhci_connect() - Connect internal xHCI
34430a4eca6SMika Westerberg  * @port: Device router lane 0 adapter
34530a4eca6SMika Westerberg  *
34630a4eca6SMika Westerberg  * Tells LC to connect the internal xHCI to @port. Returns %0 on success
34730a4eca6SMika Westerberg  * and negative errno in case of failure. Can be called for Thunderbolt 3
34830a4eca6SMika Westerberg  * routers only.
34930a4eca6SMika Westerberg  */
tb_lc_xhci_connect(struct tb_port * port)35030a4eca6SMika Westerberg int tb_lc_xhci_connect(struct tb_port *port)
35130a4eca6SMika Westerberg {
35230a4eca6SMika Westerberg 	int ret;
35330a4eca6SMika Westerberg 
35430a4eca6SMika Westerberg 	ret = __tb_lc_xhci_connect(port, true);
35530a4eca6SMika Westerberg 	if (ret)
35630a4eca6SMika Westerberg 		return ret;
35730a4eca6SMika Westerberg 
35830a4eca6SMika Westerberg 	tb_port_dbg(port, "xHCI connected\n");
35930a4eca6SMika Westerberg 	return 0;
36030a4eca6SMika Westerberg }
36130a4eca6SMika Westerberg 
36230a4eca6SMika Westerberg /**
36330a4eca6SMika Westerberg  * tb_lc_xhci_disconnect() - Disconnect internal xHCI
36430a4eca6SMika Westerberg  * @port: Device router lane 0 adapter
36530a4eca6SMika Westerberg  *
36630a4eca6SMika Westerberg  * Tells LC to disconnect the internal xHCI from @port. Can be called
36730a4eca6SMika Westerberg  * for Thunderbolt 3 routers only.
36830a4eca6SMika Westerberg  */
tb_lc_xhci_disconnect(struct tb_port * port)36930a4eca6SMika Westerberg void tb_lc_xhci_disconnect(struct tb_port *port)
37030a4eca6SMika Westerberg {
37130a4eca6SMika Westerberg 	__tb_lc_xhci_connect(port, false);
37230a4eca6SMika Westerberg 	tb_port_dbg(port, "xHCI disconnected\n");
37330a4eca6SMika Westerberg }
37430a4eca6SMika Westerberg 
tb_lc_set_wake_one(struct tb_switch * sw,unsigned int offset,unsigned int flags)375b2911a59SMika Westerberg static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
376b2911a59SMika Westerberg 			      unsigned int flags)
377b2911a59SMika Westerberg {
378b2911a59SMika Westerberg 	u32 ctrl;
379b2911a59SMika Westerberg 	int ret;
380b2911a59SMika Westerberg 
381b2911a59SMika Westerberg 	/*
382b2911a59SMika Westerberg 	 * Enable wake on PCIe and USB4 (wake coming from another
383b2911a59SMika Westerberg 	 * router).
384b2911a59SMika Westerberg 	 */
385b2911a59SMika Westerberg 	ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
386b2911a59SMika Westerberg 			 offset + TB_LC_SX_CTRL, 1);
387b2911a59SMika Westerberg 	if (ret)
388b2911a59SMika Westerberg 		return ret;
389b2911a59SMika Westerberg 
3906026b703SMika Westerberg 	ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WODPC |
3916026b703SMika Westerberg 		  TB_LC_SX_CTRL_WODPD | TB_LC_SX_CTRL_WOP | TB_LC_SX_CTRL_WOU4);
392b2911a59SMika Westerberg 
393b2911a59SMika Westerberg 	if (flags & TB_WAKE_ON_CONNECT)
394b2911a59SMika Westerberg 		ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
395b2911a59SMika Westerberg 	if (flags & TB_WAKE_ON_USB4)
396b2911a59SMika Westerberg 		ctrl |= TB_LC_SX_CTRL_WOU4;
397b2911a59SMika Westerberg 	if (flags & TB_WAKE_ON_PCIE)
398b2911a59SMika Westerberg 		ctrl |= TB_LC_SX_CTRL_WOP;
3996026b703SMika Westerberg 	if (flags & TB_WAKE_ON_DP)
4006026b703SMika Westerberg 		ctrl |= TB_LC_SX_CTRL_WODPC | TB_LC_SX_CTRL_WODPD;
401b2911a59SMika Westerberg 
402b2911a59SMika Westerberg 	return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
403b2911a59SMika Westerberg }
404b2911a59SMika Westerberg 
405b2911a59SMika Westerberg /**
406b2911a59SMika Westerberg  * tb_lc_set_wake() - Enable/disable wake
407b2911a59SMika Westerberg  * @sw: Switch whose wakes to configure
408b2911a59SMika Westerberg  * @flags: Wakeup flags (%0 to disable)
409b2911a59SMika Westerberg  *
410b2911a59SMika Westerberg  * For each LC sets wake bits accordingly.
411b2911a59SMika Westerberg  */
tb_lc_set_wake(struct tb_switch * sw,unsigned int flags)412b2911a59SMika Westerberg int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags)
413b2911a59SMika Westerberg {
414b2911a59SMika Westerberg 	int start, size, nlc, ret, i;
415b2911a59SMika Westerberg 	u32 desc;
416b2911a59SMika Westerberg 
417b2911a59SMika Westerberg 	if (sw->generation < 2)
418b2911a59SMika Westerberg 		return 0;
419b2911a59SMika Westerberg 
420b2911a59SMika Westerberg 	if (!tb_route(sw))
421b2911a59SMika Westerberg 		return 0;
422b2911a59SMika Westerberg 
423b2911a59SMika Westerberg 	ret = read_lc_desc(sw, &desc);
424b2911a59SMika Westerberg 	if (ret)
425b2911a59SMika Westerberg 		return ret;
426b2911a59SMika Westerberg 
427b2911a59SMika Westerberg 	/* Figure out number of link controllers */
428b2911a59SMika Westerberg 	nlc = desc & TB_LC_DESC_NLC_MASK;
429b2911a59SMika Westerberg 	start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
430b2911a59SMika Westerberg 	size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
431b2911a59SMika Westerberg 
432b2911a59SMika Westerberg 	/* For each link controller set sleep bit */
433b2911a59SMika Westerberg 	for (i = 0; i < nlc; i++) {
434b2911a59SMika Westerberg 		unsigned int offset = sw->cap_lc + start + i * size;
435b2911a59SMika Westerberg 
436b2911a59SMika Westerberg 		ret = tb_lc_set_wake_one(sw, offset, flags);
437b2911a59SMika Westerberg 		if (ret)
438b2911a59SMika Westerberg 			return ret;
439b2911a59SMika Westerberg 	}
440b2911a59SMika Westerberg 
441b2911a59SMika Westerberg 	return 0;
442b2911a59SMika Westerberg }
443b2911a59SMika Westerberg 
4445480dfc2SMika Westerberg /**
4455480dfc2SMika Westerberg  * tb_lc_set_sleep() - Inform LC that the switch is going to sleep
4465480dfc2SMika Westerberg  * @sw: Switch to set sleep
4475480dfc2SMika Westerberg  *
4485480dfc2SMika Westerberg  * Let the switch link controllers know that the switch is going to
4495480dfc2SMika Westerberg  * sleep.
4505480dfc2SMika Westerberg  */
tb_lc_set_sleep(struct tb_switch * sw)4515480dfc2SMika Westerberg int tb_lc_set_sleep(struct tb_switch *sw)
4525480dfc2SMika Westerberg {
4535480dfc2SMika Westerberg 	int start, size, nlc, ret, i;
4545480dfc2SMika Westerberg 	u32 desc;
4555480dfc2SMika Westerberg 
4565480dfc2SMika Westerberg 	if (sw->generation < 2)
4575480dfc2SMika Westerberg 		return 0;
4585480dfc2SMika Westerberg 
4595480dfc2SMika Westerberg 	ret = read_lc_desc(sw, &desc);
4605480dfc2SMika Westerberg 	if (ret)
4615480dfc2SMika Westerberg 		return ret;
4625480dfc2SMika Westerberg 
4635480dfc2SMika Westerberg 	/* Figure out number of link controllers */
4645480dfc2SMika Westerberg 	nlc = desc & TB_LC_DESC_NLC_MASK;
4655480dfc2SMika Westerberg 	start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
4665480dfc2SMika Westerberg 	size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
4675480dfc2SMika Westerberg 
4685480dfc2SMika Westerberg 	/* For each link controller set sleep bit */
4695480dfc2SMika Westerberg 	for (i = 0; i < nlc; i++) {
4705480dfc2SMika Westerberg 		unsigned int offset = sw->cap_lc + start + i * size;
4715480dfc2SMika Westerberg 		u32 ctrl;
4725480dfc2SMika Westerberg 
4735480dfc2SMika Westerberg 		ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
4745480dfc2SMika Westerberg 				 offset + TB_LC_SX_CTRL, 1);
4755480dfc2SMika Westerberg 		if (ret)
4765480dfc2SMika Westerberg 			return ret;
4775480dfc2SMika Westerberg 
4785480dfc2SMika Westerberg 		ctrl |= TB_LC_SX_CTRL_SLP;
4795480dfc2SMika Westerberg 		ret = tb_sw_write(sw, &ctrl, TB_CFG_SWITCH,
4805480dfc2SMika Westerberg 				  offset + TB_LC_SX_CTRL, 1);
4815480dfc2SMika Westerberg 		if (ret)
4825480dfc2SMika Westerberg 			return ret;
4835480dfc2SMika Westerberg 	}
4845480dfc2SMika Westerberg 
4855480dfc2SMika Westerberg 	return 0;
4865480dfc2SMika Westerberg }
48791c0c120SMika Westerberg 
48891c0c120SMika Westerberg /**
48991c0c120SMika Westerberg  * tb_lc_lane_bonding_possible() - Is lane bonding possible towards switch
49091c0c120SMika Westerberg  * @sw: Switch to check
49191c0c120SMika Westerberg  *
49291c0c120SMika Westerberg  * Checks whether conditions for lane bonding from parent to @sw are
49391c0c120SMika Westerberg  * possible.
49491c0c120SMika Westerberg  */
tb_lc_lane_bonding_possible(struct tb_switch * sw)49591c0c120SMika Westerberg bool tb_lc_lane_bonding_possible(struct tb_switch *sw)
49691c0c120SMika Westerberg {
49791c0c120SMika Westerberg 	struct tb_port *up;
49891c0c120SMika Westerberg 	int cap, ret;
49991c0c120SMika Westerberg 	u32 val;
50091c0c120SMika Westerberg 
50191c0c120SMika Westerberg 	if (sw->generation < 2)
50291c0c120SMika Westerberg 		return false;
50391c0c120SMika Westerberg 
50491c0c120SMika Westerberg 	up = tb_upstream_port(sw);
50591c0c120SMika Westerberg 	cap = find_port_lc_cap(up);
50691c0c120SMika Westerberg 	if (cap < 0)
50791c0c120SMika Westerberg 		return false;
50891c0c120SMika Westerberg 
50991c0c120SMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_PORT_ATTR, 1);
51091c0c120SMika Westerberg 	if (ret)
51191c0c120SMika Westerberg 		return false;
51291c0c120SMika Westerberg 
51391c0c120SMika Westerberg 	return !!(val & TB_LC_PORT_ATTR_BE);
51491c0c120SMika Westerberg }
5158afe909bSMika Westerberg 
tb_lc_dp_sink_from_port(const struct tb_switch * sw,struct tb_port * in)5168afe909bSMika Westerberg static int tb_lc_dp_sink_from_port(const struct tb_switch *sw,
5178afe909bSMika Westerberg 				   struct tb_port *in)
5188afe909bSMika Westerberg {
5198afe909bSMika Westerberg 	struct tb_port *port;
5208afe909bSMika Westerberg 
5218afe909bSMika Westerberg 	/* The first DP IN port is sink 0 and second is sink 1 */
5228afe909bSMika Westerberg 	tb_switch_for_each_port(sw, port) {
5238afe909bSMika Westerberg 		if (tb_port_is_dpin(port))
5248afe909bSMika Westerberg 			return in != port;
5258afe909bSMika Westerberg 	}
5268afe909bSMika Westerberg 
5278afe909bSMika Westerberg 	return -EINVAL;
5288afe909bSMika Westerberg }
5298afe909bSMika Westerberg 
tb_lc_dp_sink_available(struct tb_switch * sw,int sink)5308afe909bSMika Westerberg static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink)
5318afe909bSMika Westerberg {
5328afe909bSMika Westerberg 	u32 val, alloc;
5338afe909bSMika Westerberg 	int ret;
5348afe909bSMika Westerberg 
5358afe909bSMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
5368afe909bSMika Westerberg 			 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
5378afe909bSMika Westerberg 	if (ret)
5388afe909bSMika Westerberg 		return ret;
5398afe909bSMika Westerberg 
5408afe909bSMika Westerberg 	/*
5418afe909bSMika Westerberg 	 * Sink is available for CM/SW to use if the allocation valie is
5428afe909bSMika Westerberg 	 * either 0 or 1.
5438afe909bSMika Westerberg 	 */
5448afe909bSMika Westerberg 	if (!sink) {
5458afe909bSMika Westerberg 		alloc = val & TB_LC_SNK_ALLOCATION_SNK0_MASK;
5468afe909bSMika Westerberg 		if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK0_CM)
5478afe909bSMika Westerberg 			return 0;
5488afe909bSMika Westerberg 	} else {
5498afe909bSMika Westerberg 		alloc = (val & TB_LC_SNK_ALLOCATION_SNK1_MASK) >>
5508afe909bSMika Westerberg 			TB_LC_SNK_ALLOCATION_SNK1_SHIFT;
5518afe909bSMika Westerberg 		if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK1_CM)
5528afe909bSMika Westerberg 			return 0;
5538afe909bSMika Westerberg 	}
5548afe909bSMika Westerberg 
5558afe909bSMika Westerberg 	return -EBUSY;
5568afe909bSMika Westerberg }
5578afe909bSMika Westerberg 
5588afe909bSMika Westerberg /**
5598afe909bSMika Westerberg  * tb_lc_dp_sink_query() - Is DP sink available for DP IN port
5608afe909bSMika Westerberg  * @sw: Switch whose DP sink is queried
5618afe909bSMika Westerberg  * @in: DP IN port to check
5628afe909bSMika Westerberg  *
5638afe909bSMika Westerberg  * Queries through LC SNK_ALLOCATION registers whether DP sink is available
5648afe909bSMika Westerberg  * for the given DP IN port or not.
5658afe909bSMika Westerberg  */
tb_lc_dp_sink_query(struct tb_switch * sw,struct tb_port * in)5668afe909bSMika Westerberg bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in)
5678afe909bSMika Westerberg {
5688afe909bSMika Westerberg 	int sink;
5698afe909bSMika Westerberg 
5708afe909bSMika Westerberg 	/*
5718afe909bSMika Westerberg 	 * For older generations sink is always available as there is no
5728afe909bSMika Westerberg 	 * allocation mechanism.
5738afe909bSMika Westerberg 	 */
5748afe909bSMika Westerberg 	if (sw->generation < 3)
5758afe909bSMika Westerberg 		return true;
5768afe909bSMika Westerberg 
5778afe909bSMika Westerberg 	sink = tb_lc_dp_sink_from_port(sw, in);
5788afe909bSMika Westerberg 	if (sink < 0)
5798afe909bSMika Westerberg 		return false;
5808afe909bSMika Westerberg 
5818afe909bSMika Westerberg 	return !tb_lc_dp_sink_available(sw, sink);
5828afe909bSMika Westerberg }
5838afe909bSMika Westerberg 
5848afe909bSMika Westerberg /**
5858afe909bSMika Westerberg  * tb_lc_dp_sink_alloc() - Allocate DP sink
5868afe909bSMika Westerberg  * @sw: Switch whose DP sink is allocated
5878afe909bSMika Westerberg  * @in: DP IN port the DP sink is allocated for
5888afe909bSMika Westerberg  *
5898afe909bSMika Westerberg  * Allocate DP sink for @in via LC SNK_ALLOCATION registers. If the
5908afe909bSMika Westerberg  * resource is available and allocation is successful returns %0. In all
5918afe909bSMika Westerberg  * other cases returs negative errno. In particular %-EBUSY is returned if
5928afe909bSMika Westerberg  * the resource was not available.
5938afe909bSMika Westerberg  */
tb_lc_dp_sink_alloc(struct tb_switch * sw,struct tb_port * in)5948afe909bSMika Westerberg int tb_lc_dp_sink_alloc(struct tb_switch *sw, struct tb_port *in)
5958afe909bSMika Westerberg {
5968afe909bSMika Westerberg 	int ret, sink;
5978afe909bSMika Westerberg 	u32 val;
5988afe909bSMika Westerberg 
5998afe909bSMika Westerberg 	if (sw->generation < 3)
6008afe909bSMika Westerberg 		return 0;
6018afe909bSMika Westerberg 
6028afe909bSMika Westerberg 	sink = tb_lc_dp_sink_from_port(sw, in);
6038afe909bSMika Westerberg 	if (sink < 0)
6048afe909bSMika Westerberg 		return sink;
6058afe909bSMika Westerberg 
6068afe909bSMika Westerberg 	ret = tb_lc_dp_sink_available(sw, sink);
6078afe909bSMika Westerberg 	if (ret)
6088afe909bSMika Westerberg 		return ret;
6098afe909bSMika Westerberg 
6108afe909bSMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
6118afe909bSMika Westerberg 			 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
6128afe909bSMika Westerberg 	if (ret)
6138afe909bSMika Westerberg 		return ret;
6148afe909bSMika Westerberg 
6158afe909bSMika Westerberg 	if (!sink) {
6168afe909bSMika Westerberg 		val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK;
6178afe909bSMika Westerberg 		val |= TB_LC_SNK_ALLOCATION_SNK0_CM;
6188afe909bSMika Westerberg 	} else {
6198afe909bSMika Westerberg 		val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK;
6208afe909bSMika Westerberg 		val |= TB_LC_SNK_ALLOCATION_SNK1_CM <<
6218afe909bSMika Westerberg 			TB_LC_SNK_ALLOCATION_SNK1_SHIFT;
6228afe909bSMika Westerberg 	}
6238afe909bSMika Westerberg 
6248afe909bSMika Westerberg 	ret = tb_sw_write(sw, &val, TB_CFG_SWITCH,
6258afe909bSMika Westerberg 			  sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
6268afe909bSMika Westerberg 
6278afe909bSMika Westerberg 	if (ret)
6288afe909bSMika Westerberg 		return ret;
6298afe909bSMika Westerberg 
6308afe909bSMika Westerberg 	tb_port_dbg(in, "sink %d allocated\n", sink);
6318afe909bSMika Westerberg 	return 0;
6328afe909bSMika Westerberg }
6338afe909bSMika Westerberg 
6348afe909bSMika Westerberg /**
6358afe909bSMika Westerberg  * tb_lc_dp_sink_dealloc() - De-allocate DP sink
6368afe909bSMika Westerberg  * @sw: Switch whose DP sink is de-allocated
6378afe909bSMika Westerberg  * @in: DP IN port whose DP sink is de-allocated
6388afe909bSMika Westerberg  *
6398afe909bSMika Westerberg  * De-allocate DP sink from @in using LC SNK_ALLOCATION registers.
6408afe909bSMika Westerberg  */
tb_lc_dp_sink_dealloc(struct tb_switch * sw,struct tb_port * in)6418afe909bSMika Westerberg int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in)
6428afe909bSMika Westerberg {
6438afe909bSMika Westerberg 	int ret, sink;
6448afe909bSMika Westerberg 	u32 val;
6458afe909bSMika Westerberg 
6468afe909bSMika Westerberg 	if (sw->generation < 3)
6478afe909bSMika Westerberg 		return 0;
6488afe909bSMika Westerberg 
6498afe909bSMika Westerberg 	sink = tb_lc_dp_sink_from_port(sw, in);
6508afe909bSMika Westerberg 	if (sink < 0)
6518afe909bSMika Westerberg 		return sink;
6528afe909bSMika Westerberg 
6538afe909bSMika Westerberg 	/* Needs to be owned by CM/SW */
6548afe909bSMika Westerberg 	ret = tb_lc_dp_sink_available(sw, sink);
6558afe909bSMika Westerberg 	if (ret)
6568afe909bSMika Westerberg 		return ret;
6578afe909bSMika Westerberg 
6588afe909bSMika Westerberg 	ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
6598afe909bSMika Westerberg 			 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
6608afe909bSMika Westerberg 	if (ret)
6618afe909bSMika Westerberg 		return ret;
6628afe909bSMika Westerberg 
6638afe909bSMika Westerberg 	if (!sink)
6648afe909bSMika Westerberg 		val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK;
6658afe909bSMika Westerberg 	else
6668afe909bSMika Westerberg 		val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK;
6678afe909bSMika Westerberg 
6688afe909bSMika Westerberg 	ret = tb_sw_write(sw, &val, TB_CFG_SWITCH,
6698afe909bSMika Westerberg 			  sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
6708afe909bSMika Westerberg 	if (ret)
6718afe909bSMika Westerberg 		return ret;
6728afe909bSMika Westerberg 
6738afe909bSMika Westerberg 	tb_port_dbg(in, "sink %d de-allocated\n", sink);
6748afe909bSMika Westerberg 	return 0;
6758afe909bSMika Westerberg }
6761cb36293SMario Limonciello 
6771cb36293SMario Limonciello /**
6781cb36293SMario Limonciello  * tb_lc_force_power() - Forces LC to be powered on
6791cb36293SMario Limonciello  * @sw: Thunderbolt switch
6801cb36293SMario Limonciello  *
6811cb36293SMario Limonciello  * This is useful to let authentication cycle pass even without
6821cb36293SMario Limonciello  * a Thunderbolt link present.
6831cb36293SMario Limonciello  */
tb_lc_force_power(struct tb_switch * sw)6841cb36293SMario Limonciello int tb_lc_force_power(struct tb_switch *sw)
6851cb36293SMario Limonciello {
6861cb36293SMario Limonciello 	u32 in = 0xffff;
6871cb36293SMario Limonciello 
6881cb36293SMario Limonciello 	return tb_sw_write(sw, &in, TB_CFG_SWITCH, TB_LC_POWER, 1);
6891cb36293SMario Limonciello }
690