]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/net/ethernet/ti/cpsw_ale.c
net: netcp: ale: update to support unknown vlan controls for NU switch
[mirror_ubuntu-bionic-kernel.git] / drivers / net / ethernet / ti / cpsw_ale.c
CommitLineData
db82173f 1/*
ca47130a 2 * Texas Instruments N-Port Ethernet Switch Address Lookup Engine
db82173f
M
3 *
4 * Copyright (C) 2012 Texas Instruments
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation version 2.
9 *
10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11 * kind, whether express or implied; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15#include <linux/kernel.h>
58c11b5f 16#include <linux/module.h>
db82173f
M
17#include <linux/platform_device.h>
18#include <linux/seq_file.h>
19#include <linux/slab.h>
20#include <linux/err.h>
21#include <linux/io.h>
22#include <linux/stat.h>
23#include <linux/sysfs.h>
5c50a856 24#include <linux/etherdevice.h>
db82173f
M
25
26#include "cpsw_ale.h"
27
28#define BITMASK(bits) (BIT(bits) - 1)
db82173f 29
ca47130a 30#define ALE_VERSION_MAJOR(rev, mask) (((rev) >> 8) & (mask))
db82173f 31#define ALE_VERSION_MINOR(rev) (rev & 0xff)
ca47130a 32#define ALE_VERSION_1R4 0x0104
db82173f
M
33
34/* ALE Registers */
35#define ALE_IDVER 0x00
36#define ALE_CONTROL 0x08
37#define ALE_PRESCALE 0x10
38#define ALE_UNKNOWNVLAN 0x18
39#define ALE_TABLE_CONTROL 0x20
40#define ALE_TABLE 0x34
41#define ALE_PORTCTL 0x40
42
ca47130a
KM
43/* ALE NetCP NU switch specific Registers */
44#define ALE_UNKNOWNVLAN_MEMBER 0x90
45#define ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD 0x94
46#define ALE_UNKNOWNVLAN_REG_MCAST_FLOOD 0x98
47#define ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS 0x9C
48
db82173f
M
49#define ALE_TABLE_WRITE BIT(31)
50
51#define ALE_TYPE_FREE 0
52#define ALE_TYPE_ADDR 1
53#define ALE_TYPE_VLAN 2
54#define ALE_TYPE_VLAN_ADDR 3
55
56#define ALE_UCAST_PERSISTANT 0
57#define ALE_UCAST_UNTOUCHED 1
58#define ALE_UCAST_OUI 2
59#define ALE_UCAST_TOUCHED 3
60
61static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
62{
63 int idx;
64
65 idx = start / 32;
66 start -= idx * 32;
67 idx = 2 - idx; /* flip */
68 return (ale_entry[idx] >> start) & BITMASK(bits);
69}
70
71static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
72 u32 value)
73{
74 int idx;
75
76 value &= BITMASK(bits);
77 idx = start / 32;
78 start -= idx * 32;
79 idx = 2 - idx; /* flip */
80 ale_entry[idx] &= ~(BITMASK(bits) << start);
81 ale_entry[idx] |= (value << start);
82}
83
84#define DEFINE_ALE_FIELD(name, start, bits) \
85static inline int cpsw_ale_get_##name(u32 *ale_entry) \
86{ \
87 return cpsw_ale_get_field(ale_entry, start, bits); \
88} \
89static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \
90{ \
91 cpsw_ale_set_field(ale_entry, start, bits, value); \
92}
93
94DEFINE_ALE_FIELD(entry_type, 60, 2)
95DEFINE_ALE_FIELD(vlan_id, 48, 12)
96DEFINE_ALE_FIELD(mcast_state, 62, 2)
97DEFINE_ALE_FIELD(port_mask, 66, 3)
98DEFINE_ALE_FIELD(super, 65, 1)
99DEFINE_ALE_FIELD(ucast_type, 62, 2)
100DEFINE_ALE_FIELD(port_num, 66, 2)
101DEFINE_ALE_FIELD(blocked, 65, 1)
102DEFINE_ALE_FIELD(secure, 64, 1)
103DEFINE_ALE_FIELD(vlan_untag_force, 24, 3)
104DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3)
105DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3)
106DEFINE_ALE_FIELD(vlan_member_list, 0, 3)
107DEFINE_ALE_FIELD(mcast, 40, 1)
108
109/* The MAC address field in the ALE entry cannot be macroized as above */
110static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
111{
112 int i;
113
114 for (i = 0; i < 6; i++)
115 addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
116}
117
118static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
119{
120 int i;
121
122 for (i = 0; i < 6; i++)
123 cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
124}
125
126static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry)
127{
128 int i;
129
130 WARN_ON(idx > ale->params.ale_entries);
131
132 __raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL);
133
134 for (i = 0; i < ALE_ENTRY_WORDS; i++)
135 ale_entry[i] = __raw_readl(ale->params.ale_regs +
136 ALE_TABLE + 4 * i);
137
138 return idx;
139}
140
141static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
142{
143 int i;
144
145 WARN_ON(idx > ale->params.ale_entries);
146
147 for (i = 0; i < ALE_ENTRY_WORDS; i++)
148 __raw_writel(ale_entry[i], ale->params.ale_regs +
149 ALE_TABLE + 4 * i);
150
151 __raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs +
152 ALE_TABLE_CONTROL);
153
154 return idx;
155}
156
58c11b5f 157static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid)
db82173f
M
158{
159 u32 ale_entry[ALE_ENTRY_WORDS];
160 int type, idx;
161
162 for (idx = 0; idx < ale->params.ale_entries; idx++) {
163 u8 entry_addr[6];
164
165 cpsw_ale_read(ale, idx, ale_entry);
166 type = cpsw_ale_get_entry_type(ale_entry);
167 if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
168 continue;
e11b220f
M
169 if (cpsw_ale_get_vlan_id(ale_entry) != vid)
170 continue;
db82173f 171 cpsw_ale_get_addr(ale_entry, entry_addr);
d9f394fe 172 if (ether_addr_equal(entry_addr, addr))
db82173f
M
173 return idx;
174 }
175 return -ENOENT;
176}
177
58c11b5f 178static int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid)
e11b220f
M
179{
180 u32 ale_entry[ALE_ENTRY_WORDS];
181 int type, idx;
182
183 for (idx = 0; idx < ale->params.ale_entries; idx++) {
184 cpsw_ale_read(ale, idx, ale_entry);
185 type = cpsw_ale_get_entry_type(ale_entry);
186 if (type != ALE_TYPE_VLAN)
187 continue;
188 if (cpsw_ale_get_vlan_id(ale_entry) == vid)
189 return idx;
190 }
191 return -ENOENT;
192}
193
db82173f
M
194static int cpsw_ale_match_free(struct cpsw_ale *ale)
195{
196 u32 ale_entry[ALE_ENTRY_WORDS];
197 int type, idx;
198
199 for (idx = 0; idx < ale->params.ale_entries; idx++) {
200 cpsw_ale_read(ale, idx, ale_entry);
201 type = cpsw_ale_get_entry_type(ale_entry);
202 if (type == ALE_TYPE_FREE)
203 return idx;
204 }
205 return -ENOENT;
206}
207
208static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
209{
210 u32 ale_entry[ALE_ENTRY_WORDS];
211 int type, idx;
212
213 for (idx = 0; idx < ale->params.ale_entries; idx++) {
214 cpsw_ale_read(ale, idx, ale_entry);
215 type = cpsw_ale_get_entry_type(ale_entry);
216 if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
217 continue;
218 if (cpsw_ale_get_mcast(ale_entry))
219 continue;
220 type = cpsw_ale_get_ucast_type(ale_entry);
221 if (type != ALE_UCAST_PERSISTANT &&
222 type != ALE_UCAST_OUI)
223 return idx;
224 }
225 return -ENOENT;
226}
227
228static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
229 int port_mask)
230{
231 int mask;
232
233 mask = cpsw_ale_get_port_mask(ale_entry);
234 if ((mask & port_mask) == 0)
235 return; /* ports dont intersect, not interested */
236 mask &= ~port_mask;
237
238 /* free if only remaining port is host port */
5c50a856 239 if (mask)
db82173f 240 cpsw_ale_set_port_mask(ale_entry, mask);
5c50a856
M
241 else
242 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
243}
244
25906052 245int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid)
5c50a856
M
246{
247 u32 ale_entry[ALE_ENTRY_WORDS];
248 int ret, idx;
249
250 for (idx = 0; idx < ale->params.ale_entries; idx++) {
251 cpsw_ale_read(ale, idx, ale_entry);
252 ret = cpsw_ale_get_entry_type(ale_entry);
253 if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
254 continue;
255
25906052
M
256 /* if vid passed is -1 then remove all multicast entry from
257 * the table irrespective of vlan id, if a valid vlan id is
258 * passed then remove only multicast added to that vlan id.
259 * if vlan id doesn't match then move on to next entry.
260 */
261 if (vid != -1 && cpsw_ale_get_vlan_id(ale_entry) != vid)
262 continue;
263
5c50a856
M
264 if (cpsw_ale_get_mcast(ale_entry)) {
265 u8 addr[6];
266
267 cpsw_ale_get_addr(ale_entry, addr);
268 if (!is_broadcast_ether_addr(addr))
269 cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
270 }
271
272 cpsw_ale_write(ale, idx, ale_entry);
273 }
274 return 0;
db82173f 275}
58c11b5f 276EXPORT_SYMBOL_GPL(cpsw_ale_flush_multicast);
db82173f 277
e11b220f
M
278static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry,
279 int flags, u16 vid)
280{
281 if (flags & ALE_VLAN) {
282 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR);
283 cpsw_ale_set_vlan_id(ale_entry, vid);
284 } else {
285 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
286 }
287}
288
289int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
290 int flags, u16 vid)
db82173f
M
291{
292 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
293 int idx;
294
e11b220f
M
295 cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
296
db82173f
M
297 cpsw_ale_set_addr(ale_entry, addr);
298 cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
299 cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
300 cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
301 cpsw_ale_set_port_num(ale_entry, port);
302
e11b220f 303 idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
db82173f
M
304 if (idx < 0)
305 idx = cpsw_ale_match_free(ale);
306 if (idx < 0)
307 idx = cpsw_ale_find_ageable(ale);
308 if (idx < 0)
309 return -ENOMEM;
310
311 cpsw_ale_write(ale, idx, ale_entry);
312 return 0;
313}
58c11b5f 314EXPORT_SYMBOL_GPL(cpsw_ale_add_ucast);
db82173f 315
e11b220f
M
316int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
317 int flags, u16 vid)
db82173f
M
318{
319 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
320 int idx;
321
e11b220f 322 idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
db82173f
M
323 if (idx < 0)
324 return -ENOENT;
325
326 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
327 cpsw_ale_write(ale, idx, ale_entry);
328 return 0;
329}
58c11b5f 330EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast);
db82173f
M
331
332int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
e11b220f 333 int flags, u16 vid, int mcast_state)
db82173f
M
334{
335 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
336 int idx, mask;
337
e11b220f 338 idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
db82173f
M
339 if (idx >= 0)
340 cpsw_ale_read(ale, idx, ale_entry);
341
e11b220f
M
342 cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
343
db82173f 344 cpsw_ale_set_addr(ale_entry, addr);
e11b220f 345 cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
db82173f
M
346 cpsw_ale_set_mcast_state(ale_entry, mcast_state);
347
348 mask = cpsw_ale_get_port_mask(ale_entry);
349 port_mask |= mask;
350 cpsw_ale_set_port_mask(ale_entry, port_mask);
351
352 if (idx < 0)
353 idx = cpsw_ale_match_free(ale);
354 if (idx < 0)
355 idx = cpsw_ale_find_ageable(ale);
356 if (idx < 0)
357 return -ENOMEM;
358
359 cpsw_ale_write(ale, idx, ale_entry);
360 return 0;
361}
58c11b5f 362EXPORT_SYMBOL_GPL(cpsw_ale_add_mcast);
db82173f 363
e11b220f
M
364int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
365 int flags, u16 vid)
db82173f
M
366{
367 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
368 int idx;
369
e11b220f 370 idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
db82173f
M
371 if (idx < 0)
372 return -EINVAL;
373
374 cpsw_ale_read(ale, idx, ale_entry);
375
376 if (port_mask)
377 cpsw_ale_set_port_mask(ale_entry, port_mask);
378 else
379 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
380
381 cpsw_ale_write(ale, idx, ale_entry);
382 return 0;
383}
58c11b5f 384EXPORT_SYMBOL_GPL(cpsw_ale_del_mcast);
db82173f 385
e11b220f
M
386int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
387 int reg_mcast, int unreg_mcast)
388{
389 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
390 int idx;
391
392 idx = cpsw_ale_match_vlan(ale, vid);
393 if (idx >= 0)
394 cpsw_ale_read(ale, idx, ale_entry);
395
396 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
397 cpsw_ale_set_vlan_id(ale_entry, vid);
398
399 cpsw_ale_set_vlan_untag_force(ale_entry, untag);
400 cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
401 cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
402 cpsw_ale_set_vlan_member_list(ale_entry, port);
403
404 if (idx < 0)
405 idx = cpsw_ale_match_free(ale);
406 if (idx < 0)
407 idx = cpsw_ale_find_ageable(ale);
408 if (idx < 0)
409 return -ENOMEM;
410
411 cpsw_ale_write(ale, idx, ale_entry);
412 return 0;
413}
58c11b5f 414EXPORT_SYMBOL_GPL(cpsw_ale_add_vlan);
e11b220f
M
415
416int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
417{
418 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
419 int idx;
420
421 idx = cpsw_ale_match_vlan(ale, vid);
422 if (idx < 0)
423 return -ENOENT;
424
425 cpsw_ale_read(ale, idx, ale_entry);
426
427 if (port_mask)
428 cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
429 else
430 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
431
432 cpsw_ale_write(ale, idx, ale_entry);
433 return 0;
434}
58c11b5f 435EXPORT_SYMBOL_GPL(cpsw_ale_del_vlan);
e11b220f 436
1e5c4bc4
LS
437void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
438{
439 u32 ale_entry[ALE_ENTRY_WORDS];
440 int type, idx;
441 int unreg_mcast = 0;
442
443 /* Only bother doing the work if the setting is actually changing */
444 if (ale->allmulti == allmulti)
445 return;
446
447 /* Remember the new setting to check against next time */
448 ale->allmulti = allmulti;
449
450 for (idx = 0; idx < ale->params.ale_entries; idx++) {
451 cpsw_ale_read(ale, idx, ale_entry);
452 type = cpsw_ale_get_entry_type(ale_entry);
453 if (type != ALE_TYPE_VLAN)
454 continue;
455
456 unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
457 if (allmulti)
458 unreg_mcast |= 1;
459 else
460 unreg_mcast &= ~1;
461 cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
462 cpsw_ale_write(ale, idx, ale_entry);
463 }
464}
58c11b5f 465EXPORT_SYMBOL_GPL(cpsw_ale_set_allmulti);
1e5c4bc4 466
db82173f
M
467struct ale_control_info {
468 const char *name;
469 int offset, port_offset;
470 int shift, port_shift;
471 int bits;
472};
473
ca47130a 474static struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
db82173f
M
475 [ALE_ENABLE] = {
476 .name = "enable",
477 .offset = ALE_CONTROL,
478 .port_offset = 0,
479 .shift = 31,
480 .port_shift = 0,
481 .bits = 1,
482 },
483 [ALE_CLEAR] = {
484 .name = "clear",
485 .offset = ALE_CONTROL,
486 .port_offset = 0,
487 .shift = 30,
488 .port_shift = 0,
489 .bits = 1,
490 },
491 [ALE_AGEOUT] = {
492 .name = "ageout",
493 .offset = ALE_CONTROL,
494 .port_offset = 0,
495 .shift = 29,
496 .port_shift = 0,
497 .bits = 1,
498 },
0cd8f9cc
M
499 [ALE_P0_UNI_FLOOD] = {
500 .name = "port0_unicast_flood",
501 .offset = ALE_CONTROL,
502 .port_offset = 0,
503 .shift = 8,
504 .port_shift = 0,
505 .bits = 1,
506 },
db82173f
M
507 [ALE_VLAN_NOLEARN] = {
508 .name = "vlan_nolearn",
509 .offset = ALE_CONTROL,
510 .port_offset = 0,
511 .shift = 7,
512 .port_shift = 0,
513 .bits = 1,
514 },
515 [ALE_NO_PORT_VLAN] = {
516 .name = "no_port_vlan",
517 .offset = ALE_CONTROL,
518 .port_offset = 0,
519 .shift = 6,
520 .port_shift = 0,
521 .bits = 1,
522 },
523 [ALE_OUI_DENY] = {
524 .name = "oui_deny",
525 .offset = ALE_CONTROL,
526 .port_offset = 0,
527 .shift = 5,
528 .port_shift = 0,
529 .bits = 1,
530 },
531 [ALE_BYPASS] = {
532 .name = "bypass",
533 .offset = ALE_CONTROL,
534 .port_offset = 0,
535 .shift = 4,
536 .port_shift = 0,
537 .bits = 1,
538 },
539 [ALE_RATE_LIMIT_TX] = {
540 .name = "rate_limit_tx",
541 .offset = ALE_CONTROL,
542 .port_offset = 0,
543 .shift = 3,
544 .port_shift = 0,
545 .bits = 1,
546 },
547 [ALE_VLAN_AWARE] = {
548 .name = "vlan_aware",
549 .offset = ALE_CONTROL,
550 .port_offset = 0,
551 .shift = 2,
552 .port_shift = 0,
553 .bits = 1,
554 },
555 [ALE_AUTH_ENABLE] = {
556 .name = "auth_enable",
557 .offset = ALE_CONTROL,
558 .port_offset = 0,
559 .shift = 1,
560 .port_shift = 0,
561 .bits = 1,
562 },
563 [ALE_RATE_LIMIT] = {
564 .name = "rate_limit",
565 .offset = ALE_CONTROL,
566 .port_offset = 0,
567 .shift = 0,
568 .port_shift = 0,
569 .bits = 1,
570 },
571 [ALE_PORT_STATE] = {
572 .name = "port_state",
573 .offset = ALE_PORTCTL,
574 .port_offset = 4,
575 .shift = 0,
576 .port_shift = 0,
577 .bits = 2,
578 },
579 [ALE_PORT_DROP_UNTAGGED] = {
580 .name = "drop_untagged",
581 .offset = ALE_PORTCTL,
582 .port_offset = 4,
583 .shift = 2,
584 .port_shift = 0,
585 .bits = 1,
586 },
587 [ALE_PORT_DROP_UNKNOWN_VLAN] = {
588 .name = "drop_unknown",
589 .offset = ALE_PORTCTL,
590 .port_offset = 4,
591 .shift = 3,
592 .port_shift = 0,
593 .bits = 1,
594 },
595 [ALE_PORT_NOLEARN] = {
596 .name = "nolearn",
597 .offset = ALE_PORTCTL,
598 .port_offset = 4,
599 .shift = 4,
600 .port_shift = 0,
601 .bits = 1,
602 },
0cd8f9cc
M
603 [ALE_PORT_NO_SA_UPDATE] = {
604 .name = "no_source_update",
605 .offset = ALE_PORTCTL,
606 .port_offset = 4,
607 .shift = 5,
608 .port_shift = 0,
609 .bits = 1,
610 },
db82173f
M
611 [ALE_PORT_MCAST_LIMIT] = {
612 .name = "mcast_limit",
613 .offset = ALE_PORTCTL,
614 .port_offset = 4,
615 .shift = 16,
616 .port_shift = 0,
617 .bits = 8,
618 },
619 [ALE_PORT_BCAST_LIMIT] = {
620 .name = "bcast_limit",
621 .offset = ALE_PORTCTL,
622 .port_offset = 4,
623 .shift = 24,
624 .port_shift = 0,
625 .bits = 8,
626 },
627 [ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
628 .name = "unknown_vlan_member",
629 .offset = ALE_UNKNOWNVLAN,
630 .port_offset = 0,
631 .shift = 0,
632 .port_shift = 0,
633 .bits = 6,
634 },
635 [ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
636 .name = "unknown_mcast_flood",
637 .offset = ALE_UNKNOWNVLAN,
638 .port_offset = 0,
639 .shift = 8,
640 .port_shift = 0,
641 .bits = 6,
642 },
643 [ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
644 .name = "unknown_reg_flood",
645 .offset = ALE_UNKNOWNVLAN,
646 .port_offset = 0,
647 .shift = 16,
648 .port_shift = 0,
649 .bits = 6,
650 },
651 [ALE_PORT_UNTAGGED_EGRESS] = {
652 .name = "untagged_egress",
653 .offset = ALE_UNKNOWNVLAN,
654 .port_offset = 0,
655 .shift = 24,
656 .port_shift = 0,
657 .bits = 6,
658 },
659};
660
661int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
662 int value)
663{
664 const struct ale_control_info *info;
665 int offset, shift;
666 u32 tmp, mask;
667
668 if (control < 0 || control >= ARRAY_SIZE(ale_controls))
669 return -EINVAL;
670
671 info = &ale_controls[control];
672 if (info->port_offset == 0 && info->port_shift == 0)
673 port = 0; /* global, port is a dont care */
674
675 if (port < 0 || port > ale->params.ale_ports)
676 return -EINVAL;
677
678 mask = BITMASK(info->bits);
679 if (value & ~mask)
680 return -EINVAL;
681
682 offset = info->offset + (port * info->port_offset);
683 shift = info->shift + (port * info->port_shift);
684
685 tmp = __raw_readl(ale->params.ale_regs + offset);
686 tmp = (tmp & ~(mask << shift)) | (value << shift);
687 __raw_writel(tmp, ale->params.ale_regs + offset);
688
689 return 0;
690}
58c11b5f 691EXPORT_SYMBOL_GPL(cpsw_ale_control_set);
db82173f
M
692
693int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
694{
695 const struct ale_control_info *info;
696 int offset, shift;
697 u32 tmp;
698
699 if (control < 0 || control >= ARRAY_SIZE(ale_controls))
700 return -EINVAL;
701
702 info = &ale_controls[control];
703 if (info->port_offset == 0 && info->port_shift == 0)
704 port = 0; /* global, port is a dont care */
705
706 if (port < 0 || port > ale->params.ale_ports)
707 return -EINVAL;
708
709 offset = info->offset + (port * info->port_offset);
710 shift = info->shift + (port * info->port_shift);
711
712 tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
713 return tmp & BITMASK(info->bits);
714}
58c11b5f 715EXPORT_SYMBOL_GPL(cpsw_ale_control_get);
db82173f
M
716
717static void cpsw_ale_timer(unsigned long arg)
718{
719 struct cpsw_ale *ale = (struct cpsw_ale *)arg;
720
721 cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
722
723 if (ale->ageout) {
724 ale->timer.expires = jiffies + ale->ageout;
725 add_timer(&ale->timer);
726 }
727}
728
db82173f
M
729void cpsw_ale_start(struct cpsw_ale *ale)
730{
731 u32 rev;
732
733 rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
ca47130a
KM
734 if (!ale->params.major_ver_mask)
735 ale->params.major_ver_mask = 0xff;
736 ale->version =
737 (ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask) << 8) |
738 ALE_VERSION_MINOR(rev);
739 dev_info(ale->params.dev, "initialized cpsw ale version %d.%d\n",
740 ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask),
741 ALE_VERSION_MINOR(rev));
742
743 if (ale->params.nu_switch_ale) {
744 /* Separate registers for unknown vlan configuration.
745 * Also there are N bits, where N is number of ale
746 * ports and shift value should be 0
747 */
748 ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].bits =
749 ale->params.ale_ports;
750 ale_controls[ALE_PORT_UNKNOWN_VLAN_MEMBER].offset =
751 ALE_UNKNOWNVLAN_MEMBER;
752 ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].bits =
753 ale->params.ale_ports;
754 ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].shift = 0;
755 ale_controls[ALE_PORT_UNKNOWN_MCAST_FLOOD].offset =
756 ALE_UNKNOWNVLAN_UNREG_MCAST_FLOOD;
757 ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].bits =
758 ale->params.ale_ports;
759 ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].shift = 0;
760 ale_controls[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD].offset =
761 ALE_UNKNOWNVLAN_REG_MCAST_FLOOD;
762 ale_controls[ALE_PORT_UNTAGGED_EGRESS].bits =
763 ale->params.ale_ports;
764 ale_controls[ALE_PORT_UNTAGGED_EGRESS].shift = 0;
765 ale_controls[ALE_PORT_UNTAGGED_EGRESS].offset =
766 ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS;
767 }
768
db82173f
M
769 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
770 cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
771
772 init_timer(&ale->timer);
773 ale->timer.data = (unsigned long)ale;
774 ale->timer.function = cpsw_ale_timer;
775 if (ale->ageout) {
776 ale->timer.expires = jiffies + ale->ageout;
777 add_timer(&ale->timer);
778 }
779}
58c11b5f 780EXPORT_SYMBOL_GPL(cpsw_ale_start);
db82173f
M
781
782void cpsw_ale_stop(struct cpsw_ale *ale)
783{
784 del_timer_sync(&ale->timer);
785}
58c11b5f 786EXPORT_SYMBOL_GPL(cpsw_ale_stop);
db82173f
M
787
788struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
789{
790 struct cpsw_ale *ale;
791
792 ale = kzalloc(sizeof(*ale), GFP_KERNEL);
793 if (!ale)
794 return NULL;
795
796 ale->params = *params;
797 ale->ageout = ale->params.ale_ageout * HZ;
798
799 return ale;
800}
58c11b5f 801EXPORT_SYMBOL_GPL(cpsw_ale_create);
db82173f
M
802
803int cpsw_ale_destroy(struct cpsw_ale *ale)
804{
805 if (!ale)
806 return -EINVAL;
db82173f
M
807 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
808 kfree(ale);
809 return 0;
810}
58c11b5f 811EXPORT_SYMBOL_GPL(cpsw_ale_destroy);
52c4f0ec
M
812
813void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data)
814{
815 int i;
816
817 for (i = 0; i < ale->params.ale_entries; i++) {
818 cpsw_ale_read(ale, i, data);
819 data += ALE_ENTRY_WORDS;
820 }
821}
58c11b5f
KM
822EXPORT_SYMBOL_GPL(cpsw_ale_dump);
823
824MODULE_LICENSE("GPL v2");
825MODULE_DESCRIPTION("TI CPSW ALE driver");
826MODULE_AUTHOR("Texas Instruments");