blob: 0e9ccc2cf91fefce7bda15f6c9ad4ee580e6aa5d [file] [log] [blame]
Mugunthan V Ndb821732012-03-18 20:17:53 +00001/*
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/platform_device.h>
17#include <linux/seq_file.h>
18#include <linux/slab.h>
19#include <linux/err.h>
20#include <linux/io.h>
21#include <linux/stat.h>
22#include <linux/sysfs.h>
Mugunthan V N5c50a852012-10-29 08:45:11 +000023#include <linux/etherdevice.h>
Mugunthan V Ndb821732012-03-18 20:17:53 +000024
25#include "cpsw_ale.h"
26
27#define BITMASK(bits) (BIT(bits) - 1)
28#define ALE_ENTRY_BITS 68
29#define ALE_ENTRY_WORDS DIV_ROUND_UP(ALE_ENTRY_BITS, 32)
30
31#define ALE_VERSION_MAJOR(rev) ((rev >> 8) & 0xff)
32#define ALE_VERSION_MINOR(rev) (rev & 0xff)
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
43#define ALE_TABLE_WRITE BIT(31)
44
45#define ALE_TYPE_FREE 0
46#define ALE_TYPE_ADDR 1
47#define ALE_TYPE_VLAN 2
48#define ALE_TYPE_VLAN_ADDR 3
49
50#define ALE_UCAST_PERSISTANT 0
51#define ALE_UCAST_UNTOUCHED 1
52#define ALE_UCAST_OUI 2
53#define ALE_UCAST_TOUCHED 3
54
55static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
56{
57 int idx;
58
59 idx = start / 32;
60 start -= idx * 32;
61 idx = 2 - idx; /* flip */
62 return (ale_entry[idx] >> start) & BITMASK(bits);
63}
64
65static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits,
66 u32 value)
67{
68 int idx;
69
70 value &= BITMASK(bits);
71 idx = start / 32;
72 start -= idx * 32;
73 idx = 2 - idx; /* flip */
74 ale_entry[idx] &= ~(BITMASK(bits) << start);
75 ale_entry[idx] |= (value << start);
76}
77
78#define DEFINE_ALE_FIELD(name, start, bits) \
79static inline int cpsw_ale_get_##name(u32 *ale_entry) \
80{ \
81 return cpsw_ale_get_field(ale_entry, start, bits); \
82} \
83static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \
84{ \
85 cpsw_ale_set_field(ale_entry, start, bits, value); \
86}
87
88DEFINE_ALE_FIELD(entry_type, 60, 2)
89DEFINE_ALE_FIELD(vlan_id, 48, 12)
90DEFINE_ALE_FIELD(mcast_state, 62, 2)
91DEFINE_ALE_FIELD(port_mask, 66, 3)
92DEFINE_ALE_FIELD(super, 65, 1)
93DEFINE_ALE_FIELD(ucast_type, 62, 2)
94DEFINE_ALE_FIELD(port_num, 66, 2)
95DEFINE_ALE_FIELD(blocked, 65, 1)
96DEFINE_ALE_FIELD(secure, 64, 1)
97DEFINE_ALE_FIELD(vlan_untag_force, 24, 3)
98DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3)
99DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3)
100DEFINE_ALE_FIELD(vlan_member_list, 0, 3)
101DEFINE_ALE_FIELD(mcast, 40, 1)
102
103/* The MAC address field in the ALE entry cannot be macroized as above */
104static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
105{
106 int i;
107
108 for (i = 0; i < 6; i++)
109 addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
110}
111
112static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
113{
114 int i;
115
116 for (i = 0; i < 6; i++)
117 cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
118}
119
120static int cpsw_ale_read(struct cpsw_ale *ale, int idx, u32 *ale_entry)
121{
122 int i;
123
124 WARN_ON(idx > ale->params.ale_entries);
125
126 __raw_writel(idx, ale->params.ale_regs + ALE_TABLE_CONTROL);
127
128 for (i = 0; i < ALE_ENTRY_WORDS; i++)
129 ale_entry[i] = __raw_readl(ale->params.ale_regs +
130 ALE_TABLE + 4 * i);
131
132 return idx;
133}
134
135static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry)
136{
137 int i;
138
139 WARN_ON(idx > ale->params.ale_entries);
140
141 for (i = 0; i < ALE_ENTRY_WORDS; i++)
142 __raw_writel(ale_entry[i], ale->params.ale_regs +
143 ALE_TABLE + 4 * i);
144
145 __raw_writel(idx | ALE_TABLE_WRITE, ale->params.ale_regs +
146 ALE_TABLE_CONTROL);
147
148 return idx;
149}
150
151static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr)
152{
153 u32 ale_entry[ALE_ENTRY_WORDS];
154 int type, idx;
155
156 for (idx = 0; idx < ale->params.ale_entries; idx++) {
157 u8 entry_addr[6];
158
159 cpsw_ale_read(ale, idx, ale_entry);
160 type = cpsw_ale_get_entry_type(ale_entry);
161 if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
162 continue;
163 cpsw_ale_get_addr(ale_entry, entry_addr);
164 if (memcmp(entry_addr, addr, 6) == 0)
165 return idx;
166 }
167 return -ENOENT;
168}
169
170static int cpsw_ale_match_free(struct cpsw_ale *ale)
171{
172 u32 ale_entry[ALE_ENTRY_WORDS];
173 int type, idx;
174
175 for (idx = 0; idx < ale->params.ale_entries; idx++) {
176 cpsw_ale_read(ale, idx, ale_entry);
177 type = cpsw_ale_get_entry_type(ale_entry);
178 if (type == ALE_TYPE_FREE)
179 return idx;
180 }
181 return -ENOENT;
182}
183
184static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
185{
186 u32 ale_entry[ALE_ENTRY_WORDS];
187 int type, idx;
188
189 for (idx = 0; idx < ale->params.ale_entries; idx++) {
190 cpsw_ale_read(ale, idx, ale_entry);
191 type = cpsw_ale_get_entry_type(ale_entry);
192 if (type != ALE_TYPE_ADDR && type != ALE_TYPE_VLAN_ADDR)
193 continue;
194 if (cpsw_ale_get_mcast(ale_entry))
195 continue;
196 type = cpsw_ale_get_ucast_type(ale_entry);
197 if (type != ALE_UCAST_PERSISTANT &&
198 type != ALE_UCAST_OUI)
199 return idx;
200 }
201 return -ENOENT;
202}
203
204static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry,
205 int port_mask)
206{
207 int mask;
208
209 mask = cpsw_ale_get_port_mask(ale_entry);
210 if ((mask & port_mask) == 0)
211 return; /* ports dont intersect, not interested */
212 mask &= ~port_mask;
213
214 /* free if only remaining port is host port */
Mugunthan V N5c50a852012-10-29 08:45:11 +0000215 if (mask)
Mugunthan V Ndb821732012-03-18 20:17:53 +0000216 cpsw_ale_set_port_mask(ale_entry, mask);
Mugunthan V N5c50a852012-10-29 08:45:11 +0000217 else
218 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
219}
220
221int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask)
222{
223 u32 ale_entry[ALE_ENTRY_WORDS];
224 int ret, idx;
225
226 for (idx = 0; idx < ale->params.ale_entries; idx++) {
227 cpsw_ale_read(ale, idx, ale_entry);
228 ret = cpsw_ale_get_entry_type(ale_entry);
229 if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
230 continue;
231
232 if (cpsw_ale_get_mcast(ale_entry)) {
233 u8 addr[6];
234
235 cpsw_ale_get_addr(ale_entry, addr);
236 if (!is_broadcast_ether_addr(addr))
237 cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
238 }
239
240 cpsw_ale_write(ale, idx, ale_entry);
241 }
242 return 0;
Mugunthan V Ndb821732012-03-18 20:17:53 +0000243}
244
245static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
246 int port_mask)
247{
248 int port;
249
250 port = cpsw_ale_get_port_num(ale_entry);
251 if ((BIT(port) & port_mask) == 0)
252 return; /* ports dont intersect, not interested */
253 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
254}
255
256int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
257{
258 u32 ale_entry[ALE_ENTRY_WORDS];
259 int ret, idx;
260
261 for (idx = 0; idx < ale->params.ale_entries; idx++) {
262 cpsw_ale_read(ale, idx, ale_entry);
263 ret = cpsw_ale_get_entry_type(ale_entry);
264 if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
265 continue;
266
267 if (cpsw_ale_get_mcast(ale_entry))
268 cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
269 else
270 cpsw_ale_flush_ucast(ale, ale_entry, port_mask);
271
272 cpsw_ale_write(ale, idx, ale_entry);
273 }
274 return 0;
275}
276
277int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags)
278{
279 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
280 int idx;
281
282 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
283 cpsw_ale_set_addr(ale_entry, addr);
284 cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
285 cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
286 cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
287 cpsw_ale_set_port_num(ale_entry, port);
288
289 idx = cpsw_ale_match_addr(ale, addr);
290 if (idx < 0)
291 idx = cpsw_ale_match_free(ale);
292 if (idx < 0)
293 idx = cpsw_ale_find_ageable(ale);
294 if (idx < 0)
295 return -ENOMEM;
296
297 cpsw_ale_write(ale, idx, ale_entry);
298 return 0;
299}
300
301int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port)
302{
303 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
304 int idx;
305
306 idx = cpsw_ale_match_addr(ale, addr);
307 if (idx < 0)
308 return -ENOENT;
309
310 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
311 cpsw_ale_write(ale, idx, ale_entry);
312 return 0;
313}
314
315int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
316 int super, int mcast_state)
317{
318 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
319 int idx, mask;
320
321 idx = cpsw_ale_match_addr(ale, addr);
322 if (idx >= 0)
323 cpsw_ale_read(ale, idx, ale_entry);
324
325 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
326 cpsw_ale_set_addr(ale_entry, addr);
327 cpsw_ale_set_super(ale_entry, super);
328 cpsw_ale_set_mcast_state(ale_entry, mcast_state);
329
330 mask = cpsw_ale_get_port_mask(ale_entry);
331 port_mask |= mask;
332 cpsw_ale_set_port_mask(ale_entry, port_mask);
333
334 if (idx < 0)
335 idx = cpsw_ale_match_free(ale);
336 if (idx < 0)
337 idx = cpsw_ale_find_ageable(ale);
338 if (idx < 0)
339 return -ENOMEM;
340
341 cpsw_ale_write(ale, idx, ale_entry);
342 return 0;
343}
344
345int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask)
346{
347 u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
348 int idx;
349
350 idx = cpsw_ale_match_addr(ale, addr);
351 if (idx < 0)
352 return -EINVAL;
353
354 cpsw_ale_read(ale, idx, ale_entry);
355
356 if (port_mask)
357 cpsw_ale_set_port_mask(ale_entry, port_mask);
358 else
359 cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
360
361 cpsw_ale_write(ale, idx, ale_entry);
362 return 0;
363}
364
365struct ale_control_info {
366 const char *name;
367 int offset, port_offset;
368 int shift, port_shift;
369 int bits;
370};
371
372static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
373 [ALE_ENABLE] = {
374 .name = "enable",
375 .offset = ALE_CONTROL,
376 .port_offset = 0,
377 .shift = 31,
378 .port_shift = 0,
379 .bits = 1,
380 },
381 [ALE_CLEAR] = {
382 .name = "clear",
383 .offset = ALE_CONTROL,
384 .port_offset = 0,
385 .shift = 30,
386 .port_shift = 0,
387 .bits = 1,
388 },
389 [ALE_AGEOUT] = {
390 .name = "ageout",
391 .offset = ALE_CONTROL,
392 .port_offset = 0,
393 .shift = 29,
394 .port_shift = 0,
395 .bits = 1,
396 },
397 [ALE_VLAN_NOLEARN] = {
398 .name = "vlan_nolearn",
399 .offset = ALE_CONTROL,
400 .port_offset = 0,
401 .shift = 7,
402 .port_shift = 0,
403 .bits = 1,
404 },
405 [ALE_NO_PORT_VLAN] = {
406 .name = "no_port_vlan",
407 .offset = ALE_CONTROL,
408 .port_offset = 0,
409 .shift = 6,
410 .port_shift = 0,
411 .bits = 1,
412 },
413 [ALE_OUI_DENY] = {
414 .name = "oui_deny",
415 .offset = ALE_CONTROL,
416 .port_offset = 0,
417 .shift = 5,
418 .port_shift = 0,
419 .bits = 1,
420 },
421 [ALE_BYPASS] = {
422 .name = "bypass",
423 .offset = ALE_CONTROL,
424 .port_offset = 0,
425 .shift = 4,
426 .port_shift = 0,
427 .bits = 1,
428 },
429 [ALE_RATE_LIMIT_TX] = {
430 .name = "rate_limit_tx",
431 .offset = ALE_CONTROL,
432 .port_offset = 0,
433 .shift = 3,
434 .port_shift = 0,
435 .bits = 1,
436 },
437 [ALE_VLAN_AWARE] = {
438 .name = "vlan_aware",
439 .offset = ALE_CONTROL,
440 .port_offset = 0,
441 .shift = 2,
442 .port_shift = 0,
443 .bits = 1,
444 },
445 [ALE_AUTH_ENABLE] = {
446 .name = "auth_enable",
447 .offset = ALE_CONTROL,
448 .port_offset = 0,
449 .shift = 1,
450 .port_shift = 0,
451 .bits = 1,
452 },
453 [ALE_RATE_LIMIT] = {
454 .name = "rate_limit",
455 .offset = ALE_CONTROL,
456 .port_offset = 0,
457 .shift = 0,
458 .port_shift = 0,
459 .bits = 1,
460 },
461 [ALE_PORT_STATE] = {
462 .name = "port_state",
463 .offset = ALE_PORTCTL,
464 .port_offset = 4,
465 .shift = 0,
466 .port_shift = 0,
467 .bits = 2,
468 },
469 [ALE_PORT_DROP_UNTAGGED] = {
470 .name = "drop_untagged",
471 .offset = ALE_PORTCTL,
472 .port_offset = 4,
473 .shift = 2,
474 .port_shift = 0,
475 .bits = 1,
476 },
477 [ALE_PORT_DROP_UNKNOWN_VLAN] = {
478 .name = "drop_unknown",
479 .offset = ALE_PORTCTL,
480 .port_offset = 4,
481 .shift = 3,
482 .port_shift = 0,
483 .bits = 1,
484 },
485 [ALE_PORT_NOLEARN] = {
486 .name = "nolearn",
487 .offset = ALE_PORTCTL,
488 .port_offset = 4,
489 .shift = 4,
490 .port_shift = 0,
491 .bits = 1,
492 },
493 [ALE_PORT_MCAST_LIMIT] = {
494 .name = "mcast_limit",
495 .offset = ALE_PORTCTL,
496 .port_offset = 4,
497 .shift = 16,
498 .port_shift = 0,
499 .bits = 8,
500 },
501 [ALE_PORT_BCAST_LIMIT] = {
502 .name = "bcast_limit",
503 .offset = ALE_PORTCTL,
504 .port_offset = 4,
505 .shift = 24,
506 .port_shift = 0,
507 .bits = 8,
508 },
509 [ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
510 .name = "unknown_vlan_member",
511 .offset = ALE_UNKNOWNVLAN,
512 .port_offset = 0,
513 .shift = 0,
514 .port_shift = 0,
515 .bits = 6,
516 },
517 [ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
518 .name = "unknown_mcast_flood",
519 .offset = ALE_UNKNOWNVLAN,
520 .port_offset = 0,
521 .shift = 8,
522 .port_shift = 0,
523 .bits = 6,
524 },
525 [ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
526 .name = "unknown_reg_flood",
527 .offset = ALE_UNKNOWNVLAN,
528 .port_offset = 0,
529 .shift = 16,
530 .port_shift = 0,
531 .bits = 6,
532 },
533 [ALE_PORT_UNTAGGED_EGRESS] = {
534 .name = "untagged_egress",
535 .offset = ALE_UNKNOWNVLAN,
536 .port_offset = 0,
537 .shift = 24,
538 .port_shift = 0,
539 .bits = 6,
540 },
541};
542
543int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
544 int value)
545{
546 const struct ale_control_info *info;
547 int offset, shift;
548 u32 tmp, mask;
549
550 if (control < 0 || control >= ARRAY_SIZE(ale_controls))
551 return -EINVAL;
552
553 info = &ale_controls[control];
554 if (info->port_offset == 0 && info->port_shift == 0)
555 port = 0; /* global, port is a dont care */
556
557 if (port < 0 || port > ale->params.ale_ports)
558 return -EINVAL;
559
560 mask = BITMASK(info->bits);
561 if (value & ~mask)
562 return -EINVAL;
563
564 offset = info->offset + (port * info->port_offset);
565 shift = info->shift + (port * info->port_shift);
566
567 tmp = __raw_readl(ale->params.ale_regs + offset);
568 tmp = (tmp & ~(mask << shift)) | (value << shift);
569 __raw_writel(tmp, ale->params.ale_regs + offset);
570
571 return 0;
572}
573
574int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
575{
576 const struct ale_control_info *info;
577 int offset, shift;
578 u32 tmp;
579
580 if (control < 0 || control >= ARRAY_SIZE(ale_controls))
581 return -EINVAL;
582
583 info = &ale_controls[control];
584 if (info->port_offset == 0 && info->port_shift == 0)
585 port = 0; /* global, port is a dont care */
586
587 if (port < 0 || port > ale->params.ale_ports)
588 return -EINVAL;
589
590 offset = info->offset + (port * info->port_offset);
591 shift = info->shift + (port * info->port_shift);
592
593 tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
594 return tmp & BITMASK(info->bits);
595}
596
597static void cpsw_ale_timer(unsigned long arg)
598{
599 struct cpsw_ale *ale = (struct cpsw_ale *)arg;
600
601 cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
602
603 if (ale->ageout) {
604 ale->timer.expires = jiffies + ale->ageout;
605 add_timer(&ale->timer);
606 }
607}
608
609int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
610{
611 del_timer_sync(&ale->timer);
612 ale->ageout = ageout * HZ;
613 if (ale->ageout) {
614 ale->timer.expires = jiffies + ale->ageout;
615 add_timer(&ale->timer);
616 }
617 return 0;
618}
619
620void cpsw_ale_start(struct cpsw_ale *ale)
621{
622 u32 rev;
623
624 rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
625 dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
626 ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
627 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
628 cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
629
630 init_timer(&ale->timer);
631 ale->timer.data = (unsigned long)ale;
632 ale->timer.function = cpsw_ale_timer;
633 if (ale->ageout) {
634 ale->timer.expires = jiffies + ale->ageout;
635 add_timer(&ale->timer);
636 }
637}
638
639void cpsw_ale_stop(struct cpsw_ale *ale)
640{
641 del_timer_sync(&ale->timer);
642}
643
644struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
645{
646 struct cpsw_ale *ale;
647
648 ale = kzalloc(sizeof(*ale), GFP_KERNEL);
649 if (!ale)
650 return NULL;
651
652 ale->params = *params;
653 ale->ageout = ale->params.ale_ageout * HZ;
654
655 return ale;
656}
657
658int cpsw_ale_destroy(struct cpsw_ale *ale)
659{
660 if (!ale)
661 return -EINVAL;
662 cpsw_ale_stop(ale);
663 cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
664 kfree(ale);
665 return 0;
666}