xref: /openbmc/linux/drivers/thunderbolt/tb.c (revision 053596d9)
1 /*
2  * Thunderbolt Cactus Ridge driver - bus logic (NHI independent)
3  *
4  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
5  */
6 
7 #include <linux/slab.h>
8 #include <linux/errno.h>
9 #include <linux/delay.h>
10 
11 #include "tb.h"
12 #include "tb_regs.h"
13 
14 
15 /* enumeration & hot plug handling */
16 
17 
18 static void tb_scan_port(struct tb_port *port);
19 
20 /**
21  * tb_scan_switch() - scan for and initialize downstream switches
22  */
23 static void tb_scan_switch(struct tb_switch *sw)
24 {
25 	int i;
26 	for (i = 1; i <= sw->config.max_port_number; i++)
27 		tb_scan_port(&sw->ports[i]);
28 }
29 
30 /**
31  * tb_scan_port() - check for and initialize switches below port
32  */
33 static void tb_scan_port(struct tb_port *port)
34 {
35 	struct tb_switch *sw;
36 	if (tb_is_upstream_port(port))
37 		return;
38 	if (port->config.type != TB_TYPE_PORT)
39 		return;
40 	if (tb_wait_for_port(port, false) <= 0)
41 		return;
42 	if (port->remote) {
43 		tb_port_WARN(port, "port already has a remote!\n");
44 		return;
45 	}
46 	sw = tb_switch_alloc(port->sw->tb, tb_downstream_route(port));
47 	if (!sw)
48 		return;
49 	port->remote = tb_upstream_port(sw);
50 	tb_upstream_port(sw)->remote = port;
51 	tb_scan_switch(sw);
52 }
53 
54 
55 /* hotplug handling */
56 
57 struct tb_hotplug_event {
58 	struct work_struct work;
59 	struct tb *tb;
60 	u64 route;
61 	u8 port;
62 	bool unplug;
63 };
64 
65 /**
66  * tb_handle_hotplug() - handle hotplug event
67  *
68  * Executes on tb->wq.
69  */
70 static void tb_handle_hotplug(struct work_struct *work)
71 {
72 	struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work);
73 	struct tb *tb = ev->tb;
74 	struct tb_switch *sw;
75 	struct tb_port *port;
76 	mutex_lock(&tb->lock);
77 	if (!tb->hotplug_active)
78 		goto out; /* during init, suspend or shutdown */
79 
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 	}
123 out:
124 	mutex_unlock(&tb->lock);
125 	kfree(ev);
126 }
127 
128 /**
129  * tb_schedule_hotplug_handler() - callback function for the control channel
130  *
131  * Delegates to tb_handle_hotplug.
132  */
133 static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port,
134 					bool unplug)
135 {
136 	struct tb *tb = data;
137 	struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL);
138 	if (!ev)
139 		return;
140 	INIT_WORK(&ev->work, tb_handle_hotplug);
141 	ev->tb = tb;
142 	ev->route = route;
143 	ev->port = port;
144 	ev->unplug = unplug;
145 	queue_work(tb->wq, &ev->work);
146 }
147 
148 /**
149  * thunderbolt_shutdown_and_free() - shutdown everything
150  *
151  * Free all switches and the config channel.
152  *
153  * Used in the error path of thunderbolt_alloc_and_start.
154  */
155 void thunderbolt_shutdown_and_free(struct tb *tb)
156 {
157 	mutex_lock(&tb->lock);
158 
159 	if (tb->root_switch)
160 		tb_switch_free(tb->root_switch);
161 	tb->root_switch = NULL;
162 
163 	if (tb->ctl) {
164 		tb_ctl_stop(tb->ctl);
165 		tb_ctl_free(tb->ctl);
166 	}
167 	tb->ctl = NULL;
168 	tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */
169 
170 	/* allow tb_handle_hotplug to acquire the lock */
171 	mutex_unlock(&tb->lock);
172 	if (tb->wq) {
173 		flush_workqueue(tb->wq);
174 		destroy_workqueue(tb->wq);
175 		tb->wq = NULL;
176 	}
177 	mutex_destroy(&tb->lock);
178 	kfree(tb);
179 }
180 
181 /**
182  * thunderbolt_alloc_and_start() - setup the thunderbolt bus
183  *
184  * Allocates a tb_cfg control channel, initializes the root switch, enables
185  * plug events and activates pci devices.
186  *
187  * Return: Returns NULL on error.
188  */
189 struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
190 {
191 	struct tb *tb;
192 
193 	BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4);
194 	BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4);
195 	BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4);
196 
197 	tb = kzalloc(sizeof(*tb), GFP_KERNEL);
198 	if (!tb)
199 		return NULL;
200 
201 	tb->nhi = nhi;
202 	mutex_init(&tb->lock);
203 	mutex_lock(&tb->lock);
204 
205 	tb->wq = alloc_ordered_workqueue("thunderbolt", 0);
206 	if (!tb->wq)
207 		goto err_locked;
208 
209 	tb->ctl = tb_ctl_alloc(tb->nhi, tb_schedule_hotplug_handler, tb);
210 	if (!tb->ctl)
211 		goto err_locked;
212 	/*
213 	 * tb_schedule_hotplug_handler may be called as soon as the config
214 	 * channel is started. Thats why we have to hold the lock here.
215 	 */
216 	tb_ctl_start(tb->ctl);
217 
218 	tb->root_switch = tb_switch_alloc(tb, 0);
219 	if (!tb->root_switch)
220 		goto err_locked;
221 
222 	/* Full scan to discover devices added before the driver was loaded. */
223 	tb_scan_switch(tb->root_switch);
224 
225 	/* Allow tb_handle_hotplug to progress events */
226 	tb->hotplug_active = true;
227 	mutex_unlock(&tb->lock);
228 	return tb;
229 
230 err_locked:
231 	mutex_unlock(&tb->lock);
232 	thunderbolt_shutdown_and_free(tb);
233 	return NULL;
234 }
235 
236