1
2
3
4
5
6
7
8
9#include "tb.h"
10
11
12
13
14
15
16int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid)
17{
18 if (!sw->cap_lc)
19 return -EINVAL;
20 return tb_sw_read(sw, uuid, TB_CFG_SWITCH, sw->cap_lc + TB_LC_FUSE, 4);
21}
22
23static int read_lc_desc(struct tb_switch *sw, u32 *desc)
24{
25 if (!sw->cap_lc)
26 return -EINVAL;
27 return tb_sw_read(sw, desc, TB_CFG_SWITCH, sw->cap_lc + TB_LC_DESC, 1);
28}
29
30static int find_port_lc_cap(struct tb_port *port)
31{
32 struct tb_switch *sw = port->sw;
33 int start, phys, ret, size;
34 u32 desc;
35
36 ret = read_lc_desc(sw, &desc);
37 if (ret)
38 return ret;
39
40
41 start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
42 size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
43 phys = tb_phy_port_from_link(port->port);
44
45 return sw->cap_lc + start + phys * size;
46}
47
48static int tb_lc_set_port_configured(struct tb_port *port, bool configured)
49{
50 bool upstream = tb_is_upstream_port(port);
51 struct tb_switch *sw = port->sw;
52 u32 ctrl, lane;
53 int cap, ret;
54
55 if (sw->generation < 2)
56 return 0;
57
58 cap = find_port_lc_cap(port);
59 if (cap < 0)
60 return cap;
61
62 ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
63 if (ret)
64 return ret;
65
66
67 if (port->port % 2)
68 lane = TB_LC_SX_CTRL_L1C;
69 else
70 lane = TB_LC_SX_CTRL_L2C;
71
72 if (configured) {
73 ctrl |= lane;
74 if (upstream)
75 ctrl |= TB_LC_SX_CTRL_UPSTREAM;
76 } else {
77 ctrl &= ~lane;
78 if (upstream)
79 ctrl &= ~TB_LC_SX_CTRL_UPSTREAM;
80 }
81
82 return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
83}
84
85
86
87
88
89
90
91int tb_lc_configure_port(struct tb_port *port)
92{
93 return tb_lc_set_port_configured(port, true);
94}
95
96
97
98
99
100
101
102void tb_lc_unconfigure_port(struct tb_port *port)
103{
104 tb_lc_set_port_configured(port, false);
105}
106
107static int tb_lc_set_xdomain_configured(struct tb_port *port, bool configure)
108{
109 struct tb_switch *sw = port->sw;
110 u32 ctrl, lane;
111 int cap, ret;
112
113 if (sw->generation < 2)
114 return 0;
115
116 cap = find_port_lc_cap(port);
117 if (cap < 0)
118 return cap;
119
120 ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
121 if (ret)
122 return ret;
123
124
125 if (port->port % 2)
126 lane = TB_LC_SX_CTRL_L1D;
127 else
128 lane = TB_LC_SX_CTRL_L2D;
129
130 if (configure)
131 ctrl |= lane;
132 else
133 ctrl &= ~lane;
134
135 return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, cap + TB_LC_SX_CTRL, 1);
136}
137
138
139
140
141
142
143
144
145int tb_lc_configure_xdomain(struct tb_port *port)
146{
147 return tb_lc_set_xdomain_configured(port, true);
148}
149
150
151
152
153
154
155
156void tb_lc_unconfigure_xdomain(struct tb_port *port)
157{
158 tb_lc_set_xdomain_configured(port, false);
159}
160
161static int tb_lc_set_wake_one(struct tb_switch *sw, unsigned int offset,
162 unsigned int flags)
163{
164 u32 ctrl;
165 int ret;
166
167
168
169
170
171 ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
172 offset + TB_LC_SX_CTRL, 1);
173 if (ret)
174 return ret;
175
176 ctrl &= ~(TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD | TB_LC_SX_CTRL_WOP |
177 TB_LC_SX_CTRL_WOU4);
178
179 if (flags & TB_WAKE_ON_CONNECT)
180 ctrl |= TB_LC_SX_CTRL_WOC | TB_LC_SX_CTRL_WOD;
181 if (flags & TB_WAKE_ON_USB4)
182 ctrl |= TB_LC_SX_CTRL_WOU4;
183 if (flags & TB_WAKE_ON_PCIE)
184 ctrl |= TB_LC_SX_CTRL_WOP;
185
186 return tb_sw_write(sw, &ctrl, TB_CFG_SWITCH, offset + TB_LC_SX_CTRL, 1);
187}
188
189
190
191
192
193
194
195
196int tb_lc_set_wake(struct tb_switch *sw, unsigned int flags)
197{
198 int start, size, nlc, ret, i;
199 u32 desc;
200
201 if (sw->generation < 2)
202 return 0;
203
204 if (!tb_route(sw))
205 return 0;
206
207 ret = read_lc_desc(sw, &desc);
208 if (ret)
209 return ret;
210
211
212 nlc = desc & TB_LC_DESC_NLC_MASK;
213 start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
214 size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
215
216
217 for (i = 0; i < nlc; i++) {
218 unsigned int offset = sw->cap_lc + start + i * size;
219
220 ret = tb_lc_set_wake_one(sw, offset, flags);
221 if (ret)
222 return ret;
223 }
224
225 return 0;
226}
227
228
229
230
231
232
233
234
235int tb_lc_set_sleep(struct tb_switch *sw)
236{
237 int start, size, nlc, ret, i;
238 u32 desc;
239
240 if (sw->generation < 2)
241 return 0;
242
243 ret = read_lc_desc(sw, &desc);
244 if (ret)
245 return ret;
246
247
248 nlc = desc & TB_LC_DESC_NLC_MASK;
249 start = (desc & TB_LC_DESC_SIZE_MASK) >> TB_LC_DESC_SIZE_SHIFT;
250 size = (desc & TB_LC_DESC_PORT_SIZE_MASK) >> TB_LC_DESC_PORT_SIZE_SHIFT;
251
252
253 for (i = 0; i < nlc; i++) {
254 unsigned int offset = sw->cap_lc + start + i * size;
255 u32 ctrl;
256
257 ret = tb_sw_read(sw, &ctrl, TB_CFG_SWITCH,
258 offset + TB_LC_SX_CTRL, 1);
259 if (ret)
260 return ret;
261
262 ctrl |= TB_LC_SX_CTRL_SLP;
263 ret = tb_sw_write(sw, &ctrl, TB_CFG_SWITCH,
264 offset + TB_LC_SX_CTRL, 1);
265 if (ret)
266 return ret;
267 }
268
269 return 0;
270}
271
272
273
274
275
276
277
278
279bool tb_lc_lane_bonding_possible(struct tb_switch *sw)
280{
281 struct tb_port *up;
282 int cap, ret;
283 u32 val;
284
285 if (sw->generation < 2)
286 return false;
287
288 up = tb_upstream_port(sw);
289 cap = find_port_lc_cap(up);
290 if (cap < 0)
291 return false;
292
293 ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, cap + TB_LC_PORT_ATTR, 1);
294 if (ret)
295 return false;
296
297 return !!(val & TB_LC_PORT_ATTR_BE);
298}
299
300static int tb_lc_dp_sink_from_port(const struct tb_switch *sw,
301 struct tb_port *in)
302{
303 struct tb_port *port;
304
305
306 tb_switch_for_each_port(sw, port) {
307 if (tb_port_is_dpin(port))
308 return in != port;
309 }
310
311 return -EINVAL;
312}
313
314static int tb_lc_dp_sink_available(struct tb_switch *sw, int sink)
315{
316 u32 val, alloc;
317 int ret;
318
319 ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
320 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
321 if (ret)
322 return ret;
323
324
325
326
327
328 if (!sink) {
329 alloc = val & TB_LC_SNK_ALLOCATION_SNK0_MASK;
330 if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK0_CM)
331 return 0;
332 } else {
333 alloc = (val & TB_LC_SNK_ALLOCATION_SNK1_MASK) >>
334 TB_LC_SNK_ALLOCATION_SNK1_SHIFT;
335 if (!alloc || alloc == TB_LC_SNK_ALLOCATION_SNK1_CM)
336 return 0;
337 }
338
339 return -EBUSY;
340}
341
342
343
344
345
346
347
348
349
350bool tb_lc_dp_sink_query(struct tb_switch *sw, struct tb_port *in)
351{
352 int sink;
353
354
355
356
357
358 if (sw->generation < 3)
359 return true;
360
361 sink = tb_lc_dp_sink_from_port(sw, in);
362 if (sink < 0)
363 return false;
364
365 return !tb_lc_dp_sink_available(sw, sink);
366}
367
368
369
370
371
372
373
374
375
376
377
378int tb_lc_dp_sink_alloc(struct tb_switch *sw, struct tb_port *in)
379{
380 int ret, sink;
381 u32 val;
382
383 if (sw->generation < 3)
384 return 0;
385
386 sink = tb_lc_dp_sink_from_port(sw, in);
387 if (sink < 0)
388 return sink;
389
390 ret = tb_lc_dp_sink_available(sw, sink);
391 if (ret)
392 return ret;
393
394 ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
395 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
396 if (ret)
397 return ret;
398
399 if (!sink) {
400 val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK;
401 val |= TB_LC_SNK_ALLOCATION_SNK0_CM;
402 } else {
403 val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK;
404 val |= TB_LC_SNK_ALLOCATION_SNK1_CM <<
405 TB_LC_SNK_ALLOCATION_SNK1_SHIFT;
406 }
407
408 ret = tb_sw_write(sw, &val, TB_CFG_SWITCH,
409 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
410
411 if (ret)
412 return ret;
413
414 tb_port_dbg(in, "sink %d allocated\n", sink);
415 return 0;
416}
417
418
419
420
421
422
423
424
425int tb_lc_dp_sink_dealloc(struct tb_switch *sw, struct tb_port *in)
426{
427 int ret, sink;
428 u32 val;
429
430 if (sw->generation < 3)
431 return 0;
432
433 sink = tb_lc_dp_sink_from_port(sw, in);
434 if (sink < 0)
435 return sink;
436
437
438 ret = tb_lc_dp_sink_available(sw, sink);
439 if (ret)
440 return ret;
441
442 ret = tb_sw_read(sw, &val, TB_CFG_SWITCH,
443 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
444 if (ret)
445 return ret;
446
447 if (!sink)
448 val &= ~TB_LC_SNK_ALLOCATION_SNK0_MASK;
449 else
450 val &= ~TB_LC_SNK_ALLOCATION_SNK1_MASK;
451
452 ret = tb_sw_write(sw, &val, TB_CFG_SWITCH,
453 sw->cap_lc + TB_LC_SNK_ALLOCATION, 1);
454 if (ret)
455 return ret;
456
457 tb_port_dbg(in, "sink %d de-allocated\n", sink);
458 return 0;
459}
460
461
462
463
464
465
466
467
468int tb_lc_force_power(struct tb_switch *sw)
469{
470 u32 in = 0xffff;
471
472 return tb_sw_write(sw, &in, TB_CFG_SWITCH, TB_LC_POWER, 1);
473}
474