1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Texas Instruments N-Port Ethernet Switch Address Lookup Engine |
4 | * |
5 | * Copyright (C) 2012 Texas Instruments |
6 | * |
7 | */ |
8 | #include <linux/bitmap.h> |
9 | #include <linux/if_vlan.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/seq_file.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/err.h> |
16 | #include <linux/io.h> |
17 | #include <linux/stat.h> |
18 | #include <linux/sysfs.h> |
19 | #include <linux/etherdevice.h> |
20 | |
21 | #include "cpsw_ale.h" |
22 | |
23 | #define BITMASK(bits) (BIT(bits) - 1) |
24 | |
25 | #define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask)) |
26 | #define ALE_VERSION_MINOR(rev) (rev & 0xff) |
27 | #define ALE_VERSION_1R3 0x0103 |
28 | #define ALE_VERSION_1R4 0x0104 |
29 | |
30 | /* ALE Registers */ |
31 | #define ALE_IDVER 0x00 |
32 | #define ALE_STATUS 0x04 |
33 | #define ALE_CONTROL 0x08 |
34 | #define ALE_PRESCALE 0x10 |
35 | #define ALE_AGING_TIMER 0x14 |
36 | #define ALE_UNKNOWNVLAN 0x18 |
37 | #define ALE_TABLE_CONTROL 0x20 |
38 | #define ALE_TABLE 0x34 |
39 | #define ALE_PORTCTL 0x40 |
40 | |
41 | /* ALE NetCP NU switch specific Registers */ |
42 | #define ALE_UNKNOWNVLAN_MEMBER 0x90 |
43 | #define ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD 0x94 |
44 | #define ALE_UNKNOWNVLAN_REG_MCAST_FLOOD 0x98 |
45 | #define ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS 0x9C |
46 | #define ALE_VLAN_MASK_MUX(reg) (0xc0 + (0x4 * (reg))) |
47 | |
48 | #define AM65_CPSW_ALE_THREAD_DEF_REG 0x134 |
49 | |
50 | /* ALE_AGING_TIMER */ |
51 | #define ALE_AGING_TIMER_MASK GENMASK(23, 0) |
52 | |
53 | #define ALE_RATE_LIMIT_MIN_PPS 1000 |
54 | |
55 | /** |
56 | * struct ale_entry_fld - The ALE tbl entry field description |
57 | * @start_bit: field start bit |
58 | * @num_bits: field bit length |
59 | * @flags: field flags |
60 | */ |
61 | struct ale_entry_fld { |
62 | u8 start_bit; |
63 | u8 num_bits; |
64 | u8 flags; |
65 | }; |
66 | |
67 | enum { |
68 | CPSW_ALE_F_STATUS_REG = BIT(0), /* Status register present */ |
69 | CPSW_ALE_F_HW_AUTOAGING = BIT(1), /* HW auto aging */ |
70 | |
71 | CPSW_ALE_F_COUNT |
72 | }; |
73 | |
74 | /** |
75 | * struct cpsw_ale_dev_id - The ALE version/SoC specific configuration |
76 | * @dev_id: ALE version/SoC id |
77 | * @features: features supported by ALE |
78 | * @tbl_entries: number of ALE entries |
79 | * @major_ver_mask: mask of ALE Major Version Value in ALE_IDVER reg. |
80 | * @nu_switch_ale: NU Switch ALE |
81 | * @vlan_entry_tbl: ALE vlan entry fields description tbl |
82 | */ |
83 | struct cpsw_ale_dev_id { |
84 | const char *dev_id; |
85 | u32 features; |
86 | u32 tbl_entries; |
87 | u32 major_ver_mask; |
88 | bool nu_switch_ale; |
89 | const struct ale_entry_fld *vlan_entry_tbl; |
90 | }; |
91 | |
92 | #define ALE_TABLE_WRITE BIT(31) |
93 | |
94 | #define ALE_TYPE_FREE 0 |
95 | #define ALE_TYPE_ADDR 1 |
96 | #define ALE_TYPE_VLAN 2 |
97 | #define ALE_TYPE_VLAN_ADDR 3 |
98 | |
99 | #define ALE_UCAST_PERSISTANT 0 |
100 | #define ALE_UCAST_UNTOUCHED 1 |
101 | #define ALE_UCAST_OUI 2 |
102 | #define ALE_UCAST_TOUCHED 3 |
103 | |
104 | #define ALE_TABLE_SIZE_MULTIPLIER 1024 |
105 | #define ALE_STATUS_SIZE_MASK 0x1f |
106 | |
107 | static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) |
108 | { |
109 | int idx, idx2; |
110 | u32 hi_val = 0; |
111 | |
112 | idx = start / 32; |
113 | idx2 = (start + bits - 1) / 32; |
114 | /* Check if bits to be fetched exceed a word */ |
115 | if (idx != idx2) { |
116 | idx2 = 2 - idx2; /* flip */ |
117 | hi_val = ale_entry[idx2] << ((idx2 * 32) - start); |
118 | } |
119 | start -= idx * 32; |
120 | idx = 2 - idx; /* flip */ |
121 | return (hi_val + (ale_entry[idx] >> start)) & BITMASK(bits); |
122 | } |
123 | |
124 | static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits, |
125 | u32 value) |
126 | { |
127 | int idx, idx2; |
128 | |
129 | value &= BITMASK(bits); |
130 | idx = start / 32; |
131 | idx2 = (start + bits - 1) / 32; |
132 | /* Check if bits to be set exceed a word */ |
133 | if (idx != idx2) { |
134 | idx2 = 2 - idx2; /* flip */ |
135 | ale_entry[idx2] &= ~(BITMASK(bits + start - (idx2 * 32))); |
136 | ale_entry[idx2] |= (value >> ((idx2 * 32) - start)); |
137 | } |
138 | start -= idx * 32; |
139 | idx = 2 - idx; /* flip */ |
140 | ale_entry[idx] &= ~(BITMASK(bits) << start); |
141 | ale_entry[idx] |= (value << start); |
142 | } |
143 | |
144 | #define DEFINE_ALE_FIELD(name, start, bits) \ |
145 | static inline int cpsw_ale_get_##name(u32 *ale_entry) \ |
146 | { \ |
147 | return cpsw_ale_get_field(ale_entry, start, bits); \ |
148 | } \ |
149 | static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ |
150 | { \ |
151 | cpsw_ale_set_field(ale_entry, start, bits, value); \ |
152 | } |
153 | |
154 | #define DEFINE_ALE_FIELD1(name, start) \ |
155 | static inline int cpsw_ale_get_##name(u32 *ale_entry, u32 bits) \ |
156 | { \ |
157 | return cpsw_ale_get_field(ale_entry, start, bits); \ |
158 | } \ |
159 | static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value, \ |
160 | u32 bits) \ |
161 | { \ |
162 | cpsw_ale_set_field(ale_entry, start, bits, value); \ |
163 | } |
164 | |
165 | enum { |
166 | ALE_ENT_VID_MEMBER_LIST = 0, |
167 | ALE_ENT_VID_UNREG_MCAST_MSK, |
168 | ALE_ENT_VID_REG_MCAST_MSK, |
169 | ALE_ENT_VID_FORCE_UNTAGGED_MSK, |
170 | ALE_ENT_VID_UNREG_MCAST_IDX, |
171 | ALE_ENT_VID_REG_MCAST_IDX, |
172 | ALE_ENT_VID_LAST, |
173 | }; |
174 | |
175 | #define ALE_FLD_ALLOWED BIT(0) |
176 | #define ALE_FLD_SIZE_PORT_MASK_BITS BIT(1) |
177 | #define ALE_FLD_SIZE_PORT_NUM_BITS BIT(2) |
178 | |
179 | #define ALE_ENTRY_FLD(id, start, bits) \ |
180 | [id] = { \ |
181 | .start_bit = start, \ |
182 | .num_bits = bits, \ |
183 | .flags = ALE_FLD_ALLOWED, \ |
184 | } |
185 | |
186 | #define ALE_ENTRY_FLD_DYN_MSK_SIZE(id, start) \ |
187 | [id] = { \ |
188 | .start_bit = start, \ |
189 | .num_bits = 0, \ |
190 | .flags = ALE_FLD_ALLOWED | \ |
191 | ALE_FLD_SIZE_PORT_MASK_BITS, \ |
192 | } |
193 | |
194 | /* dm814x, am3/am4/am5, k2hk */ |
195 | static const struct ale_entry_fld vlan_entry_cpsw[ALE_ENT_VID_LAST] = { |
196 | ALE_ENTRY_FLD(ALE_ENT_VID_MEMBER_LIST, 0, 3), |
197 | ALE_ENTRY_FLD(ALE_ENT_VID_UNREG_MCAST_MSK, 8, 3), |
198 | ALE_ENTRY_FLD(ALE_ENT_VID_REG_MCAST_MSK, 16, 3), |
199 | ALE_ENTRY_FLD(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24, 3), |
200 | }; |
201 | |
202 | /* k2e/k2l, k3 am65/j721e cpsw2g */ |
203 | static const struct ale_entry_fld vlan_entry_nu[ALE_ENT_VID_LAST] = { |
204 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_MEMBER_LIST, 0), |
205 | ALE_ENTRY_FLD(ALE_ENT_VID_UNREG_MCAST_IDX, 20, 3), |
206 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24), |
207 | ALE_ENTRY_FLD(ALE_ENT_VID_REG_MCAST_IDX, 44, 3), |
208 | }; |
209 | |
210 | /* K3 j721e/j7200 cpsw9g/5g, am64x cpsw3g */ |
211 | static const struct ale_entry_fld vlan_entry_k3_cpswxg[] = { |
212 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_MEMBER_LIST, 0), |
213 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_UNREG_MCAST_MSK, 12), |
214 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24), |
215 | ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_REG_MCAST_MSK, 36), |
216 | }; |
217 | |
218 | DEFINE_ALE_FIELD(entry_type, 60, 2) |
219 | DEFINE_ALE_FIELD(vlan_id, 48, 12) |
220 | DEFINE_ALE_FIELD(mcast_state, 62, 2) |
221 | DEFINE_ALE_FIELD1(port_mask, 66) |
222 | DEFINE_ALE_FIELD(super, 65, 1) |
223 | DEFINE_ALE_FIELD(ucast_type, 62, 2) |
224 | DEFINE_ALE_FIELD1(port_num, 66) |
225 | DEFINE_ALE_FIELD(blocked, 65, 1) |
226 | DEFINE_ALE_FIELD(secure, 64, 1) |
227 | DEFINE_ALE_FIELD(mcast, 40, 1) |
228 | |
229 | #define NU_VLAN_UNREG_MCAST_IDX 1 |
230 | |
231 | static int cpsw_ale_entry_get_fld(struct cpsw_ale *ale, |
232 | u32 *ale_entry, |
233 | const struct ale_entry_fld *entry_tbl, |
234 | int fld_id) |
235 | { |
236 | const struct ale_entry_fld *entry_fld; |
237 | u32 bits; |
238 | |
239 | if (!ale || !ale_entry) |
240 | return -EINVAL; |
241 | |
242 | entry_fld = &entry_tbl[fld_id]; |
243 | if (!(entry_fld->flags & ALE_FLD_ALLOWED)) { |
244 | dev_err(ale->params.dev, "get: wrong ale fld id %d\n" , fld_id); |
245 | return -ENOENT; |
246 | } |
247 | |
248 | bits = entry_fld->num_bits; |
249 | if (entry_fld->flags & ALE_FLD_SIZE_PORT_MASK_BITS) |
250 | bits = ale->port_mask_bits; |
251 | |
252 | return cpsw_ale_get_field(ale_entry, start: entry_fld->start_bit, bits); |
253 | } |
254 | |
255 | static void cpsw_ale_entry_set_fld(struct cpsw_ale *ale, |
256 | u32 *ale_entry, |
257 | const struct ale_entry_fld *entry_tbl, |
258 | int fld_id, |
259 | u32 value) |
260 | { |
261 | const struct ale_entry_fld *entry_fld; |
262 | u32 bits; |
263 | |
264 | if (!ale || !ale_entry) |
265 | return; |
266 | |
267 | entry_fld = &entry_tbl[fld_id]; |
268 | if (!(entry_fld->flags & ALE_FLD_ALLOWED)) { |
269 | dev_err(ale->params.dev, "set: wrong ale fld id %d\n" , fld_id); |
270 | return; |
271 | } |
272 | |
273 | bits = entry_fld->num_bits; |
274 | if (entry_fld->flags & ALE_FLD_SIZE_PORT_MASK_BITS) |
275 | bits = ale->port_mask_bits; |
276 | |
277 | cpsw_ale_set_field(ale_entry, start: entry_fld->start_bit, bits, value); |
278 | } |
279 | |
280 | static int cpsw_ale_vlan_get_fld(struct cpsw_ale *ale, |
281 | u32 *ale_entry, |
282 | int fld_id) |
283 | { |
284 | return cpsw_ale_entry_get_fld(ale, ale_entry, |
285 | entry_tbl: ale->vlan_entry_tbl, fld_id); |
286 | } |
287 | |
288 | static void cpsw_ale_vlan_set_fld(struct cpsw_ale *ale, |
289 | u32 *ale_entry, |
290 | int fld_id, |
291 | u32 value) |
292 | { |
293 | cpsw_ale_entry_set_fld(ale, ale_entry, |
294 | entry_tbl: ale->vlan_entry_tbl, fld_id, value); |
295 | } |
296 | |
297 | /* The MAC address field in the ALE entry cannot be macroized as above */ |
298 | static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr) |
299 | { |
300 | int i; |
301 | |
302 | for (i = 0; i < 6; i++) |
303 | addr[i] = cpsw_ale_get_field(ale_entry, start: 40 - 8*i, bits: 8); |
304 | } |
305 | |
306 | static inline void cpsw_ale_set_addr(u32 *ale_entry, const u8 *addr) |
307 | { |
308 | int i; |
309 | |
310 | for (i = 0; i < 6; i++) |
311 | cpsw_ale_set_field(ale_entry, start: 40 - 8*i, bits: 8, value: addr[i]); |
312 | } |
313 | |
314 | static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry) |
315 | { |
316 | int i; |
317 | |
318 | WARN_ON(idx > ale->params.ale_entries); |
319 | |
320 | writel_relaxed(idx, ale->params.ale_regs + ALE_TABLE_CONTROL); |
321 | |
322 | for (i = 0; i < ALE_ENTRY_WORDS; i++) |
323 | ale_entry[i] = readl_relaxed(ale->params.ale_regs + |
324 | ALE_TABLE + 4 * i); |
325 | |
326 | return idx; |
327 | } |
328 | |
329 | static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry) |
330 | { |
331 | int i; |
332 | |
333 | WARN_ON(idx > ale->params.ale_entries); |
334 | |
335 | for (i = 0; i < ALE_ENTRY_WORDS; i++) |
336 | writel_relaxed(ale_entry[i], ale->params.ale_regs + |
337 | ALE_TABLE + 4 * i); |
338 | |
339 | writel_relaxed(idx | ALE_TABLE_WRITE, ale->params.ale_regs + |
340 | ALE_TABLE_CONTROL); |
341 | |
342 | return idx; |
343 | } |
344 | |
345 | static int cpsw_ale_match_addr(struct cpsw_ale *ale, const u8 *addr, u16 vid) |
346 | { |
347 | u32 ale_entry[ALE_ENTRY_WORDS]; |
348 | int type, idx; |
349 | |
350 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
351 | u8 entry_addr[6]; |
352 | |
353 | cpsw_ale_read(ale, idx, ale_entry); |
354 | type = cpsw_ale_get_entry_type(ale_entry); |
355 | if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) |
356 | continue; |
357 | if (cpsw_ale_get_vlan_id(ale_entry) != vid) |
358 | continue; |
359 | cpsw_ale_get_addr(ale_entry, addr: entry_addr); |
360 | if (ether_addr_equal(addr1: entry_addr, addr2: addr)) |
361 | return idx; |
362 | } |
363 | return -ENOENT; |
364 | } |
365 | |
366 | static int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid) |
367 | { |
368 | u32 ale_entry[ALE_ENTRY_WORDS]; |
369 | int type, idx; |
370 | |
371 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
372 | cpsw_ale_read(ale, idx, ale_entry); |
373 | type = cpsw_ale_get_entry_type(ale_entry); |
374 | if (type != ALE_TYPE_VLAN) |
375 | continue; |
376 | if (cpsw_ale_get_vlan_id(ale_entry) == vid) |
377 | return idx; |
378 | } |
379 | return -ENOENT; |
380 | } |
381 | |
382 | static int cpsw_ale_match_free(struct cpsw_ale *ale) |
383 | { |
384 | u32 ale_entry[ALE_ENTRY_WORDS]; |
385 | int type, idx; |
386 | |
387 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
388 | cpsw_ale_read(ale, idx, ale_entry); |
389 | type = cpsw_ale_get_entry_type(ale_entry); |
390 | if (type == ALE_TYPE_FREE) |
391 | return idx; |
392 | } |
393 | return -ENOENT; |
394 | } |
395 | |
396 | static int cpsw_ale_find_ageable(struct cpsw_ale *ale) |
397 | { |
398 | u32 ale_entry[ALE_ENTRY_WORDS]; |
399 | int type, idx; |
400 | |
401 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
402 | cpsw_ale_read(ale, idx, ale_entry); |
403 | type = cpsw_ale_get_entry_type(ale_entry); |
404 | if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR) |
405 | continue; |
406 | if (cpsw_ale_get_mcast(ale_entry)) |
407 | continue; |
408 | type = cpsw_ale_get_ucast_type(ale_entry); |
409 | if (type != ALE_UCAST_PERSISTANT && |
410 | type != ALE_UCAST_OUI) |
411 | return idx; |
412 | } |
413 | return -ENOENT; |
414 | } |
415 | |
416 | static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry, |
417 | int port_mask) |
418 | { |
419 | int mask; |
420 | |
421 | mask = cpsw_ale_get_port_mask(ale_entry, |
422 | bits: ale->port_mask_bits); |
423 | if ((mask & port_mask) == 0) |
424 | return; /* ports dont intersect, not interested */ |
425 | mask &= ~port_mask; |
426 | |
427 | /* free if only remaining port is host port */ |
428 | if (mask) |
429 | cpsw_ale_set_port_mask(ale_entry, value: mask, |
430 | bits: ale->port_mask_bits); |
431 | else |
432 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); |
433 | } |
434 | |
435 | int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid) |
436 | { |
437 | u32 ale_entry[ALE_ENTRY_WORDS]; |
438 | int ret, idx; |
439 | |
440 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
441 | cpsw_ale_read(ale, idx, ale_entry); |
442 | ret = cpsw_ale_get_entry_type(ale_entry); |
443 | if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR) |
444 | continue; |
445 | |
446 | /* if vid passed is -1 then remove all multicast entry from |
447 | * the table irrespective of vlan id, if a valid vlan id is |
448 | * passed then remove only multicast added to that vlan id. |
449 | * if vlan id doesn't match then move on to next entry. |
450 | */ |
451 | if (vid != -1 && cpsw_ale_get_vlan_id(ale_entry) != vid) |
452 | continue; |
453 | |
454 | if (cpsw_ale_get_mcast(ale_entry)) { |
455 | u8 addr[6]; |
456 | |
457 | if (cpsw_ale_get_super(ale_entry)) |
458 | continue; |
459 | |
460 | cpsw_ale_get_addr(ale_entry, addr); |
461 | if (!is_broadcast_ether_addr(addr)) |
462 | cpsw_ale_flush_mcast(ale, ale_entry, port_mask); |
463 | } |
464 | |
465 | cpsw_ale_write(ale, idx, ale_entry); |
466 | } |
467 | return 0; |
468 | } |
469 | |
470 | static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry, |
471 | int flags, u16 vid) |
472 | { |
473 | if (flags & ALE_VLAN) { |
474 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); |
475 | cpsw_ale_set_vlan_id(ale_entry, value: vid); |
476 | } else { |
477 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); |
478 | } |
479 | } |
480 | |
481 | int cpsw_ale_add_ucast(struct cpsw_ale *ale, const u8 *addr, int port, |
482 | int flags, u16 vid) |
483 | { |
484 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
485 | int idx; |
486 | |
487 | cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid); |
488 | |
489 | cpsw_ale_set_addr(ale_entry, addr); |
490 | cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); |
491 | cpsw_ale_set_secure(ale_entry, value: (flags & ALE_SECURE) ? 1 : 0); |
492 | cpsw_ale_set_blocked(ale_entry, value: (flags & ALE_BLOCKED) ? 1 : 0); |
493 | cpsw_ale_set_port_num(ale_entry, value: port, bits: ale->port_num_bits); |
494 | |
495 | idx = cpsw_ale_match_addr(ale, addr, vid: (flags & ALE_VLAN) ? vid : 0); |
496 | if (idx < 0) |
497 | idx = cpsw_ale_match_free(ale); |
498 | if (idx < 0) |
499 | idx = cpsw_ale_find_ageable(ale); |
500 | if (idx < 0) |
501 | return -ENOMEM; |
502 | |
503 | cpsw_ale_write(ale, idx, ale_entry); |
504 | return 0; |
505 | } |
506 | |
507 | int cpsw_ale_del_ucast(struct cpsw_ale *ale, const u8 *addr, int port, |
508 | int flags, u16 vid) |
509 | { |
510 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
511 | int idx; |
512 | |
513 | idx = cpsw_ale_match_addr(ale, addr, vid: (flags & ALE_VLAN) ? vid : 0); |
514 | if (idx < 0) |
515 | return -ENOENT; |
516 | |
517 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); |
518 | cpsw_ale_write(ale, idx, ale_entry); |
519 | return 0; |
520 | } |
521 | |
522 | int cpsw_ale_add_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, |
523 | int flags, u16 vid, int mcast_state) |
524 | { |
525 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
526 | int idx, mask; |
527 | |
528 | idx = cpsw_ale_match_addr(ale, addr, vid: (flags & ALE_VLAN) ? vid : 0); |
529 | if (idx >= 0) |
530 | cpsw_ale_read(ale, idx, ale_entry); |
531 | |
532 | cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid); |
533 | |
534 | cpsw_ale_set_addr(ale_entry, addr); |
535 | cpsw_ale_set_super(ale_entry, value: (flags & ALE_SUPER) ? 1 : 0); |
536 | cpsw_ale_set_mcast_state(ale_entry, value: mcast_state); |
537 | |
538 | mask = cpsw_ale_get_port_mask(ale_entry, |
539 | bits: ale->port_mask_bits); |
540 | port_mask |= mask; |
541 | cpsw_ale_set_port_mask(ale_entry, value: port_mask, |
542 | bits: ale->port_mask_bits); |
543 | |
544 | if (idx < 0) |
545 | idx = cpsw_ale_match_free(ale); |
546 | if (idx < 0) |
547 | idx = cpsw_ale_find_ageable(ale); |
548 | if (idx < 0) |
549 | return -ENOMEM; |
550 | |
551 | cpsw_ale_write(ale, idx, ale_entry); |
552 | return 0; |
553 | } |
554 | |
555 | int cpsw_ale_del_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, |
556 | int flags, u16 vid) |
557 | { |
558 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
559 | int mcast_members = 0; |
560 | int idx; |
561 | |
562 | idx = cpsw_ale_match_addr(ale, addr, vid: (flags & ALE_VLAN) ? vid : 0); |
563 | if (idx < 0) |
564 | return -ENOENT; |
565 | |
566 | cpsw_ale_read(ale, idx, ale_entry); |
567 | |
568 | if (port_mask) { |
569 | mcast_members = cpsw_ale_get_port_mask(ale_entry, |
570 | bits: ale->port_mask_bits); |
571 | mcast_members &= ~port_mask; |
572 | } |
573 | |
574 | if (mcast_members) |
575 | cpsw_ale_set_port_mask(ale_entry, value: mcast_members, |
576 | bits: ale->port_mask_bits); |
577 | else |
578 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); |
579 | |
580 | cpsw_ale_write(ale, idx, ale_entry); |
581 | return 0; |
582 | } |
583 | |
584 | /* ALE NetCP NU switch specific vlan functions */ |
585 | static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry, |
586 | int reg_mcast, int unreg_mcast) |
587 | { |
588 | int idx; |
589 | |
590 | /* Set VLAN registered multicast flood mask */ |
591 | idx = cpsw_ale_vlan_get_fld(ale, ale_entry, |
592 | fld_id: ALE_ENT_VID_REG_MCAST_IDX); |
593 | writel(val: reg_mcast, addr: ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx)); |
594 | |
595 | /* Set VLAN unregistered multicast flood mask */ |
596 | idx = cpsw_ale_vlan_get_fld(ale, ale_entry, |
597 | fld_id: ALE_ENT_VID_UNREG_MCAST_IDX); |
598 | writel(val: unreg_mcast, addr: ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx)); |
599 | } |
600 | |
601 | static void cpsw_ale_set_vlan_untag(struct cpsw_ale *ale, u32 *ale_entry, |
602 | u16 vid, int untag_mask) |
603 | { |
604 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
605 | fld_id: ALE_ENT_VID_FORCE_UNTAGGED_MSK, |
606 | value: untag_mask); |
607 | if (untag_mask & ALE_PORT_HOST) |
608 | bitmap_set(map: ale->p0_untag_vid_mask, start: vid, nbits: 1); |
609 | else |
610 | bitmap_clear(map: ale->p0_untag_vid_mask, start: vid, nbits: 1); |
611 | } |
612 | |
613 | int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port_mask, int untag, |
614 | int reg_mcast, int unreg_mcast) |
615 | { |
616 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
617 | int idx; |
618 | |
619 | idx = cpsw_ale_match_vlan(ale, vid); |
620 | if (idx >= 0) |
621 | cpsw_ale_read(ale, idx, ale_entry); |
622 | |
623 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); |
624 | cpsw_ale_set_vlan_id(ale_entry, value: vid); |
625 | cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag_mask: untag); |
626 | |
627 | if (!ale->params.nu_switch_ale) { |
628 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
629 | fld_id: ALE_ENT_VID_REG_MCAST_MSK, value: reg_mcast); |
630 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
631 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK, value: unreg_mcast); |
632 | } else { |
633 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
634 | fld_id: ALE_ENT_VID_UNREG_MCAST_IDX, |
635 | NU_VLAN_UNREG_MCAST_IDX); |
636 | cpsw_ale_set_vlan_mcast(ale, ale_entry, reg_mcast, unreg_mcast); |
637 | } |
638 | |
639 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
640 | fld_id: ALE_ENT_VID_MEMBER_LIST, value: port_mask); |
641 | |
642 | if (idx < 0) |
643 | idx = cpsw_ale_match_free(ale); |
644 | if (idx < 0) |
645 | idx = cpsw_ale_find_ageable(ale); |
646 | if (idx < 0) |
647 | return -ENOMEM; |
648 | |
649 | cpsw_ale_write(ale, idx, ale_entry); |
650 | return 0; |
651 | } |
652 | |
653 | static void cpsw_ale_vlan_del_modify_int(struct cpsw_ale *ale, u32 *ale_entry, |
654 | u16 vid, int port_mask) |
655 | { |
656 | int reg_mcast, unreg_mcast; |
657 | int members, untag; |
658 | |
659 | members = cpsw_ale_vlan_get_fld(ale, ale_entry, |
660 | fld_id: ALE_ENT_VID_MEMBER_LIST); |
661 | members &= ~port_mask; |
662 | if (!members) { |
663 | cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag_mask: 0); |
664 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); |
665 | return; |
666 | } |
667 | |
668 | untag = cpsw_ale_vlan_get_fld(ale, ale_entry, |
669 | fld_id: ALE_ENT_VID_FORCE_UNTAGGED_MSK); |
670 | reg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry, |
671 | fld_id: ALE_ENT_VID_REG_MCAST_MSK); |
672 | unreg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry, |
673 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK); |
674 | untag &= members; |
675 | reg_mcast &= members; |
676 | unreg_mcast &= members; |
677 | |
678 | cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag_mask: untag); |
679 | |
680 | if (!ale->params.nu_switch_ale) { |
681 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
682 | fld_id: ALE_ENT_VID_REG_MCAST_MSK, value: reg_mcast); |
683 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
684 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK, value: unreg_mcast); |
685 | } else { |
686 | cpsw_ale_set_vlan_mcast(ale, ale_entry, reg_mcast, |
687 | unreg_mcast); |
688 | } |
689 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
690 | fld_id: ALE_ENT_VID_MEMBER_LIST, value: members); |
691 | } |
692 | |
693 | int cpsw_ale_vlan_del_modify(struct cpsw_ale *ale, u16 vid, int port_mask) |
694 | { |
695 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
696 | int idx; |
697 | |
698 | idx = cpsw_ale_match_vlan(ale, vid); |
699 | if (idx < 0) |
700 | return -ENOENT; |
701 | |
702 | cpsw_ale_read(ale, idx, ale_entry); |
703 | |
704 | cpsw_ale_vlan_del_modify_int(ale, ale_entry, vid, port_mask); |
705 | cpsw_ale_write(ale, idx, ale_entry); |
706 | |
707 | return 0; |
708 | } |
709 | |
710 | int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) |
711 | { |
712 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
713 | int members, idx; |
714 | |
715 | idx = cpsw_ale_match_vlan(ale, vid); |
716 | if (idx < 0) |
717 | return -ENOENT; |
718 | |
719 | cpsw_ale_read(ale, idx, ale_entry); |
720 | |
721 | /* if !port_mask - force remove VLAN (legacy). |
722 | * Check if there are other VLAN members ports |
723 | * if no - remove VLAN. |
724 | * if yes it means same VLAN was added to >1 port in multi port mode, so |
725 | * remove port_mask ports from VLAN ALE entry excluding Host port. |
726 | */ |
727 | members = cpsw_ale_vlan_get_fld(ale, ale_entry, fld_id: ALE_ENT_VID_MEMBER_LIST); |
728 | members &= ~port_mask; |
729 | |
730 | if (!port_mask || !members) { |
731 | /* last port or force remove - remove VLAN */ |
732 | cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag_mask: 0); |
733 | cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); |
734 | } else { |
735 | port_mask &= ~ALE_PORT_HOST; |
736 | cpsw_ale_vlan_del_modify_int(ale, ale_entry, vid, port_mask); |
737 | } |
738 | |
739 | cpsw_ale_write(ale, idx, ale_entry); |
740 | |
741 | return 0; |
742 | } |
743 | |
744 | int cpsw_ale_vlan_add_modify(struct cpsw_ale *ale, u16 vid, int port_mask, |
745 | int untag_mask, int reg_mask, int unreg_mask) |
746 | { |
747 | u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; |
748 | int reg_mcast_members, unreg_mcast_members; |
749 | int vlan_members, untag_members; |
750 | int idx, ret = 0; |
751 | |
752 | idx = cpsw_ale_match_vlan(ale, vid); |
753 | if (idx >= 0) |
754 | cpsw_ale_read(ale, idx, ale_entry); |
755 | |
756 | vlan_members = cpsw_ale_vlan_get_fld(ale, ale_entry, |
757 | fld_id: ALE_ENT_VID_MEMBER_LIST); |
758 | reg_mcast_members = cpsw_ale_vlan_get_fld(ale, ale_entry, |
759 | fld_id: ALE_ENT_VID_REG_MCAST_MSK); |
760 | unreg_mcast_members = |
761 | cpsw_ale_vlan_get_fld(ale, ale_entry, |
762 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK); |
763 | untag_members = cpsw_ale_vlan_get_fld(ale, ale_entry, |
764 | fld_id: ALE_ENT_VID_FORCE_UNTAGGED_MSK); |
765 | |
766 | vlan_members |= port_mask; |
767 | untag_members = (untag_members & ~port_mask) | untag_mask; |
768 | reg_mcast_members = (reg_mcast_members & ~port_mask) | reg_mask; |
769 | unreg_mcast_members = (unreg_mcast_members & ~port_mask) | unreg_mask; |
770 | |
771 | ret = cpsw_ale_add_vlan(ale, vid, port_mask: vlan_members, untag: untag_members, |
772 | reg_mcast: reg_mcast_members, unreg_mcast: unreg_mcast_members); |
773 | if (ret) { |
774 | dev_err(ale->params.dev, "Unable to add vlan\n" ); |
775 | return ret; |
776 | } |
777 | dev_dbg(ale->params.dev, "port mask 0x%x untag 0x%x\n" , vlan_members, |
778 | untag_mask); |
779 | |
780 | return ret; |
781 | } |
782 | |
783 | void cpsw_ale_set_unreg_mcast(struct cpsw_ale *ale, int unreg_mcast_mask, |
784 | bool add) |
785 | { |
786 | u32 ale_entry[ALE_ENTRY_WORDS]; |
787 | int unreg_members = 0; |
788 | int type, idx; |
789 | |
790 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
791 | cpsw_ale_read(ale, idx, ale_entry); |
792 | type = cpsw_ale_get_entry_type(ale_entry); |
793 | if (type != ALE_TYPE_VLAN) |
794 | continue; |
795 | |
796 | unreg_members = |
797 | cpsw_ale_vlan_get_fld(ale, ale_entry, |
798 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK); |
799 | if (add) |
800 | unreg_members |= unreg_mcast_mask; |
801 | else |
802 | unreg_members &= ~unreg_mcast_mask; |
803 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
804 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK, |
805 | value: unreg_members); |
806 | cpsw_ale_write(ale, idx, ale_entry); |
807 | } |
808 | } |
809 | |
810 | static void cpsw_ale_vlan_set_unreg_mcast(struct cpsw_ale *ale, u32 *ale_entry, |
811 | int allmulti) |
812 | { |
813 | int unreg_mcast; |
814 | |
815 | unreg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry, |
816 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK); |
817 | if (allmulti) |
818 | unreg_mcast |= ALE_PORT_HOST; |
819 | else |
820 | unreg_mcast &= ~ALE_PORT_HOST; |
821 | |
822 | cpsw_ale_vlan_set_fld(ale, ale_entry, |
823 | fld_id: ALE_ENT_VID_UNREG_MCAST_MSK, value: unreg_mcast); |
824 | } |
825 | |
826 | static void |
827 | cpsw_ale_vlan_set_unreg_mcast_idx(struct cpsw_ale *ale, u32 *ale_entry, |
828 | int allmulti) |
829 | { |
830 | int unreg_mcast; |
831 | int idx; |
832 | |
833 | idx = cpsw_ale_vlan_get_fld(ale, ale_entry, |
834 | fld_id: ALE_ENT_VID_UNREG_MCAST_IDX); |
835 | |
836 | unreg_mcast = readl(addr: ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx)); |
837 | |
838 | if (allmulti) |
839 | unreg_mcast |= ALE_PORT_HOST; |
840 | else |
841 | unreg_mcast &= ~ALE_PORT_HOST; |
842 | |
843 | writel(val: unreg_mcast, addr: ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx)); |
844 | } |
845 | |
846 | void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti, int port) |
847 | { |
848 | u32 ale_entry[ALE_ENTRY_WORDS]; |
849 | int type, idx; |
850 | |
851 | for (idx = 0; idx < ale->params.ale_entries; idx++) { |
852 | int vlan_members; |
853 | |
854 | cpsw_ale_read(ale, idx, ale_entry); |
855 | type = cpsw_ale_get_entry_type(ale_entry); |
856 | if (type != ALE_TYPE_VLAN) |
857 | continue; |
858 | |
859 | vlan_members = cpsw_ale_vlan_get_fld(ale, ale_entry, |
860 | fld_id: ALE_ENT_VID_MEMBER_LIST); |
861 | |
862 | if (port != -1 && !(vlan_members & BIT(port))) |
863 | continue; |
864 | |
865 | if (!ale->params.nu_switch_ale) |
866 | cpsw_ale_vlan_set_unreg_mcast(ale, ale_entry, allmulti); |
867 | else |
868 | cpsw_ale_vlan_set_unreg_mcast_idx(ale, ale_entry, |
869 | allmulti); |
870 | |
871 | cpsw_ale_write(ale, idx, ale_entry); |
872 | } |
873 | } |
874 | |
875 | struct ale_control_info { |
876 | const char *name; |
877 | int offset, port_offset; |
878 | int shift, port_shift; |
879 | int bits; |
880 | }; |
881 | |
882 | static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = { |
883 | [ALE_ENABLE] = { |
884 | .name = "enable" , |
885 | .offset = ALE_CONTROL, |
886 | .port_offset = 0, |
887 | .shift = 31, |
888 | .port_shift = 0, |
889 | .bits = 1, |
890 | }, |
891 | [ALE_CLEAR] = { |
892 | .name = "clear" , |
893 | .offset = ALE_CONTROL, |
894 | .port_offset = 0, |
895 | .shift = 30, |
896 | .port_shift = 0, |
897 | .bits = 1, |
898 | }, |
899 | [ALE_AGEOUT] = { |
900 | .name = "ageout" , |
901 | .offset = ALE_CONTROL, |
902 | .port_offset = 0, |
903 | .shift = 29, |
904 | .port_shift = 0, |
905 | .bits = 1, |
906 | }, |
907 | [ALE_P0_UNI_FLOOD] = { |
908 | .name = "port0_unicast_flood" , |
909 | .offset = ALE_CONTROL, |
910 | .port_offset = 0, |
911 | .shift = 8, |
912 | .port_shift = 0, |
913 | .bits = 1, |
914 | }, |
915 | [ALE_VLAN_NOLEARN] = { |
916 | .name = "vlan_nolearn" , |
917 | .offset = ALE_CONTROL, |
918 | .port_offset = 0, |
919 | .shift = 7, |
920 | .port_shift = 0, |
921 | .bits = 1, |
922 | }, |
923 | [ALE_NO_PORT_VLAN] = { |
924 | .name = "no_port_vlan" , |
925 | .offset = ALE_CONTROL, |
926 | .port_offset = 0, |
927 | .shift = 6, |
928 | .port_shift = 0, |
929 | .bits = 1, |
930 | }, |
931 | [ALE_OUI_DENY] = { |
932 | .name = "oui_deny" , |
933 | .offset = ALE_CONTROL, |
934 | .port_offset = 0, |
935 | .shift = 5, |
936 | .port_shift = 0, |
937 | .bits = 1, |
938 | }, |
939 | [ALE_BYPASS] = { |
940 | .name = "bypass" , |
941 | .offset = ALE_CONTROL, |
942 | .port_offset = 0, |
943 | .shift = 4, |
944 | .port_shift = 0, |
945 | .bits = 1, |
946 | }, |
947 | [ALE_RATE_LIMIT_TX] = { |
948 | .name = "rate_limit_tx" , |
949 | .offset = ALE_CONTROL, |
950 | .port_offset = 0, |
951 | .shift = 3, |
952 | .port_shift = 0, |
953 | .bits = 1, |
954 | }, |
955 | [ALE_VLAN_AWARE] = { |
956 | .name = "vlan_aware" , |
957 | .offset = ALE_CONTROL, |
958 | .port_offset = 0, |
959 | .shift = 2, |
960 | .port_shift = 0, |
961 | .bits = 1, |
962 | }, |
963 | [ALE_AUTH_ENABLE] = { |
964 | .name = "auth_enable" , |
965 | .offset = ALE_CONTROL, |
966 | .port_offset = 0, |
967 | .shift = 1, |
968 | .port_shift = 0, |
969 | .bits = 1, |
970 | }, |
971 | [ALE_RATE_LIMIT] = { |
972 | .name = "rate_limit" , |
973 | .offset = ALE_CONTROL, |
974 | .port_offset = 0, |
975 | .shift = 0, |
976 | .port_shift = 0, |
977 | .bits = 1, |
978 | }, |
979 | [ALE_PORT_STATE] = { |
980 | .name = "port_state" , |
981 | .offset = ALE_PORTCTL, |
982 | .port_offset = 4, |
983 | .shift = 0, |
984 | .port_shift = 0, |
985 | .bits = 2, |
986 | }, |
987 | [ALE_PORT_DROP_UNTAGGED] = { |
988 | .name = "drop_untagged" , |
989 | .offset = ALE_PORTCTL, |
990 | .port_offset = 4, |
991 | .shift = 2, |
992 | .port_shift = 0, |
993 | .bits = 1, |
994 | }, |
995 | [ALE_PORT_DROP_UNKNOWN_VLAN] = { |
996 | .name = "drop_unknown" , |
997 | .offset = ALE_PORTCTL, |
998 | .port_offset = 4, |
999 | .shift = 3, |
1000 | .port_shift = 0, |
1001 | .bits = 1, |
1002 | }, |
1003 | [ALE_PORT_NOLEARN] = { |
1004 | .name = "nolearn" , |
1005 | .offset = ALE_PORTCTL, |
1006 | .port_offset = 4, |
1007 | .shift = 4, |
1008 | .port_shift = 0, |
1009 | .bits = 1, |
1010 | }, |
1011 | [ALE_PORT_NO_SA_UPDATE] = { |
1012 | .name = "no_source_update" , |
1013 | .offset = ALE_PORTCTL, |
1014 | .port_offset = 4, |
1015 | .shift = 5, |
1016 | .port_shift = 0, |
1017 | .bits = 1, |
1018 | }, |
1019 | [ALE_PORT_MACONLY] = { |
1020 | .name = "mac_only_port_mode" , |
1021 | .offset = ALE_PORTCTL, |
1022 | .port_offset = 4, |
1023 | .shift = 11, |
1024 | .port_shift = 0, |
1025 | .bits = 1, |
1026 | }, |
1027 | [ALE_PORT_MACONLY_CAF] = { |
1028 | .name = "mac_only_port_caf" , |
1029 | .offset = ALE_PORTCTL, |
1030 | .port_offset = 4, |
1031 | .shift = 13, |
1032 | .port_shift = 0, |
1033 | .bits = 1, |
1034 | }, |
1035 | [ALE_PORT_MCAST_LIMIT] = { |
1036 | .name = "mcast_limit" , |
1037 | .offset = ALE_PORTCTL, |
1038 | .port_offset = 4, |
1039 | .shift = 16, |
1040 | .port_shift = 0, |
1041 | .bits = 8, |
1042 | }, |
1043 | [ALE_PORT_BCAST_LIMIT] = { |
1044 | .name = "bcast_limit" , |
1045 | .offset = ALE_PORTCTL, |
1046 | .port_offset = 4, |
1047 | .shift = 24, |
1048 | .port_shift = 0, |
1049 | .bits = 8, |
1050 | }, |
1051 | [ALE_PORT_UNKNOWN_VLAN_MEMBER] = { |
1052 | .name = "unknown_vlan_member" , |
1053 | .offset = ALE_UNKNOWNVLAN, |
1054 | .port_offset = 0, |
1055 | .shift = 0, |
1056 | .port_shift = 0, |
1057 | .bits = 6, |
1058 | }, |
1059 | [ALE_PORT_UNKNOWN_MCAST_FLOOD] = { |
1060 | .name = "unknown_mcast_flood" , |
1061 | .offset = ALE_UNKNOWNVLAN, |
1062 | .port_offset = 0, |
1063 | .shift = 8, |
1064 | .port_shift = 0, |
1065 | .bits = 6, |
1066 | }, |
1067 | [ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = { |
1068 | .name = "unknown_reg_flood" , |
1069 | .offset = ALE_UNKNOWNVLAN, |
1070 | .port_offset = 0, |
1071 | .shift = 16, |
1072 | .port_shift = 0, |
1073 | .bits = 6, |
1074 | }, |
1075 | [ALE_PORT_UNTAGGED_EGRESS] = { |
1076 | .name = "untagged_egress" , |
1077 | .offset = ALE_UNKNOWNVLAN, |
1078 | .port_offset = 0, |
1079 | .shift = 24, |
1080 | .port_shift = 0, |
1081 | .bits = 6, |
1082 | }, |
1083 | [ALE_DEFAULT_THREAD_ID] = { |
1084 | .name = "default_thread_id" , |
1085 | .offset = AM65_CPSW_ALE_THREAD_DEF_REG, |
1086 | .port_offset = 0, |
1087 | .shift = 0, |
1088 | .port_shift = 0, |
1089 | .bits = 6, |
1090 | }, |
1091 | [ALE_DEFAULT_THREAD_ENABLE] = { |
1092 | .name = "default_thread_id_enable" , |
1093 | .offset = AM65_CPSW_ALE_THREAD_DEF_REG, |
1094 | .port_offset = 0, |
1095 | .shift = 15, |
1096 | .port_shift = 0, |
1097 | .bits = 1, |
1098 | }, |
1099 | }; |
1100 | |
1101 | int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, |
1102 | int value) |
1103 | { |
1104 | const struct ale_control_info *info; |
1105 | int offset, shift; |
1106 | u32 tmp, mask; |
1107 | |
1108 | if (control < 0 || control >= ARRAY_SIZE(ale_controls)) |
1109 | return -EINVAL; |
1110 | |
1111 | info = &ale_controls[control]; |
1112 | if (info->port_offset == 0 && info->port_shift == 0) |
1113 | port = 0; /* global, port is a dont care */ |
1114 | |
1115 | if (port < 0 || port >= ale->params.ale_ports) |
1116 | return -EINVAL; |
1117 | |
1118 | mask = BITMASK(info->bits); |
1119 | if (value & ~mask) |
1120 | return -EINVAL; |
1121 | |
1122 | offset = info->offset + (port * info->port_offset); |
1123 | shift = info->shift + (port * info->port_shift); |
1124 | |
1125 | tmp = readl_relaxed(ale->params.ale_regs + offset); |
1126 | tmp = (tmp & ~(mask << shift)) | (value << shift); |
1127 | writel_relaxed(tmp, ale->params.ale_regs + offset); |
1128 | |
1129 | return 0; |
1130 | } |
1131 | |
1132 | int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) |
1133 | { |
1134 | const struct ale_control_info *info; |
1135 | int offset, shift; |
1136 | u32 tmp; |
1137 | |
1138 | if (control < 0 || control >= ARRAY_SIZE(ale_controls)) |
1139 | return -EINVAL; |
1140 | |
1141 | info = &ale_controls[control]; |
1142 | if (info->port_offset == 0 && info->port_shift == 0) |
1143 | port = 0; /* global, port is a dont care */ |
1144 | |
1145 | if (port < 0 || port >= ale->params.ale_ports) |
1146 | return -EINVAL; |
1147 | |
1148 | offset = info->offset + (port * info->port_offset); |
1149 | shift = info->shift + (port * info->port_shift); |
1150 | |
1151 | tmp = readl_relaxed(ale->params.ale_regs + offset) >> shift; |
1152 | return tmp & BITMASK(info->bits); |
1153 | } |
1154 | |
1155 | int cpsw_ale_rx_ratelimit_mc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps) |
1156 | |
1157 | { |
1158 | int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS; |
1159 | u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS; |
1160 | |
1161 | if (ratelimit_pps && !val) { |
1162 | dev_err(ale->params.dev, "ALE MC port:%d ratelimit min value 1000pps\n" , port); |
1163 | return -EINVAL; |
1164 | } |
1165 | |
1166 | if (remainder) |
1167 | dev_info(ale->params.dev, "ALE port:%d MC ratelimit set to %dpps (requested %d)\n" , |
1168 | port, ratelimit_pps - remainder, ratelimit_pps); |
1169 | |
1170 | cpsw_ale_control_set(ale, port, control: ALE_PORT_MCAST_LIMIT, value: val); |
1171 | |
1172 | dev_dbg(ale->params.dev, "ALE port:%d MC ratelimit set %d\n" , |
1173 | port, val * ALE_RATE_LIMIT_MIN_PPS); |
1174 | return 0; |
1175 | } |
1176 | |
1177 | int cpsw_ale_rx_ratelimit_bc(struct cpsw_ale *ale, int port, unsigned int ratelimit_pps) |
1178 | |
1179 | { |
1180 | int val = ratelimit_pps / ALE_RATE_LIMIT_MIN_PPS; |
1181 | u32 remainder = ratelimit_pps % ALE_RATE_LIMIT_MIN_PPS; |
1182 | |
1183 | if (ratelimit_pps && !val) { |
1184 | dev_err(ale->params.dev, "ALE port:%d BC ratelimit min value 1000pps\n" , port); |
1185 | return -EINVAL; |
1186 | } |
1187 | |
1188 | if (remainder) |
1189 | dev_info(ale->params.dev, "ALE port:%d BC ratelimit set to %dpps (requested %d)\n" , |
1190 | port, ratelimit_pps - remainder, ratelimit_pps); |
1191 | |
1192 | cpsw_ale_control_set(ale, port, control: ALE_PORT_BCAST_LIMIT, value: val); |
1193 | |
1194 | dev_dbg(ale->params.dev, "ALE port:%d BC ratelimit set %d\n" , |
1195 | port, val * ALE_RATE_LIMIT_MIN_PPS); |
1196 | return 0; |
1197 | } |
1198 | |
1199 | static void cpsw_ale_timer(struct timer_list *t) |
1200 | { |
1201 | struct cpsw_ale *ale = from_timer(ale, t, timer); |
1202 | |
1203 | cpsw_ale_control_set(ale, port: 0, control: ALE_AGEOUT, value: 1); |
1204 | |
1205 | if (ale->ageout) { |
1206 | ale->timer.expires = jiffies + ale->ageout; |
1207 | add_timer(timer: &ale->timer); |
1208 | } |
1209 | } |
1210 | |
1211 | static void cpsw_ale_hw_aging_timer_start(struct cpsw_ale *ale) |
1212 | { |
1213 | u32 aging_timer; |
1214 | |
1215 | aging_timer = ale->params.bus_freq / 1000000; |
1216 | aging_timer *= ale->params.ale_ageout; |
1217 | |
1218 | if (aging_timer & ~ALE_AGING_TIMER_MASK) { |
1219 | aging_timer = ALE_AGING_TIMER_MASK; |
1220 | dev_warn(ale->params.dev, |
1221 | "ALE aging timer overflow, set to max\n" ); |
1222 | } |
1223 | |
1224 | writel(val: aging_timer, addr: ale->params.ale_regs + ALE_AGING_TIMER); |
1225 | } |
1226 | |
1227 | static void cpsw_ale_hw_aging_timer_stop(struct cpsw_ale *ale) |
1228 | { |
1229 | writel(val: 0, addr: ale->params.ale_regs + ALE_AGING_TIMER); |
1230 | } |
1231 | |
1232 | static void cpsw_ale_aging_start(struct cpsw_ale *ale) |
1233 | { |
1234 | if (!ale->params.ale_ageout) |
1235 | return; |
1236 | |
1237 | if (ale->features & CPSW_ALE_F_HW_AUTOAGING) { |
1238 | cpsw_ale_hw_aging_timer_start(ale); |
1239 | return; |
1240 | } |
1241 | |
1242 | timer_setup(&ale->timer, cpsw_ale_timer, 0); |
1243 | ale->timer.expires = jiffies + ale->ageout; |
1244 | add_timer(timer: &ale->timer); |
1245 | } |
1246 | |
1247 | static void cpsw_ale_aging_stop(struct cpsw_ale *ale) |
1248 | { |
1249 | if (!ale->params.ale_ageout) |
1250 | return; |
1251 | |
1252 | if (ale->features & CPSW_ALE_F_HW_AUTOAGING) { |
1253 | cpsw_ale_hw_aging_timer_stop(ale); |
1254 | return; |
1255 | } |
1256 | |
1257 | del_timer_sync(timer: &ale->timer); |
1258 | } |
1259 | |
1260 | void cpsw_ale_start(struct cpsw_ale *ale) |
1261 | { |
1262 | unsigned long ale_prescale; |
1263 | |
1264 | /* configure Broadcast and Multicast Rate Limit |
1265 | * number_of_packets = (Fclk / ALE_PRESCALE) * port.BCAST/MCAST_LIMIT |
1266 | * ALE_PRESCALE width is 19bit and min value 0x10 |
1267 | * port.BCAST/MCAST_LIMIT is 8bit |
1268 | * |
1269 | * For multi port configuration support the ALE_PRESCALE is configured to 1ms interval, |
1270 | * which allows to configure port.BCAST/MCAST_LIMIT per port and achieve: |
1271 | * min number_of_packets = 1000 when port.BCAST/MCAST_LIMIT = 1 |
1272 | * max number_of_packets = 1000 * 255 = 255000 when port.BCAST/MCAST_LIMIT = 0xFF |
1273 | */ |
1274 | ale_prescale = ale->params.bus_freq / ALE_RATE_LIMIT_MIN_PPS; |
1275 | writel(val: (u32)ale_prescale, addr: ale->params.ale_regs + ALE_PRESCALE); |
1276 | |
1277 | /* Allow MC/BC rate limiting globally. |
1278 | * The actual Rate Limit cfg enabled per-port by port.BCAST/MCAST_LIMIT |
1279 | */ |
1280 | cpsw_ale_control_set(ale, port: 0, control: ALE_RATE_LIMIT, value: 1); |
1281 | |
1282 | cpsw_ale_control_set(ale, port: 0, control: ALE_ENABLE, value: 1); |
1283 | cpsw_ale_control_set(ale, port: 0, control: ALE_CLEAR, value: 1); |
1284 | |
1285 | cpsw_ale_aging_start(ale); |
1286 | } |
1287 | |
1288 | void cpsw_ale_stop(struct cpsw_ale *ale) |
1289 | { |
1290 | cpsw_ale_aging_stop(ale); |
1291 | cpsw_ale_control_set(ale, port: 0, control: ALE_CLEAR, value: 1); |
1292 | cpsw_ale_control_set(ale, port: 0, control: ALE_ENABLE, value: 0); |
1293 | } |
1294 | |
1295 | static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = { |
1296 | { |
1297 | /* am3/4/5, dra7. dm814x, 66ak2hk-gbe */ |
1298 | .dev_id = "cpsw" , |
1299 | .tbl_entries = 1024, |
1300 | .major_ver_mask = 0xff, |
1301 | .vlan_entry_tbl = vlan_entry_cpsw, |
1302 | }, |
1303 | { |
1304 | /* 66ak2h_xgbe */ |
1305 | .dev_id = "66ak2h-xgbe" , |
1306 | .tbl_entries = 2048, |
1307 | .major_ver_mask = 0xff, |
1308 | .vlan_entry_tbl = vlan_entry_cpsw, |
1309 | }, |
1310 | { |
1311 | .dev_id = "66ak2el" , |
1312 | .features = CPSW_ALE_F_STATUS_REG, |
1313 | .major_ver_mask = 0x7, |
1314 | .nu_switch_ale = true, |
1315 | .vlan_entry_tbl = vlan_entry_nu, |
1316 | }, |
1317 | { |
1318 | .dev_id = "66ak2g" , |
1319 | .features = CPSW_ALE_F_STATUS_REG, |
1320 | .tbl_entries = 64, |
1321 | .major_ver_mask = 0x7, |
1322 | .nu_switch_ale = true, |
1323 | .vlan_entry_tbl = vlan_entry_nu, |
1324 | }, |
1325 | { |
1326 | .dev_id = "am65x-cpsw2g" , |
1327 | .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING, |
1328 | .tbl_entries = 64, |
1329 | .major_ver_mask = 0x7, |
1330 | .nu_switch_ale = true, |
1331 | .vlan_entry_tbl = vlan_entry_nu, |
1332 | }, |
1333 | { |
1334 | .dev_id = "j721e-cpswxg" , |
1335 | .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING, |
1336 | .major_ver_mask = 0x7, |
1337 | .vlan_entry_tbl = vlan_entry_k3_cpswxg, |
1338 | }, |
1339 | { |
1340 | .dev_id = "am64-cpswxg" , |
1341 | .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING, |
1342 | .major_ver_mask = 0x7, |
1343 | .vlan_entry_tbl = vlan_entry_k3_cpswxg, |
1344 | .tbl_entries = 512, |
1345 | }, |
1346 | { }, |
1347 | }; |
1348 | |
1349 | static const struct |
1350 | cpsw_ale_dev_id *cpsw_ale_match_id(const struct cpsw_ale_dev_id *id, |
1351 | const char *dev_id) |
1352 | { |
1353 | if (!dev_id) |
1354 | return NULL; |
1355 | |
1356 | while (id->dev_id) { |
1357 | if (strcmp(dev_id, id->dev_id) == 0) |
1358 | return id; |
1359 | id++; |
1360 | } |
1361 | return NULL; |
1362 | } |
1363 | |
1364 | struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) |
1365 | { |
1366 | const struct cpsw_ale_dev_id *ale_dev_id; |
1367 | struct cpsw_ale *ale; |
1368 | u32 rev, ale_entries; |
1369 | |
1370 | ale_dev_id = cpsw_ale_match_id(id: cpsw_ale_id_match, dev_id: params->dev_id); |
1371 | if (!ale_dev_id) |
1372 | return ERR_PTR(error: -EINVAL); |
1373 | |
1374 | params->ale_entries = ale_dev_id->tbl_entries; |
1375 | params->major_ver_mask = ale_dev_id->major_ver_mask; |
1376 | params->nu_switch_ale = ale_dev_id->nu_switch_ale; |
1377 | |
1378 | ale = devm_kzalloc(dev: params->dev, size: sizeof(*ale), GFP_KERNEL); |
1379 | if (!ale) |
1380 | return ERR_PTR(error: -ENOMEM); |
1381 | |
1382 | ale->p0_untag_vid_mask = devm_bitmap_zalloc(dev: params->dev, VLAN_N_VID, |
1383 | GFP_KERNEL); |
1384 | if (!ale->p0_untag_vid_mask) |
1385 | return ERR_PTR(error: -ENOMEM); |
1386 | |
1387 | ale->params = *params; |
1388 | ale->ageout = ale->params.ale_ageout * HZ; |
1389 | ale->features = ale_dev_id->features; |
1390 | ale->vlan_entry_tbl = ale_dev_id->vlan_entry_tbl; |
1391 | |
1392 | rev = readl_relaxed(ale->params.ale_regs + ALE_IDVER); |
1393 | ale->version = |
1394 | (ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask) << 8) | |
1395 | ALE_VERSION_MINOR(rev); |
1396 | dev_info(ale->params.dev, "initialized cpsw ale version %d.%d\n" , |
1397 | ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask), |
1398 | ALE_VERSION_MINOR(rev)); |
1399 | |
1400 | if (ale->features & CPSW_ALE_F_STATUS_REG && |
1401 | !ale->params.ale_entries) { |
1402 | ale_entries = |
1403 | readl_relaxed(ale->params.ale_regs + ALE_STATUS) & |
1404 | ALE_STATUS_SIZE_MASK; |
1405 | /* ALE available on newer NetCP switches has introduced |
1406 | * a register, ALE_STATUS, to indicate the size of ALE |
1407 | * table which shows the size as a multiple of 1024 entries. |
1408 | * For these, params.ale_entries will be set to zero. So |
1409 | * read the register and update the value of ale_entries. |
1410 | * return error if ale_entries is zero in ALE_STATUS. |
1411 | */ |
1412 | if (!ale_entries) |
1413 | return ERR_PTR(error: -EINVAL); |
1414 | |
1415 | ale_entries *= ALE_TABLE_SIZE_MULTIPLIER; |
1416 | ale->params.ale_entries = ale_entries; |
1417 | } |
1418 | dev_info(ale->params.dev, |
1419 | "ALE Table size %ld\n" , ale->params.ale_entries); |
1420 | |
1421 | /* set default bits for existing h/w */ |
1422 | ale->port_mask_bits = ale->params.ale_ports; |
1423 | ale->port_num_bits = order_base_2(ale->params.ale_ports); |
1424 | ale->vlan_field_bits = ale->params.ale_ports; |
1425 | |
1426 | /* Set defaults override for ALE on NetCP NU switch and for version |
1427 | * 1R3 |
1428 | */ |
1429 | if (ale->params.nu_switch_ale) { |
1430 | /* Separate registers for unknown vlan configuration. |
1431 | * Also there are N bits, where N is number of ale |
1432 | * ports and shift value should be 0 |
1433 | */ |
1434 | ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].bits = |
1435 | ale->params.ale_ports; |
1436 | ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].offset = |
1437 | ALE_UNKNOWNVLAN_MEMBER; |
1438 | ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].bits = |
1439 | ale->params.ale_ports; |
1440 | ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].shift = 0; |
1441 | ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].offset = |
1442 | ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD; |
1443 | ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].bits = |
1444 | ale->params.ale_ports; |
1445 | ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].shift = 0; |
1446 | ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].offset = |
1447 | ALE_UNKNOWNVLAN_REG_MCAST_FLOOD; |
1448 | ale_controls[ALE_PORT_UNTAGGED_EGRESS].bits = |
1449 | ale->params.ale_ports; |
1450 | ale_controls[ALE_PORT_UNTAGGED_EGRESS].shift = 0; |
1451 | ale_controls[ALE_PORT_UNTAGGED_EGRESS].offset = |
1452 | ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS; |
1453 | } |
1454 | |
1455 | cpsw_ale_control_set(ale, port: 0, control: ALE_CLEAR, value: 1); |
1456 | return ale; |
1457 | } |
1458 | |
1459 | void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) |
1460 | { |
1461 | int i; |
1462 | |
1463 | for (i = 0; i < ale->params.ale_entries; i++) { |
1464 | cpsw_ale_read(ale, idx: i, ale_entry: data); |
1465 | data += ALE_ENTRY_WORDS; |
1466 | } |
1467 | } |
1468 | |
1469 | void cpsw_ale_restore(struct cpsw_ale *ale, u32 *data) |
1470 | { |
1471 | int i; |
1472 | |
1473 | for (i = 0; i < ale->params.ale_entries; i++) { |
1474 | cpsw_ale_write(ale, idx: i, ale_entry: data); |
1475 | data += ALE_ENTRY_WORDS; |
1476 | } |
1477 | } |
1478 | |
1479 | u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale) |
1480 | { |
1481 | return ale ? ale->params.ale_entries : 0; |
1482 | } |
1483 | |