aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/tunnel_pci.c
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 /drivers/thunderbolt/tunnel_pci.c
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>
Diffstat (limited to 'drivers/thunderbolt/tunnel_pci.c')
-rw-r--r--drivers/thunderbolt/tunnel_pci.c232
1 files changed, 232 insertions, 0 deletions
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