1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
3  */
4 #include "sja1105.h"
5 
6 #define SJA1105_SIZE_DYN_CMD					4
7 
8 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY			\
9 	SJA1105_SIZE_DYN_CMD
10 
11 #define SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD			\
12 	(SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_L2_LOOKUP_ENTRY)
13 
14 #define SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD			\
15 	(SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY)
16 
17 #define SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD			\
18 	(SJA1105_SIZE_DYN_CMD + 4 + SJA1105_SIZE_VLAN_LOOKUP_ENTRY)
19 
20 #define SJA1105_SIZE_L2_FORWARDING_DYN_CMD			\
21 	(SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_L2_FORWARDING_ENTRY)
22 
23 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD			\
24 	(SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY)
25 
26 #define SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD			\
27 	(SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY)
28 
29 #define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD			\
30 	SJA1105_SIZE_DYN_CMD
31 
32 #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD			\
33 	SJA1105_SIZE_DYN_CMD
34 
35 #define SJA1105_MAX_DYN_CMD_SIZE				\
36 	SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
37 
38 static void
39 sja1105pqrs_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
40 				  enum packing_op op)
41 {
42 	u8 *p = buf + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY;
43 	const int size = SJA1105_SIZE_DYN_CMD;
44 
45 	sja1105_packing(p, &cmd->valid,    31, 31, size, op);
46 	sja1105_packing(p, &cmd->rdwrset,  30, 30, size, op);
47 	sja1105_packing(p, &cmd->errors,   29, 29, size, op);
48 	sja1105_packing(p, &cmd->valident, 27, 27, size, op);
49 	/* Hack - The hardware takes the 'index' field within
50 	 * struct sja1105_l2_lookup_entry as the index on which this command
51 	 * will operate. However it will ignore everything else, so 'index'
52 	 * is logically part of command but physically part of entry.
53 	 * Populate the 'index' entry field from within the command callback,
54 	 * such that our API doesn't need to ask for a full-blown entry
55 	 * structure when e.g. a delete is requested.
56 	 */
57 	sja1105_packing(buf, &cmd->index, 29, 20,
58 			SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY, op);
59 	/* TODO hostcmd */
60 }
61 
62 static void
63 sja1105et_l2_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
64 				enum packing_op op)
65 {
66 	u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY;
67 	const int size = SJA1105_SIZE_DYN_CMD;
68 
69 	sja1105_packing(p, &cmd->valid,    31, 31, size, op);
70 	sja1105_packing(p, &cmd->rdwrset,  30, 30, size, op);
71 	sja1105_packing(p, &cmd->errors,   29, 29, size, op);
72 	sja1105_packing(p, &cmd->valident, 27, 27, size, op);
73 	/* Hack - see comments above. */
74 	sja1105_packing(buf, &cmd->index, 29, 20,
75 			SJA1105ET_SIZE_L2_LOOKUP_ENTRY, op);
76 }
77 
78 static void
79 sja1105et_mgmt_route_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
80 				 enum packing_op op)
81 {
82 	u8 *p = buf + SJA1105ET_SIZE_L2_LOOKUP_ENTRY;
83 	u64 mgmtroute = 1;
84 
85 	sja1105et_l2_lookup_cmd_packing(buf, cmd, op);
86 	if (op == PACK)
87 		sja1105_pack(p, &mgmtroute, 26, 26, SJA1105_SIZE_DYN_CMD);
88 }
89 
90 static size_t sja1105et_mgmt_route_entry_packing(void *buf, void *entry_ptr,
91 						 enum packing_op op)
92 {
93 	struct sja1105_mgmt_entry *entry = entry_ptr;
94 	const size_t size = SJA1105ET_SIZE_L2_LOOKUP_ENTRY;
95 
96 	/* UM10944: To specify if a PTP egress timestamp shall be captured on
97 	 * each port upon transmission of the frame, the LSB of VLANID in the
98 	 * ENTRY field provided by the host must be set.
99 	 * Bit 1 of VLANID then specifies the register where the timestamp for
100 	 * this port is stored in.
101 	 */
102 	sja1105_packing(buf, &entry->tsreg,     85, 85, size, op);
103 	sja1105_packing(buf, &entry->takets,    84, 84, size, op);
104 	sja1105_packing(buf, &entry->macaddr,   83, 36, size, op);
105 	sja1105_packing(buf, &entry->destports, 35, 31, size, op);
106 	sja1105_packing(buf, &entry->enfport,   30, 30, size, op);
107 	return size;
108 }
109 
110 /* In E/T, entry is at addresses 0x27-0x28. There is a 4 byte gap at 0x29,
111  * and command is at 0x2a. Similarly in P/Q/R/S there is a 1 register gap
112  * between entry (0x2d, 0x2e) and command (0x30).
113  */
114 static void
115 sja1105_vlan_lookup_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
116 				enum packing_op op)
117 {
118 	u8 *p = buf + SJA1105_SIZE_VLAN_LOOKUP_ENTRY + 4;
119 	const int size = SJA1105_SIZE_DYN_CMD;
120 
121 	sja1105_packing(p, &cmd->valid,    31, 31, size, op);
122 	sja1105_packing(p, &cmd->rdwrset,  30, 30, size, op);
123 	sja1105_packing(p, &cmd->valident, 27, 27, size, op);
124 	/* Hack - see comments above, applied for 'vlanid' field of
125 	 * struct sja1105_vlan_lookup_entry.
126 	 */
127 	sja1105_packing(buf, &cmd->index, 38, 27,
128 			SJA1105_SIZE_VLAN_LOOKUP_ENTRY, op);
129 }
130 
131 static void
132 sja1105_l2_forwarding_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
133 				  enum packing_op op)
134 {
135 	u8 *p = buf + SJA1105_SIZE_L2_FORWARDING_ENTRY;
136 	const int size = SJA1105_SIZE_DYN_CMD;
137 
138 	sja1105_packing(p, &cmd->valid,   31, 31, size, op);
139 	sja1105_packing(p, &cmd->errors,  30, 30, size, op);
140 	sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op);
141 	sja1105_packing(p, &cmd->index,    4,  0, size, op);
142 }
143 
144 static void
145 sja1105et_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
146 				 enum packing_op op)
147 {
148 	const int size = SJA1105_SIZE_DYN_CMD;
149 	/* Yup, user manual definitions are reversed */
150 	u8 *reg1 = buf + 4;
151 
152 	sja1105_packing(reg1, &cmd->valid, 31, 31, size, op);
153 	sja1105_packing(reg1, &cmd->index, 26, 24, size, op);
154 }
155 
156 static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr,
157 						 enum packing_op op)
158 {
159 	const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY;
160 	struct sja1105_mac_config_entry *entry = entry_ptr;
161 	/* Yup, user manual definitions are reversed */
162 	u8 *reg1 = buf + 4;
163 	u8 *reg2 = buf;
164 
165 	sja1105_packing(reg1, &entry->speed,     30, 29, size, op);
166 	sja1105_packing(reg1, &entry->drpdtag,   23, 23, size, op);
167 	sja1105_packing(reg1, &entry->drpuntag,  22, 22, size, op);
168 	sja1105_packing(reg1, &entry->retag,     21, 21, size, op);
169 	sja1105_packing(reg1, &entry->dyn_learn, 20, 20, size, op);
170 	sja1105_packing(reg1, &entry->egress,    19, 19, size, op);
171 	sja1105_packing(reg1, &entry->ingress,   18, 18, size, op);
172 	sja1105_packing(reg1, &entry->ing_mirr,  17, 17, size, op);
173 	sja1105_packing(reg1, &entry->egr_mirr,  16, 16, size, op);
174 	sja1105_packing(reg1, &entry->vlanprio,  14, 12, size, op);
175 	sja1105_packing(reg1, &entry->vlanid,    11,  0, size, op);
176 	sja1105_packing(reg2, &entry->tp_delin,  31, 16, size, op);
177 	sja1105_packing(reg2, &entry->tp_delout, 15,  0, size, op);
178 	/* MAC configuration table entries which can't be reconfigured:
179 	 * top, base, enabled, ifg, maxage, drpnona664
180 	 */
181 	/* Bogus return value, not used anywhere */
182 	return 0;
183 }
184 
185 static void
186 sja1105pqrs_mac_config_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
187 				   enum packing_op op)
188 {
189 	const int size = SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY;
190 	u8 *p = buf + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY;
191 
192 	sja1105_packing(p, &cmd->valid,   31, 31, size, op);
193 	sja1105_packing(p, &cmd->errors,  30, 30, size, op);
194 	sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op);
195 	sja1105_packing(p, &cmd->index,    2,  0, size, op);
196 }
197 
198 static void
199 sja1105et_l2_lookup_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
200 				       enum packing_op op)
201 {
202 	sja1105_packing(buf, &cmd->valid, 31, 31,
203 			SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op);
204 }
205 
206 static size_t
207 sja1105et_l2_lookup_params_entry_packing(void *buf, void *entry_ptr,
208 					 enum packing_op op)
209 {
210 	struct sja1105_l2_lookup_params_entry *entry = entry_ptr;
211 
212 	sja1105_packing(buf, &entry->poly, 7, 0,
213 			SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD, op);
214 	/* Bogus return value, not used anywhere */
215 	return 0;
216 }
217 
218 static void
219 sja1105et_general_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
220 				     enum packing_op op)
221 {
222 	const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD;
223 
224 	sja1105_packing(buf, &cmd->valid,  31, 31, size, op);
225 	sja1105_packing(buf, &cmd->errors, 30, 30, size, op);
226 }
227 
228 static size_t
229 sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
230 				       enum packing_op op)
231 {
232 	struct sja1105_general_params_entry *entry = entry_ptr;
233 	const int size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD;
234 
235 	sja1105_packing(buf, &entry->mirr_port, 2, 0, size, op);
236 	/* Bogus return value, not used anywhere */
237 	return 0;
238 }
239 
240 #define OP_READ		BIT(0)
241 #define OP_WRITE	BIT(1)
242 #define OP_DEL		BIT(2)
243 
244 /* SJA1105E/T: First generation */
245 struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {
246 	[BLK_IDX_L2_LOOKUP] = {
247 		.entry_packing = sja1105et_l2_lookup_entry_packing,
248 		.cmd_packing = sja1105et_l2_lookup_cmd_packing,
249 		.access = (OP_READ | OP_WRITE | OP_DEL),
250 		.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
251 		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD,
252 		.addr = 0x20,
253 	},
254 	[BLK_IDX_MGMT_ROUTE] = {
255 		.entry_packing = sja1105et_mgmt_route_entry_packing,
256 		.cmd_packing = sja1105et_mgmt_route_cmd_packing,
257 		.access = (OP_READ | OP_WRITE),
258 		.max_entry_count = SJA1105_NUM_PORTS,
259 		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD,
260 		.addr = 0x20,
261 	},
262 	[BLK_IDX_L2_POLICING] = {0},
263 	[BLK_IDX_VLAN_LOOKUP] = {
264 		.entry_packing = sja1105_vlan_lookup_entry_packing,
265 		.cmd_packing = sja1105_vlan_lookup_cmd_packing,
266 		.access = (OP_WRITE | OP_DEL),
267 		.max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
268 		.packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD,
269 		.addr = 0x27,
270 	},
271 	[BLK_IDX_L2_FORWARDING] = {
272 		.entry_packing = sja1105_l2_forwarding_entry_packing,
273 		.cmd_packing = sja1105_l2_forwarding_cmd_packing,
274 		.max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
275 		.access = OP_WRITE,
276 		.packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD,
277 		.addr = 0x24,
278 	},
279 	[BLK_IDX_MAC_CONFIG] = {
280 		.entry_packing = sja1105et_mac_config_entry_packing,
281 		.cmd_packing = sja1105et_mac_config_cmd_packing,
282 		.max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
283 		.access = OP_WRITE,
284 		.packed_size = SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD,
285 		.addr = 0x36,
286 	},
287 	[BLK_IDX_L2_LOOKUP_PARAMS] = {
288 		.entry_packing = sja1105et_l2_lookup_params_entry_packing,
289 		.cmd_packing = sja1105et_l2_lookup_params_cmd_packing,
290 		.max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT,
291 		.access = OP_WRITE,
292 		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD,
293 		.addr = 0x38,
294 	},
295 	[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
296 	[BLK_IDX_GENERAL_PARAMS] = {
297 		.entry_packing = sja1105et_general_params_entry_packing,
298 		.cmd_packing = sja1105et_general_params_cmd_packing,
299 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
300 		.access = OP_WRITE,
301 		.packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD,
302 		.addr = 0x34,
303 	},
304 	[BLK_IDX_XMII_PARAMS] = {0},
305 };
306 
307 /* SJA1105P/Q/R/S: Second generation: TODO */
308 struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
309 	[BLK_IDX_L2_LOOKUP] = {
310 		.entry_packing = sja1105pqrs_l2_lookup_entry_packing,
311 		.cmd_packing = sja1105pqrs_l2_lookup_cmd_packing,
312 		.access = (OP_READ | OP_WRITE | OP_DEL),
313 		.max_entry_count = SJA1105_MAX_L2_LOOKUP_COUNT,
314 		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD,
315 		.addr = 0x24,
316 	},
317 	[BLK_IDX_L2_POLICING] = {0},
318 	[BLK_IDX_VLAN_LOOKUP] = {
319 		.entry_packing = sja1105_vlan_lookup_entry_packing,
320 		.cmd_packing = sja1105_vlan_lookup_cmd_packing,
321 		.access = (OP_READ | OP_WRITE | OP_DEL),
322 		.max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT,
323 		.packed_size = SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD,
324 		.addr = 0x2D,
325 	},
326 	[BLK_IDX_L2_FORWARDING] = {
327 		.entry_packing = sja1105_l2_forwarding_entry_packing,
328 		.cmd_packing = sja1105_l2_forwarding_cmd_packing,
329 		.max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT,
330 		.access = OP_WRITE,
331 		.packed_size = SJA1105_SIZE_L2_FORWARDING_DYN_CMD,
332 		.addr = 0x2A,
333 	},
334 	[BLK_IDX_MAC_CONFIG] = {
335 		.entry_packing = sja1105pqrs_mac_config_entry_packing,
336 		.cmd_packing = sja1105pqrs_mac_config_cmd_packing,
337 		.max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT,
338 		.access = (OP_READ | OP_WRITE),
339 		.packed_size = SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD,
340 		.addr = 0x4B,
341 	},
342 	[BLK_IDX_L2_LOOKUP_PARAMS] = {
343 		.entry_packing = sja1105et_l2_lookup_params_entry_packing,
344 		.cmd_packing = sja1105et_l2_lookup_params_cmd_packing,
345 		.max_entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT,
346 		.access = (OP_READ | OP_WRITE),
347 		.packed_size = SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD,
348 		.addr = 0x38,
349 	},
350 	[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
351 	[BLK_IDX_GENERAL_PARAMS] = {
352 		.entry_packing = sja1105et_general_params_entry_packing,
353 		.cmd_packing = sja1105et_general_params_cmd_packing,
354 		.max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT,
355 		.access = OP_WRITE,
356 		.packed_size = SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD,
357 		.addr = 0x34,
358 	},
359 	[BLK_IDX_XMII_PARAMS] = {0},
360 };
361 
362 int sja1105_dynamic_config_read(struct sja1105_private *priv,
363 				enum sja1105_blk_idx blk_idx,
364 				int index, void *entry)
365 {
366 	const struct sja1105_dynamic_table_ops *ops;
367 	struct sja1105_dyn_cmd cmd = {0};
368 	/* SPI payload buffer */
369 	u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0};
370 	int retries = 3;
371 	int rc;
372 
373 	if (blk_idx >= BLK_IDX_MAX_DYN)
374 		return -ERANGE;
375 
376 	ops = &priv->info->dyn_ops[blk_idx];
377 
378 	if (index >= ops->max_entry_count)
379 		return -ERANGE;
380 	if (!(ops->access & OP_READ))
381 		return -EOPNOTSUPP;
382 	if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE)
383 		return -ERANGE;
384 	if (!ops->cmd_packing)
385 		return -EOPNOTSUPP;
386 	if (!ops->entry_packing)
387 		return -EOPNOTSUPP;
388 
389 	cmd.valid = true; /* Trigger action on table entry */
390 	cmd.rdwrset = SPI_READ; /* Action is read */
391 	cmd.index = index;
392 	ops->cmd_packing(packed_buf, &cmd, PACK);
393 
394 	/* Send SPI write operation: read config table entry */
395 	rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr,
396 					 packed_buf, ops->packed_size);
397 	if (rc < 0)
398 		return rc;
399 
400 	/* Loop until we have confirmation that hardware has finished
401 	 * processing the command and has cleared the VALID field
402 	 */
403 	do {
404 		memset(packed_buf, 0, ops->packed_size);
405 
406 		/* Retrieve the read operation's result */
407 		rc = sja1105_spi_send_packed_buf(priv, SPI_READ, ops->addr,
408 						 packed_buf, ops->packed_size);
409 		if (rc < 0)
410 			return rc;
411 
412 		cmd = (struct sja1105_dyn_cmd) {0};
413 		ops->cmd_packing(packed_buf, &cmd, UNPACK);
414 		/* UM10944: [valident] will always be found cleared
415 		 * during a read access with MGMTROUTE set.
416 		 * So don't error out in that case.
417 		 */
418 		if (!cmd.valident && blk_idx != BLK_IDX_MGMT_ROUTE)
419 			return -EINVAL;
420 		cpu_relax();
421 	} while (cmd.valid && --retries);
422 
423 	if (cmd.valid)
424 		return -ETIMEDOUT;
425 
426 	/* Don't dereference possibly NULL pointer - maybe caller
427 	 * only wanted to see whether the entry existed or not.
428 	 */
429 	if (entry)
430 		ops->entry_packing(packed_buf, entry, UNPACK);
431 	return 0;
432 }
433 
434 int sja1105_dynamic_config_write(struct sja1105_private *priv,
435 				 enum sja1105_blk_idx blk_idx,
436 				 int index, void *entry, bool keep)
437 {
438 	const struct sja1105_dynamic_table_ops *ops;
439 	struct sja1105_dyn_cmd cmd = {0};
440 	/* SPI payload buffer */
441 	u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {0};
442 	int rc;
443 
444 	if (blk_idx >= BLK_IDX_MAX_DYN)
445 		return -ERANGE;
446 
447 	ops = &priv->info->dyn_ops[blk_idx];
448 
449 	if (index >= ops->max_entry_count)
450 		return -ERANGE;
451 	if (!(ops->access & OP_WRITE))
452 		return -EOPNOTSUPP;
453 	if (!keep && !(ops->access & OP_DEL))
454 		return -EOPNOTSUPP;
455 	if (ops->packed_size > SJA1105_MAX_DYN_CMD_SIZE)
456 		return -ERANGE;
457 
458 	cmd.valident = keep; /* If false, deletes entry */
459 	cmd.valid = true; /* Trigger action on table entry */
460 	cmd.rdwrset = SPI_WRITE; /* Action is write */
461 	cmd.index = index;
462 
463 	if (!ops->cmd_packing)
464 		return -EOPNOTSUPP;
465 	ops->cmd_packing(packed_buf, &cmd, PACK);
466 
467 	if (!ops->entry_packing)
468 		return -EOPNOTSUPP;
469 	/* Don't dereference potentially NULL pointer if just
470 	 * deleting a table entry is what was requested. For cases
471 	 * where 'index' field is physically part of entry structure,
472 	 * and needed here, we deal with that in the cmd_packing callback.
473 	 */
474 	if (keep)
475 		ops->entry_packing(packed_buf, entry, PACK);
476 
477 	/* Send SPI write operation: read config table entry */
478 	rc = sja1105_spi_send_packed_buf(priv, SPI_WRITE, ops->addr,
479 					 packed_buf, ops->packed_size);
480 	if (rc < 0)
481 		return rc;
482 
483 	cmd = (struct sja1105_dyn_cmd) {0};
484 	ops->cmd_packing(packed_buf, &cmd, UNPACK);
485 	if (cmd.errors)
486 		return -EINVAL;
487 
488 	return 0;
489 }
490 
491 static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly)
492 {
493 	int i;
494 
495 	for (i = 0; i < 8; i++) {
496 		if ((crc ^ byte) & (1 << 7)) {
497 			crc <<= 1;
498 			crc ^= poly;
499 		} else {
500 			crc <<= 1;
501 		}
502 		byte <<= 1;
503 	}
504 	return crc;
505 }
506 
507 /* CRC8 algorithm with non-reversed input, non-reversed output,
508  * no input xor and no output xor. Code customized for receiving
509  * the SJA1105 E/T FDB keys (vlanid, macaddr) as input. CRC polynomial
510  * is also received as argument in the Koopman notation that the switch
511  * hardware stores it in.
512  */
513 u8 sja1105_fdb_hash(struct sja1105_private *priv, const u8 *addr, u16 vid)
514 {
515 	struct sja1105_l2_lookup_params_entry *l2_lookup_params =
516 		priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS].entries;
517 	u64 poly_koopman = l2_lookup_params->poly;
518 	/* Convert polynomial from Koopman to 'normal' notation */
519 	u8 poly = (u8)(1 + (poly_koopman << 1));
520 	u64 vlanid = l2_lookup_params->shared_learn ? 0 : vid;
521 	u64 input = (vlanid << 48) | ether_addr_to_u64(addr);
522 	u8 crc = 0; /* seed */
523 	int i;
524 
525 	/* Mask the eight bytes starting from MSB one at a time */
526 	for (i = 56; i >= 0; i -= 8) {
527 		u8 byte = (input & (0xffull << i)) >> i;
528 
529 		crc = sja1105_crc8_add(crc, byte, poly);
530 	}
531 	return crc;
532 }
533