aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Noever <andreas.noever@gmail.com>2014-06-03 16:04:08 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-06-19 17:07:47 -0400
commit3364f0c12795713e89ae1209081c60d64bfb4ca1 (patch)
treec13a0c83e2a0268bb2af82a34f10732a6b46312b
parent520b670216a15fb949e6ec6a1af9b5dd55d219c7 (diff)
thunderbolt: Add support for simple pci tunnels
A pci downstream and pci upstream port can be connected through a tunnel. To establish the tunnel we have to setup two unidirectional paths between the two ports. Right now we only support paths with two hops (i.e. no chaining) and at most one pci device per thunderbolt device. Signed-off-by: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/thunderbolt/Makefile2
-rw-r--r--drivers/thunderbolt/tb.c135
-rw-r--r--drivers/thunderbolt/tb.h1
-rw-r--r--drivers/thunderbolt/tunnel_pci.c232
-rw-r--r--drivers/thunderbolt/tunnel_pci.h30
5 files changed, 399 insertions, 1 deletions
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 3532f3684efc..0122ca698f78 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,3 @@
1obj-${CONFIG_THUNDERBOLT} := thunderbolt.o 1obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
2thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o 2thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o
3 3
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 1efcacc72104..177f61df464d 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -10,6 +10,7 @@
10 10
11#include "tb.h" 11#include "tb.h"
12#include "tb_regs.h" 12#include "tb_regs.h"
13#include "tunnel_pci.h"
13 14
14 15
15/* enumeration & hot plug handling */ 16/* enumeration & hot plug handling */
@@ -51,6 +52,124 @@ static void tb_scan_port(struct tb_port *port)
51 tb_scan_switch(sw); 52 tb_scan_switch(sw);
52} 53}
53 54
55/**
56 * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away
57 */
58static void tb_free_invalid_tunnels(struct tb *tb)
59{
60 struct tb_pci_tunnel *tunnel;
61 struct tb_pci_tunnel *n;
62 list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list)
63 {
64 if (tb_pci_is_invalid(tunnel)) {
65 tb_pci_deactivate(tunnel);
66 tb_pci_free(tunnel);
67 }
68 }
69}
70
71/**
72 * find_pci_up_port() - return the first PCIe up port on @sw or NULL
73 */
74static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw)
75{
76 int i;
77 for (i = 1; i <= sw->config.max_port_number; i++)
78 if (sw->ports[i].config.type == TB_TYPE_PCIE_UP)
79 return &sw->ports[i];
80 return NULL;
81}
82
83/**
84 * find_unused_down_port() - return the first inactive PCIe down port on @sw
85 */
86static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw)
87{
88 int i;
89 int cap;
90 int res;
91 int data;
92 for (i = 1; i <= sw->config.max_port_number; i++) {
93 if (tb_is_upstream_port(&sw->ports[i]))
94 continue;
95 if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN)
96 continue;
97 cap = tb_find_cap(&sw->ports[i], TB_CFG_PORT, TB_CAP_PCIE);
98 if (cap <= 0)
99 continue;
100 res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1);
101 if (res < 0)
102 continue;
103 if (data & 0x80000000)
104 continue;
105 return &sw->ports[i];
106 }
107 return NULL;
108}
109
110/**
111 * tb_activate_pcie_devices() - scan for and activate PCIe devices
112 *
113 * This method is somewhat ad hoc. For now it only supports one device
114 * per port and only devices at depth 1.
115 */
116static void tb_activate_pcie_devices(struct tb *tb)
117{
118 int i;
119 int cap;
120 u32 data;
121 struct tb_switch *sw;
122 struct tb_port *up_port;
123 struct tb_port *down_port;
124 struct tb_pci_tunnel *tunnel;
125 /* scan for pcie devices at depth 1*/
126 for (i = 1; i <= tb->root_switch->config.max_port_number; i++) {
127 if (tb_is_upstream_port(&tb->root_switch->ports[i]))
128 continue;
129 if (tb->root_switch->ports[i].config.type != TB_TYPE_PORT)
130 continue;
131 if (!tb->root_switch->ports[i].remote)
132 continue;
133 sw = tb->root_switch->ports[i].remote->sw;
134 up_port = tb_find_pci_up_port(sw);
135 if (!up_port) {
136 tb_sw_info(sw, "no PCIe devices found, aborting\n");
137 continue;
138 }
139
140 /* check whether port is already activated */
141 cap = tb_find_cap(up_port, TB_CFG_PORT, TB_CAP_PCIE);
142 if (cap <= 0)
143 continue;
144 if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1))
145 continue;
146 if (data & 0x80000000) {
147 tb_port_info(up_port,
148 "PCIe port already activated, aborting\n");
149 continue;
150 }
151
152 down_port = tb_find_unused_down_port(tb->root_switch);
153 if (!down_port) {
154 tb_port_info(up_port,
155 "All PCIe down ports are occupied, aborting\n");
156 continue;
157 }
158 tunnel = tb_pci_alloc(tb, up_port, down_port);
159 if (!tunnel) {
160 tb_port_info(up_port,
161 "PCIe tunnel allocation failed, aborting\n");
162 continue;
163 }
164
165 if (tb_pci_activate(tunnel)) {
166 tb_port_info(up_port,
167 "PCIe tunnel activation failed, aborting\n");
168 tb_pci_free(tunnel);
169 }
170
171 }
172}
54 173
55/* hotplug handling */ 174/* hotplug handling */
56 175
@@ -101,6 +220,7 @@ static void tb_handle_hotplug(struct work_struct *work)
101 if (port->remote) { 220 if (port->remote) {
102 tb_port_info(port, "unplugged\n"); 221 tb_port_info(port, "unplugged\n");
103 tb_sw_set_unpplugged(port->remote->sw); 222 tb_sw_set_unpplugged(port->remote->sw);
223 tb_free_invalid_tunnels(tb);
104 tb_switch_free(port->remote->sw); 224 tb_switch_free(port->remote->sw);
105 port->remote = NULL; 225 port->remote = NULL;
106 } else { 226 } else {
@@ -118,6 +238,10 @@ static void tb_handle_hotplug(struct work_struct *work)
118 } else if (port->remote->sw->config.depth > 1) { 238 } else if (port->remote->sw->config.depth > 1) {
119 tb_sw_warn(port->remote->sw, 239 tb_sw_warn(port->remote->sw,
120 "hotplug: chaining not supported\n"); 240 "hotplug: chaining not supported\n");
241 } else {
242 tb_sw_info(port->remote->sw,
243 "hotplug: activating pcie devices\n");
244 tb_activate_pcie_devices(tb);
121 } 245 }
122 } 246 }
123out: 247out:
@@ -154,8 +278,17 @@ static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port,
154 */ 278 */
155void thunderbolt_shutdown_and_free(struct tb *tb) 279void thunderbolt_shutdown_and_free(struct tb *tb)
156{ 280{
281 struct tb_pci_tunnel *tunnel;
282 struct tb_pci_tunnel *n;
283
157 mutex_lock(&tb->lock); 284 mutex_lock(&tb->lock);
158 285
286 /* tunnels are only present after everything has been initialized */
287 list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) {
288 tb_pci_deactivate(tunnel);
289 tb_pci_free(tunnel);
290 }
291
159 if (tb->root_switch) 292 if (tb->root_switch)
160 tb_switch_free(tb->root_switch); 293 tb_switch_free(tb->root_switch);
161 tb->root_switch = NULL; 294 tb->root_switch = NULL;
@@ -201,6 +334,7 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
201 tb->nhi = nhi; 334 tb->nhi = nhi;
202 mutex_init(&tb->lock); 335 mutex_init(&tb->lock);
203 mutex_lock(&tb->lock); 336 mutex_lock(&tb->lock);
337 INIT_LIST_HEAD(&tb->tunnel_list);
204 338
205 tb->wq = alloc_ordered_workqueue("thunderbolt", 0); 339 tb->wq = alloc_ordered_workqueue("thunderbolt", 0);
206 if (!tb->wq) 340 if (!tb->wq)
@@ -221,6 +355,7 @@ struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi)
221 355
222 /* Full scan to discover devices added before the driver was loaded. */ 356 /* Full scan to discover devices added before the driver was loaded. */
223 tb_scan_switch(tb->root_switch); 357 tb_scan_switch(tb->root_switch);
358 tb_activate_pcie_devices(tb);
224 359
225 /* Allow tb_handle_hotplug to progress events */ 360 /* Allow tb_handle_hotplug to progress events */
226 tb->hotplug_active = true; 361 tb->hotplug_active = true;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 8bbdc2bc4d09..508abc426563 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -100,6 +100,7 @@ struct tb {
100 struct tb_ctl *ctl; 100 struct tb_ctl *ctl;
101 struct workqueue_struct *wq; /* ordered workqueue for plug events */ 101 struct workqueue_struct *wq; /* ordered workqueue for plug events */
102 struct tb_switch *root_switch; 102 struct tb_switch *root_switch;
103 struct list_head tunnel_list; /* list of active PCIe tunnels */
103 bool hotplug_active; /* 104 bool hotplug_active; /*
104 * tb_handle_hotplug will stop progressing plug 105 * tb_handle_hotplug will stop progressing plug
105 * events and exit if this is not set (it needs to 106 * events and exit if this is not set (it needs to
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c
new file mode 100644
index 000000000000..baf1cd370446
--- /dev/null
+++ b/drivers/thunderbolt/tunnel_pci.c
@@ -0,0 +1,232 @@
1/*
2 * Thunderbolt Cactus Ridge driver - PCIe tunnel
3 *
4 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
5 */
6
7#include <linux/slab.h>
8#include <linux/list.h>
9
10#include "tunnel_pci.h"
11#include "tb.h"
12
13#define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
14 do { \
15 struct tb_pci_tunnel *__tunnel = (tunnel); \
16 level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt, \
17 tb_route(__tunnel->down_port->sw), \
18 __tunnel->down_port->port, \
19 tb_route(__tunnel->up_port->sw), \
20 __tunnel->up_port->port, \
21 ## arg); \
22 } while (0)
23
24#define tb_tunnel_WARN(tunnel, fmt, arg...) \
25 __TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg)
26#define tb_tunnel_warn(tunnel, fmt, arg...) \
27 __TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg)
28#define tb_tunnel_info(tunnel, fmt, arg...) \
29 __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
30
31static void tb_pci_init_path(struct tb_path *path)
32{
33 path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
34 path->egress_shared_buffer = TB_PATH_NONE;
35 path->ingress_fc_enable = TB_PATH_ALL;
36 path->ingress_shared_buffer = TB_PATH_NONE;
37 path->priority = 3;
38 path->weight = 1;
39 path->drop_packages = 0;
40 path->nfc_credits = 0;
41}
42
43/**
44 * tb_pci_alloc() - allocate a pci tunnel
45 *
46 * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
47 * TB_TYPE_PCIE_DOWN.
48 *
49 * Currently only paths consisting of two hops are supported (that is the
50 * ports must be on "adjacent" switches).
51 *
52 * The paths are hard-coded to use hop 8 (the only working hop id available on
53 * my thunderbolt devices). Therefore at most ONE path per device may be
54 * activated.
55 *
56 * Return: Returns a tb_pci_tunnel on success or NULL on failure.
57 */
58struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
59 struct tb_port *down)
60{
61 struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
62 if (!tunnel)
63 goto err;
64 tunnel->tb = tb;
65 tunnel->down_port = down;
66 tunnel->up_port = up;
67 INIT_LIST_HEAD(&tunnel->list);
68 tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2);
69 if (!tunnel->path_to_up)
70 goto err;
71 tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2);
72 if (!tunnel->path_to_down)
73 goto err;
74 tb_pci_init_path(tunnel->path_to_up);
75 tb_pci_init_path(tunnel->path_to_down);
76
77 tunnel->path_to_up->hops[0].in_port = down;
78 tunnel->path_to_up->hops[0].in_hop_index = 8;
79 tunnel->path_to_up->hops[0].in_counter_index = -1;
80 tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote;
81 tunnel->path_to_up->hops[0].next_hop_index = 8;
82
83 tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw);
84 tunnel->path_to_up->hops[1].in_hop_index = 8;
85 tunnel->path_to_up->hops[1].in_counter_index = -1;
86 tunnel->path_to_up->hops[1].out_port = up;
87 tunnel->path_to_up->hops[1].next_hop_index = 8;
88
89 tunnel->path_to_down->hops[0].in_port = up;
90 tunnel->path_to_down->hops[0].in_hop_index = 8;
91 tunnel->path_to_down->hops[0].in_counter_index = -1;
92 tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw);
93 tunnel->path_to_down->hops[0].next_hop_index = 8;
94
95 tunnel->path_to_down->hops[1].in_port =
96 tb_upstream_port(up->sw)->remote;
97 tunnel->path_to_down->hops[1].in_hop_index = 8;
98 tunnel->path_to_down->hops[1].in_counter_index = -1;
99 tunnel->path_to_down->hops[1].out_port = down;
100 tunnel->path_to_down->hops[1].next_hop_index = 8;
101 return tunnel;
102
103err:
104 if (tunnel) {
105 if (tunnel->path_to_down)
106 tb_path_free(tunnel->path_to_down);
107 if (tunnel->path_to_up)
108 tb_path_free(tunnel->path_to_up);
109 kfree(tunnel);
110 }
111 return NULL;
112}
113
114/**
115 * tb_pci_free() - free a tunnel
116 *
117 * The tunnel must have been deactivated.
118 */
119void tb_pci_free(struct tb_pci_tunnel *tunnel)
120{
121 if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
122 tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n");
123 return;
124 }
125 tb_path_free(tunnel->path_to_up);
126 tb_path_free(tunnel->path_to_down);
127 kfree(tunnel);
128}
129
130/**
131 * tb_pci_is_invalid - check whether an activated path is still valid
132 */
133bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
134{
135 WARN_ON(!tunnel->path_to_up->activated);
136 WARN_ON(!tunnel->path_to_down->activated);
137
138 return tb_path_is_invalid(tunnel->path_to_up)
139 || tb_path_is_invalid(tunnel->path_to_down);
140}
141
142/**
143 * tb_pci_port_active() - activate/deactivate PCI capability
144 *
145 * Return: Returns 0 on success or an error code on failure.
146 */
147static int tb_pci_port_active(struct tb_port *port, bool active)
148{
149 u32 word = active ? 0x80000000 : 0x0;
150 int cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PCIE);
151 if (cap <= 0) {
152 tb_port_warn(port, "TB_CAP_PCIE not found: %d\n", cap);
153 return cap ? cap : -ENXIO;
154 }
155 return tb_port_write(port, &word, TB_CFG_PORT, cap, 1);
156}
157
158/**
159 * tb_pci_restart() - activate a tunnel after a hardware reset
160 */
161int tb_pci_restart(struct tb_pci_tunnel *tunnel)
162{
163 int res;
164 tunnel->path_to_up->activated = false;
165 tunnel->path_to_down->activated = false;
166
167 tb_tunnel_info(tunnel, "activating\n");
168
169 res = tb_path_activate(tunnel->path_to_up);
170 if (res)
171 goto err;
172 res = tb_path_activate(tunnel->path_to_down);
173 if (res)
174 goto err;
175
176 res = tb_pci_port_active(tunnel->down_port, true);
177 if (res)
178 goto err;
179
180 res = tb_pci_port_active(tunnel->up_port, true);
181 if (res)
182 goto err;
183 return 0;
184err:
185 tb_tunnel_warn(tunnel, "activation failed\n");
186 tb_pci_deactivate(tunnel);
187 return res;
188}
189
190/**
191 * tb_pci_activate() - activate a tunnel
192 *
193 * Return: Returns 0 on success or an error code on failure.
194 */
195int tb_pci_activate(struct tb_pci_tunnel *tunnel)
196{
197 int res;
198 if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) {
199 tb_tunnel_WARN(tunnel,
200 "trying to activate an already activated tunnel\n");
201 return -EINVAL;
202 }
203
204 res = tb_pci_restart(tunnel);
205 if (res)
206 return res;
207
208 list_add(&tunnel->list, &tunnel->tb->tunnel_list);
209 return 0;
210}
211
212
213
214/**
215 * tb_pci_deactivate() - deactivate a tunnel
216 */
217void tb_pci_deactivate(struct tb_pci_tunnel *tunnel)
218{
219 tb_tunnel_info(tunnel, "deactivating\n");
220 /*
221 * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up
222 * port. Seems to have no effect?
223 */
224 tb_pci_port_active(tunnel->up_port, false);
225 tb_pci_port_active(tunnel->down_port, false);
226 if (tunnel->path_to_down->activated)
227 tb_path_deactivate(tunnel->path_to_down);
228 if (tunnel->path_to_up->activated)
229 tb_path_deactivate(tunnel->path_to_up);
230 list_del_init(&tunnel->list);
231}
232
diff --git a/drivers/thunderbolt/tunnel_pci.h b/drivers/thunderbolt/tunnel_pci.h
new file mode 100644
index 000000000000..a67f93c140fa
--- /dev/null
+++ b/drivers/thunderbolt/tunnel_pci.h
@@ -0,0 +1,30 @@
1/*
2 * Thunderbolt Cactus Ridge driver - PCIe tunnel
3 *
4 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
5 */
6
7#ifndef TB_PCI_H_
8#define TB_PCI_H_
9
10#include "tb.h"
11
12struct tb_pci_tunnel {
13 struct tb *tb;
14 struct tb_port *up_port;
15 struct tb_port *down_port;
16 struct tb_path *path_to_up;
17 struct tb_path *path_to_down;
18 struct list_head list;
19};
20
21struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up,
22 struct tb_port *down);
23void tb_pci_free(struct tb_pci_tunnel *tunnel);
24int tb_pci_activate(struct tb_pci_tunnel *tunnel);
25int tb_pci_restart(struct tb_pci_tunnel *tunnel);
26void tb_pci_deactivate(struct tb_pci_tunnel *tunnel);
27bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel);
28
29#endif
30