Commit | Line | Data |
---|---|---|
d6cc51cd AN |
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 | ||
a25c8b2f AN |
10 | #include <linux/pci.h> |
11 | ||
12 | #include "tb_regs.h" | |
d6cc51cd AN |
13 | #include "ctl.h" |
14 | ||
a25c8b2f AN |
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; | |
c90553b3 | 22 | u64 uid; |
ca389f71 | 23 | int cap_plug_events; /* offset, zero if not found */ |
053596d9 | 24 | bool is_unplugged; /* unplugged, will go away */ |
cd22e73b | 25 | u8 *drom; |
a25c8b2f AN |
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 */ | |
9da672a4 | 35 | int cap_phy; /* offset, zero if not found */ |
a25c8b2f | 36 | u8 port; /* port number on switch */ |
cd22e73b AN |
37 | bool disabled; /* disabled by eeprom */ |
38 | struct tb_port *dual_link_port; | |
39 | u8 link_nr:1; | |
a25c8b2f AN |
40 | }; |
41 | ||
520b6702 AN |
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 | ||
d6cc51cd AN |
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 */ | |
a25c8b2f | 107 | struct tb_switch *root_switch; |
3364f0c1 | 108 | struct list_head tunnel_list; /* list of active PCIe tunnels */ |
d6cc51cd AN |
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 | ||
a25c8b2f AN |
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 | ||
d6cc51cd AN |
219 | struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi); |
220 | void thunderbolt_shutdown_and_free(struct tb *tb); | |
23dd5bb4 AN |
221 | void thunderbolt_suspend(struct tb *tb); |
222 | void thunderbolt_resume(struct tb *tb); | |
d6cc51cd | 223 | |
a25c8b2f AN |
224 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route); |
225 | void tb_switch_free(struct tb_switch *sw); | |
23dd5bb4 AN |
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); | |
053596d9 AN |
229 | void tb_sw_set_unpplugged(struct tb_switch *sw); |
230 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); | |
a25c8b2f | 231 | |
9da672a4 | 232 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); |
520b6702 AN |
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); | |
9da672a4 | 235 | |
7f2d5f7b | 236 | int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap); |
e2b8785e | 237 | |
520b6702 AN |
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 | ||
cd22e73b AN |
244 | int tb_drom_read(struct tb_switch *sw); |
245 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); | |
c90553b3 | 246 | |
a25c8b2f AN |
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 | ||
9da672a4 AN |
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 | ||
d6cc51cd | 271 | #endif |