aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorAndreas Noever <andreas.noever@gmail.com>2014-06-03 16:04:06 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-06-19 17:07:47 -0400
commit053596d9e26c86352c4b2b372f43f2746b97de45 (patch)
tree7568b6b0dc29e9030bf1d6ffd38f522d6907e980 /drivers/thunderbolt
parent9da672a42878c58af5c50d7389dbae17bea9df38 (diff)
thunderbolt: Handle hotplug events
We receive a plug event callback whenever a thunderbolt device is added or removed. This patch fills in the tb_handle_hotplug method and starts reacting to these events by adding/removing switches from the hierarchy. 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/switch.c42
-rw-r--r--drivers/thunderbolt/tb.c46
-rw-r--r--drivers/thunderbolt/tb.h3
3 files changed, 89 insertions, 2 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index b31b8cef301d..d6c32e1e2b42 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -195,6 +195,24 @@ static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw)
195 sw->__unknown1, sw->__unknown4); 195 sw->__unknown1, sw->__unknown4);
196} 196}
197 197
198struct 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
198/** 216/**
199 * tb_plug_events_active() - enable/disable plug events on a switch 217 * tb_plug_events_active() - enable/disable plug events on a switch
200 * 218 *
@@ -249,7 +267,8 @@ void tb_switch_free(struct tb_switch *sw)
249 sw->ports[i].remote = NULL; 267 sw->ports[i].remote = NULL;
250 } 268 }
251 269
252 tb_plug_events_active(sw, false); 270 if (!sw->is_unplugged)
271 tb_plug_events_active(sw, false);
253 272
254 kfree(sw->ports); 273 kfree(sw->ports);
255 kfree(sw); 274 kfree(sw);
@@ -333,3 +352,24 @@ err:
333 return NULL; 352 return NULL;
334} 353}
335 354
355/**
356 * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches
357 */
358void 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
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 3b716fd123f6..1efcacc72104 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -71,11 +71,55 @@ static void tb_handle_hotplug(struct work_struct *work)
71{ 71{
72 struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); 72 struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
73 struct tb *tb = ev->tb; 73 struct tb *tb = ev->tb;
74 struct tb_switch *sw;
75 struct tb_port *port;
74 mutex_lock(&tb->lock); 76 mutex_lock(&tb->lock);
75 if (!tb->hotplug_active) 77 if (!tb->hotplug_active)
76 goto out; /* during init, suspend or shutdown */ 78 goto out; /* during init, suspend or shutdown */
77 79
78 /* do nothing for now */ 80 sw = get_switch_at_route(tb->root_switch, ev->route);
81 if (!sw) {
82 tb_warn(tb,
83 "hotplug event from non existent switch %llx:%x (unplug: %d)\n",
84 ev->route, ev->port, ev->unplug);
85 goto out;
86 }
87 if (ev->port > sw->config.max_port_number) {
88 tb_warn(tb,
89 "hotplug event from non existent port %llx:%x (unplug: %d)\n",
90 ev->route, ev->port, ev->unplug);
91 goto out;
92 }
93 port = &sw->ports[ev->port];
94 if (tb_is_upstream_port(port)) {
95 tb_warn(tb,
96 "hotplug event for upstream port %llx:%x (unplug: %d)\n",
97 ev->route, ev->port, ev->unplug);
98 goto out;
99 }
100 if (ev->unplug) {
101 if (port->remote) {
102 tb_port_info(port, "unplugged\n");
103 tb_sw_set_unpplugged(port->remote->sw);
104 tb_switch_free(port->remote->sw);
105 port->remote = NULL;
106 } else {
107 tb_port_info(port,
108 "got unplug event for disconnected port, ignoring\n");
109 }
110 } else if (port->remote) {
111 tb_port_info(port,
112 "got plug event for connected port, ignoring\n");
113 } else {
114 tb_port_info(port, "hotplug: scanning\n");
115 tb_scan_port(port);
116 if (!port->remote) {
117 tb_port_info(port, "hotplug: no switch found\n");
118 } else if (port->remote->sw->config.depth > 1) {
119 tb_sw_warn(port->remote->sw,
120 "hotplug: chaining not supported\n");
121 }
122 }
79out: 123out:
80 mutex_unlock(&tb->lock); 124 mutex_unlock(&tb->lock);
81 kfree(ev); 125 kfree(ev);
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 70a66fef0177..661f1828527a 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -20,6 +20,7 @@ struct tb_switch {
20 struct tb_port *ports; 20 struct tb_port *ports;
21 struct tb *tb; 21 struct tb *tb;
22 int cap_plug_events; /* offset, zero if not found */ 22 int cap_plug_events; /* offset, zero if not found */
23 bool is_unplugged; /* unplugged, will go away */
23}; 24};
24 25
25/** 26/**
@@ -160,6 +161,8 @@ void thunderbolt_shutdown_and_free(struct tb *tb);
160 161
161struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route); 162struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route);
162void tb_switch_free(struct tb_switch *sw); 163void tb_switch_free(struct tb_switch *sw);
164void tb_sw_set_unpplugged(struct tb_switch *sw);
165struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
163 166
164int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); 167int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
165 168