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
54static 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
64static 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)				\
78static inline int cpsw_ale_get_##name(u32 *ale_entry)			\
79{									\
80	return cpsw_ale_get_field(ale_entry, start, bits);		\
81}									\
82static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)	\
83{									\
84	cpsw_ale_set_field(ale_entry, start, bits, value);		\
85}
86
87DEFINE_ALE_FIELD(entry_type,		60,	2)
88DEFINE_ALE_FIELD(vlan_id,		48,	12)
89DEFINE_ALE_FIELD(mcast_state,		62,	2)
90DEFINE_ALE_FIELD(port_mask,		66,     3)
91DEFINE_ALE_FIELD(super,			65,	1)
92DEFINE_ALE_FIELD(ucast_type,		62,     2)
93DEFINE_ALE_FIELD(port_num,		66,     2)
94DEFINE_ALE_FIELD(blocked,		65,     1)
95DEFINE_ALE_FIELD(secure,		64,     1)
96DEFINE_ALE_FIELD(vlan_untag_force,	24,	3)
97DEFINE_ALE_FIELD(vlan_reg_mcast,	16,	3)
98DEFINE_ALE_FIELD(vlan_unreg_mcast,	8,	3)
99DEFINE_ALE_FIELD(vlan_member_list,	0,	3)
100DEFINE_ALE_FIELD(mcast,			40,	1)
101
102/* The MAC address field in the ALE entry cannot be macroized as above */
103static 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
111static 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
119static 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
134static 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
150static 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
171static 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
187static 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
201static 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
221static 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
238int 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}
269EXPORT_SYMBOL_GPL(cpsw_ale_flush_multicast);
270
271static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry,
272				 int port_mask)
273{
274	int port;
275
276	port = cpsw_ale_get_port_num(ale_entry);
277	if ((BIT(port) & port_mask) == 0)
278		return; /* ports dont intersect, not interested */
279	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
280}
281
282int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask)
283{
284	u32 ale_entry[ALE_ENTRY_WORDS];
285	int ret, idx;
286
287	for (idx = 0; idx < ale->params.ale_entries; idx++) {
288		cpsw_ale_read(ale, idx, ale_entry);
289		ret = cpsw_ale_get_entry_type(ale_entry);
290		if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR)
291			continue;
292
293		if (cpsw_ale_get_mcast(ale_entry))
294			cpsw_ale_flush_mcast(ale, ale_entry, port_mask);
295		else
296			cpsw_ale_flush_ucast(ale, ale_entry, port_mask);
297
298		cpsw_ale_write(ale, idx, ale_entry);
299	}
300	return 0;
301}
302EXPORT_SYMBOL_GPL(cpsw_ale_flush);
303
304static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry,
305						int flags, u16 vid)
306{
307	if (flags & ALE_VLAN) {
308		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR);
309		cpsw_ale_set_vlan_id(ale_entry, vid);
310	} else {
311		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR);
312	}
313}
314
315int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port,
316		       int flags, u16 vid)
317{
318	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
319	int idx;
320
321	cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
322
323	cpsw_ale_set_addr(ale_entry, addr);
324	cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT);
325	cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0);
326	cpsw_ale_set_blocked(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
327	cpsw_ale_set_port_num(ale_entry, port);
328
329	idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
330	if (idx < 0)
331		idx = cpsw_ale_match_free(ale);
332	if (idx < 0)
333		idx = cpsw_ale_find_ageable(ale);
334	if (idx < 0)
335		return -ENOMEM;
336
337	cpsw_ale_write(ale, idx, ale_entry);
338	return 0;
339}
340EXPORT_SYMBOL_GPL(cpsw_ale_add_ucast);
341
342int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port,
343		       int flags, u16 vid)
344{
345	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
346	int idx;
347
348	idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
349	if (idx < 0)
350		return -ENOENT;
351
352	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
353	cpsw_ale_write(ale, idx, ale_entry);
354	return 0;
355}
356EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast);
357
358int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
359		       int flags, u16 vid, int mcast_state)
360{
361	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
362	int idx, mask;
363
364	idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
365	if (idx >= 0)
366		cpsw_ale_read(ale, idx, ale_entry);
367
368	cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid);
369
370	cpsw_ale_set_addr(ale_entry, addr);
371	cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0);
372	cpsw_ale_set_mcast_state(ale_entry, mcast_state);
373
374	mask = cpsw_ale_get_port_mask(ale_entry);
375	port_mask |= mask;
376	cpsw_ale_set_port_mask(ale_entry, port_mask);
377
378	if (idx < 0)
379		idx = cpsw_ale_match_free(ale);
380	if (idx < 0)
381		idx = cpsw_ale_find_ageable(ale);
382	if (idx < 0)
383		return -ENOMEM;
384
385	cpsw_ale_write(ale, idx, ale_entry);
386	return 0;
387}
388EXPORT_SYMBOL_GPL(cpsw_ale_add_mcast);
389
390int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
391		       int flags, u16 vid)
392{
393	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
394	int idx;
395
396	idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0);
397	if (idx < 0)
398		return -EINVAL;
399
400	cpsw_ale_read(ale, idx, ale_entry);
401
402	if (port_mask)
403		cpsw_ale_set_port_mask(ale_entry, port_mask);
404	else
405		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
406
407	cpsw_ale_write(ale, idx, ale_entry);
408	return 0;
409}
410EXPORT_SYMBOL_GPL(cpsw_ale_del_mcast);
411
412int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
413		      int reg_mcast, int unreg_mcast)
414{
415	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
416	int idx;
417
418	idx = cpsw_ale_match_vlan(ale, vid);
419	if (idx >= 0)
420		cpsw_ale_read(ale, idx, ale_entry);
421
422	cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
423	cpsw_ale_set_vlan_id(ale_entry, vid);
424
425	cpsw_ale_set_vlan_untag_force(ale_entry, untag);
426	cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast);
427	cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
428	cpsw_ale_set_vlan_member_list(ale_entry, port);
429
430	if (idx < 0)
431		idx = cpsw_ale_match_free(ale);
432	if (idx < 0)
433		idx = cpsw_ale_find_ageable(ale);
434	if (idx < 0)
435		return -ENOMEM;
436
437	cpsw_ale_write(ale, idx, ale_entry);
438	return 0;
439}
440EXPORT_SYMBOL_GPL(cpsw_ale_add_vlan);
441
442int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
443{
444	u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0};
445	int idx;
446
447	idx = cpsw_ale_match_vlan(ale, vid);
448	if (idx < 0)
449		return -ENOENT;
450
451	cpsw_ale_read(ale, idx, ale_entry);
452
453	if (port_mask)
454		cpsw_ale_set_vlan_member_list(ale_entry, port_mask);
455	else
456		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
457
458	cpsw_ale_write(ale, idx, ale_entry);
459	return 0;
460}
461EXPORT_SYMBOL_GPL(cpsw_ale_del_vlan);
462
463void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
464{
465	u32 ale_entry[ALE_ENTRY_WORDS];
466	int type, idx;
467	int unreg_mcast = 0;
468
469	/* Only bother doing the work if the setting is actually changing */
470	if (ale->allmulti == allmulti)
471		return;
472
473	/* Remember the new setting to check against next time */
474	ale->allmulti = allmulti;
475
476	for (idx = 0; idx < ale->params.ale_entries; idx++) {
477		cpsw_ale_read(ale, idx, ale_entry);
478		type = cpsw_ale_get_entry_type(ale_entry);
479		if (type != ALE_TYPE_VLAN)
480			continue;
481
482		unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
483		if (allmulti)
484			unreg_mcast |= 1;
485		else
486			unreg_mcast &= ~1;
487		cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
488		cpsw_ale_write(ale, idx, ale_entry);
489	}
490}
491EXPORT_SYMBOL_GPL(cpsw_ale_set_allmulti);
492
493struct ale_control_info {
494	const char	*name;
495	int		offset, port_offset;
496	int		shift, port_shift;
497	int		bits;
498};
499
500static const struct ale_control_info ale_controls[ALE_NUM_CONTROLS] = {
501	[ALE_ENABLE]		= {
502		.name		= "enable",
503		.offset		= ALE_CONTROL,
504		.port_offset	= 0,
505		.shift		= 31,
506		.port_shift	= 0,
507		.bits		= 1,
508	},
509	[ALE_CLEAR]		= {
510		.name		= "clear",
511		.offset		= ALE_CONTROL,
512		.port_offset	= 0,
513		.shift		= 30,
514		.port_shift	= 0,
515		.bits		= 1,
516	},
517	[ALE_AGEOUT]		= {
518		.name		= "ageout",
519		.offset		= ALE_CONTROL,
520		.port_offset	= 0,
521		.shift		= 29,
522		.port_shift	= 0,
523		.bits		= 1,
524	},
525	[ALE_P0_UNI_FLOOD]	= {
526		.name		= "port0_unicast_flood",
527		.offset		= ALE_CONTROL,
528		.port_offset	= 0,
529		.shift		= 8,
530		.port_shift	= 0,
531		.bits		= 1,
532	},
533	[ALE_VLAN_NOLEARN]	= {
534		.name		= "vlan_nolearn",
535		.offset		= ALE_CONTROL,
536		.port_offset	= 0,
537		.shift		= 7,
538		.port_shift	= 0,
539		.bits		= 1,
540	},
541	[ALE_NO_PORT_VLAN]	= {
542		.name		= "no_port_vlan",
543		.offset		= ALE_CONTROL,
544		.port_offset	= 0,
545		.shift		= 6,
546		.port_shift	= 0,
547		.bits		= 1,
548	},
549	[ALE_OUI_DENY]		= {
550		.name		= "oui_deny",
551		.offset		= ALE_CONTROL,
552		.port_offset	= 0,
553		.shift		= 5,
554		.port_shift	= 0,
555		.bits		= 1,
556	},
557	[ALE_BYPASS]		= {
558		.name		= "bypass",
559		.offset		= ALE_CONTROL,
560		.port_offset	= 0,
561		.shift		= 4,
562		.port_shift	= 0,
563		.bits		= 1,
564	},
565	[ALE_RATE_LIMIT_TX]	= {
566		.name		= "rate_limit_tx",
567		.offset		= ALE_CONTROL,
568		.port_offset	= 0,
569		.shift		= 3,
570		.port_shift	= 0,
571		.bits		= 1,
572	},
573	[ALE_VLAN_AWARE]	= {
574		.name		= "vlan_aware",
575		.offset		= ALE_CONTROL,
576		.port_offset	= 0,
577		.shift		= 2,
578		.port_shift	= 0,
579		.bits		= 1,
580	},
581	[ALE_AUTH_ENABLE]	= {
582		.name		= "auth_enable",
583		.offset		= ALE_CONTROL,
584		.port_offset	= 0,
585		.shift		= 1,
586		.port_shift	= 0,
587		.bits		= 1,
588	},
589	[ALE_RATE_LIMIT]	= {
590		.name		= "rate_limit",
591		.offset		= ALE_CONTROL,
592		.port_offset	= 0,
593		.shift		= 0,
594		.port_shift	= 0,
595		.bits		= 1,
596	},
597	[ALE_PORT_STATE]	= {
598		.name		= "port_state",
599		.offset		= ALE_PORTCTL,
600		.port_offset	= 4,
601		.shift		= 0,
602		.port_shift	= 0,
603		.bits		= 2,
604	},
605	[ALE_PORT_DROP_UNTAGGED] = {
606		.name		= "drop_untagged",
607		.offset		= ALE_PORTCTL,
608		.port_offset	= 4,
609		.shift		= 2,
610		.port_shift	= 0,
611		.bits		= 1,
612	},
613	[ALE_PORT_DROP_UNKNOWN_VLAN] = {
614		.name		= "drop_unknown",
615		.offset		= ALE_PORTCTL,
616		.port_offset	= 4,
617		.shift		= 3,
618		.port_shift	= 0,
619		.bits		= 1,
620	},
621	[ALE_PORT_NOLEARN]	= {
622		.name		= "nolearn",
623		.offset		= ALE_PORTCTL,
624		.port_offset	= 4,
625		.shift		= 4,
626		.port_shift	= 0,
627		.bits		= 1,
628	},
629	[ALE_PORT_NO_SA_UPDATE]	= {
630		.name		= "no_source_update",
631		.offset		= ALE_PORTCTL,
632		.port_offset	= 4,
633		.shift		= 5,
634		.port_shift	= 0,
635		.bits		= 1,
636	},
637	[ALE_PORT_MCAST_LIMIT]	= {
638		.name		= "mcast_limit",
639		.offset		= ALE_PORTCTL,
640		.port_offset	= 4,
641		.shift		= 16,
642		.port_shift	= 0,
643		.bits		= 8,
644	},
645	[ALE_PORT_BCAST_LIMIT]	= {
646		.name		= "bcast_limit",
647		.offset		= ALE_PORTCTL,
648		.port_offset	= 4,
649		.shift		= 24,
650		.port_shift	= 0,
651		.bits		= 8,
652	},
653	[ALE_PORT_UNKNOWN_VLAN_MEMBER] = {
654		.name		= "unknown_vlan_member",
655		.offset		= ALE_UNKNOWNVLAN,
656		.port_offset	= 0,
657		.shift		= 0,
658		.port_shift	= 0,
659		.bits		= 6,
660	},
661	[ALE_PORT_UNKNOWN_MCAST_FLOOD] = {
662		.name		= "unknown_mcast_flood",
663		.offset		= ALE_UNKNOWNVLAN,
664		.port_offset	= 0,
665		.shift		= 8,
666		.port_shift	= 0,
667		.bits		= 6,
668	},
669	[ALE_PORT_UNKNOWN_REG_MCAST_FLOOD] = {
670		.name		= "unknown_reg_flood",
671		.offset		= ALE_UNKNOWNVLAN,
672		.port_offset	= 0,
673		.shift		= 16,
674		.port_shift	= 0,
675		.bits		= 6,
676	},
677	[ALE_PORT_UNTAGGED_EGRESS] = {
678		.name		= "untagged_egress",
679		.offset		= ALE_UNKNOWNVLAN,
680		.port_offset	= 0,
681		.shift		= 24,
682		.port_shift	= 0,
683		.bits		= 6,
684	},
685};
686
687int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control,
688			 int value)
689{
690	const struct ale_control_info *info;
691	int offset, shift;
692	u32 tmp, mask;
693
694	if (control < 0 || control >= ARRAY_SIZE(ale_controls))
695		return -EINVAL;
696
697	info = &ale_controls[control];
698	if (info->port_offset == 0 && info->port_shift == 0)
699		port = 0; /* global, port is a dont care */
700
701	if (port < 0 || port > ale->params.ale_ports)
702		return -EINVAL;
703
704	mask = BITMASK(info->bits);
705	if (value & ~mask)
706		return -EINVAL;
707
708	offset = info->offset + (port * info->port_offset);
709	shift  = info->shift  + (port * info->port_shift);
710
711	tmp = __raw_readl(ale->params.ale_regs + offset);
712	tmp = (tmp & ~(mask << shift)) | (value << shift);
713	__raw_writel(tmp, ale->params.ale_regs + offset);
714
715	return 0;
716}
717EXPORT_SYMBOL_GPL(cpsw_ale_control_set);
718
719int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control)
720{
721	const struct ale_control_info *info;
722	int offset, shift;
723	u32 tmp;
724
725	if (control < 0 || control >= ARRAY_SIZE(ale_controls))
726		return -EINVAL;
727
728	info = &ale_controls[control];
729	if (info->port_offset == 0 && info->port_shift == 0)
730		port = 0; /* global, port is a dont care */
731
732	if (port < 0 || port > ale->params.ale_ports)
733		return -EINVAL;
734
735	offset = info->offset + (port * info->port_offset);
736	shift  = info->shift  + (port * info->port_shift);
737
738	tmp = __raw_readl(ale->params.ale_regs + offset) >> shift;
739	return tmp & BITMASK(info->bits);
740}
741EXPORT_SYMBOL_GPL(cpsw_ale_control_get);
742
743static void cpsw_ale_timer(unsigned long arg)
744{
745	struct cpsw_ale *ale = (struct cpsw_ale *)arg;
746
747	cpsw_ale_control_set(ale, 0, ALE_AGEOUT, 1);
748
749	if (ale->ageout) {
750		ale->timer.expires = jiffies + ale->ageout;
751		add_timer(&ale->timer);
752	}
753}
754
755int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout)
756{
757	del_timer_sync(&ale->timer);
758	ale->ageout = ageout * HZ;
759	if (ale->ageout) {
760		ale->timer.expires = jiffies + ale->ageout;
761		add_timer(&ale->timer);
762	}
763	return 0;
764}
765EXPORT_SYMBOL_GPL(cpsw_ale_set_ageout);
766
767void cpsw_ale_start(struct cpsw_ale *ale)
768{
769	u32 rev;
770
771	rev = __raw_readl(ale->params.ale_regs + ALE_IDVER);
772	dev_dbg(ale->params.dev, "initialized cpsw ale revision %d.%d\n",
773		ALE_VERSION_MAJOR(rev), ALE_VERSION_MINOR(rev));
774	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
775	cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
776
777	init_timer(&ale->timer);
778	ale->timer.data	    = (unsigned long)ale;
779	ale->timer.function = cpsw_ale_timer;
780	if (ale->ageout) {
781		ale->timer.expires = jiffies + ale->ageout;
782		add_timer(&ale->timer);
783	}
784}
785EXPORT_SYMBOL_GPL(cpsw_ale_start);
786
787void cpsw_ale_stop(struct cpsw_ale *ale)
788{
789	del_timer_sync(&ale->timer);
790}
791EXPORT_SYMBOL_GPL(cpsw_ale_stop);
792
793struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
794{
795	struct cpsw_ale *ale;
796
797	ale = kzalloc(sizeof(*ale), GFP_KERNEL);
798	if (!ale)
799		return NULL;
800
801	ale->params = *params;
802	ale->ageout = ale->params.ale_ageout * HZ;
803
804	return ale;
805}
806EXPORT_SYMBOL_GPL(cpsw_ale_create);
807
808int cpsw_ale_destroy(struct cpsw_ale *ale)
809{
810	if (!ale)
811		return -EINVAL;
812	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
813	kfree(ale);
814	return 0;
815}
816EXPORT_SYMBOL_GPL(cpsw_ale_destroy);
817
818void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data)
819{
820	int i;
821
822	for (i = 0; i < ale->params.ale_entries; i++) {
823		cpsw_ale_read(ale, i, data);
824		data += ALE_ENTRY_WORDS;
825	}
826}
827EXPORT_SYMBOL_GPL(cpsw_ale_dump);
828
829MODULE_LICENSE("GPL v2");
830MODULE_DESCRIPTION("TI CPSW ALE driver");
831MODULE_AUTHOR("Texas Instruments");
832