1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> |
3 | */ |
4 | #include "sja1105.h" |
5 | |
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. |
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. |
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 | |
101 | #define SJA1105_SIZE_DYN_CMD 4 |
102 | |
103 | #define SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD \ |
104 | SJA1105_SIZE_DYN_CMD |
105 | |
106 | #define SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD \ |
107 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_VL_LOOKUP_ENTRY) |
108 | |
109 | #define SJA1110_SIZE_VL_POLICING_DYN_CMD \ |
110 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_VL_POLICING_ENTRY) |
111 | |
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 | |
121 | #define SJA1110_SIZE_L2_LOOKUP_DYN_CMD \ |
122 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_L2_LOOKUP_ENTRY) |
123 | |
124 | #define SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD \ |
125 | (SJA1105_SIZE_DYN_CMD + 4 + SJA1105_SIZE_VLAN_LOOKUP_ENTRY) |
126 | |
127 | #define SJA1110_SIZE_VLAN_LOOKUP_DYN_CMD \ |
128 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_VLAN_LOOKUP_ENTRY) |
129 | |
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 | |
142 | #define SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ |
143 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_ENTRY) |
144 | |
145 | #define SJA1110_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \ |
146 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_L2_LOOKUP_PARAMS_ENTRY) |
147 | |
148 | #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \ |
149 | SJA1105_SIZE_DYN_CMD |
150 | |
151 | #define SJA1105PQRS_SIZE_GENERAL_PARAMS_DYN_CMD \ |
152 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY) |
153 | |
154 | #define SJA1110_SIZE_GENERAL_PARAMS_DYN_CMD \ |
155 | (SJA1105_SIZE_DYN_CMD + SJA1110_SIZE_GENERAL_PARAMS_ENTRY) |
156 | |
157 | #define SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD \ |
158 | (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY) |
159 | |
160 | #define SJA1105_SIZE_RETAGGING_DYN_CMD \ |
161 | (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_RETAGGING_ENTRY) |
162 | |
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 | |
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 | |
178 | #define SJA1105_MAX_DYN_CMD_SIZE \ |
179 | SJA1110_SIZE_GENERAL_PARAMS_DYN_CMD |
180 | |
181 | struct sja1105_dyn_cmd { |
182 | bool search; |
183 | u64 valid; |
184 | u64 rdwrset; |
185 | u64 errors; |
186 | u64 valident; |
187 | u64 index; |
188 | }; |
189 | |
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 | |
197 | /* Command and entry overlap */ |
198 | static void |
199 | sja1105et_vl_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
200 | enum packing_op op) |
201 | { |
202 | const int size = SJA1105_SIZE_DYN_CMD; |
203 | |
204 | sja1105_packing(buf, val: &cmd->valid, start: 31, end: 31, len: size, op); |
205 | sja1105_packing(buf, val: &cmd->errors, start: 30, end: 30, len: size, op); |
206 | sja1105_packing(buf, val: &cmd->rdwrset, start: 29, end: 29, len: size, op); |
207 | sja1105_packing(buf, val: &cmd->index, start: 9, end: 0, len: size, op); |
208 | } |
209 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
219 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
220 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 29, end: 29, len: size, op); |
221 | sja1105_packing(buf: p, val: &cmd->index, start: 9, end: 0, len: size, op); |
222 | } |
223 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
232 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
233 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
234 | sja1105_packing(buf: p, val: &cmd->index, start: 11, end: 0, len: size, op); |
235 | } |
236 | |
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; |
241 | const int size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD; |
242 | |
243 | sja1105_packing(buf, val: &entry->egrmirr, start: 21, end: 17, len: size, op); |
244 | sja1105_packing(buf, val: &entry->ingrmirr, start: 16, end: 16, len: size, op); |
245 | return size; |
246 | } |
247 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
256 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
257 | sja1105_packing(buf: p, val: &cmd->index, start: 11, end: 0, len: size, op); |
258 | } |
259 | |
260 | static void |
261 | sja1105pqrs_common_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
262 | enum packing_op op, int entry_size) |
263 | { |
264 | const int size = SJA1105_SIZE_DYN_CMD; |
265 | u8 *p = buf + entry_size; |
266 | u64 hostcmd; |
267 | |
268 | sja1105_packing(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
269 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
270 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
271 | sja1105_packing(buf: p, val: &cmd->valident, start: 27, end: 27, len: size, op); |
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(buf: p, val: &hostcmd, start: 25, end: 23, len: size, op); |
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); |
316 | |
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 | */ |
325 | sja1105_packing(buf, val: &cmd->index, start: 15, end: 6, len: entry_size, op); |
326 | } |
327 | |
328 | static void |
329 | sja1110_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd, |
330 | enum packing_op op) |
331 | { |
332 | int entry_size = SJA1110_SIZE_L2_LOOKUP_ENTRY; |
333 | |
334 | sja1105pqrs_common_l2_lookup_cmd_packing(buf, cmd, op, entry_size); |
335 | |
336 | sja1105_packing(buf, val: &cmd->index, start: 10, end: 1, len: entry_size, op); |
337 | } |
338 | |
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(buf: cmd, val: &entry->lockeds, start: 28, end: 28, len: size, op); |
393 | |
394 | return sja1105pqrs_l2_lookup_entry_packing(buf, entry_ptr, op); |
395 | } |
396 | |
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(buf: cmd, val: &entry->lockeds, start: 28, end: 28, len: size, op); |
405 | |
406 | return sja1110_l2_lookup_entry_packing(buf, entry_ptr, op); |
407 | } |
408 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
417 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
418 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
419 | sja1105_packing(buf: p, val: &cmd->valident, start: 27, end: 27, len: size, op); |
420 | /* Hack - see comments above. */ |
421 | sja1105_packing(buf, val: &cmd->index, start: 29, end: 20, |
422 | SJA1105ET_SIZE_L2_LOOKUP_ENTRY, op); |
423 | } |
424 | |
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(buf: cmd, val: &entry->lockeds, start: 28, end: 28, len: size, op); |
433 | |
434 | return sja1105et_l2_lookup_entry_packing(buf, entry_ptr, op); |
435 | } |
436 | |
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(buf: p, val: &mgmtroute, start: 26, end: 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, val: &entry->tsreg, start: 85, end: 85, len: size, op); |
462 | sja1105_packing(buf, val: &entry->takets, start: 84, end: 84, len: size, op); |
463 | sja1105_packing(buf, val: &entry->macaddr, start: 83, end: 36, len: size, op); |
464 | sja1105_packing(buf, val: &entry->destports, start: 35, end: 31, len: size, op); |
465 | sja1105_packing(buf, val: &entry->enfport, start: 30, end: 30, len: size, op); |
466 | return size; |
467 | } |
468 | |
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(buf: p, val: &mgmtroute, start: 26, end: 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, val: &entry->tsreg, start: 71, end: 71, len: size, op); |
492 | sja1105_packing(buf, val: &entry->takets, start: 70, end: 70, len: size, op); |
493 | sja1105_packing(buf, val: &entry->macaddr, start: 69, end: 22, len: size, op); |
494 | sja1105_packing(buf, val: &entry->destports, start: 21, end: 17, len: size, op); |
495 | sja1105_packing(buf, val: &entry->enfport, start: 16, end: 16, len: size, op); |
496 | return size; |
497 | } |
498 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
511 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
512 | sja1105_packing(buf: p, val: &cmd->valident, start: 27, end: 27, len: size, op); |
513 | /* Hack - see comments above, applied for 'vlanid' field of |
514 | * struct sja1105_vlan_lookup_entry. |
515 | */ |
516 | sja1105_packing(buf, val: &cmd->index, start: 38, end: 27, |
517 | SJA1105_SIZE_VLAN_LOOKUP_ENTRY, op); |
518 | } |
519 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
530 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
531 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
532 | /* Hack: treat 'vlanid' field of struct sja1105_vlan_lookup_entry as |
533 | * cmd->index. |
534 | */ |
535 | sja1105_packing(buf, val: &cmd->index, start: 38, end: 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, val: &type_entry, start: 40, end: 39, |
545 | SJA1110_SIZE_VLAN_LOOKUP_ENTRY, op: PACK); |
546 | } else if (op == UNPACK) { |
547 | sja1105_packing(buf, val: &type_entry, start: 40, end: 39, |
548 | SJA1110_SIZE_VLAN_LOOKUP_ENTRY, op: UNPACK); |
549 | cmd->valident = !!type_entry; |
550 | } |
551 | } |
552 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
561 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
562 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 29, end: 29, len: size, op); |
563 | sja1105_packing(buf: p, val: &cmd->index, start: 4, end: 0, len: size, op); |
564 | } |
565 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
574 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
575 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
576 | sja1105_packing(buf: p, val: &cmd->index, start: 4, end: 0, len: size, op); |
577 | } |
578 | |
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(buf: reg1, val: &cmd->valid, start: 31, end: 31, len: size, op); |
588 | sja1105_packing(buf: reg1, val: &cmd->index, start: 26, end: 24, len: 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(buf: reg1, val: &entry->speed, start: 30, end: 29, len: size, op); |
601 | sja1105_packing(buf: reg1, val: &entry->drpdtag, start: 23, end: 23, len: size, op); |
602 | sja1105_packing(buf: reg1, val: &entry->drpuntag, start: 22, end: 22, len: size, op); |
603 | sja1105_packing(buf: reg1, val: &entry->retag, start: 21, end: 21, len: size, op); |
604 | sja1105_packing(buf: reg1, val: &entry->dyn_learn, start: 20, end: 20, len: size, op); |
605 | sja1105_packing(buf: reg1, val: &entry->egress, start: 19, end: 19, len: size, op); |
606 | sja1105_packing(buf: reg1, val: &entry->ingress, start: 18, end: 18, len: size, op); |
607 | sja1105_packing(buf: reg1, val: &entry->ing_mirr, start: 17, end: 17, len: size, op); |
608 | sja1105_packing(buf: reg1, val: &entry->egr_mirr, start: 16, end: 16, len: size, op); |
609 | sja1105_packing(buf: reg1, val: &entry->vlanprio, start: 14, end: 12, len: size, op); |
610 | sja1105_packing(buf: reg1, val: &entry->vlanid, start: 11, end: 0, len: size, op); |
611 | sja1105_packing(buf: reg2, val: &entry->tp_delin, start: 31, end: 16, len: size, op); |
612 | sja1105_packing(buf: reg2, val: &entry->tp_delout, start: 15, end: 0, len: 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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
628 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
629 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 29, end: 29, len: size, op); |
630 | sja1105_packing(buf: p, val: &cmd->index, start: 2, end: 0, len: size, op); |
631 | } |
632 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
641 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
642 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
643 | sja1105_packing(buf: p, val: &cmd->index, start: 3, end: 0, len: size, op); |
644 | } |
645 | |
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, val: &cmd->valid, start: 31, end: 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, val: &entry->poly, start: 7, end: 0, |
661 | SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op); |
662 | /* Bogus return value, not used anywhere */ |
663 | return 0; |
664 | } |
665 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
675 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
676 | } |
677 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
686 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
687 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
688 | } |
689 | |
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, val: &cmd->valid, start: 31, end: 31, len: size, op); |
697 | sja1105_packing(buf, val: &cmd->errors, start: 30, end: 30, len: 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, val: &entry->mirr_port, start: 2, end: 0, len: size, op); |
708 | /* Bogus return value, not used anywhere */ |
709 | return 0; |
710 | } |
711 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
720 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
721 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 28, end: 28, len: size, op); |
722 | } |
723 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
732 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
733 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
734 | } |
735 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
744 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
745 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 29, end: 29, len: size, op); |
746 | } |
747 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
756 | sja1105_packing(buf: p, val: &cmd->errors, start: 30, end: 30, len: size, op); |
757 | sja1105_packing(buf: p, val: &cmd->valident, start: 29, end: 29, len: size, op); |
758 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 28, end: 28, len: size, op); |
759 | sja1105_packing(buf: p, val: &cmd->index, start: 5, end: 0, len: size, op); |
760 | } |
761 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
770 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
771 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
772 | sja1105_packing(buf: p, val: &cmd->valident, start: 28, end: 28, len: size, op); |
773 | sja1105_packing(buf: p, val: &cmd->index, start: 4, end: 0, len: size, op); |
774 | } |
775 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
783 | sja1105_packing(buf: p, val: &cmd->index, start: 19, end: 16, len: 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(buf: cmd, val: &entry->port, start: 5, end: 3, SJA1105_SIZE_DYN_CMD, op); |
795 | sja1105_packing(buf: cmd, val: &entry->prio, start: 2, end: 0, SJA1105_SIZE_DYN_CMD, op); |
796 | sja1105_packing(buf: p + 3, val: &entry->credit_lo, start: 31, end: 0, len: size, op); |
797 | sja1105_packing(buf: p + 2, val: &entry->credit_hi, start: 31, end: 0, len: size, op); |
798 | sja1105_packing(buf: p + 1, val: &entry->send_slope, start: 31, end: 0, len: size, op); |
799 | sja1105_packing(buf: p + 0, val: &entry->idle_slope, start: 31, end: 0, len: 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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
810 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
811 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
812 | sja1105_packing(buf: p, val: &cmd->index, start: 3, end: 0, len: size, op); |
813 | } |
814 | |
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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
822 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
823 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
824 | sja1105_packing(buf: p, val: &cmd->index, start: 7, end: 0, len: size, op); |
825 | } |
826 | |
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, val: &entry->port, start: 159, end: 157, len: size, op); |
834 | sja1105_packing(buf, val: &entry->prio, start: 156, end: 154, len: size, op); |
835 | sja1105_packing(buf, val: &entry->credit_lo, start: 153, end: 122, len: size, op); |
836 | sja1105_packing(buf, val: &entry->credit_hi, start: 121, end: 90, len: size, op); |
837 | sja1105_packing(buf, val: &entry->send_slope, start: 89, end: 58, len: size, op); |
838 | sja1105_packing(buf, val: &entry->idle_slope, start: 57, end: 26, len: size, op); |
839 | return size; |
840 | } |
841 | |
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, val: &entry_type, start: 159, end: 159, len: size, op); |
850 | sja1105_packing(buf, val: &entry->credit_lo, start: 151, end: 120, len: size, op); |
851 | sja1105_packing(buf, val: &entry->credit_hi, start: 119, end: 88, len: size, op); |
852 | sja1105_packing(buf, val: &entry->send_slope, start: 87, end: 56, len: size, op); |
853 | sja1105_packing(buf, val: &entry->idle_slope, start: 55, end: 24, len: 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(buf: p, val: &cmd->valid, start: 31, end: 31, len: size, op); |
870 | sja1105_packing(buf: p, val: &cmd->rdwrset, start: 30, end: 30, len: size, op); |
871 | sja1105_packing(buf: p, val: &cmd->errors, start: 29, end: 29, len: size, op); |
872 | sja1105_packing(buf: p, val: &cmd->index, start: 6, end: 0, len: size, op); |
873 | } |
874 | |
875 | #define OP_READ BIT(0) |
876 | #define OP_WRITE BIT(1) |
877 | #define OP_DEL BIT(2) |
878 | #define OP_SEARCH BIT(3) |
879 | #define OP_VALID_ANYWAY BIT(4) |
880 | |
881 | /* SJA1105E/T: First generation */ |
882 | const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = { |
883 | [BLK_IDX_VL_LOOKUP] = { |
884 | .entry_packing = sja1105et_vl_lookup_entry_packing, |
885 | .cmd_packing = sja1105et_vl_lookup_cmd_packing, |
886 | .access = OP_WRITE, |
887 | .max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, |
888 | .packed_size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD, |
889 | .addr = 0x35, |
890 | }, |
891 | [BLK_IDX_L2_LOOKUP] = { |
892 | .entry_packing = sja1105et_dyn_l2_lookup_entry_packing, |
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, |
902 | .access = (OP_READ | OP_WRITE | OP_VALID_ANYWAY), |
903 | .max_entry_count = SJA1105_NUM_PORTS, |
904 | .packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD, |
905 | .addr = 0x20, |
906 | }, |
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 | }, |
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 | }, |
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 | }, |
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 | }, |
963 | }; |
964 | |
965 | /* SJA1105P/Q/R/S: Second generation */ |
966 | const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = { |
967 | [BLK_IDX_VL_LOOKUP] = { |
968 | .entry_packing = sja1105_vl_lookup_entry_packing, |
969 | .cmd_packing = sja1105pqrs_vl_lookup_cmd_packing, |
970 | .access = (OP_READ | OP_WRITE), |
971 | .max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, |
972 | .packed_size = SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD, |
973 | .addr = 0x47, |
974 | }, |
975 | [BLK_IDX_L2_LOOKUP] = { |
976 | .entry_packing = sja1105pqrs_dyn_l2_lookup_entry_packing, |
977 | .cmd_packing = sja1105pqrs_l2_lookup_cmd_packing, |
978 | .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH), |
979 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT, |
980 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, |
981 | .addr = 0x24, |
982 | }, |
983 | [BLK_IDX_MGMT_ROUTE] = { |
984 | .entry_packing = sja1105pqrs_mgmt_route_entry_packing, |
985 | .cmd_packing = sja1105pqrs_mgmt_route_cmd_packing, |
986 | .access = (OP_READ | OP_WRITE | OP_DEL | OP_SEARCH | OP_VALID_ANYWAY), |
987 | .max_entry_count = SJA1105_NUM_PORTS, |
988 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD, |
989 | .addr = 0x24, |
990 | }, |
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] = { |
1016 | .entry_packing = sja1105pqrs_l2_lookup_params_entry_packing, |
1017 | .cmd_packing = sja1105pqrs_l2_lookup_params_cmd_packing, |
1018 | .max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, |
1019 | .access = (OP_READ | OP_WRITE), |
1020 | .packed_size = SJA1105PQRS_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, |
1021 | .addr = 0x54, |
1022 | }, |
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 | }, |
1031 | [BLK_IDX_GENERAL_PARAMS] = { |
1032 | .entry_packing = sja1105pqrs_general_params_entry_packing, |
1033 | .cmd_packing = sja1105pqrs_general_params_cmd_packing, |
1034 | .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, |
1035 | .access = (OP_READ | OP_WRITE), |
1036 | .packed_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_DYN_CMD, |
1037 | .addr = 0x3B, |
1038 | }, |
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 | }, |
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 | }, |
1055 | }; |
1056 | |
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, |
1077 | .cmd_packing = sja1110_l2_lookup_cmd_packing, |
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 | |
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, |
1178 | const struct sja1105_dynamic_table_ops *ops, |
1179 | void *entry, bool check_valident, |
1180 | bool check_errors) |
1181 | { |
1182 | u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {}; |
1183 | struct sja1105_dyn_cmd cmd = {}; |
1184 | int rc; |
1185 | |
1186 | /* Read back the whole entry + command structure. */ |
1187 | rc = sja1105_xfer_buf(priv, rw: SPI_READ, reg_addr: ops->addr, buf: packed_buf, |
1188 | len: 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 | */ |
1195 | ops->cmd_packing(packed_buf, &cmd, UNPACK); |
1196 | |
1197 | /* Hardware hasn't cleared VALID => still working on it */ |
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; |
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, |
1222 | const struct sja1105_dynamic_table_ops *ops, |
1223 | void *entry, bool check_valident, |
1224 | bool check_errors) |
1225 | { |
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, |
1232 | false, priv, ops, entry, check_valident, |
1233 | check_errors); |
1234 | return err < 0 ? err : rc; |
1235 | } |
1236 | |
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 | */ |
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}; |
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 | |
1270 | if (index >= 0 && index >= ops->max_entry_count) |
1271 | return -ERANGE; |
1272 | if (index < 0 && !(ops->access & OP_SEARCH)) |
1273 | return -EOPNOTSUPP; |
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 */ |
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 | } |
1293 | cmd.valident = true; |
1294 | ops->cmd_packing(packed_buf, &cmd, PACK); |
1295 | |
1296 | if (cmd.search) |
1297 | ops->entry_packing(packed_buf, entry, PACK); |
1298 | |
1299 | /* Send SPI write operation: read config table entry */ |
1300 | mutex_lock(&priv->dynamic_config_lock); |
1301 | rc = sja1105_xfer_buf(priv, rw: SPI_WRITE, reg_addr: ops->addr, buf: packed_buf, |
1302 | len: ops->packed_size); |
1303 | if (rc < 0) |
1304 | goto out; |
1305 | |
1306 | rc = sja1105_dynamic_config_wait_complete(priv, ops, entry, check_valident: true, check_errors: false); |
1307 | out: |
1308 | mutex_unlock(lock: &priv->dynamic_config_lock); |
1309 | |
1310 | return rc; |
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; |
1330 | if (index < 0) |
1331 | return -ERANGE; |
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 */ |
1359 | mutex_lock(&priv->dynamic_config_lock); |
1360 | rc = sja1105_xfer_buf(priv, rw: SPI_WRITE, reg_addr: ops->addr, buf: packed_buf, |
1361 | len: ops->packed_size); |
1362 | if (rc < 0) |
1363 | goto out; |
1364 | |
1365 | rc = sja1105_dynamic_config_wait_complete(priv, ops, NULL, check_valident: false, check_errors: true); |
1366 | out: |
1367 | mutex_unlock(lock: &priv->dynamic_config_lock); |
1368 | |
1369 | return rc; |
1370 | } |
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 | */ |
1394 | u8 sja1105et_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid) |
1395 | { |
1396 | struct sja1105_l2_lookup_params_entry *l2_lookup_params = |
1397 | priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries; |
1398 | u64 input, poly_koopman = l2_lookup_params->poly; |
1399 | /* Convert polynomial from Koopman to 'normal' notation */ |
1400 | u8 poly = (u8)(1 + (poly_koopman << 1)); |
1401 | u8 crc = 0; /* seed */ |
1402 | int i; |
1403 | |
1404 | input = ((u64)vid << 48) | ether_addr_to_u64(addr); |
1405 | |
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 | } |
1414 | |