Commit | Line | Data |
---|---|---|
8aa9ebcc VO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> | |
3 | */ | |
4 | #include "sja1105.h" | |
5 | ||
4a950786 VO |
6 | /* In the dynamic configuration interface, the switch exposes a register-like |
7 | * view of some of the static configuration tables. | |
8 | * Many times the field organization of the dynamic tables is abbreviated (not | |
9 | * all fields are dynamically reconfigurable) and different from the static | |
10 | * ones, but the key reason for having it is that we can spare a switch reset | |
11 | * for settings that can be changed dynamically. | |
12 | * | |
13 | * This file creates a per-switch-family abstraction called | |
14 | * struct sja1105_dynamic_table_ops and two operations that work with it: | |
15 | * - sja1105_dynamic_config_write | |
16 | * - sja1105_dynamic_config_read | |
17 | * | |
18 | * Compared to the struct sja1105_table_ops from sja1105_static_config.c, | |
19 | * the dynamic accessors work with a compound buffer: | |
20 | * | |
21 | * packed_buf | |
22 | * | |
23 | * | | |
24 | * V | |
25 | * +-----------------------------------------+------------------+ | |
26 | * | ENTRY BUFFER | COMMAND BUFFER | | |
27 | * +-----------------------------------------+------------------+ | |
28 | * | |
29 | * <----------------------- packed_size ------------------------> | |
30 | * | |
31 | * The ENTRY BUFFER may or may not have the same layout, or size, as its static | |
32 | * configuration table entry counterpart. When it does, the same packing | |
33 | * function is reused (bar exceptional cases - see | |
34 | * sja1105pqrs_dyn_l2_lookup_entry_packing). | |
35 | * | |
36 | * The reason for the COMMAND BUFFER being at the end is to be able to send | |
37 | * a dynamic write command through a single SPI burst. By the time the switch | |
38 | * reacts to the command, the ENTRY BUFFER is already populated with the data | |
39 | * sent by the core. | |
40 | * | |
41 | * The COMMAND BUFFER is always SJA1105_SIZE_DYN_CMD bytes (one 32-bit word) in | |
42 | * size. | |
43 | * | |
44 | * Sometimes the ENTRY BUFFER does not really exist (when the number of fields | |
45 | * that can be reconfigured is small), then the switch repurposes some of the | |
46 | * unused 32 bits of the COMMAND BUFFER to hold ENTRY data. | |
47 | * | |
48 | * The key members of struct sja1105_dynamic_table_ops are: | |
49 | * - .entry_packing: A function that deals with packing an ENTRY structure | |
50 | * into an SPI buffer, or retrieving an ENTRY structure | |
51 | * from one. | |
52 | * The @packed_buf pointer it's given does always point to | |
53 | * the ENTRY portion of the buffer. | |
54 | * - .cmd_packing: A function that deals with packing/unpacking the COMMAND | |
55 | * structure to/from the SPI buffer. | |
56 | * It is given the same @packed_buf pointer as .entry_packing, | |
57 | * so most of the time, the @packed_buf points *behind* the | |
58 | * COMMAND offset inside the buffer. | |
59 | * To access the COMMAND portion of the buffer, the function | |
60 | * knows its correct offset. | |
61 | * Giving both functions the same pointer is handy because in | |
62 | * extreme cases (see sja1105pqrs_dyn_l2_lookup_entry_packing) | |
63 | * the .entry_packing is able to jump to the COMMAND portion, | |
64 | * or vice-versa (sja1105pqrs_l2_lookup_cmd_packing). | |
65 | * - .access: A bitmap of: | |
66 | * OP_READ: Set if the hardware manual marks the ENTRY portion of the | |
67 | * dynamic configuration table buffer as R (readable) after | |
68 | * an SPI read command (the switch will populate the buffer). | |
69 | * OP_WRITE: Set if the manual marks the ENTRY portion of the dynamic | |
70 | * table buffer as W (writable) after an SPI write command | |
71 | * (the switch will read the fields provided in the buffer). | |
72 | * OP_DEL: Set if the manual says the VALIDENT bit is supported in the | |
73 | * COMMAND portion of this dynamic config buffer (i.e. the | |
74 | * specified entry can be invalidated through a SPI write | |
75 | * command). | |
76 | * OP_SEARCH: Set if the manual says that the index of an entry can | |
77 | * be retrieved in the COMMAND portion of the buffer based | |
78 | * on its ENTRY portion, as a result of a SPI write command. | |
79 | * Only the TCAM-based FDB table on SJA1105 P/Q/R/S supports | |
80 | * this. | |
96c85f51 VO |
81 | * OP_VALID_ANYWAY: Reading some tables through the dynamic config |
82 | * interface is possible even if the VALIDENT bit is not | |
83 | * set in the writeback. So don't error out in that case. | |
4a950786 VO |
84 | * - .max_entry_count: The number of entries, counting from zero, that can be |
85 | * reconfigured through the dynamic interface. If a static | |
86 | * table can be reconfigured at all dynamically, this | |
87 | * number always matches the maximum number of supported | |
88 | * static entries. | |
89 | * - .packed_size: The length in bytes of the compound ENTRY + COMMAND BUFFER. | |
90 | * Note that sometimes the compound buffer may contain holes in | |
91 | * it (see sja1105_vlan_lookup_cmd_packing). The @packed_buf is | |
92 | * contiguous however, so @packed_size includes any unused | |
93 | * bytes. | |
94 | * - .addr: The base SPI address at which the buffer must be written to the | |
95 | * switch's memory. When looking at the hardware manual, this must | |
96 | * always match the lowest documented address for the ENTRY, and not | |
97 | * that of the COMMAND, since the other 32-bit words will follow along | |
98 | * at the correct addresses. | |
99 | */ | |
100 | ||
8aa9ebcc VO |
101 | #define SJA1105_SIZE_DYN_CMD 4 |
102 | ||
2b7fea0d | 103 | #define SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD \ |
94f94d4a VO |
104 | SJA1105_SIZE_DYN_CMD |
105 | ||
2b7fea0d | 106 | #define SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD \ |
94f94d4a VO |
107 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_VL_LOOKUP_ENTRY) |
108 | ||
3e77e59b VO |
109 | #define SJA1110_SIZE_VL_POLICING_DYN_CMD \ |
110 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_VL_POLICING_ENTRY) | |
111 | ||
8aa9ebcc VO |
112 | #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \ |
113 | SJA1105_SIZE_DYN_CMD | |
114 | ||
115 | #define SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD \ | |
116 | (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_L2_LOOKUP_ENTRY) | |
117 | ||
118 | #define SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD \ | |
119 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY) | |
120 | ||
3e77e59b VO |
121 | #define SJA1110_SIZE_L2_LOOKUP_DYN_CMD \ |
122 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_L2_LOOKUP_ENTRY) | |
123 | ||
8aa9ebcc VO |
124 | #define SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD \ |
125 | (SJA1105_SIZE_DYN_CMD + 4 + SJA1105_SIZE_VLAN_LOOKUP_ENTRY) | |
126 | ||
3e77e59b VO |
127 | #define SJA1110_SIZE_VLAN_LOOKUP_DYN_CMD \ |
128 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_VLAN_LOOKUP_ENTRY) | |
129 | ||
8aa9ebcc VO |
130 | #define SJA1105_SIZE_L2_FORWARDING_DYN_CMD \ |
131 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_L2_FORWARDING_ENTRY) | |
132 | ||
133 | #define SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD \ | |
134 | (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY) | |
135 | ||
136 | #define SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD \ | |
137 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY) | |
138 | ||
139 | #define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ | |
140 | SJA1105_SIZE_DYN_CMD | |
141 | ||
99b981f4 VO |
142 | #define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ |
143 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY) | |
144 | ||
3e77e59b VO |
145 | #define SJA1110_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ |
146 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_L2_LOOKUP_PARAMS_ENTRY) | |
147 | ||
8aa9ebcc VO |
148 | #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \ |
149 | SJA1105_SIZE_DYN_CMD | |
150 | ||
99b981f4 VO |
151 | #define SJA1105PQRS_SIZE_GENERAL_PARAMS_DYN_CMD \ |
152 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY) | |
153 | ||
3e77e59b VO |
154 | #define SJA1110_SIZE_GENERAL_PARAMS_DYN_CMD \ |
155 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_GENERAL_PARAMS_ENTRY) | |
156 | ||
0a7e984c VO |
157 | #define SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD \ |
158 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY) | |
159 | ||
88cac0fa VO |
160 | #define SJA1105_SIZE_RETAGGING_DYN_CMD \ |
161 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_RETAGGING_ENTRY) | |
162 | ||
4d752508 VO |
163 | #define SJA1105ET_SIZE_CBS_DYN_CMD \ |
164 | (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_CBS_ENTRY) | |
165 | ||
166 | #define SJA1105PQRS_SIZE_CBS_DYN_CMD \ | |
167 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_CBS_ENTRY) | |
168 | ||
3e77e59b VO |
169 | #define SJA1110_SIZE_XMII_PARAMS_DYN_CMD \ |
170 | SJA1110_SIZE_XMII_PARAMS_ENTRY | |
171 | ||
172 | #define SJA1110_SIZE_L2_POLICING_DYN_CMD \ | |
173 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_L2_POLICING_ENTRY) | |
174 | ||
175 | #define SJA1110_SIZE_L2_FORWARDING_PARAMS_DYN_CMD \ | |
176 | SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY | |
177 | ||
8aa9ebcc | 178 | #define SJA1105_MAX_DYN_CMD_SIZE \ |
3e77e59b | 179 | SJA1110_SIZE_GENERAL_PARAMS_DYN_CMD |
8aa9ebcc | 180 | |
31b31120 | 181 | struct sja1105_dyn_cmd { |
90c96cca | 182 | bool search; |
31b31120 VO |
183 | u64 valid; |
184 | u64 rdwrset; | |
185 | u64 errors; | |
186 | u64 valident; | |
187 | u64 index; | |
188 | }; | |
189 | ||
10c3be65 VO |
190 | enum sja1105_hostcmd { |
191 | SJA1105_HOSTCMD_SEARCH = 1, | |
192 | SJA1105_HOSTCMD_READ = 2, | |
193 | SJA1105_HOSTCMD_WRITE = 3, | |
194 | SJA1105_HOSTCMD_INVALIDATE = 4, | |
195 | }; | |
196 | ||
ba61cf16 | 197 | /* Command and entry overlap */ |
94f94d4a | 198 | static void |
ba61cf16 VO |
199 | sja1105et_vl_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
200 | enum packing_op op) | |
94f94d4a VO |
201 | { |
202 | const int size = SJA1105_SIZE_DYN_CMD; | |
203 | ||
204 | sja1105_packing(buf, &cmd->valid, 31, 31, size, op); | |
205 | sja1105_packing(buf, &cmd->errors, 30, 30, size, op); | |
206 | sja1105_packing(buf, &cmd->rdwrset, 29, 29, size, op); | |
207 | sja1105_packing(buf, &cmd->index, 9, 0, size, op); | |
208 | } | |
209 | ||
ba61cf16 VO |
210 | /* Command and entry are separate */ |
211 | static void | |
212 | sja1105pqrs_vl_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
213 | enum packing_op op) | |
214 | { | |
215 | u8 *p = buf + SJA1105_SIZE_VL_LOOKUP_ENTRY; | |
216 | const int size = SJA1105_SIZE_DYN_CMD; | |
217 | ||
218 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
219 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
220 | sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); | |
221 | sja1105_packing(p, &cmd->index, 9, 0, size, op); | |
222 | } | |
223 | ||
3e77e59b VO |
224 | static void |
225 | sja1110_vl_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
226 | enum packing_op op) | |
227 | { | |
228 | u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; | |
229 | const int size = SJA1105_SIZE_DYN_CMD; | |
230 | ||
231 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
232 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
233 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
234 | sja1105_packing(p, &cmd->index, 11, 0, size, op); | |
235 | } | |
236 | ||
94f94d4a VO |
237 | static size_t sja1105et_vl_lookup_entry_packing(void *buf, void *entry_ptr, |
238 | enum packing_op op) | |
239 | { | |
240 | struct sja1105_vl_lookup_entry *entry = entry_ptr; | |
2b7fea0d | 241 | const int size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD; |
94f94d4a VO |
242 | |
243 | sja1105_packing(buf, &entry->egrmirr, 21, 17, size, op); | |
244 | sja1105_packing(buf, &entry->ingrmirr, 16, 16, size, op); | |
245 | return size; | |
246 | } | |
247 | ||
3e77e59b VO |
248 | static void |
249 | sja1110_vl_policing_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
250 | enum packing_op op) | |
251 | { | |
252 | u8 *p = buf + SJA1105_SIZE_VL_LOOKUP_ENTRY; | |
253 | const int size = SJA1105_SIZE_DYN_CMD; | |
254 | ||
255 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
256 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
257 | sja1105_packing(p, &cmd->index, 11, 0, size, op); | |
258 | } | |
259 | ||
8aa9ebcc | 260 | static void |
74e7feff VO |
261 | sja1105pqrs_common_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
262 | enum packing_op op, int entry_size) | |
8aa9ebcc | 263 | { |
8aa9ebcc | 264 | const int size = SJA1105_SIZE_DYN_CMD; |
74e7feff | 265 | u8 *p = buf + entry_size; |
10c3be65 | 266 | u64 hostcmd; |
8aa9ebcc VO |
267 | |
268 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
269 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
270 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
271 | sja1105_packing(p, &cmd->valident, 27, 27, size, op); | |
10c3be65 VO |
272 | |
273 | /* VALIDENT is supposed to indicate "keep or not", but in SJA1105 E/T, | |
274 | * using it to delete a management route was unsupported. UM10944 | |
275 | * said about it: | |
276 | * | |
277 | * In case of a write access with the MGMTROUTE flag set, | |
278 | * the flag will be ignored. It will always be found cleared | |
279 | * for read accesses with the MGMTROUTE flag set. | |
280 | * | |
281 | * SJA1105 P/Q/R/S keeps the same behavior w.r.t. VALIDENT, but there | |
282 | * is now another flag called HOSTCMD which does more stuff (quoting | |
283 | * from UM11040): | |
284 | * | |
285 | * A write request is accepted only when HOSTCMD is set to write host | |
286 | * or invalid. A read request is accepted only when HOSTCMD is set to | |
287 | * search host or read host. | |
288 | * | |
289 | * So it is possible to translate a RDWRSET/VALIDENT combination into | |
290 | * HOSTCMD so that we keep the dynamic command API in place, and | |
291 | * at the same time achieve compatibility with the management route | |
292 | * command structure. | |
293 | */ | |
294 | if (cmd->rdwrset == SPI_READ) { | |
295 | if (cmd->search) | |
296 | hostcmd = SJA1105_HOSTCMD_SEARCH; | |
297 | else | |
298 | hostcmd = SJA1105_HOSTCMD_READ; | |
299 | } else { | |
300 | /* SPI_WRITE */ | |
301 | if (cmd->valident) | |
302 | hostcmd = SJA1105_HOSTCMD_WRITE; | |
303 | else | |
304 | hostcmd = SJA1105_HOSTCMD_INVALIDATE; | |
305 | } | |
306 | sja1105_packing(p, &hostcmd, 25, 23, size, op); | |
cb81698f VO |
307 | } |
308 | ||
309 | static void | |
310 | sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
311 | enum packing_op op) | |
312 | { | |
313 | int entry_size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; | |
314 | ||
315 | sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, entry_size); | |
10c3be65 | 316 | |
8aa9ebcc VO |
317 | /* Hack - The hardware takes the 'index' field within |
318 | * struct sja1105_l2_lookup_entry as the index on which this command | |
319 | * will operate. However it will ignore everything else, so 'index' | |
320 | * is logically part of command but physically part of entry. | |
321 | * Populate the 'index' entry field from within the command callback, | |
322 | * such that our API doesn't need to ask for a full-blown entry | |
323 | * structure when e.g. a delete is requested. | |
324 | */ | |
cb81698f | 325 | sja1105_packing(buf, &cmd->index, 15, 6, entry_size, op); |
74e7feff VO |
326 | } |
327 | ||
328 | static void | |
329 | sja1110_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
330 | enum packing_op op) | |
331 | { | |
cb81698f VO |
332 | int entry_size = SJA1110_SIZE_L2_LOOKUP_ENTRY; |
333 | ||
334 | sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, entry_size); | |
74e7feff | 335 | |
cb81698f | 336 | sja1105_packing(buf, &cmd->index, 10, 1, entry_size, op); |
74e7feff VO |
337 | } |
338 | ||
17ae6555 VO |
339 | /* The switch is so retarded that it makes our command/entry abstraction |
340 | * crumble apart. | |
341 | * | |
342 | * On P/Q/R/S, the switch tries to say whether a FDB entry | |
343 | * is statically programmed or dynamically learned via a flag called LOCKEDS. | |
344 | * The hardware manual says about this fiels: | |
345 | * | |
346 | * On write will specify the format of ENTRY. | |
347 | * On read the flag will be found cleared at times the VALID flag is found | |
348 | * set. The flag will also be found cleared in response to a read having the | |
349 | * MGMTROUTE flag set. In response to a read with the MGMTROUTE flag | |
350 | * cleared, the flag be set if the most recent access operated on an entry | |
351 | * that was either loaded by configuration or through dynamic reconfiguration | |
352 | * (as opposed to automatically learned entries). | |
353 | * | |
354 | * The trouble with this flag is that it's part of the *command* to access the | |
355 | * dynamic interface, and not part of the *entry* retrieved from it. | |
356 | * Otherwise said, for a sja1105_dynamic_config_read, LOCKEDS is supposed to be | |
357 | * an output from the switch into the command buffer, and for a | |
358 | * sja1105_dynamic_config_write, the switch treats LOCKEDS as an input | |
359 | * (hence we can write either static, or automatically learned entries, from | |
360 | * the core). | |
361 | * But the manual contradicts itself in the last phrase where it says that on | |
362 | * read, LOCKEDS will be set to 1 for all FDB entries written through the | |
363 | * dynamic interface (therefore, the value of LOCKEDS from the | |
364 | * sja1105_dynamic_config_write is not really used for anything, it'll store a | |
365 | * 1 anyway). | |
366 | * This means you can't really write a FDB entry with LOCKEDS=0 (automatically | |
367 | * learned) into the switch, which kind of makes sense. | |
368 | * As for reading through the dynamic interface, it doesn't make too much sense | |
369 | * to put LOCKEDS into the command, since the switch will inevitably have to | |
370 | * ignore it (otherwise a command would be like "read the FDB entry 123, but | |
371 | * only if it's dynamically learned" <- well how am I supposed to know?) and | |
372 | * just use it as an output buffer for its findings. But guess what... that's | |
373 | * what the entry buffer is for! | |
374 | * Unfortunately, what really breaks this abstraction is the fact that it | |
375 | * wasn't designed having the fact in mind that the switch can output | |
376 | * entry-related data as writeback through the command buffer. | |
377 | * However, whether a FDB entry is statically or dynamically learned *is* part | |
378 | * of the entry and not the command data, no matter what the switch thinks. | |
379 | * In order to do that, we'll need to wrap around the | |
380 | * sja1105pqrs_l2_lookup_entry_packing from sja1105_static_config.c, and take | |
381 | * a peek outside of the caller-supplied @buf (the entry buffer), to reach the | |
382 | * command buffer. | |
383 | */ | |
384 | static size_t | |
385 | sja1105pqrs_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr, | |
386 | enum packing_op op) | |
387 | { | |
388 | struct sja1105_l2_lookup_entry *entry = entry_ptr; | |
389 | u8 *cmd = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; | |
390 | const int size = SJA1105_SIZE_DYN_CMD; | |
391 | ||
392 | sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op); | |
393 | ||
394 | return sja1105pqrs_l2_lookup_entry_packing(buf, entry_ptr, op); | |
395 | } | |
396 | ||
3e77e59b VO |
397 | static size_t sja1110_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr, |
398 | enum packing_op op) | |
399 | { | |
400 | struct sja1105_l2_lookup_entry *entry = entry_ptr; | |
401 | u8 *cmd = buf + SJA1110_SIZE_L2_LOOKUP_ENTRY; | |
402 | const int size = SJA1105_SIZE_DYN_CMD; | |
403 | ||
404 | sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op); | |
405 | ||
406 | return sja1110_l2_lookup_entry_packing(buf, entry_ptr, op); | |
407 | } | |
408 | ||
8aa9ebcc VO |
409 | static void |
410 | sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
411 | enum packing_op op) | |
412 | { | |
413 | u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; | |
414 | const int size = SJA1105_SIZE_DYN_CMD; | |
415 | ||
416 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
417 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
418 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
419 | sja1105_packing(p, &cmd->valident, 27, 27, size, op); | |
420 | /* Hack - see comments above. */ | |
421 | sja1105_packing(buf, &cmd->index, 29, 20, | |
422 | SJA1105ET_SIZE_L2_LOOKUP_ENTRY, op); | |
423 | } | |
424 | ||
4b7da3d8 VO |
425 | static size_t sja1105et_dyn_l2_lookup_entry_packing(void *buf, void *entry_ptr, |
426 | enum packing_op op) | |
427 | { | |
428 | struct sja1105_l2_lookup_entry *entry = entry_ptr; | |
429 | u8 *cmd = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; | |
430 | const int size = SJA1105_SIZE_DYN_CMD; | |
431 | ||
432 | sja1105_packing(cmd, &entry->lockeds, 28, 28, size, op); | |
433 | ||
434 | return sja1105et_l2_lookup_entry_packing(buf, entry_ptr, op); | |
435 | } | |
436 | ||
8aa9ebcc VO |
437 | static void |
438 | sja1105et_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
439 | enum packing_op op) | |
440 | { | |
441 | u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY; | |
442 | u64 mgmtroute = 1; | |
443 | ||
444 | sja1105et_l2_lookup_cmd_packing(buf, cmd, op); | |
445 | if (op == PACK) | |
446 | sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD); | |
447 | } | |
448 | ||
449 | static size_t sja1105et_mgmt_route_entry_packing(void *buf, void *entry_ptr, | |
450 | enum packing_op op) | |
451 | { | |
452 | struct sja1105_mgmt_entry *entry = entry_ptr; | |
453 | const size_t size = SJA1105ET_SIZE_L2_LOOKUP_ENTRY; | |
454 | ||
455 | /* UM10944: To specify if a PTP egress timestamp shall be captured on | |
456 | * each port upon transmission of the frame, the LSB of VLANID in the | |
457 | * ENTRY field provided by the host must be set. | |
458 | * Bit 1 of VLANID then specifies the register where the timestamp for | |
459 | * this port is stored in. | |
460 | */ | |
461 | sja1105_packing(buf, &entry->tsreg, 85, 85, size, op); | |
462 | sja1105_packing(buf, &entry->takets, 84, 84, size, op); | |
463 | sja1105_packing(buf, &entry->macaddr, 83, 36, size, op); | |
464 | sja1105_packing(buf, &entry->destports, 35, 31, size, op); | |
465 | sja1105_packing(buf, &entry->enfport, 30, 30, size, op); | |
466 | return size; | |
467 | } | |
468 | ||
2a7e7409 VO |
469 | static void |
470 | sja1105pqrs_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
471 | enum packing_op op) | |
472 | { | |
473 | u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; | |
474 | u64 mgmtroute = 1; | |
475 | ||
476 | sja1105pqrs_l2_lookup_cmd_packing(buf, cmd, op); | |
477 | if (op == PACK) | |
478 | sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD); | |
479 | } | |
480 | ||
481 | static size_t sja1105pqrs_mgmt_route_entry_packing(void *buf, void *entry_ptr, | |
482 | enum packing_op op) | |
483 | { | |
484 | const size_t size = SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY; | |
485 | struct sja1105_mgmt_entry *entry = entry_ptr; | |
486 | ||
487 | /* In P/Q/R/S, enfport got renamed to mgmtvalid, but its purpose | |
488 | * is the same (driver uses it to confirm that frame was sent). | |
489 | * So just keep the name from E/T. | |
490 | */ | |
491 | sja1105_packing(buf, &entry->tsreg, 71, 71, size, op); | |
492 | sja1105_packing(buf, &entry->takets, 70, 70, size, op); | |
493 | sja1105_packing(buf, &entry->macaddr, 69, 22, size, op); | |
494 | sja1105_packing(buf, &entry->destports, 21, 17, size, op); | |
495 | sja1105_packing(buf, &entry->enfport, 16, 16, size, op); | |
496 | return size; | |
497 | } | |
498 | ||
8aa9ebcc VO |
499 | /* In E/T, entry is at addresses 0x27-0x28. There is a 4 byte gap at 0x29, |
500 | * and command is at 0x2a. Similarly in P/Q/R/S there is a 1 register gap | |
501 | * between entry (0x2d, 0x2e) and command (0x30). | |
502 | */ | |
503 | static void | |
504 | sja1105_vlan_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
505 | enum packing_op op) | |
506 | { | |
507 | u8 *p = buf + SJA1105_SIZE_VLAN_LOOKUP_ENTRY + 4; | |
508 | const int size = SJA1105_SIZE_DYN_CMD; | |
509 | ||
510 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
511 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
512 | sja1105_packing(p, &cmd->valident, 27, 27, size, op); | |
513 | /* Hack - see comments above, applied for 'vlanid' field of | |
514 | * struct sja1105_vlan_lookup_entry. | |
515 | */ | |
516 | sja1105_packing(buf, &cmd->index, 38, 27, | |
517 | SJA1105_SIZE_VLAN_LOOKUP_ENTRY, op); | |
518 | } | |
519 | ||
3e77e59b VO |
520 | /* In SJA1110 there is no gap between the command and the data, yay... */ |
521 | static void | |
522 | sja1110_vlan_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
523 | enum packing_op op) | |
524 | { | |
525 | u8 *p = buf + SJA1110_SIZE_VLAN_LOOKUP_ENTRY; | |
526 | const int size = SJA1105_SIZE_DYN_CMD; | |
527 | u64 type_entry = 0; | |
528 | ||
529 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
530 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
531 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
532 | /* Hack: treat 'vlanid' field of struct sja1105_vlan_lookup_entry as | |
533 | * cmd->index. | |
534 | */ | |
535 | sja1105_packing(buf, &cmd->index, 38, 27, | |
536 | SJA1110_SIZE_VLAN_LOOKUP_ENTRY, op); | |
537 | ||
538 | /* But the VALIDENT bit has disappeared, now we are supposed to | |
539 | * invalidate an entry through the TYPE_ENTRY field of the entry.. | |
540 | * This is a hack to transform the non-zero quality of the TYPE_ENTRY | |
541 | * field into a VALIDENT bit. | |
542 | */ | |
543 | if (op == PACK && !cmd->valident) { | |
544 | sja1105_packing(buf, &type_entry, 40, 39, | |
545 | SJA1110_SIZE_VLAN_LOOKUP_ENTRY, PACK); | |
546 | } else if (op == UNPACK) { | |
547 | sja1105_packing(buf, &type_entry, 40, 39, | |
548 | SJA1110_SIZE_VLAN_LOOKUP_ENTRY, UNPACK); | |
549 | cmd->valident = !!type_entry; | |
550 | } | |
551 | } | |
552 | ||
8aa9ebcc VO |
553 | static void |
554 | sja1105_l2_forwarding_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
555 | enum packing_op op) | |
556 | { | |
557 | u8 *p = buf + SJA1105_SIZE_L2_FORWARDING_ENTRY; | |
558 | const int size = SJA1105_SIZE_DYN_CMD; | |
559 | ||
560 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
561 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
562 | sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); | |
563 | sja1105_packing(p, &cmd->index, 4, 0, size, op); | |
564 | } | |
565 | ||
3e77e59b VO |
566 | static void |
567 | sja1110_l2_forwarding_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
568 | enum packing_op op) | |
569 | { | |
570 | u8 *p = buf + SJA1105_SIZE_L2_FORWARDING_ENTRY; | |
571 | const int size = SJA1105_SIZE_DYN_CMD; | |
572 | ||
573 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
574 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
575 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
576 | sja1105_packing(p, &cmd->index, 4, 0, size, op); | |
577 | } | |
578 | ||
8aa9ebcc VO |
579 | static void |
580 | sja1105et_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
581 | enum packing_op op) | |
582 | { | |
583 | const int size = SJA1105_SIZE_DYN_CMD; | |
584 | /* Yup, user manual definitions are reversed */ | |
585 | u8 *reg1 = buf + 4; | |
586 | ||
587 | sja1105_packing(reg1, &cmd->valid, 31, 31, size, op); | |
588 | sja1105_packing(reg1, &cmd->index, 26, 24, size, op); | |
589 | } | |
590 | ||
591 | static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr, | |
592 | enum packing_op op) | |
593 | { | |
594 | const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY; | |
595 | struct sja1105_mac_config_entry *entry = entry_ptr; | |
596 | /* Yup, user manual definitions are reversed */ | |
597 | u8 *reg1 = buf + 4; | |
598 | u8 *reg2 = buf; | |
599 | ||
600 | sja1105_packing(reg1, &entry->speed, 30, 29, size, op); | |
601 | sja1105_packing(reg1, &entry->drpdtag, 23, 23, size, op); | |
602 | sja1105_packing(reg1, &entry->drpuntag, 22, 22, size, op); | |
603 | sja1105_packing(reg1, &entry->retag, 21, 21, size, op); | |
604 | sja1105_packing(reg1, &entry->dyn_learn, 20, 20, size, op); | |
605 | sja1105_packing(reg1, &entry->egress, 19, 19, size, op); | |
606 | sja1105_packing(reg1, &entry->ingress, 18, 18, size, op); | |
607 | sja1105_packing(reg1, &entry->ing_mirr, 17, 17, size, op); | |
608 | sja1105_packing(reg1, &entry->egr_mirr, 16, 16, size, op); | |
609 | sja1105_packing(reg1, &entry->vlanprio, 14, 12, size, op); | |
610 | sja1105_packing(reg1, &entry->vlanid, 11, 0, size, op); | |
611 | sja1105_packing(reg2, &entry->tp_delin, 31, 16, size, op); | |
612 | sja1105_packing(reg2, &entry->tp_delout, 15, 0, size, op); | |
613 | /* MAC configuration table entries which can't be reconfigured: | |
614 | * top, base, enabled, ifg, maxage, drpnona664 | |
615 | */ | |
616 | /* Bogus return value, not used anywhere */ | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static void | |
621 | sja1105pqrs_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
622 | enum packing_op op) | |
623 | { | |
624 | const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY; | |
625 | u8 *p = buf + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; | |
626 | ||
627 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
628 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
629 | sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); | |
630 | sja1105_packing(p, &cmd->index, 2, 0, size, op); | |
631 | } | |
632 | ||
3e77e59b VO |
633 | static void |
634 | sja1110_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
635 | enum packing_op op) | |
636 | { | |
637 | u8 *p = buf + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; | |
638 | const int size = SJA1105_SIZE_DYN_CMD; | |
639 | ||
640 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
641 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
642 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
643 | sja1105_packing(p, &cmd->index, 3, 0, size, op); | |
644 | } | |
645 | ||
8aa9ebcc VO |
646 | static void |
647 | sja1105et_l2_lookup_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
648 | enum packing_op op) | |
649 | { | |
650 | sja1105_packing(buf, &cmd->valid, 31, 31, | |
651 | SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op); | |
652 | } | |
653 | ||
654 | static size_t | |
655 | sja1105et_l2_lookup_params_entry_packing(void *buf, void *entry_ptr, | |
656 | enum packing_op op) | |
657 | { | |
658 | struct sja1105_l2_lookup_params_entry *entry = entry_ptr; | |
659 | ||
660 | sja1105_packing(buf, &entry->poly, 7, 0, | |
661 | SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op); | |
662 | /* Bogus return value, not used anywhere */ | |
663 | return 0; | |
664 | } | |
665 | ||
99b981f4 VO |
666 | static void |
667 | sja1105pqrs_l2_lookup_params_cmd_packing(void *buf, | |
668 | struct sja1105_dyn_cmd *cmd, | |
669 | enum packing_op op) | |
670 | { | |
671 | u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY; | |
672 | const int size = SJA1105_SIZE_DYN_CMD; | |
673 | ||
674 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
675 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
676 | } | |
677 | ||
3e77e59b VO |
678 | static void |
679 | sja1110_l2_lookup_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
680 | enum packing_op op) | |
681 | { | |
682 | u8 *p = buf + SJA1110_SIZE_L2_LOOKUP_PARAMS_ENTRY; | |
683 | const int size = SJA1105_SIZE_DYN_CMD; | |
684 | ||
685 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
686 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
687 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
688 | } | |
689 | ||
8aa9ebcc VO |
690 | static void |
691 | sja1105et_general_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
692 | enum packing_op op) | |
693 | { | |
694 | const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD; | |
695 | ||
696 | sja1105_packing(buf, &cmd->valid, 31, 31, size, op); | |
697 | sja1105_packing(buf, &cmd->errors, 30, 30, size, op); | |
698 | } | |
699 | ||
700 | static size_t | |
701 | sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, | |
702 | enum packing_op op) | |
703 | { | |
704 | struct sja1105_general_params_entry *entry = entry_ptr; | |
705 | const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD; | |
706 | ||
707 | sja1105_packing(buf, &entry->mirr_port, 2, 0, size, op); | |
708 | /* Bogus return value, not used anywhere */ | |
709 | return 0; | |
710 | } | |
711 | ||
99b981f4 VO |
712 | static void |
713 | sja1105pqrs_general_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
714 | enum packing_op op) | |
715 | { | |
716 | u8 *p = buf + SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY; | |
717 | const int size = SJA1105_SIZE_DYN_CMD; | |
718 | ||
719 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
720 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
721 | sja1105_packing(p, &cmd->rdwrset, 28, 28, size, op); | |
722 | } | |
723 | ||
3e77e59b VO |
724 | static void |
725 | sja1110_general_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
726 | enum packing_op op) | |
727 | { | |
728 | u8 *p = buf + SJA1110_SIZE_GENERAL_PARAMS_ENTRY; | |
729 | const int size = SJA1105_SIZE_DYN_CMD; | |
730 | ||
731 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
732 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
733 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
734 | } | |
735 | ||
0a7e984c VO |
736 | static void |
737 | sja1105pqrs_avb_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
738 | enum packing_op op) | |
739 | { | |
740 | u8 *p = buf + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY; | |
741 | const int size = SJA1105_SIZE_DYN_CMD; | |
742 | ||
743 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
744 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
745 | sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op); | |
746 | } | |
747 | ||
88cac0fa VO |
748 | static void |
749 | sja1105_retagging_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
750 | enum packing_op op) | |
751 | { | |
752 | u8 *p = buf + SJA1105_SIZE_RETAGGING_ENTRY; | |
753 | const int size = SJA1105_SIZE_DYN_CMD; | |
754 | ||
755 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
756 | sja1105_packing(p, &cmd->errors, 30, 30, size, op); | |
757 | sja1105_packing(p, &cmd->valident, 29, 29, size, op); | |
758 | sja1105_packing(p, &cmd->rdwrset, 28, 28, size, op); | |
759 | sja1105_packing(p, &cmd->index, 5, 0, size, op); | |
760 | } | |
761 | ||
3e77e59b VO |
762 | static void |
763 | sja1110_retagging_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
764 | enum packing_op op) | |
765 | { | |
766 | u8 *p = buf + SJA1105_SIZE_RETAGGING_ENTRY; | |
767 | const int size = SJA1105_SIZE_DYN_CMD; | |
768 | ||
769 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
770 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
771 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
772 | sja1105_packing(p, &cmd->valident, 28, 28, size, op); | |
773 | sja1105_packing(p, &cmd->index, 4, 0, size, op); | |
774 | } | |
775 | ||
4d752508 VO |
776 | static void sja1105et_cbs_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
777 | enum packing_op op) | |
778 | { | |
779 | u8 *p = buf + SJA1105ET_SIZE_CBS_ENTRY; | |
780 | const int size = SJA1105_SIZE_DYN_CMD; | |
781 | ||
782 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
783 | sja1105_packing(p, &cmd->index, 19, 16, size, op); | |
784 | } | |
785 | ||
786 | static size_t sja1105et_cbs_entry_packing(void *buf, void *entry_ptr, | |
787 | enum packing_op op) | |
788 | { | |
789 | const size_t size = SJA1105ET_SIZE_CBS_ENTRY; | |
790 | struct sja1105_cbs_entry *entry = entry_ptr; | |
791 | u8 *cmd = buf + size; | |
792 | u32 *p = buf; | |
793 | ||
794 | sja1105_packing(cmd, &entry->port, 5, 3, SJA1105_SIZE_DYN_CMD, op); | |
795 | sja1105_packing(cmd, &entry->prio, 2, 0, SJA1105_SIZE_DYN_CMD, op); | |
796 | sja1105_packing(p + 3, &entry->credit_lo, 31, 0, size, op); | |
797 | sja1105_packing(p + 2, &entry->credit_hi, 31, 0, size, op); | |
798 | sja1105_packing(p + 1, &entry->send_slope, 31, 0, size, op); | |
799 | sja1105_packing(p + 0, &entry->idle_slope, 31, 0, size, op); | |
800 | return size; | |
801 | } | |
802 | ||
803 | static void sja1105pqrs_cbs_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
804 | enum packing_op op) | |
805 | { | |
806 | u8 *p = buf + SJA1105PQRS_SIZE_CBS_ENTRY; | |
807 | const int size = SJA1105_SIZE_DYN_CMD; | |
808 | ||
809 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
810 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
811 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
812 | sja1105_packing(p, &cmd->index, 3, 0, size, op); | |
813 | } | |
814 | ||
3e77e59b VO |
815 | static void sja1110_cbs_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
816 | enum packing_op op) | |
817 | { | |
818 | u8 *p = buf + SJA1105PQRS_SIZE_CBS_ENTRY; | |
819 | const int size = SJA1105_SIZE_DYN_CMD; | |
820 | ||
821 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
822 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
823 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
824 | sja1105_packing(p, &cmd->index, 7, 0, size, op); | |
825 | } | |
826 | ||
4d752508 VO |
827 | static size_t sja1105pqrs_cbs_entry_packing(void *buf, void *entry_ptr, |
828 | enum packing_op op) | |
829 | { | |
830 | const size_t size = SJA1105PQRS_SIZE_CBS_ENTRY; | |
831 | struct sja1105_cbs_entry *entry = entry_ptr; | |
832 | ||
833 | sja1105_packing(buf, &entry->port, 159, 157, size, op); | |
834 | sja1105_packing(buf, &entry->prio, 156, 154, size, op); | |
835 | sja1105_packing(buf, &entry->credit_lo, 153, 122, size, op); | |
836 | sja1105_packing(buf, &entry->credit_hi, 121, 90, size, op); | |
837 | sja1105_packing(buf, &entry->send_slope, 89, 58, size, op); | |
838 | sja1105_packing(buf, &entry->idle_slope, 57, 26, size, op); | |
839 | return size; | |
840 | } | |
841 | ||
3e77e59b VO |
842 | static size_t sja1110_cbs_entry_packing(void *buf, void *entry_ptr, |
843 | enum packing_op op) | |
844 | { | |
845 | const size_t size = SJA1105PQRS_SIZE_CBS_ENTRY; | |
846 | struct sja1105_cbs_entry *entry = entry_ptr; | |
847 | u64 entry_type = SJA1110_CBS_SHAPER; | |
848 | ||
849 | sja1105_packing(buf, &entry_type, 159, 159, size, op); | |
850 | sja1105_packing(buf, &entry->credit_lo, 151, 120, size, op); | |
851 | sja1105_packing(buf, &entry->credit_hi, 119, 88, size, op); | |
852 | sja1105_packing(buf, &entry->send_slope, 87, 56, size, op); | |
853 | sja1105_packing(buf, &entry->idle_slope, 55, 24, size, op); | |
854 | return size; | |
855 | } | |
856 | ||
857 | static void sja1110_dummy_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
858 | enum packing_op op) | |
859 | { | |
860 | } | |
861 | ||
862 | static void | |
863 | sja1110_l2_policing_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, | |
864 | enum packing_op op) | |
865 | { | |
866 | u8 *p = buf + SJA1105_SIZE_L2_POLICING_ENTRY; | |
867 | const int size = SJA1105_SIZE_DYN_CMD; | |
868 | ||
869 | sja1105_packing(p, &cmd->valid, 31, 31, size, op); | |
870 | sja1105_packing(p, &cmd->rdwrset, 30, 30, size, op); | |
871 | sja1105_packing(p, &cmd->errors, 29, 29, size, op); | |
872 | sja1105_packing(p, &cmd->index, 6, 0, size, op); | |
873 | } | |
874 | ||
8aa9ebcc VO |
875 | #define OP_READ BIT(0) |
876 | #define OP_WRITE BIT(1) | |
877 | #define OP_DEL BIT(2) | |
90c96cca | 878 | #define OP_SEARCH BIT(3) |
96c85f51 | 879 | #define OP_VALID_ANYWAY BIT(4) |
8aa9ebcc VO |
880 | |
881 | /* SJA1105E/T: First generation */ | |
718e44b6 | 882 | const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { |
94f94d4a VO |
883 | [BLK_IDX_VL_LOOKUP] = { |
884 | .entry_packing = sja1105et_vl_lookup_entry_packing, | |
ba61cf16 | 885 | .cmd_packing = sja1105et_vl_lookup_cmd_packing, |
94f94d4a VO |
886 | .access = OP_WRITE, |
887 | .max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, | |
2b7fea0d | 888 | .packed_size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD, |
94f94d4a VO |
889 | .addr = 0x35, |
890 | }, | |
8aa9ebcc | 891 | [BLK_IDX_L2_LOOKUP] = { |
4b7da3d8 | 892 | .entry_packing = sja1105et_dyn_l2_lookup_entry_packing, |
8aa9ebcc VO |
893 | .cmd_packing = sja1105et_l2_lookup_cmd_packing, |
894 | .access = (OP_READ | OP_WRITE | OP_DEL), | |
895 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, | |
896 | .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, | |
897 | .addr = 0x20, | |
898 | }, | |
899 | [BLK_IDX_MGMT_ROUTE] = { | |
900 | .entry_packing = sja1105et_mgmt_route_entry_packing, | |
901 | .cmd_packing = sja1105et_mgmt_route_cmd_packing, | |
96c85f51 | 902 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), |
8aa9ebcc VO |
903 | .max_entry_count = SJA1105_NUM_PORTS, |
904 | .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, | |
905 | .addr = 0x20, | |
906 | }, | |
8aa9ebcc VO |
907 | [BLK_IDX_VLAN_LOOKUP] = { |
908 | .entry_packing = sja1105_vlan_lookup_entry_packing, | |
909 | .cmd_packing = sja1105_vlan_lookup_cmd_packing, | |
910 | .access = (OP_WRITE | OP_DEL), | |
911 | .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, | |
912 | .packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD, | |
913 | .addr = 0x27, | |
914 | }, | |
915 | [BLK_IDX_L2_FORWARDING] = { | |
916 | .entry_packing = sja1105_l2_forwarding_entry_packing, | |
917 | .cmd_packing = sja1105_l2_forwarding_cmd_packing, | |
918 | .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, | |
919 | .access = OP_WRITE, | |
920 | .packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD, | |
921 | .addr = 0x24, | |
922 | }, | |
923 | [BLK_IDX_MAC_CONFIG] = { | |
924 | .entry_packing = sja1105et_mac_config_entry_packing, | |
925 | .cmd_packing = sja1105et_mac_config_cmd_packing, | |
926 | .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, | |
927 | .access = OP_WRITE, | |
928 | .packed_size = SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD, | |
929 | .addr = 0x36, | |
930 | }, | |
931 | [BLK_IDX_L2_LOOKUP_PARAMS] = { | |
932 | .entry_packing = sja1105et_l2_lookup_params_entry_packing, | |
933 | .cmd_packing = sja1105et_l2_lookup_params_cmd_packing, | |
934 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, | |
935 | .access = OP_WRITE, | |
936 | .packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, | |
937 | .addr = 0x38, | |
938 | }, | |
8aa9ebcc VO |
939 | [BLK_IDX_GENERAL_PARAMS] = { |
940 | .entry_packing = sja1105et_general_params_entry_packing, | |
941 | .cmd_packing = sja1105et_general_params_cmd_packing, | |
942 | .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, | |
943 | .access = OP_WRITE, | |
944 | .packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD, | |
945 | .addr = 0x34, | |
946 | }, | |
88cac0fa VO |
947 | [BLK_IDX_RETAGGING] = { |
948 | .entry_packing = sja1105_retagging_entry_packing, | |
949 | .cmd_packing = sja1105_retagging_cmd_packing, | |
950 | .max_entry_count = SJA1105_MAX_RETAGGING_COUNT, | |
951 | .access = (OP_WRITE | OP_DEL), | |
952 | .packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD, | |
953 | .addr = 0x31, | |
954 | }, | |
4d752508 VO |
955 | [BLK_IDX_CBS] = { |
956 | .entry_packing = sja1105et_cbs_entry_packing, | |
957 | .cmd_packing = sja1105et_cbs_cmd_packing, | |
958 | .max_entry_count = SJA1105ET_MAX_CBS_COUNT, | |
959 | .access = OP_WRITE, | |
960 | .packed_size = SJA1105ET_SIZE_CBS_DYN_CMD, | |
961 | .addr = 0x2c, | |
962 | }, | |
8aa9ebcc VO |
963 | }; |
964 | ||
2a7e7409 | 965 | /* SJA1105P/Q/R/S: Second generation */ |
718e44b6 | 966 | const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { |
94f94d4a VO |
967 | [BLK_IDX_VL_LOOKUP] = { |
968 | .entry_packing = sja1105_vl_lookup_entry_packing, | |
ba61cf16 | 969 | .cmd_packing = sja1105pqrs_vl_lookup_cmd_packing, |
94f94d4a VO |
970 | .access = (OP_READ | OP_WRITE), |
971 | .max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, | |
2b7fea0d | 972 | .packed_size = SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD, |
94f94d4a VO |
973 | .addr = 0x47, |
974 | }, | |
8aa9ebcc | 975 | [BLK_IDX_L2_LOOKUP] = { |
17ae6555 | 976 | .entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing, |
8aa9ebcc | 977 | .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, |
10c3be65 | 978 | .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), |
8aa9ebcc | 979 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, |
10c3be65 | 980 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, |
8aa9ebcc VO |
981 | .addr = 0x24, |
982 | }, | |
2a7e7409 VO |
983 | [BLK_IDX_MGMT_ROUTE] = { |
984 | .entry_packing = sja1105pqrs_mgmt_route_entry_packing, | |
985 | .cmd_packing = sja1105pqrs_mgmt_route_cmd_packing, | |
96c85f51 | 986 | .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH | OP_VALID_ANYWAY), |
2a7e7409 VO |
987 | .max_entry_count = SJA1105_NUM_PORTS, |
988 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, | |
989 | .addr = 0x24, | |
990 | }, | |
8aa9ebcc VO |
991 | [BLK_IDX_VLAN_LOOKUP] = { |
992 | .entry_packing = sja1105_vlan_lookup_entry_packing, | |
993 | .cmd_packing = sja1105_vlan_lookup_cmd_packing, | |
994 | .access = (OP_READ | OP_WRITE | OP_DEL), | |
995 | .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, | |
996 | .packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD, | |
997 | .addr = 0x2D, | |
998 | }, | |
999 | [BLK_IDX_L2_FORWARDING] = { | |
1000 | .entry_packing = sja1105_l2_forwarding_entry_packing, | |
1001 | .cmd_packing = sja1105_l2_forwarding_cmd_packing, | |
1002 | .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, | |
1003 | .access = OP_WRITE, | |
1004 | .packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD, | |
1005 | .addr = 0x2A, | |
1006 | }, | |
1007 | [BLK_IDX_MAC_CONFIG] = { | |
1008 | .entry_packing = sja1105pqrs_mac_config_entry_packing, | |
1009 | .cmd_packing = sja1105pqrs_mac_config_cmd_packing, | |
1010 | .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, | |
1011 | .access = (OP_READ | OP_WRITE), | |
1012 | .packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD, | |
1013 | .addr = 0x4B, | |
1014 | }, | |
1015 | [BLK_IDX_L2_LOOKUP_PARAMS] = { | |
99b981f4 VO |
1016 | .entry_packing = sja1105pqrs_l2_lookup_params_entry_packing, |
1017 | .cmd_packing = sja1105pqrs_l2_lookup_params_cmd_packing, | |
8aa9ebcc VO |
1018 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, |
1019 | .access = (OP_READ | OP_WRITE), | |
99b981f4 VO |
1020 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, |
1021 | .addr = 0x54, | |
8aa9ebcc | 1022 | }, |
0a7e984c VO |
1023 | [BLK_IDX_AVB_PARAMS] = { |
1024 | .entry_packing = sja1105pqrs_avb_params_entry_packing, | |
1025 | .cmd_packing = sja1105pqrs_avb_params_cmd_packing, | |
1026 | .max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT, | |
1027 | .access = (OP_READ | OP_WRITE), | |
1028 | .packed_size = SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD, | |
1029 | .addr = 0x8003, | |
1030 | }, | |
8aa9ebcc | 1031 | [BLK_IDX_GENERAL_PARAMS] = { |
99b981f4 VO |
1032 | .entry_packing = sja1105pqrs_general_params_entry_packing, |
1033 | .cmd_packing = sja1105pqrs_general_params_cmd_packing, | |
8aa9ebcc | 1034 | .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, |
99b981f4 VO |
1035 | .access = (OP_READ | OP_WRITE), |
1036 | .packed_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_DYN_CMD, | |
1037 | .addr = 0x3B, | |
8aa9ebcc | 1038 | }, |
88cac0fa VO |
1039 | [BLK_IDX_RETAGGING] = { |
1040 | .entry_packing = sja1105_retagging_entry_packing, | |
1041 | .cmd_packing = sja1105_retagging_cmd_packing, | |
1042 | .max_entry_count = SJA1105_MAX_RETAGGING_COUNT, | |
1043 | .access = (OP_READ | OP_WRITE | OP_DEL), | |
1044 | .packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD, | |
1045 | .addr = 0x38, | |
1046 | }, | |
4d752508 VO |
1047 | [BLK_IDX_CBS] = { |
1048 | .entry_packing = sja1105pqrs_cbs_entry_packing, | |
1049 | .cmd_packing = sja1105pqrs_cbs_cmd_packing, | |
1050 | .max_entry_count = SJA1105PQRS_MAX_CBS_COUNT, | |
1051 | .access = OP_WRITE, | |
1052 | .packed_size = SJA1105PQRS_SIZE_CBS_DYN_CMD, | |
1053 | .addr = 0x32, | |
1054 | }, | |
8aa9ebcc VO |
1055 | }; |
1056 | ||
3e77e59b VO |
1057 | /* SJA1110: Third generation */ |
1058 | const struct sja1105_dynamic_table_ops sja1110_dyn_ops[BLK_IDX_MAX_DYN] = { | |
1059 | [BLK_IDX_VL_LOOKUP] = { | |
1060 | .entry_packing = sja1110_vl_lookup_entry_packing, | |
1061 | .cmd_packing = sja1110_vl_lookup_cmd_packing, | |
1062 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1063 | .max_entry_count = SJA1110_MAX_VL_LOOKUP_COUNT, | |
1064 | .packed_size = SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD, | |
1065 | .addr = SJA1110_SPI_ADDR(0x124), | |
1066 | }, | |
1067 | [BLK_IDX_VL_POLICING] = { | |
1068 | .entry_packing = sja1110_vl_policing_entry_packing, | |
1069 | .cmd_packing = sja1110_vl_policing_cmd_packing, | |
1070 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1071 | .max_entry_count = SJA1110_MAX_VL_POLICING_COUNT, | |
1072 | .packed_size = SJA1110_SIZE_VL_POLICING_DYN_CMD, | |
1073 | .addr = SJA1110_SPI_ADDR(0x310), | |
1074 | }, | |
1075 | [BLK_IDX_L2_LOOKUP] = { | |
1076 | .entry_packing = sja1110_dyn_l2_lookup_entry_packing, | |
74e7feff | 1077 | .cmd_packing = sja1110_l2_lookup_cmd_packing, |
3e77e59b VO |
1078 | .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), |
1079 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, | |
1080 | .packed_size = SJA1110_SIZE_L2_LOOKUP_DYN_CMD, | |
1081 | .addr = SJA1110_SPI_ADDR(0x8c), | |
1082 | }, | |
1083 | [BLK_IDX_VLAN_LOOKUP] = { | |
1084 | .entry_packing = sja1110_vlan_lookup_entry_packing, | |
1085 | .cmd_packing = sja1110_vlan_lookup_cmd_packing, | |
1086 | .access = (OP_READ | OP_WRITE | OP_DEL), | |
1087 | .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, | |
1088 | .packed_size = SJA1110_SIZE_VLAN_LOOKUP_DYN_CMD, | |
1089 | .addr = SJA1110_SPI_ADDR(0xb4), | |
1090 | }, | |
1091 | [BLK_IDX_L2_FORWARDING] = { | |
1092 | .entry_packing = sja1110_l2_forwarding_entry_packing, | |
1093 | .cmd_packing = sja1110_l2_forwarding_cmd_packing, | |
1094 | .max_entry_count = SJA1110_MAX_L2_FORWARDING_COUNT, | |
1095 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1096 | .packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD, | |
1097 | .addr = SJA1110_SPI_ADDR(0xa8), | |
1098 | }, | |
1099 | [BLK_IDX_MAC_CONFIG] = { | |
1100 | .entry_packing = sja1110_mac_config_entry_packing, | |
1101 | .cmd_packing = sja1110_mac_config_cmd_packing, | |
1102 | .max_entry_count = SJA1110_MAX_MAC_CONFIG_COUNT, | |
1103 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1104 | .packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD, | |
1105 | .addr = SJA1110_SPI_ADDR(0x134), | |
1106 | }, | |
1107 | [BLK_IDX_L2_LOOKUP_PARAMS] = { | |
1108 | .entry_packing = sja1110_l2_lookup_params_entry_packing, | |
1109 | .cmd_packing = sja1110_l2_lookup_params_cmd_packing, | |
1110 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, | |
1111 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1112 | .packed_size = SJA1110_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, | |
1113 | .addr = SJA1110_SPI_ADDR(0x158), | |
1114 | }, | |
1115 | [BLK_IDX_AVB_PARAMS] = { | |
1116 | .entry_packing = sja1105pqrs_avb_params_entry_packing, | |
1117 | .cmd_packing = sja1105pqrs_avb_params_cmd_packing, | |
1118 | .max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT, | |
1119 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1120 | .packed_size = SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD, | |
1121 | .addr = SJA1110_SPI_ADDR(0x2000C), | |
1122 | }, | |
1123 | [BLK_IDX_GENERAL_PARAMS] = { | |
1124 | .entry_packing = sja1110_general_params_entry_packing, | |
1125 | .cmd_packing = sja1110_general_params_cmd_packing, | |
1126 | .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, | |
1127 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1128 | .packed_size = SJA1110_SIZE_GENERAL_PARAMS_DYN_CMD, | |
1129 | .addr = SJA1110_SPI_ADDR(0xe8), | |
1130 | }, | |
1131 | [BLK_IDX_RETAGGING] = { | |
1132 | .entry_packing = sja1110_retagging_entry_packing, | |
1133 | .cmd_packing = sja1110_retagging_cmd_packing, | |
1134 | .max_entry_count = SJA1105_MAX_RETAGGING_COUNT, | |
1135 | .access = (OP_READ | OP_WRITE | OP_DEL), | |
1136 | .packed_size = SJA1105_SIZE_RETAGGING_DYN_CMD, | |
1137 | .addr = SJA1110_SPI_ADDR(0xdc), | |
1138 | }, | |
1139 | [BLK_IDX_CBS] = { | |
1140 | .entry_packing = sja1110_cbs_entry_packing, | |
1141 | .cmd_packing = sja1110_cbs_cmd_packing, | |
1142 | .max_entry_count = SJA1110_MAX_CBS_COUNT, | |
1143 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1144 | .packed_size = SJA1105PQRS_SIZE_CBS_DYN_CMD, | |
1145 | .addr = SJA1110_SPI_ADDR(0xc4), | |
1146 | }, | |
1147 | [BLK_IDX_XMII_PARAMS] = { | |
1148 | .entry_packing = sja1110_xmii_params_entry_packing, | |
1149 | .cmd_packing = sja1110_dummy_cmd_packing, | |
1150 | .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, | |
1151 | .access = (OP_READ | OP_VALID_ANYWAY), | |
1152 | .packed_size = SJA1110_SIZE_XMII_PARAMS_DYN_CMD, | |
1153 | .addr = SJA1110_SPI_ADDR(0x3c), | |
1154 | }, | |
1155 | [BLK_IDX_L2_POLICING] = { | |
1156 | .entry_packing = sja1110_l2_policing_entry_packing, | |
1157 | .cmd_packing = sja1110_l2_policing_cmd_packing, | |
1158 | .max_entry_count = SJA1110_MAX_L2_POLICING_COUNT, | |
1159 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), | |
1160 | .packed_size = SJA1110_SIZE_L2_POLICING_DYN_CMD, | |
1161 | .addr = SJA1110_SPI_ADDR(0x2fc), | |
1162 | }, | |
1163 | [BLK_IDX_L2_FORWARDING_PARAMS] = { | |
1164 | .entry_packing = sja1110_l2_forwarding_params_entry_packing, | |
1165 | .cmd_packing = sja1110_dummy_cmd_packing, | |
1166 | .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, | |
1167 | .access = (OP_READ | OP_VALID_ANYWAY), | |
1168 | .packed_size = SJA1110_SIZE_L2_FORWARDING_PARAMS_DYN_CMD, | |
1169 | .addr = SJA1110_SPI_ADDR(0x20000), | |
1170 | }, | |
1171 | }; | |
1172 | ||
df405910 VO |
1173 | #define SJA1105_DYNAMIC_CONFIG_SLEEP_US 10 |
1174 | #define SJA1105_DYNAMIC_CONFIG_TIMEOUT_US 100000 | |
1175 | ||
1176 | static int | |
1177 | sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, | |
7cef293b VO |
1178 | const struct sja1105_dynamic_table_ops *ops, |
1179 | void *entry, bool check_valident, | |
1180 | bool check_errors) | |
df405910 VO |
1181 | { |
1182 | u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {}; | |
7cef293b | 1183 | struct sja1105_dyn_cmd cmd = {}; |
df405910 VO |
1184 | int rc; |
1185 | ||
7cef293b | 1186 | /* Read back the whole entry + command structure. */ |
df405910 VO |
1187 | rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf, |
1188 | ops->packed_size); | |
1189 | if (rc) | |
1190 | return rc; | |
1191 | ||
1192 | /* Unpack the command structure, and return it to the caller in case it | |
1193 | * needs to perform further checks on it (VALIDENT). | |
1194 | */ | |
7cef293b | 1195 | ops->cmd_packing(packed_buf, &cmd, UNPACK); |
df405910 VO |
1196 | |
1197 | /* Hardware hasn't cleared VALID => still working on it */ | |
7cef293b VO |
1198 | if (cmd.valid) |
1199 | return -EAGAIN; | |
1200 | ||
1201 | if (check_valident && !cmd.valident && !(ops->access & OP_VALID_ANYWAY)) | |
1202 | return -ENOENT; | |
1203 | ||
1204 | if (check_errors && cmd.errors) | |
1205 | return -EINVAL; | |
1206 | ||
1207 | /* Don't dereference possibly NULL pointer - maybe caller | |
1208 | * only wanted to see whether the entry existed or not. | |
1209 | */ | |
1210 | if (entry) | |
1211 | ops->entry_packing(packed_buf, entry, UNPACK); | |
1212 | ||
1213 | return 0; | |
df405910 VO |
1214 | } |
1215 | ||
1216 | /* Poll the dynamic config entry's control area until the hardware has | |
1217 | * cleared the VALID bit, which means we have confirmation that it has | |
1218 | * finished processing the command. | |
1219 | */ | |
1220 | static int | |
1221 | sja1105_dynamic_config_wait_complete(struct sja1105_private *priv, | |
7cef293b VO |
1222 | const struct sja1105_dynamic_table_ops *ops, |
1223 | void *entry, bool check_valident, | |
1224 | bool check_errors) | |
df405910 | 1225 | { |
c9567980 VO |
1226 | int err, rc; |
1227 | ||
1228 | err = read_poll_timeout(sja1105_dynamic_config_poll_valid, | |
1229 | rc, rc != -EAGAIN, | |
1230 | SJA1105_DYNAMIC_CONFIG_SLEEP_US, | |
1231 | SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, | |
7cef293b VO |
1232 | false, priv, ops, entry, check_valident, |
1233 | check_errors); | |
c9567980 | 1234 | return err < 0 ? err : rc; |
df405910 VO |
1235 | } |
1236 | ||
90c96cca VO |
1237 | /* Provides read access to the settings through the dynamic interface |
1238 | * of the switch. | |
1239 | * @blk_idx is used as key to select from the sja1105_dynamic_table_ops. | |
1240 | * The selection is limited by the hardware in respect to which | |
1241 | * configuration blocks can be read through the dynamic interface. | |
1242 | * @index is used to retrieve a particular table entry. If negative, | |
1243 | * (and if the @blk_idx supports the searching operation) a search | |
1244 | * is performed by the @entry parameter. | |
1245 | * @entry Type-casted to an unpacked structure that holds a table entry | |
1246 | * of the type specified in @blk_idx. | |
1247 | * Usually an output argument. If @index is negative, then this | |
1248 | * argument is used as input/output: it should be pre-populated | |
1249 | * with the element to search for. Entries which support the | |
1250 | * search operation will have an "index" field (not the @index | |
1251 | * argument to this function) and that is where the found index | |
1252 | * will be returned (or left unmodified - thus negative - if not | |
1253 | * found). | |
1254 | */ | |
8aa9ebcc VO |
1255 | int sja1105_dynamic_config_read(struct sja1105_private *priv, |
1256 | enum sja1105_blk_idx blk_idx, | |
1257 | int index, void *entry) | |
1258 | { | |
1259 | const struct sja1105_dynamic_table_ops *ops; | |
1260 | struct sja1105_dyn_cmd cmd = {0}; | |
1261 | /* SPI payload buffer */ | |
1262 | u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0}; | |
8aa9ebcc VO |
1263 | int rc; |
1264 | ||
1265 | if (blk_idx >= BLK_IDX_MAX_DYN) | |
1266 | return -ERANGE; | |
1267 | ||
1268 | ops = &priv->info->dyn_ops[blk_idx]; | |
1269 | ||
10c3be65 | 1270 | if (index >= 0 && index >= ops->max_entry_count) |
8aa9ebcc | 1271 | return -ERANGE; |
90c96cca VO |
1272 | if (index < 0 && !(ops->access & OP_SEARCH)) |
1273 | return -EOPNOTSUPP; | |
8aa9ebcc VO |
1274 | if (!(ops->access & OP_READ)) |
1275 | return -EOPNOTSUPP; | |
1276 | if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE) | |
1277 | return -ERANGE; | |
1278 | if (!ops->cmd_packing) | |
1279 | return -EOPNOTSUPP; | |
1280 | if (!ops->entry_packing) | |
1281 | return -EOPNOTSUPP; | |
1282 | ||
1283 | cmd.valid = true; /* Trigger action on table entry */ | |
1284 | cmd.rdwrset = SPI_READ; /* Action is read */ | |
90c96cca VO |
1285 | if (index < 0) { |
1286 | /* Avoid copying a signed negative number to an u64 */ | |
1287 | cmd.index = 0; | |
1288 | cmd.search = true; | |
1289 | } else { | |
1290 | cmd.index = index; | |
1291 | cmd.search = false; | |
1292 | } | |
10c3be65 | 1293 | cmd.valident = true; |
8aa9ebcc VO |
1294 | ops->cmd_packing(packed_buf, &cmd, PACK); |
1295 | ||
90c96cca VO |
1296 | if (cmd.search) |
1297 | ops->entry_packing(packed_buf, entry, PACK); | |
1298 | ||
8aa9ebcc | 1299 | /* Send SPI write operation: read config table entry */ |
eb016afd | 1300 | mutex_lock(&priv->dynamic_config_lock); |
1bd44870 VO |
1301 | rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, |
1302 | ops->packed_size); | |
df405910 | 1303 | if (rc < 0) |
7cef293b | 1304 | goto out; |
2d7e73f0 | 1305 | |
7cef293b VO |
1306 | rc = sja1105_dynamic_config_wait_complete(priv, ops, entry, true, false); |
1307 | out: | |
1308 | mutex_unlock(&priv->dynamic_config_lock); | |
8aa9ebcc | 1309 | |
7cef293b | 1310 | return rc; |
8aa9ebcc VO |
1311 | } |
1312 | ||
1313 | int sja1105_dynamic_config_write(struct sja1105_private *priv, | |
1314 | enum sja1105_blk_idx blk_idx, | |
1315 | int index, void *entry, bool keep) | |
1316 | { | |
1317 | const struct sja1105_dynamic_table_ops *ops; | |
1318 | struct sja1105_dyn_cmd cmd = {0}; | |
1319 | /* SPI payload buffer */ | |
1320 | u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0}; | |
1321 | int rc; | |
1322 | ||
1323 | if (blk_idx >= BLK_IDX_MAX_DYN) | |
1324 | return -ERANGE; | |
1325 | ||
1326 | ops = &priv->info->dyn_ops[blk_idx]; | |
1327 | ||
1328 | if (index >= ops->max_entry_count) | |
1329 | return -ERANGE; | |
90c96cca VO |
1330 | if (index < 0) |
1331 | return -ERANGE; | |
8aa9ebcc VO |
1332 | if (!(ops->access & OP_WRITE)) |
1333 | return -EOPNOTSUPP; | |
1334 | if (!keep && !(ops->access & OP_DEL)) | |
1335 | return -EOPNOTSUPP; | |
1336 | if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE) | |
1337 | return -ERANGE; | |
1338 | ||
1339 | cmd.valident = keep; /* If false, deletes entry */ | |
1340 | cmd.valid = true; /* Trigger action on table entry */ | |
1341 | cmd.rdwrset = SPI_WRITE; /* Action is write */ | |
1342 | cmd.index = index; | |
1343 | ||
1344 | if (!ops->cmd_packing) | |
1345 | return -EOPNOTSUPP; | |
1346 | ops->cmd_packing(packed_buf, &cmd, PACK); | |
1347 | ||
1348 | if (!ops->entry_packing) | |
1349 | return -EOPNOTSUPP; | |
1350 | /* Don't dereference potentially NULL pointer if just | |
1351 | * deleting a table entry is what was requested. For cases | |
1352 | * where 'index' field is physically part of entry structure, | |
1353 | * and needed here, we deal with that in the cmd_packing callback. | |
1354 | */ | |
1355 | if (keep) | |
1356 | ops->entry_packing(packed_buf, entry, PACK); | |
1357 | ||
1358 | /* Send SPI write operation: read config table entry */ | |
eb016afd | 1359 | mutex_lock(&priv->dynamic_config_lock); |
1bd44870 VO |
1360 | rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, |
1361 | ops->packed_size); | |
df405910 | 1362 | if (rc < 0) |
7cef293b | 1363 | goto out; |
df405910 | 1364 | |
7cef293b VO |
1365 | rc = sja1105_dynamic_config_wait_complete(priv, ops, NULL, false, true); |
1366 | out: | |
1367 | mutex_unlock(&priv->dynamic_config_lock); | |
8aa9ebcc | 1368 | |
7cef293b | 1369 | return rc; |
8aa9ebcc | 1370 | } |
291d1e72 VO |
1371 | |
1372 | static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly) | |
1373 | { | |
1374 | int i; | |
1375 | ||
1376 | for (i = 0; i < 8; i++) { | |
1377 | if ((crc ^ byte) & (1 << 7)) { | |
1378 | crc <<= 1; | |
1379 | crc ^= poly; | |
1380 | } else { | |
1381 | crc <<= 1; | |
1382 | } | |
1383 | byte <<= 1; | |
1384 | } | |
1385 | return crc; | |
1386 | } | |
1387 | ||
1388 | /* CRC8 algorithm with non-reversed input, non-reversed output, | |
1389 | * no input xor and no output xor. Code customized for receiving | |
1390 | * the SJA1105 E/T FDB keys (vlanid, macaddr) as input. CRC polynomial | |
1391 | * is also received as argument in the Koopman notation that the switch | |
1392 | * hardware stores it in. | |
1393 | */ | |
9dfa6911 | 1394 | u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid) |
291d1e72 VO |
1395 | { |
1396 | struct sja1105_l2_lookup_params_entry *l2_lookup_params = | |
1397 | priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries; | |
b11f0a4c | 1398 | u64 input, poly_koopman = l2_lookup_params->poly; |
291d1e72 VO |
1399 | /* Convert polynomial from Koopman to 'normal' notation */ |
1400 | u8 poly = (u8)(1 + (poly_koopman << 1)); | |
291d1e72 VO |
1401 | u8 crc = 0; /* seed */ |
1402 | int i; | |
1403 | ||
b11f0a4c VO |
1404 | input = ((u64)vid << 48) | ether_addr_to_u64(addr); |
1405 | ||
291d1e72 VO |
1406 | /* Mask the eight bytes starting from MSB one at a time */ |
1407 | for (i = 56; i >= 0; i -= 8) { | |
1408 | u8 byte = (input & (0xffull << i)) >> i; | |
1409 | ||
1410 | crc = sja1105_crc8_add(crc, byte, poly); | |
1411 | } | |
1412 | return crc; | |
1413 | } |