diff options
Diffstat (limited to 'drivers/thunderbolt/switch.c')
-rw-r--r-- | drivers/thunderbolt/switch.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c new file mode 100644 index 000000000000..aeb982969629 --- /dev/null +++ b/drivers/thunderbolt/switch.c | |||
@@ -0,0 +1,507 @@ | |||
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> | ||
8 | #include <linux/slab.h> | ||
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 | |||
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 | |||
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 | |||
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 | */ | ||
184 | static int tb_init_port(struct tb_port *port) | ||
185 | { | ||
186 | int res; | ||
187 | int cap; | ||
188 | |||
189 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); | ||
190 | if (res) | ||
191 | return res; | ||
192 | |||
193 | /* Port 0 is the switch itself and has no PHY. */ | ||
194 | if (port->config.type == TB_TYPE_PORT && port->port != 0) { | ||
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 | |||
203 | tb_dump_port(port->sw->tb, &port->config); | ||
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 | |||
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 | |||
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) | ||
263 | return NULL; | ||
264 | if (tb_is_upstream_port(&sw->ports[next_port])) | ||
265 | return NULL; | ||
266 | if (!sw->ports[next_port].remote) | ||
267 | return NULL; | ||
268 | return get_switch_at_route(sw->ports[next_port].remote->sw, | ||
269 | route >> TB_ROUTE_SHIFT); | ||
270 | } | ||
271 | |||
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 | |||
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 | |||
326 | if (!sw->is_unplugged) | ||
327 | tb_plug_events_active(sw, false); | ||
328 | |||
329 | kfree(sw->ports); | ||
330 | kfree(sw->drom); | ||
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; | ||
342 | int cap; | ||
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), | ||
383 | GFP_KERNEL); | ||
384 | if (!sw->ports) | ||
385 | goto err; | ||
386 | |||
387 | for (i = 0; i <= sw->config.max_port_number; i++) { | ||
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; | ||
391 | } | ||
392 | |||
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 | |||
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 */ | ||
415 | |||
416 | if (tb_plug_events_active(sw, true)) | ||
417 | goto err; | ||
418 | |||
419 | return sw; | ||
420 | err: | ||
421 | kfree(sw->ports); | ||
422 | kfree(sw->drom); | ||
423 | kfree(sw); | ||
424 | return NULL; | ||
425 | } | ||
426 | |||
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 | |||
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 | |||
454 | err = tb_drom_read_uid_only(sw, &uid); | ||
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 | } | ||