diff options
author | Andreas Noever <andreas.noever@gmail.com> | 2014-06-12 17:11:47 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-06-19 17:14:35 -0400 |
commit | 343fcb8c70d76967ba64493ca984e40baad9d0f6 (patch) | |
tree | 8e8b8731fe2094124f459ba33b66b703dcab2178 /drivers/thunderbolt | |
parent | cd22e73bdf5eff7e68a0f8bdfbce123ad43651f6 (diff) |
thunderbolt: Fix nontrivial endpoint devices.
Fix issues observed with the Startech docking station:
Fix the type of the route parameter in tb_ctl_rx. It should be u64 and not
u8 (which only worked for short routes).
A thunderbolt cable contains two lanes. If both endpoints support it a
connection will be established on both lanes. Previously we tried to
scan below both "dual link ports". Use the information extracted from
the drom to only scan behind ports with lane_nr == 0.
Endpoints with more complex thunderbolt controllers have some of their
ports disabled (for example the NHI port or one of the HDMI/DP ports).
Accessing them results in an error so we now ignore ports which are
marked as disabled in the drom.
Signed-off-by: Andreas Noever <andreas.noever@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r-- | drivers/thunderbolt/ctl.c | 2 | ||||
-rw-r--r-- | drivers/thunderbolt/switch.c | 42 | ||||
-rw-r--r-- | drivers/thunderbolt/tb.c | 5 |
3 files changed, 31 insertions, 18 deletions
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 9b0120bede51..d04fee4acb2e 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c | |||
@@ -439,7 +439,7 @@ rx: | |||
439 | */ | 439 | */ |
440 | static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer, | 440 | static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer, |
441 | size_t length, int timeout_msec, | 441 | size_t length, int timeout_msec, |
442 | u8 route, enum tb_cfg_pkg_type type) | 442 | u64 route, enum tb_cfg_pkg_type type) |
443 | { | 443 | { |
444 | struct tb_cfg_result res; | 444 | struct tb_cfg_result res; |
445 | struct ctl_pkg *pkg; | 445 | struct ctl_pkg *pkg; |
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 9dfb8e18cdf7..0d50e7e7b29b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c | |||
@@ -180,20 +180,17 @@ int tb_port_clear_counter(struct tb_port *port, int counter) | |||
180 | * | 180 | * |
181 | * Return: Returns 0 on success or an error code on failure. | 181 | * Return: Returns 0 on success or an error code on failure. |
182 | */ | 182 | */ |
183 | static int tb_init_port(struct tb_switch *sw, u8 port_nr) | 183 | static int tb_init_port(struct tb_port *port) |
184 | { | 184 | { |
185 | int res; | 185 | int res; |
186 | int cap; | 186 | int cap; |
187 | struct tb_port *port = &sw->ports[port_nr]; | 187 | |
188 | port->sw = sw; | ||
189 | port->port = port_nr; | ||
190 | port->remote = NULL; | ||
191 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); | 188 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); |
192 | if (res) | 189 | if (res) |
193 | return res; | 190 | return res; |
194 | 191 | ||
195 | /* Port 0 is the switch itself and has no PHY. */ | 192 | /* Port 0 is the switch itself and has no PHY. */ |
196 | if (port->config.type == TB_TYPE_PORT && port_nr != 0) { | 193 | if (port->config.type == TB_TYPE_PORT && port->port != 0) { |
197 | cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); | 194 | cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); |
198 | 195 | ||
199 | if (cap > 0) | 196 | if (cap > 0) |
@@ -202,7 +199,7 @@ static int tb_init_port(struct tb_switch *sw, u8 port_nr) | |||
202 | tb_port_WARN(port, "non switch port without a PHY\n"); | 199 | tb_port_WARN(port, "non switch port without a PHY\n"); |
203 | } | 200 | } |
204 | 201 | ||
205 | tb_dump_port(sw->tb, &port->config); | 202 | tb_dump_port(port->sw->tb, &port->config); |
206 | 203 | ||
207 | /* TODO: Read dual link port, DP port and more from EEPROM. */ | 204 | /* TODO: Read dual link port, DP port and more from EEPROM. */ |
208 | return 0; | 205 | return 0; |
@@ -329,6 +326,7 @@ void tb_switch_free(struct tb_switch *sw) | |||
329 | tb_plug_events_active(sw, false); | 326 | tb_plug_events_active(sw, false); |
330 | 327 | ||
331 | kfree(sw->ports); | 328 | kfree(sw->ports); |
329 | kfree(sw->drom); | ||
332 | kfree(sw); | 330 | kfree(sw); |
333 | } | 331 | } |
334 | 332 | ||
@@ -381,18 +379,16 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | |||
381 | 379 | ||
382 | /* initialize ports */ | 380 | /* initialize ports */ |
383 | sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), | 381 | sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), |
384 | GFP_KERNEL); | 382 | GFP_KERNEL); |
385 | if (!sw->ports) | 383 | if (!sw->ports) |
386 | goto err; | 384 | goto err; |
387 | 385 | ||
388 | for (i = 0; i <= sw->config.max_port_number; i++) { | 386 | for (i = 0; i <= sw->config.max_port_number; i++) { |
389 | if (tb_init_port(sw, i)) | 387 | /* minimum setup for tb_find_cap and tb_drom_read to work */ |
390 | goto err; | 388 | sw->ports[i].sw = sw; |
391 | /* TODO: check if port is disabled (EEPROM) */ | 389 | sw->ports[i].port = i; |
392 | } | 390 | } |
393 | 391 | ||
394 | /* TODO: I2C, IECS, EEPROM, link controller */ | ||
395 | |||
396 | cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); | 392 | cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); |
397 | if (cap < 0) { | 393 | if (cap < 0) { |
398 | tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); | 394 | tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); |
@@ -400,10 +396,21 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | |||
400 | } | 396 | } |
401 | sw->cap_plug_events = cap; | 397 | sw->cap_plug_events = cap; |
402 | 398 | ||
403 | if (tb_drom_read_uid_only(sw, &sw->uid)) | 399 | /* read drom */ |
404 | tb_sw_warn(sw, "could not read uid from eeprom\n"); | 400 | if (tb_drom_read(sw)) |
405 | else | 401 | tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n"); |
406 | tb_sw_info(sw, "uid: %#llx\n", sw->uid); | 402 | tb_sw_info(sw, "uid: %#llx\n", sw->uid); |
403 | |||
404 | for (i = 0; i <= sw->config.max_port_number; i++) { | ||
405 | if (sw->ports[i].disabled) { | ||
406 | tb_port_info(&sw->ports[i], "disabled by eeprom\n"); | ||
407 | continue; | ||
408 | } | ||
409 | if (tb_init_port(&sw->ports[i])) | ||
410 | goto err; | ||
411 | } | ||
412 | |||
413 | /* TODO: I2C, IECS, link controller */ | ||
407 | 414 | ||
408 | if (tb_plug_events_active(sw, true)) | 415 | if (tb_plug_events_active(sw, true)) |
409 | goto err; | 416 | goto err; |
@@ -411,6 +418,7 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | |||
411 | return sw; | 418 | return sw; |
412 | err: | 419 | err: |
413 | kfree(sw->ports); | 420 | kfree(sw->ports); |
421 | kfree(sw->drom); | ||
414 | kfree(sw); | 422 | kfree(sw); |
415 | return NULL; | 423 | return NULL; |
416 | } | 424 | } |
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1aa6dd7dc68b..d2c3fe346e91 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c | |||
@@ -38,6 +38,11 @@ static void tb_scan_port(struct tb_port *port) | |||
38 | return; | 38 | return; |
39 | if (port->config.type != TB_TYPE_PORT) | 39 | if (port->config.type != TB_TYPE_PORT) |
40 | return; | 40 | return; |
41 | if (port->dual_link_port && port->link_nr) | ||
42 | return; /* | ||
43 | * Downstream switch is reachable through two ports. | ||
44 | * Only scan on the primary port (link_nr == 0). | ||
45 | */ | ||
41 | if (tb_wait_for_port(port, false) <= 0) | 46 | if (tb_wait_for_port(port, false) <= 0) |
42 | return; | 47 | return; |
43 | if (port->remote) { | 48 | if (port->remote) { |