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> | |
8 | ||
9 | #include "tb.h" | |
10 | ||
11 | /* port utility functions */ | |
12 | ||
13 | static const char *tb_port_type(struct tb_regs_port_header *port) | |
14 | { | |
15 | switch (port->type >> 16) { | |
16 | case 0: | |
17 | switch ((u8) port->type) { | |
18 | case 0: | |
19 | return "Inactive"; | |
20 | case 1: | |
21 | return "Port"; | |
22 | case 2: | |
23 | return "NHI"; | |
24 | default: | |
25 | return "unknown"; | |
26 | } | |
27 | case 0x2: | |
28 | return "Ethernet"; | |
29 | case 0x8: | |
30 | return "SATA"; | |
31 | case 0xe: | |
32 | return "DP/HDMI"; | |
33 | case 0x10: | |
34 | return "PCIe"; | |
35 | case 0x20: | |
36 | return "USB"; | |
37 | default: | |
38 | return "unknown"; | |
39 | } | |
40 | } | |
41 | ||
42 | static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) | |
43 | { | |
44 | tb_info(tb, | |
45 | " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", | |
46 | port->port_number, port->vendor_id, port->device_id, | |
47 | port->revision, port->thunderbolt_version, tb_port_type(port), | |
48 | port->type); | |
49 | tb_info(tb, " Max hop id (in/out): %d/%d\n", | |
50 | port->max_in_hop_id, port->max_out_hop_id); | |
51 | tb_info(tb, " Max counters: %d\n", port->max_counters); | |
52 | tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); | |
53 | } | |
54 | ||
9da672a4 AN |
55 | /** |
56 | * tb_port_state() - get connectedness state of a port | |
57 | * | |
58 | * The port must have a TB_CAP_PHY (i.e. it should be a real port). | |
59 | * | |
60 | * Return: Returns an enum tb_port_state on success or an error code on failure. | |
61 | */ | |
62 | static int tb_port_state(struct tb_port *port) | |
63 | { | |
64 | struct tb_cap_phy phy; | |
65 | int res; | |
66 | if (port->cap_phy == 0) { | |
67 | tb_port_WARN(port, "does not have a PHY\n"); | |
68 | return -EINVAL; | |
69 | } | |
70 | res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2); | |
71 | if (res) | |
72 | return res; | |
73 | return phy.state; | |
74 | } | |
75 | ||
76 | /** | |
77 | * tb_wait_for_port() - wait for a port to become ready | |
78 | * | |
79 | * Wait up to 1 second for a port to reach state TB_PORT_UP. If | |
80 | * wait_if_unplugged is set then we also wait if the port is in state | |
81 | * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after | |
82 | * switch resume). Otherwise we only wait if a device is registered but the link | |
83 | * has not yet been established. | |
84 | * | |
85 | * Return: Returns an error code on failure. Returns 0 if the port is not | |
86 | * connected or failed to reach state TB_PORT_UP within one second. Returns 1 | |
87 | * if the port is connected and in state TB_PORT_UP. | |
88 | */ | |
89 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) | |
90 | { | |
91 | int retries = 10; | |
92 | int state; | |
93 | if (!port->cap_phy) { | |
94 | tb_port_WARN(port, "does not have PHY\n"); | |
95 | return -EINVAL; | |
96 | } | |
97 | if (tb_is_upstream_port(port)) { | |
98 | tb_port_WARN(port, "is the upstream port\n"); | |
99 | return -EINVAL; | |
100 | } | |
101 | ||
102 | while (retries--) { | |
103 | state = tb_port_state(port); | |
104 | if (state < 0) | |
105 | return state; | |
106 | if (state == TB_PORT_DISABLED) { | |
107 | tb_port_info(port, "is disabled (state: 0)\n"); | |
108 | return 0; | |
109 | } | |
110 | if (state == TB_PORT_UNPLUGGED) { | |
111 | if (wait_if_unplugged) { | |
112 | /* used during resume */ | |
113 | tb_port_info(port, | |
114 | "is unplugged (state: 7), retrying...\n"); | |
115 | msleep(100); | |
116 | continue; | |
117 | } | |
118 | tb_port_info(port, "is unplugged (state: 7)\n"); | |
119 | return 0; | |
120 | } | |
121 | if (state == TB_PORT_UP) { | |
122 | tb_port_info(port, | |
123 | "is connected, link is up (state: 2)\n"); | |
124 | return 1; | |
125 | } | |
126 | ||
127 | /* | |
128 | * After plug-in the state is TB_PORT_CONNECTING. Give it some | |
129 | * time. | |
130 | */ | |
131 | tb_port_info(port, | |
132 | "is connected, link is not up (state: %d), retrying...\n", | |
133 | state); | |
134 | msleep(100); | |
135 | } | |
136 | tb_port_warn(port, | |
137 | "failed to reach state TB_PORT_UP. Ignoring port...\n"); | |
138 | return 0; | |
139 | } | |
140 | ||
a25c8b2f AN |
141 | /** |
142 | * tb_init_port() - initialize a port | |
143 | * | |
144 | * This is a helper method for tb_switch_alloc. Does not check or initialize | |
145 | * any downstream switches. | |
146 | * | |
147 | * Return: Returns 0 on success or an error code on failure. | |
148 | */ | |
149 | static int tb_init_port(struct tb_switch *sw, u8 port_nr) | |
150 | { | |
151 | int res; | |
9da672a4 | 152 | int cap; |
a25c8b2f AN |
153 | struct tb_port *port = &sw->ports[port_nr]; |
154 | port->sw = sw; | |
155 | port->port = port_nr; | |
156 | port->remote = NULL; | |
157 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); | |
158 | if (res) | |
159 | return res; | |
160 | ||
9da672a4 AN |
161 | /* Port 0 is the switch itself and has no PHY. */ |
162 | if (port->config.type == TB_TYPE_PORT && port_nr != 0) { | |
163 | cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); | |
164 | ||
165 | if (cap > 0) | |
166 | port->cap_phy = cap; | |
167 | else | |
168 | tb_port_WARN(port, "non switch port without a PHY\n"); | |
169 | } | |
170 | ||
a25c8b2f AN |
171 | tb_dump_port(sw->tb, &port->config); |
172 | ||
173 | /* TODO: Read dual link port, DP port and more from EEPROM. */ | |
174 | return 0; | |
175 | ||
176 | } | |
177 | ||
178 | /* switch utility functions */ | |
179 | ||
180 | static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) | |
181 | { | |
182 | tb_info(tb, | |
183 | " Switch: %x:%x (Revision: %d, TB Version: %d)\n", | |
184 | sw->vendor_id, sw->device_id, sw->revision, | |
185 | sw->thunderbolt_version); | |
186 | tb_info(tb, " Max Port Number: %d\n", sw->max_port_number); | |
187 | tb_info(tb, " Config:\n"); | |
188 | tb_info(tb, | |
189 | " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", | |
190 | sw->upstream_port_number, sw->depth, | |
191 | (((u64) sw->route_hi) << 32) | sw->route_lo, | |
192 | sw->enabled, sw->plug_events_delay); | |
193 | tb_info(tb, | |
194 | " unknown1: %#x unknown4: %#x\n", | |
195 | sw->__unknown1, sw->__unknown4); | |
196 | } | |
197 | ||
053596d9 AN |
198 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route) |
199 | { | |
200 | u8 next_port = route; /* | |
201 | * Routes use a stride of 8 bits, | |
202 | * eventhough a port index has 6 bits at most. | |
203 | * */ | |
204 | if (route == 0) | |
205 | return sw; | |
206 | if (next_port > sw->config.max_port_number) | |
207 | return 0; | |
208 | if (tb_is_upstream_port(&sw->ports[next_port])) | |
209 | return 0; | |
210 | if (!sw->ports[next_port].remote) | |
211 | return 0; | |
212 | return get_switch_at_route(sw->ports[next_port].remote->sw, | |
213 | route >> TB_ROUTE_SHIFT); | |
214 | } | |
215 | ||
ca389f71 AN |
216 | /** |
217 | * tb_plug_events_active() - enable/disable plug events on a switch | |
218 | * | |
219 | * Also configures a sane plug_events_delay of 255ms. | |
220 | * | |
221 | * Return: Returns 0 on success or an error code on failure. | |
222 | */ | |
223 | static int tb_plug_events_active(struct tb_switch *sw, bool active) | |
224 | { | |
225 | u32 data; | |
226 | int res; | |
227 | ||
228 | sw->config.plug_events_delay = 0xff; | |
229 | res = tb_sw_write(sw, ((u32 *) &sw->config) + 4, TB_CFG_SWITCH, 4, 1); | |
230 | if (res) | |
231 | return res; | |
232 | ||
233 | res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1); | |
234 | if (res) | |
235 | return res; | |
236 | ||
237 | if (active) { | |
238 | data = data & 0xFFFFFF83; | |
239 | switch (sw->config.device_id) { | |
240 | case 0x1513: | |
241 | case 0x151a: | |
242 | case 0x1549: | |
243 | break; | |
244 | default: | |
245 | data |= 4; | |
246 | } | |
247 | } else { | |
248 | data = data | 0x7c; | |
249 | } | |
250 | return tb_sw_write(sw, &data, TB_CFG_SWITCH, | |
251 | sw->cap_plug_events + 1, 1); | |
252 | } | |
253 | ||
254 | ||
a25c8b2f AN |
255 | /** |
256 | * tb_switch_free() - free a tb_switch and all downstream switches | |
257 | */ | |
258 | void tb_switch_free(struct tb_switch *sw) | |
259 | { | |
260 | int i; | |
261 | /* port 0 is the switch itself and never has a remote */ | |
262 | for (i = 1; i <= sw->config.max_port_number; i++) { | |
263 | if (tb_is_upstream_port(&sw->ports[i])) | |
264 | continue; | |
265 | if (sw->ports[i].remote) | |
266 | tb_switch_free(sw->ports[i].remote->sw); | |
267 | sw->ports[i].remote = NULL; | |
268 | } | |
269 | ||
053596d9 AN |
270 | if (!sw->is_unplugged) |
271 | tb_plug_events_active(sw, false); | |
ca389f71 | 272 | |
a25c8b2f AN |
273 | kfree(sw->ports); |
274 | kfree(sw); | |
275 | } | |
276 | ||
277 | /** | |
278 | * tb_switch_alloc() - allocate and initialize a switch | |
279 | * | |
280 | * Return: Returns a NULL on failure. | |
281 | */ | |
282 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | |
283 | { | |
284 | int i; | |
ca389f71 | 285 | int cap; |
a25c8b2f AN |
286 | struct tb_switch *sw; |
287 | int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route); | |
288 | if (upstream_port < 0) | |
289 | return NULL; | |
290 | ||
291 | sw = kzalloc(sizeof(*sw), GFP_KERNEL); | |
292 | if (!sw) | |
293 | return NULL; | |
294 | ||
295 | sw->tb = tb; | |
296 | if (tb_cfg_read(tb->ctl, &sw->config, route, 0, 2, 0, 5)) | |
297 | goto err; | |
298 | tb_info(tb, | |
299 | "initializing Switch at %#llx (depth: %d, up port: %d)\n", | |
300 | route, tb_route_length(route), upstream_port); | |
301 | tb_info(tb, "old switch config:\n"); | |
302 | tb_dump_switch(tb, &sw->config); | |
303 | ||
304 | /* configure switch */ | |
305 | sw->config.upstream_port_number = upstream_port; | |
306 | sw->config.depth = tb_route_length(route); | |
307 | sw->config.route_lo = route; | |
308 | sw->config.route_hi = route >> 32; | |
309 | sw->config.enabled = 1; | |
310 | /* from here on we may use the tb_sw_* functions & macros */ | |
311 | ||
312 | if (sw->config.vendor_id != 0x8086) | |
313 | tb_sw_warn(sw, "unknown switch vendor id %#x\n", | |
314 | sw->config.vendor_id); | |
315 | ||
316 | if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549) | |
317 | tb_sw_warn(sw, "unsupported switch device id %#x\n", | |
318 | sw->config.device_id); | |
319 | ||
320 | /* upload configuration */ | |
321 | if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3)) | |
322 | goto err; | |
323 | ||
324 | /* initialize ports */ | |
325 | sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), | |
326 | GFP_KERNEL); | |
327 | if (!sw->ports) | |
328 | goto err; | |
329 | ||
330 | for (i = 0; i <= sw->config.max_port_number; i++) { | |
331 | if (tb_init_port(sw, i)) | |
332 | goto err; | |
333 | /* TODO: check if port is disabled (EEPROM) */ | |
334 | } | |
335 | ||
336 | /* TODO: I2C, IECS, EEPROM, link controller */ | |
337 | ||
ca389f71 AN |
338 | cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); |
339 | if (cap < 0) { | |
340 | tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); | |
341 | goto err; | |
342 | } | |
343 | sw->cap_plug_events = cap; | |
344 | ||
345 | if (tb_plug_events_active(sw, true)) | |
346 | goto err; | |
347 | ||
a25c8b2f AN |
348 | return sw; |
349 | err: | |
350 | kfree(sw->ports); | |
351 | kfree(sw); | |
352 | return NULL; | |
353 | } | |
354 | ||
053596d9 AN |
355 | /** |
356 | * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches | |
357 | */ | |
358 | void tb_sw_set_unpplugged(struct tb_switch *sw) | |
359 | { | |
360 | int i; | |
361 | if (sw == sw->tb->root_switch) { | |
362 | tb_sw_WARN(sw, "cannot unplug root switch\n"); | |
363 | return; | |
364 | } | |
365 | if (sw->is_unplugged) { | |
366 | tb_sw_WARN(sw, "is_unplugged already set\n"); | |
367 | return; | |
368 | } | |
369 | sw->is_unplugged = true; | |
370 | for (i = 0; i <= sw->config.max_port_number; i++) { | |
371 | if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) | |
372 | tb_sw_set_unpplugged(sw->ports[i].remote->sw); | |
373 | } | |
374 | } | |
375 |