summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/ctl.c2
-rw-r--r--drivers/thunderbolt/switch.c42
-rw-r--r--drivers/thunderbolt/tb.c5
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) {
OpenPOWER on IntegriCloud