1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>
6 #define SJA1105_SIZE_DYN_CMD 4
8 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY \
11 #define SJA1105ET_SIZE_L2_LOOKUP_DYN_CMD \
12 (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_L2_LOOKUP_ENTRY)
14 #define SJA1105PQRS_SIZE_L2_LOOKUP_DYN_CMD \
15 (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY)
17 #define SJA1105_SIZE_VLAN_LOOKUP_DYN_CMD \
18 (SJA1105_SIZE_DYN_CMD + 4 + SJA1105_SIZE_VLAN_LOOKUP_ENTRY)
20 #define SJA1105_SIZE_L2_FORWARDING_DYN_CMD \
21 (SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_L2_FORWARDING_ENTRY)
23 #define SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD \
24 (SJA1105_SIZE_DYN_CMD + SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY)
26 #define SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD \
27 (SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY)
29 #define SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD \
32 #define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \
35 #define SJA1105_MAX_DYN_CMD_SIZE \
36 SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
39 sja1105pqrs_l2_lookup_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
42 u8
*p
= buf
+ SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY
;
43 const int size
= SJA1105_SIZE_DYN_CMD
;
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.
57 sja1105_packing(buf
, &cmd
->index
, 29, 20,
58 SJA1105PQRS_SIZE_L2_LOOKUP_ENTRY
, op
);
63 sja1105et_l2_lookup_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
66 u8
*p
= buf
+ SJA1105ET_SIZE_L2_LOOKUP_ENTRY
;
67 const int size
= SJA1105_SIZE_DYN_CMD
;
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
);
79 sja1105et_mgmt_route_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
82 u8
*p
= buf
+ SJA1105ET_SIZE_L2_LOOKUP_ENTRY
;
85 sja1105et_l2_lookup_cmd_packing(buf
, cmd
, op
);
87 sja1105_pack(p
, &mgmtroute
, 26, 26, SJA1105_SIZE_DYN_CMD
);
90 static size_t sja1105et_mgmt_route_entry_packing(void *buf
, void *entry_ptr
,
93 struct sja1105_mgmt_entry
*entry
= entry_ptr
;
94 const size_t size
= SJA1105ET_SIZE_L2_LOOKUP_ENTRY
;
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.
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
);
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).
115 sja1105_vlan_lookup_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
118 u8
*p
= buf
+ SJA1105_SIZE_VLAN_LOOKUP_ENTRY
+ 4;
119 const int size
= SJA1105_SIZE_DYN_CMD
;
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.
127 sja1105_packing(buf
, &cmd
->index
, 38, 27,
128 SJA1105_SIZE_VLAN_LOOKUP_ENTRY
, op
);
132 sja1105_l2_forwarding_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
135 u8
*p
= buf
+ SJA1105_SIZE_L2_FORWARDING_ENTRY
;
136 const int size
= SJA1105_SIZE_DYN_CMD
;
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
);
145 sja1105et_mac_config_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
148 const int size
= SJA1105_SIZE_DYN_CMD
;
149 /* Yup, user manual definitions are reversed */
152 sja1105_packing(reg1
, &cmd
->valid
, 31, 31, size
, op
);
153 sja1105_packing(reg1
, &cmd
->index
, 26, 24, size
, op
);
156 static size_t sja1105et_mac_config_entry_packing(void *buf
, void *entry_ptr
,
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 */
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
181 /* Bogus return value, not used anywhere */
186 sja1105pqrs_mac_config_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
189 const int size
= SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY
;
190 u8
*p
= buf
+ SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY
;
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
);
199 sja1105et_l2_lookup_params_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
202 sja1105_packing(buf
, &cmd
->valid
, 31, 31,
203 SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD
, op
);
207 sja1105et_l2_lookup_params_entry_packing(void *buf
, void *entry_ptr
,
210 struct sja1105_l2_lookup_params_entry
*entry
= entry_ptr
;
212 sja1105_packing(buf
, &entry
->poly
, 7, 0,
213 SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD
, op
);
214 /* Bogus return value, not used anywhere */
219 sja1105et_general_params_cmd_packing(void *buf
, struct sja1105_dyn_cmd
*cmd
,
222 const int size
= SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD
;
224 sja1105_packing(buf
, &cmd
->valid
, 31, 31, size
, op
);
225 sja1105_packing(buf
, &cmd
->errors
, 30, 30, size
, op
);
229 sja1105et_general_params_entry_packing(void *buf
, void *entry_ptr
,
232 struct sja1105_general_params_entry
*entry
= entry_ptr
;
233 const int size
= SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD
;
235 sja1105_packing(buf
, &entry
->mirr_port
, 2, 0, size
, op
);
236 /* Bogus return value, not used anywhere */
240 #define OP_READ BIT(0)
241 #define OP_WRITE BIT(1)
242 #define OP_DEL BIT(2)
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
,
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
,
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
,
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
,
276 .packed_size
= SJA1105_SIZE_L2_FORWARDING_DYN_CMD
,
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
,
284 .packed_size
= SJA1105ET_SIZE_MAC_CONFIG_DYN_CMD
,
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
,
292 .packed_size
= SJA1105ET_SIZE_L2_LOOKUP_PARAMS_DYN_CMD
,
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
,
301 .packed_size
= SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD
,
304 [BLK_IDX_XMII_PARAMS
] = {0},
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
,
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
,
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
,
331 .packed_size
= SJA1105_SIZE_L2_FORWARDING_DYN_CMD
,
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
,
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
,
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
,
356 .packed_size
= SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD
,
359 [BLK_IDX_XMII_PARAMS
] = {0},
362 int sja1105_dynamic_config_read(struct sja1105_private
*priv
,
363 enum sja1105_blk_idx blk_idx
,
364 int index
, void *entry
)
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};
373 if (blk_idx
>= BLK_IDX_MAX_DYN
)
376 ops
= &priv
->info
->dyn_ops
[blk_idx
];
378 if (index
>= ops
->max_entry_count
)
380 if (!(ops
->access
& OP_READ
))
382 if (ops
->packed_size
> SJA1105_MAX_DYN_CMD_SIZE
)
384 if (!ops
->cmd_packing
)
386 if (!ops
->entry_packing
)
389 cmd
.valid
= true; /* Trigger action on table entry */
390 cmd
.rdwrset
= SPI_READ
; /* Action is read */
392 ops
->cmd_packing(packed_buf
, &cmd
, PACK
);
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
);
400 /* Loop until we have confirmation that hardware has finished
401 * processing the command and has cleared the VALID field
404 memset(packed_buf
, 0, ops
->packed_size
);
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
);
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.
418 if (!cmd
.valident
&& blk_idx
!= BLK_IDX_MGMT_ROUTE
)
421 } while (cmd
.valid
&& --retries
);
426 /* Don't dereference possibly NULL pointer - maybe caller
427 * only wanted to see whether the entry existed or not.
430 ops
->entry_packing(packed_buf
, entry
, UNPACK
);
434 int sja1105_dynamic_config_write(struct sja1105_private
*priv
,
435 enum sja1105_blk_idx blk_idx
,
436 int index
, void *entry
, bool keep
)
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};
444 if (blk_idx
>= BLK_IDX_MAX_DYN
)
447 ops
= &priv
->info
->dyn_ops
[blk_idx
];
449 if (index
>= ops
->max_entry_count
)
451 if (!(ops
->access
& OP_WRITE
))
453 if (!keep
&& !(ops
->access
& OP_DEL
))
455 if (ops
->packed_size
> SJA1105_MAX_DYN_CMD_SIZE
)
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 */
463 if (!ops
->cmd_packing
)
465 ops
->cmd_packing(packed_buf
, &cmd
, PACK
);
467 if (!ops
->entry_packing
)
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.
475 ops
->entry_packing(packed_buf
, entry
, PACK
);
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
);
483 cmd
= (struct sja1105_dyn_cmd
) {0};
484 ops
->cmd_packing(packed_buf
, &cmd
, UNPACK
);
491 static u8
sja1105_crc8_add(u8 crc
, u8 byte
, u8 poly
)
495 for (i
= 0; i
< 8; i
++) {
496 if ((crc
^ byte
) & (1 << 7)) {
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.
513 u8
sja1105_fdb_hash(struct sja1105_private
*priv
, const u8
*addr
, u16 vid
)
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 */
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
;
529 crc
= sja1105_crc8_add(crc
, byte
, poly
);