Commit | Line | Data |
---|---|---|
a25c8b2f AN |
1 | /* |
2 | * Thunderbolt Cactus Ridge driver - switch/port utility functions | |
3 | * | |
4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | |
5 | */ | |
6 | ||
7 | #include <linux/delay.h> | |
10fefe56 | 8 | #include <linux/slab.h> |
a25c8b2f AN |
9 | |
10 | #include "tb.h" | |
11 | ||
12 | /* port utility functions */ | |
13 | ||
14 | static const char *tb_port_type(struct tb_regs_port_header *port) | |
15 | { | |
16 | switch (port->type >> 16) { | |
17 | case 0: | |
18 | switch ((u8) port->type) { | |
19 | case 0: | |
20 | return "Inactive"; | |
21 | case 1: | |
22 | return "Port"; | |
23 | case 2: | |
24 | return "NHI"; | |
25 | default: | |
26 | return "unknown"; | |
27 | } | |
28 | case 0x2: | |
29 | return "Ethernet"; | |
30 | case 0x8: | |
31 | return "SATA"; | |
32 | case 0xe: | |
33 | return "DP/HDMI"; | |
34 | case 0x10: | |
35 | return "PCIe"; | |
36 | case 0x20: | |
37 | return "USB"; | |
38 | default: | |
39 | return "unknown"; | |
40 | } | |
41 | } | |
42 | ||
43 | static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) | |
44 | { | |
45 | tb_info(tb, | |
46 | " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", | |
47 | port->port_number, port->vendor_id, port->device_id, | |
48 | port->revision, port->thunderbolt_version, tb_port_type(port), | |
49 | port->type); | |
50 | tb_info(tb, " Max hop id (in/out): %d/%d\n", | |
51 | port->max_in_hop_id, port->max_out_hop_id); | |
52 | tb_info(tb, " Max counters: %d\n", port->max_counters); | |
53 | tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); | |
54 | } | |
55 | ||
9da672a4 AN |
56 | /** |
57 | * tb_port_state() - get connectedness state of a port | |
58 | * | |
59 | * The port must have a TB_CAP_PHY (i.e. it should be a real port). | |
60 | * | |
61 | * Return: Returns an enum tb_port_state on success or an error code on failure. | |
62 | */ | |
63 | static int tb_port_state(struct tb_port *port) | |
64 | { | |
65 | struct tb_cap_phy phy; | |
66 | int res; | |
67 | if (port->cap_phy == 0) { | |
68 | tb_port_WARN(port, "does not have a PHY\n"); | |
69 | return -EINVAL; | |
70 | } | |
71 | res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2); | |
72 | if (res) | |
73 | return res; | |
74 | return phy.state; | |
75 | } | |
76 | ||
77 | /** | |
78 | * tb_wait_for_port() - wait for a port to become ready | |
79 | * | |
80 | * Wait up to 1 second for a port to reach state TB_PORT_UP. If | |
81 | * wait_if_unplugged is set then we also wait if the port is in state | |
82 | * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after | |
83 | * switch resume). Otherwise we only wait if a device is registered but the link | |
84 | * has not yet been established. | |
85 | * | |
86 | * Return: Returns an error code on failure. Returns 0 if the port is not | |
87 | * connected or failed to reach state TB_PORT_UP within one second. Returns 1 | |
88 | * if the port is connected and in state TB_PORT_UP. | |
89 | */ | |
90 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) | |
91 | { | |
92 | int retries = 10; | |
93 | int state; | |
94 | if (!port->cap_phy) { | |
95 | tb_port_WARN(port, "does not have PHY\n"); | |
96 | return -EINVAL; | |
97 | } | |
98 | if (tb_is_upstream_port(port)) { | |
99 | tb_port_WARN(port, "is the upstream port\n"); | |
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | while (retries--) { | |
104 | state = tb_port_state(port); | |
105 | if (state < 0) | |
106 | return state; | |
107 | if (state == TB_PORT_DISABLED) { | |
108 | tb_port_info(port, "is disabled (state: 0)\n"); | |
109 | return 0; | |
110 | } | |
111 | if (state == TB_PORT_UNPLUGGED) { | |
112 | if (wait_if_unplugged) { | |
113 | /* used during resume */ | |
114 | tb_port_info(port, | |
115 | "is unplugged (state: 7), retrying...\n"); | |
116 | msleep(100); | |
117 | continue; | |
118 | } | |
119 | tb_port_info(port, "is unplugged (state: 7)\n"); | |
120 | return 0; | |
121 | } | |
122 | if (state == TB_PORT_UP) { | |
123 | tb_port_info(port, | |
124 | "is connected, link is up (state: 2)\n"); | |
125 | return 1; | |
126 | } | |
127 | ||
128 | /* | |
129 | * After plug-in the state is TB_PORT_CONNECTING. Give it some | |
130 | * time. | |
131 | */ | |
132 | tb_port_info(port, | |
133 | "is connected, link is not up (state: %d), retrying...\n", | |
134 | state); | |
135 | msleep(100); | |
136 | } | |
137 | tb_port_warn(port, | |
138 | "failed to reach state TB_PORT_UP. Ignoring port...\n"); | |
139 | return 0; | |
140 | } | |
141 | ||
520b6702 AN |
142 | /** |
143 | * tb_port_add_nfc_credits() - add/remove non flow controlled credits to port | |
144 | * | |
145 | * Change the number of NFC credits allocated to @port by @credits. To remove | |
146 | * NFC credits pass a negative amount of credits. | |
147 | * | |
148 | * Return: Returns 0 on success or an error code on failure. | |
149 | */ | |
150 | int tb_port_add_nfc_credits(struct tb_port *port, int credits) | |
151 | { | |
152 | if (credits == 0) | |
153 | return 0; | |
154 | tb_port_info(port, | |
155 | "adding %#x NFC credits (%#x -> %#x)", | |
156 | credits, | |
157 | port->config.nfc_credits, | |
158 | port->config.nfc_credits + credits); | |
159 | port->config.nfc_credits += credits; | |
160 | return tb_port_write(port, &port->config.nfc_credits, | |
161 | TB_CFG_PORT, 4, 1); | |
162 | } | |
163 | ||
164 | /** | |
165 | * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER | |
166 | * | |
167 | * Return: Returns 0 on success or an error code on failure. | |
168 | */ | |
169 | int tb_port_clear_counter(struct tb_port *port, int counter) | |
170 | { | |
171 | u32 zero[3] = { 0, 0, 0 }; | |
172 | tb_port_info(port, "clearing counter %d\n", counter); | |
173 | return tb_port_write(port, zero, TB_CFG_COUNTERS, 3 * counter, 3); | |
174 | } | |
175 | ||
a25c8b2f AN |
176 | /** |
177 | * tb_init_port() - initialize a port | |
178 | * | |
179 | * This is a helper method for tb_switch_alloc. Does not check or initialize | |
180 | * any downstream switches. | |
181 | * | |
182 | * Return: Returns 0 on success or an error code on failure. | |
183 | */ | |
343fcb8c | 184 | static int tb_init_port(struct tb_port *port) |
a25c8b2f AN |
185 | { |
186 | int res; | |
9da672a4 | 187 | int cap; |
343fcb8c | 188 | |
a25c8b2f AN |
189 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); |
190 | if (res) | |
191 | return res; | |
192 | ||
9da672a4 | 193 | /* Port 0 is the switch itself and has no PHY. */ |
343fcb8c | 194 | if (port->config.type == TB_TYPE_PORT && port->port != 0) { |
9da672a4 AN |
195 | cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); |
196 | ||
197 | if (cap > 0) | |
198 | port->cap_phy = cap; | |
199 | else | |
200 | tb_port_WARN(port, "non switch port without a PHY\n"); | |
201 | } | |
202 | ||
343fcb8c | 203 | tb_dump_port(port->sw->tb, &port->config); |
a25c8b2f AN |
204 | |
205 | /* TODO: Read dual link port, DP port and more from EEPROM. */ | |
206 | return 0; | |
207 | ||
208 | } | |
209 | ||
210 | /* switch utility functions */ | |
211 | ||
212 | static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) | |
213 | { | |
214 | tb_info(tb, | |
215 | " Switch: %x:%x (Revision: %d, TB Version: %d)\n", | |
216 | sw->vendor_id, sw->device_id, sw->revision, | |
217 | sw->thunderbolt_version); | |
218 | tb_info(tb, " Max Port Number: %d\n", sw->max_port_number); | |
219 | tb_info(tb, " Config:\n"); | |
220 | tb_info(tb, | |
221 | " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", | |
222 | sw->upstream_port_number, sw->depth, | |
223 | (((u64) sw->route_hi) << 32) | sw->route_lo, | |
224 | sw->enabled, sw->plug_events_delay); | |
225 | tb_info(tb, | |
226 | " unknown1: %#x unknown4: %#x\n", | |
227 | sw->__unknown1, sw->__unknown4); | |
228 | } | |
229 | ||
23dd5bb4 AN |
230 | /** |
231 | * reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET | |
232 | * | |
233 | * Return: Returns 0 on success or an error code on failure. | |
234 | */ | |
235 | int tb_switch_reset(struct tb *tb, u64 route) | |
236 | { | |
237 | struct tb_cfg_result res; | |
238 | struct tb_regs_switch_header header = { | |
239 | header.route_hi = route >> 32, | |
240 | header.route_lo = route, | |
241 | header.enabled = true, | |
242 | }; | |
243 | tb_info(tb, "resetting switch at %llx\n", route); | |
244 | res.err = tb_cfg_write(tb->ctl, ((u32 *) &header) + 2, route, | |
245 | 0, 2, 2, 2); | |
246 | if (res.err) | |
247 | return res.err; | |
248 | res = tb_cfg_reset(tb->ctl, route, TB_CFG_DEFAULT_TIMEOUT); | |
249 | if (res.err > 0) | |
250 | return -EIO; | |
251 | return res.err; | |
252 | } | |
253 | ||
053596d9 AN |
254 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route) |
255 | { | |
256 | u8 next_port = route; /* | |
257 | * Routes use a stride of 8 bits, | |
258 | * eventhough a port index has 6 bits at most. | |
259 | * */ | |
260 | if (route == 0) | |
261 | return sw; | |
262 | if (next_port > sw->config.max_port_number) | |
c9c2deef | 263 | return NULL; |
053596d9 | 264 | if (tb_is_upstream_port(&sw->ports[next_port])) |
c9c2deef | 265 | return NULL; |
053596d9 | 266 | if (!sw->ports[next_port].remote) |
c9c2deef | 267 | return NULL; |
053596d9 AN |
268 | return get_switch_at_route(sw->ports[next_port].remote->sw, |
269 | route >> TB_ROUTE_SHIFT); | |
270 | } | |
271 | ||
ca389f71 AN |
272 | /** |
273 | * tb_plug_events_active() - enable/disable plug events on a switch | |
274 | * | |
275 | * Also configures a sane plug_events_delay of 255ms. | |
276 | * | |
277 | * Return: Returns 0 on success or an error code on failure. | |
278 | */ | |
279 | static int tb_plug_events_active(struct tb_switch *sw, bool active) | |
280 | { | |
281 | u32 data; | |
282 | int res; | |
283 | ||
284 | sw->config.plug_events_delay = 0xff; | |
285 | res = tb_sw_write(sw, ((u32 *) &sw->config) + 4, TB_CFG_SWITCH, 4, 1); | |
286 | if (res) | |
287 | return res; | |
288 | ||
289 | res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1); | |
290 | if (res) | |
291 | return res; | |
292 | ||
293 | if (active) { | |
294 | data = data & 0xFFFFFF83; | |
295 | switch (sw->config.device_id) { | |
296 | case 0x1513: | |
297 | case 0x151a: | |
298 | case 0x1549: | |
299 | break; | |
300 | default: | |
301 | data |= 4; | |
302 | } | |
303 | } else { | |
304 | data = data | 0x7c; | |
305 | } | |
306 | return tb_sw_write(sw, &data, TB_CFG_SWITCH, | |
307 | sw->cap_plug_events + 1, 1); | |
308 | } | |
309 | ||
310 | ||
a25c8b2f AN |
311 | /** |
312 | * tb_switch_free() - free a tb_switch and all downstream switches | |
313 | */ | |
314 | void tb_switch_free(struct tb_switch *sw) | |
315 | { | |
316 | int i; | |
317 | /* port 0 is the switch itself and never has a remote */ | |
318 | for (i = 1; i <= sw->config.max_port_number; i++) { | |
319 | if (tb_is_upstream_port(&sw->ports[i])) | |
320 | continue; | |
321 | if (sw->ports[i].remote) | |
322 | tb_switch_free(sw->ports[i].remote->sw); | |
323 | sw->ports[i].remote = NULL; | |
324 | } | |
325 | ||
053596d9 AN |
326 | if (!sw->is_unplugged) |
327 | tb_plug_events_active(sw, false); | |
ca389f71 | 328 | |
a25c8b2f | 329 | kfree(sw->ports); |
343fcb8c | 330 | kfree(sw->drom); |
a25c8b2f AN |
331 | kfree(sw); |
332 | } | |
333 | ||
334 | /** | |
335 | * tb_switch_alloc() - allocate and initialize a switch | |
336 | * | |
337 | * Return: Returns a NULL on failure. | |
338 | */ | |
339 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | |
340 | { | |
341 | int i; | |
ca389f71 | 342 | int cap; |
a25c8b2f AN |
343 | struct tb_switch *sw; |
344 | int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route); | |
345 | if (upstream_port < 0) | |
346 | return NULL; | |
347 | ||
348 | sw = kzalloc(sizeof(*sw), GFP_KERNEL); | |
349 | if (!sw) | |
350 | return NULL; | |
351 | ||
352 | sw->tb = tb; | |
353 | if (tb_cfg_read(tb->ctl, &sw->config, route, 0, 2, 0, 5)) | |
354 | goto err; | |
355 | tb_info(tb, | |
356 | "initializing Switch at %#llx (depth: %d, up port: %d)\n", | |
357 | route, tb_route_length(route), upstream_port); | |
358 | tb_info(tb, "old switch config:\n"); | |
359 | tb_dump_switch(tb, &sw->config); | |
360 | ||
361 | /* configure switch */ | |
362 | sw->config.upstream_port_number = upstream_port; | |
363 | sw->config.depth = tb_route_length(route); | |
364 | sw->config.route_lo = route; | |
365 | sw->config.route_hi = route >> 32; | |
366 | sw->config.enabled = 1; | |
367 | /* from here on we may use the tb_sw_* functions & macros */ | |
368 | ||
369 | if (sw->config.vendor_id != 0x8086) | |
370 | tb_sw_warn(sw, "unknown switch vendor id %#x\n", | |
371 | sw->config.vendor_id); | |
372 | ||
373 | if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549) | |
374 | tb_sw_warn(sw, "unsupported switch device id %#x\n", | |
375 | sw->config.device_id); | |
376 | ||
377 | /* upload configuration */ | |
378 | if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3)) | |
379 | goto err; | |
380 | ||
381 | /* initialize ports */ | |
382 | sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), | |
343fcb8c | 383 | GFP_KERNEL); |
a25c8b2f AN |
384 | if (!sw->ports) |
385 | goto err; | |
386 | ||
387 | for (i = 0; i <= sw->config.max_port_number; i++) { | |
343fcb8c AN |
388 | /* minimum setup for tb_find_cap and tb_drom_read to work */ |
389 | sw->ports[i].sw = sw; | |
390 | sw->ports[i].port = i; | |
a25c8b2f AN |
391 | } |
392 | ||
ca389f71 AN |
393 | cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); |
394 | if (cap < 0) { | |
395 | tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); | |
396 | goto err; | |
397 | } | |
398 | sw->cap_plug_events = cap; | |
399 | ||
343fcb8c AN |
400 | /* read drom */ |
401 | if (tb_drom_read(sw)) | |
402 | tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n"); | |
403 | tb_sw_info(sw, "uid: %#llx\n", sw->uid); | |
404 | ||
405 | for (i = 0; i <= sw->config.max_port_number; i++) { | |
406 | if (sw->ports[i].disabled) { | |
407 | tb_port_info(&sw->ports[i], "disabled by eeprom\n"); | |
408 | continue; | |
409 | } | |
410 | if (tb_init_port(&sw->ports[i])) | |
411 | goto err; | |
412 | } | |
413 | ||
414 | /* TODO: I2C, IECS, link controller */ | |
c90553b3 | 415 | |
ca389f71 AN |
416 | if (tb_plug_events_active(sw, true)) |
417 | goto err; | |
418 | ||
a25c8b2f AN |
419 | return sw; |
420 | err: | |
421 | kfree(sw->ports); | |
343fcb8c | 422 | kfree(sw->drom); |
a25c8b2f AN |
423 | kfree(sw); |
424 | return NULL; | |
425 | } | |
426 | ||
053596d9 AN |
427 | /** |
428 | * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches | |
429 | */ | |
430 | void tb_sw_set_unpplugged(struct tb_switch *sw) | |
431 | { | |
432 | int i; | |
433 | if (sw == sw->tb->root_switch) { | |
434 | tb_sw_WARN(sw, "cannot unplug root switch\n"); | |
435 | return; | |
436 | } | |
437 | if (sw->is_unplugged) { | |
438 | tb_sw_WARN(sw, "is_unplugged already set\n"); | |
439 | return; | |
440 | } | |
441 | sw->is_unplugged = true; | |
442 | for (i = 0; i <= sw->config.max_port_number; i++) { | |
443 | if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) | |
444 | tb_sw_set_unpplugged(sw->ports[i].remote->sw); | |
445 | } | |
446 | } | |
447 | ||
23dd5bb4 AN |
448 | int tb_switch_resume(struct tb_switch *sw) |
449 | { | |
450 | int i, err; | |
451 | u64 uid; | |
452 | tb_sw_info(sw, "resuming switch\n"); | |
453 | ||
cd22e73b | 454 | err = tb_drom_read_uid_only(sw, &uid); |
23dd5bb4 AN |
455 | if (err) { |
456 | tb_sw_warn(sw, "uid read failed\n"); | |
457 | return err; | |
458 | } | |
459 | if (sw->uid != uid) { | |
460 | tb_sw_info(sw, | |
461 | "changed while suspended (uid %#llx -> %#llx)\n", | |
462 | sw->uid, uid); | |
463 | return -ENODEV; | |
464 | } | |
465 | ||
466 | /* upload configuration */ | |
467 | err = tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3); | |
468 | if (err) | |
469 | return err; | |
470 | ||
471 | err = tb_plug_events_active(sw, true); | |
472 | if (err) | |
473 | return err; | |
474 | ||
475 | /* check for surviving downstream switches */ | |
476 | for (i = 1; i <= sw->config.max_port_number; i++) { | |
477 | struct tb_port *port = &sw->ports[i]; | |
478 | if (tb_is_upstream_port(port)) | |
479 | continue; | |
480 | if (!port->remote) | |
481 | continue; | |
482 | if (tb_wait_for_port(port, true) <= 0 | |
483 | || tb_switch_resume(port->remote->sw)) { | |
484 | tb_port_warn(port, | |
485 | "lost during suspend, disconnecting\n"); | |
486 | tb_sw_set_unpplugged(port->remote->sw); | |
487 | } | |
488 | } | |
489 | return 0; | |
490 | } | |
491 | ||
492 | void tb_switch_suspend(struct tb_switch *sw) | |
493 | { | |
494 | int i, err; | |
495 | err = tb_plug_events_active(sw, false); | |
496 | if (err) | |
497 | return; | |
498 | ||
499 | for (i = 1; i <= sw->config.max_port_number; i++) { | |
500 | if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) | |
501 | tb_switch_suspend(sw->ports[i].remote->sw); | |
502 | } | |
503 | /* | |
504 | * TODO: invoke tb_cfg_prepare_to_sleep here? does not seem to have any | |
505 | * effect? | |
506 | */ | |
507 | } |