diff options
Diffstat (limited to 'drivers/thunderbolt/tb.h')
-rw-r--r-- | drivers/thunderbolt/tb.h | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h new file mode 100644 index 000000000000..8b0d7cf2b6d6 --- /dev/null +++ b/drivers/thunderbolt/tb.h | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) | ||
3 | * | ||
4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
5 | */ | ||
6 | |||
7 | #ifndef TB_H_ | ||
8 | #define TB_H_ | ||
9 | |||
10 | #include <linux/pci.h> | ||
11 | |||
12 | #include "tb_regs.h" | ||
13 | #include "ctl.h" | ||
14 | |||
15 | /** | ||
16 | * struct tb_switch - a thunderbolt switch | ||
17 | */ | ||
18 | struct tb_switch { | ||
19 | struct tb_regs_switch_header config; | ||
20 | struct tb_port *ports; | ||
21 | struct tb *tb; | ||
22 | u64 uid; | ||
23 | int cap_plug_events; /* offset, zero if not found */ | ||
24 | bool is_unplugged; /* unplugged, will go away */ | ||
25 | u8 *drom; | ||
26 | }; | ||
27 | |||
28 | /** | ||
29 | * struct tb_port - a thunderbolt port, part of a tb_switch | ||
30 | */ | ||
31 | struct tb_port { | ||
32 | struct tb_regs_port_header config; | ||
33 | struct tb_switch *sw; | ||
34 | struct tb_port *remote; /* remote port, NULL if not connected */ | ||
35 | int cap_phy; /* offset, zero if not found */ | ||
36 | u8 port; /* port number on switch */ | ||
37 | bool disabled; /* disabled by eeprom */ | ||
38 | struct tb_port *dual_link_port; | ||
39 | u8 link_nr:1; | ||
40 | }; | ||
41 | |||
42 | /** | ||
43 | * struct tb_path_hop - routing information for a tb_path | ||
44 | * | ||
45 | * Hop configuration is always done on the IN port of a switch. | ||
46 | * in_port and out_port have to be on the same switch. Packets arriving on | ||
47 | * in_port with "hop" = in_hop_index will get routed to through out_port. The | ||
48 | * next hop to take (on out_port->remote) is determined by next_hop_index. | ||
49 | * | ||
50 | * in_counter_index is the index of a counter (in TB_CFG_COUNTERS) on the in | ||
51 | * port. | ||
52 | */ | ||
53 | struct tb_path_hop { | ||
54 | struct tb_port *in_port; | ||
55 | struct tb_port *out_port; | ||
56 | int in_hop_index; | ||
57 | int in_counter_index; /* write -1 to disable counters for this hop. */ | ||
58 | int next_hop_index; | ||
59 | }; | ||
60 | |||
61 | /** | ||
62 | * enum tb_path_port - path options mask | ||
63 | */ | ||
64 | enum tb_path_port { | ||
65 | TB_PATH_NONE = 0, | ||
66 | TB_PATH_SOURCE = 1, /* activate on the first hop (out of src) */ | ||
67 | TB_PATH_INTERNAL = 2, /* activate on other hops (not the first/last) */ | ||
68 | TB_PATH_DESTINATION = 4, /* activate on the last hop (into dst) */ | ||
69 | TB_PATH_ALL = 7, | ||
70 | }; | ||
71 | |||
72 | /** | ||
73 | * struct tb_path - a unidirectional path between two ports | ||
74 | * | ||
75 | * A path consists of a number of hops (see tb_path_hop). To establish a PCIe | ||
76 | * tunnel two paths have to be created between the two PCIe ports. | ||
77 | * | ||
78 | */ | ||
79 | struct tb_path { | ||
80 | struct tb *tb; | ||
81 | int nfc_credits; /* non flow controlled credits */ | ||
82 | enum tb_path_port ingress_shared_buffer; | ||
83 | enum tb_path_port egress_shared_buffer; | ||
84 | enum tb_path_port ingress_fc_enable; | ||
85 | enum tb_path_port egress_fc_enable; | ||
86 | |||
87 | int priority:3; | ||
88 | int weight:4; | ||
89 | bool drop_packages; | ||
90 | bool activated; | ||
91 | struct tb_path_hop *hops; | ||
92 | int path_length; /* number of hops */ | ||
93 | }; | ||
94 | |||
95 | |||
96 | /** | ||
97 | * struct tb - main thunderbolt bus structure | ||
98 | */ | ||
99 | struct tb { | ||
100 | struct mutex lock; /* | ||
101 | * Big lock. Must be held when accessing cfg or | ||
102 | * any struct tb_switch / struct tb_port. | ||
103 | */ | ||
104 | struct tb_nhi *nhi; | ||
105 | struct tb_ctl *ctl; | ||
106 | struct workqueue_struct *wq; /* ordered workqueue for plug events */ | ||
107 | struct tb_switch *root_switch; | ||
108 | struct list_head tunnel_list; /* list of active PCIe tunnels */ | ||
109 | bool hotplug_active; /* | ||
110 | * tb_handle_hotplug will stop progressing plug | ||
111 | * events and exit if this is not set (it needs to | ||
112 | * acquire the lock one more time). Used to drain | ||
113 | * wq after cfg has been paused. | ||
114 | */ | ||
115 | |||
116 | }; | ||
117 | |||
118 | /* helper functions & macros */ | ||
119 | |||
120 | /** | ||
121 | * tb_upstream_port() - return the upstream port of a switch | ||
122 | * | ||
123 | * Every switch has an upstream port (for the root switch it is the NHI). | ||
124 | * | ||
125 | * During switch alloc/init tb_upstream_port()->remote may be NULL, even for | ||
126 | * non root switches (on the NHI port remote is always NULL). | ||
127 | * | ||
128 | * Return: Returns the upstream port of the switch. | ||
129 | */ | ||
130 | static inline struct tb_port *tb_upstream_port(struct tb_switch *sw) | ||
131 | { | ||
132 | return &sw->ports[sw->config.upstream_port_number]; | ||
133 | } | ||
134 | |||
135 | static inline u64 tb_route(struct tb_switch *sw) | ||
136 | { | ||
137 | return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo; | ||
138 | } | ||
139 | |||
140 | static inline int tb_sw_read(struct tb_switch *sw, void *buffer, | ||
141 | enum tb_cfg_space space, u32 offset, u32 length) | ||
142 | { | ||
143 | return tb_cfg_read(sw->tb->ctl, | ||
144 | buffer, | ||
145 | tb_route(sw), | ||
146 | 0, | ||
147 | space, | ||
148 | offset, | ||
149 | length); | ||
150 | } | ||
151 | |||
152 | static inline int tb_sw_write(struct tb_switch *sw, void *buffer, | ||
153 | enum tb_cfg_space space, u32 offset, u32 length) | ||
154 | { | ||
155 | return tb_cfg_write(sw->tb->ctl, | ||
156 | buffer, | ||
157 | tb_route(sw), | ||
158 | 0, | ||
159 | space, | ||
160 | offset, | ||
161 | length); | ||
162 | } | ||
163 | |||
164 | static inline int tb_port_read(struct tb_port *port, void *buffer, | ||
165 | enum tb_cfg_space space, u32 offset, u32 length) | ||
166 | { | ||
167 | return tb_cfg_read(port->sw->tb->ctl, | ||
168 | buffer, | ||
169 | tb_route(port->sw), | ||
170 | port->port, | ||
171 | space, | ||
172 | offset, | ||
173 | length); | ||
174 | } | ||
175 | |||
176 | static inline int tb_port_write(struct tb_port *port, void *buffer, | ||
177 | enum tb_cfg_space space, u32 offset, u32 length) | ||
178 | { | ||
179 | return tb_cfg_write(port->sw->tb->ctl, | ||
180 | buffer, | ||
181 | tb_route(port->sw), | ||
182 | port->port, | ||
183 | space, | ||
184 | offset, | ||
185 | length); | ||
186 | } | ||
187 | |||
188 | #define tb_err(tb, fmt, arg...) dev_err(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
189 | #define tb_WARN(tb, fmt, arg...) dev_WARN(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
190 | #define tb_warn(tb, fmt, arg...) dev_warn(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
191 | #define tb_info(tb, fmt, arg...) dev_info(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
192 | |||
193 | |||
194 | #define __TB_SW_PRINT(level, sw, fmt, arg...) \ | ||
195 | do { \ | ||
196 | struct tb_switch *__sw = (sw); \ | ||
197 | level(__sw->tb, "%llx: " fmt, \ | ||
198 | tb_route(__sw), ## arg); \ | ||
199 | } while (0) | ||
200 | #define tb_sw_WARN(sw, fmt, arg...) __TB_SW_PRINT(tb_WARN, sw, fmt, ##arg) | ||
201 | #define tb_sw_warn(sw, fmt, arg...) __TB_SW_PRINT(tb_warn, sw, fmt, ##arg) | ||
202 | #define tb_sw_info(sw, fmt, arg...) __TB_SW_PRINT(tb_info, sw, fmt, ##arg) | ||
203 | |||
204 | |||
205 | #define __TB_PORT_PRINT(level, _port, fmt, arg...) \ | ||
206 | do { \ | ||
207 | struct tb_port *__port = (_port); \ | ||
208 | level(__port->sw->tb, "%llx:%x: " fmt, \ | ||
209 | tb_route(__port->sw), __port->port, ## arg); \ | ||
210 | } while (0) | ||
211 | #define tb_port_WARN(port, fmt, arg...) \ | ||
212 | __TB_PORT_PRINT(tb_WARN, port, fmt, ##arg) | ||
213 | #define tb_port_warn(port, fmt, arg...) \ | ||
214 | __TB_PORT_PRINT(tb_warn, port, fmt, ##arg) | ||
215 | #define tb_port_info(port, fmt, arg...) \ | ||
216 | __TB_PORT_PRINT(tb_info, port, fmt, ##arg) | ||
217 | |||
218 | |||
219 | struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi); | ||
220 | void thunderbolt_shutdown_and_free(struct tb *tb); | ||
221 | void thunderbolt_suspend(struct tb *tb); | ||
222 | void thunderbolt_resume(struct tb *tb); | ||
223 | |||
224 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route); | ||
225 | void tb_switch_free(struct tb_switch *sw); | ||
226 | void tb_switch_suspend(struct tb_switch *sw); | ||
227 | int tb_switch_resume(struct tb_switch *sw); | ||
228 | int tb_switch_reset(struct tb *tb, u64 route); | ||
229 | void tb_sw_set_unpplugged(struct tb_switch *sw); | ||
230 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); | ||
231 | |||
232 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); | ||
233 | int tb_port_add_nfc_credits(struct tb_port *port, int credits); | ||
234 | int tb_port_clear_counter(struct tb_port *port, int counter); | ||
235 | |||
236 | int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap); | ||
237 | |||
238 | struct tb_path *tb_path_alloc(struct tb *tb, int num_hops); | ||
239 | void tb_path_free(struct tb_path *path); | ||
240 | int tb_path_activate(struct tb_path *path); | ||
241 | void tb_path_deactivate(struct tb_path *path); | ||
242 | bool tb_path_is_invalid(struct tb_path *path); | ||
243 | |||
244 | int tb_drom_read(struct tb_switch *sw); | ||
245 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); | ||
246 | |||
247 | |||
248 | static inline int tb_route_length(u64 route) | ||
249 | { | ||
250 | return (fls64(route) + TB_ROUTE_SHIFT - 1) / TB_ROUTE_SHIFT; | ||
251 | } | ||
252 | |||
253 | static inline bool tb_is_upstream_port(struct tb_port *port) | ||
254 | { | ||
255 | return port == tb_upstream_port(port->sw); | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * tb_downstream_route() - get route to downstream switch | ||
260 | * | ||
261 | * Port must not be the upstream port (otherwise a loop is created). | ||
262 | * | ||
263 | * Return: Returns a route to the switch behind @port. | ||
264 | */ | ||
265 | static inline u64 tb_downstream_route(struct tb_port *port) | ||
266 | { | ||
267 | return tb_route(port->sw) | ||
268 | | ((u64) port->port << (port->sw->config.depth * 8)); | ||
269 | } | ||
270 | |||
271 | #endif | ||