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