160 lines
5.1 KiB
Diff
160 lines
5.1 KiB
Diff
From: Andreas Noever <andreas.noever@gmail.com>
|
|
Date: Thu, 12 Jun 2014 23:11:47 +0200
|
|
Subject: [15/31] thunderbolt: Fix nontrivial endpoint devices.
|
|
Origin: https://git.kernel.org/linus/343fcb8c70d76967ba64493ca984e40baad9d0f6
|
|
|
|
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>
|
|
---
|
|
drivers/thunderbolt/ctl.c | 2 +-
|
|
drivers/thunderbolt/switch.c | 42 +++++++++++++++++++++++++-----------------
|
|
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 9b0120b..d04fee4 100644
|
|
--- a/drivers/thunderbolt/ctl.c
|
|
+++ b/drivers/thunderbolt/ctl.c
|
|
@@ -439,7 +439,7 @@ rx:
|
|
*/
|
|
static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer,
|
|
size_t length, int timeout_msec,
|
|
- u8 route, enum tb_cfg_pkg_type type)
|
|
+ u64 route, enum tb_cfg_pkg_type type)
|
|
{
|
|
struct tb_cfg_result res;
|
|
struct ctl_pkg *pkg;
|
|
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
|
|
index 9dfb8e1..0d50e7e 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)
|
|
*
|
|
* Return: Returns 0 on success or an error code on failure.
|
|
*/
|
|
-static int tb_init_port(struct tb_switch *sw, u8 port_nr)
|
|
+static int tb_init_port(struct tb_port *port)
|
|
{
|
|
int res;
|
|
int cap;
|
|
- struct tb_port *port = &sw->ports[port_nr];
|
|
- port->sw = sw;
|
|
- port->port = port_nr;
|
|
- port->remote = NULL;
|
|
+
|
|
res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8);
|
|
if (res)
|
|
return res;
|
|
|
|
/* Port 0 is the switch itself and has no PHY. */
|
|
- if (port->config.type == TB_TYPE_PORT && port_nr != 0) {
|
|
+ if (port->config.type == TB_TYPE_PORT && port->port != 0) {
|
|
cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY);
|
|
|
|
if (cap > 0)
|
|
@@ -202,7 +199,7 @@ static int tb_init_port(struct tb_switch *sw, u8 port_nr)
|
|
tb_port_WARN(port, "non switch port without a PHY\n");
|
|
}
|
|
|
|
- tb_dump_port(sw->tb, &port->config);
|
|
+ tb_dump_port(port->sw->tb, &port->config);
|
|
|
|
/* TODO: Read dual link port, DP port and more from EEPROM. */
|
|
return 0;
|
|
@@ -329,6 +326,7 @@ void tb_switch_free(struct tb_switch *sw)
|
|
tb_plug_events_active(sw, false);
|
|
|
|
kfree(sw->ports);
|
|
+ kfree(sw->drom);
|
|
kfree(sw);
|
|
}
|
|
|
|
@@ -381,18 +379,16 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
|
|
|
|
/* initialize ports */
|
|
sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports),
|
|
- GFP_KERNEL);
|
|
+ GFP_KERNEL);
|
|
if (!sw->ports)
|
|
goto err;
|
|
|
|
for (i = 0; i <= sw->config.max_port_number; i++) {
|
|
- if (tb_init_port(sw, i))
|
|
- goto err;
|
|
- /* TODO: check if port is disabled (EEPROM) */
|
|
+ /* minimum setup for tb_find_cap and tb_drom_read to work */
|
|
+ sw->ports[i].sw = sw;
|
|
+ sw->ports[i].port = i;
|
|
}
|
|
|
|
- /* TODO: I2C, IECS, EEPROM, link controller */
|
|
-
|
|
cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS);
|
|
if (cap < 0) {
|
|
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)
|
|
}
|
|
sw->cap_plug_events = cap;
|
|
|
|
- if (tb_drom_read_uid_only(sw, &sw->uid))
|
|
- tb_sw_warn(sw, "could not read uid from eeprom\n");
|
|
- else
|
|
- tb_sw_info(sw, "uid: %#llx\n", sw->uid);
|
|
+ /* read drom */
|
|
+ if (tb_drom_read(sw))
|
|
+ tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n");
|
|
+ tb_sw_info(sw, "uid: %#llx\n", sw->uid);
|
|
+
|
|
+ for (i = 0; i <= sw->config.max_port_number; i++) {
|
|
+ if (sw->ports[i].disabled) {
|
|
+ tb_port_info(&sw->ports[i], "disabled by eeprom\n");
|
|
+ continue;
|
|
+ }
|
|
+ if (tb_init_port(&sw->ports[i]))
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* TODO: I2C, IECS, link controller */
|
|
|
|
if (tb_plug_events_active(sw, true))
|
|
goto err;
|
|
@@ -411,6 +418,7 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
|
|
return sw;
|
|
err:
|
|
kfree(sw->ports);
|
|
+ kfree(sw->drom);
|
|
kfree(sw);
|
|
return NULL;
|
|
}
|
|
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
|
|
index 1aa6dd7..d2c3fe3 100644
|
|
--- a/drivers/thunderbolt/tb.c
|
|
+++ b/drivers/thunderbolt/tb.c
|
|
@@ -38,6 +38,11 @@ static void tb_scan_port(struct tb_port *port)
|
|
return;
|
|
if (port->config.type != TB_TYPE_PORT)
|
|
return;
|
|
+ if (port->dual_link_port && port->link_nr)
|
|
+ return; /*
|
|
+ * Downstream switch is reachable through two ports.
|
|
+ * Only scan on the primary port (link_nr == 0).
|
|
+ */
|
|
if (tb_wait_for_port(port, false) <= 0)
|
|
return;
|
|
if (port->remote) {
|