Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andreas Noever and committed by
Greg Kroah-Hartman
343fcb8c cd22e73b

+31 -18
+1 -1
drivers/thunderbolt/ctl.c
··· 439 439 */ 440 440 static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer, 441 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 444 struct tb_cfg_result res; 445 445 struct ctl_pkg *pkg;
+25 -17
drivers/thunderbolt/switch.c
··· 180 180 * 181 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 185 int res; 186 186 int cap; 187 - struct tb_port *port = &sw->ports[port_nr]; 188 - port->sw = sw; 189 - port->port = port_nr; 190 - port->remote = NULL; 187 + 191 188 res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); 192 189 if (res) 193 190 return res; 194 191 195 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 194 cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); 198 195 199 196 if (cap > 0) ··· 199 202 tb_port_WARN(port, "non switch port without a PHY\n"); 200 203 } 201 204 202 - tb_dump_port(sw->tb, &port->config); 205 + tb_dump_port(port->sw->tb, &port->config); 203 206 204 207 /* TODO: Read dual link port, DP port and more from EEPROM. */ 205 208 return 0; ··· 326 329 tb_plug_events_active(sw, false); 327 330 328 331 kfree(sw->ports); 332 + kfree(sw->drom); 329 333 kfree(sw); 330 334 } 331 335 ··· 379 381 380 382 /* initialize ports */ 381 383 sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), 382 - GFP_KERNEL); 384 + GFP_KERNEL); 383 385 if (!sw->ports) 384 386 goto err; 385 387 386 388 for (i = 0; i <= sw->config.max_port_number; i++) { 387 - if (tb_init_port(sw, i)) 388 - goto err; 389 - /* TODO: check if port is disabled (EEPROM) */ 389 + /* minimum setup for tb_find_cap and tb_drom_read to work */ 390 + sw->ports[i].sw = sw; 391 + sw->ports[i].port = i; 390 392 } 391 - 392 - /* TODO: I2C, IECS, EEPROM, link controller */ 393 393 394 394 cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); 395 395 if (cap < 0) { ··· 396 400 } 397 401 sw->cap_plug_events = cap; 398 402 399 - if (tb_drom_read_uid_only(sw, &sw->uid)) 400 - tb_sw_warn(sw, "could not read uid from eeprom\n"); 401 - else 402 - tb_sw_info(sw, "uid: %#llx\n", sw->uid); 403 + /* read drom */ 404 + if (tb_drom_read(sw)) 405 + tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n"); 406 + tb_sw_info(sw, "uid: %#llx\n", sw->uid); 407 + 408 + for (i = 0; i <= sw->config.max_port_number; i++) { 409 + if (sw->ports[i].disabled) { 410 + tb_port_info(&sw->ports[i], "disabled by eeprom\n"); 411 + continue; 412 + } 413 + if (tb_init_port(&sw->ports[i])) 414 + goto err; 415 + } 416 + 417 + /* TODO: I2C, IECS, link controller */ 403 418 404 419 if (tb_plug_events_active(sw, true)) 405 420 goto err; ··· 418 411 return sw; 419 412 err: 420 413 kfree(sw->ports); 414 + kfree(sw->drom); 421 415 kfree(sw); 422 416 return NULL; 423 417 }
+5
drivers/thunderbolt/tb.c
··· 38 38 return; 39 39 if (port->config.type != TB_TYPE_PORT) 40 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 46 if (tb_wait_for_port(port, false) <= 0) 42 47 return; 43 48 if (port->remote) {