xref: /openbmc/linux/drivers/net/dsa/b53/b53_common.c (revision 1da6df85c6fbed8f39c56eadcae7fa75a7a0c635)
1967dd82fSFlorian Fainelli /*
2967dd82fSFlorian Fainelli  * B53 switch driver main logic
3967dd82fSFlorian Fainelli  *
4967dd82fSFlorian Fainelli  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
5967dd82fSFlorian Fainelli  * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
6967dd82fSFlorian Fainelli  *
7967dd82fSFlorian Fainelli  * Permission to use, copy, modify, and/or distribute this software for any
8967dd82fSFlorian Fainelli  * purpose with or without fee is hereby granted, provided that the above
9967dd82fSFlorian Fainelli  * copyright notice and this permission notice appear in all copies.
10967dd82fSFlorian Fainelli  *
11967dd82fSFlorian Fainelli  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12967dd82fSFlorian Fainelli  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13967dd82fSFlorian Fainelli  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14967dd82fSFlorian Fainelli  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15967dd82fSFlorian Fainelli  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16967dd82fSFlorian Fainelli  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17967dd82fSFlorian Fainelli  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18967dd82fSFlorian Fainelli  */
19967dd82fSFlorian Fainelli 
20967dd82fSFlorian Fainelli #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21967dd82fSFlorian Fainelli 
22967dd82fSFlorian Fainelli #include <linux/delay.h>
23967dd82fSFlorian Fainelli #include <linux/export.h>
24967dd82fSFlorian Fainelli #include <linux/gpio.h>
25967dd82fSFlorian Fainelli #include <linux/kernel.h>
26967dd82fSFlorian Fainelli #include <linux/module.h>
27967dd82fSFlorian Fainelli #include <linux/platform_data/b53.h>
28967dd82fSFlorian Fainelli #include <linux/phy.h>
29*1da6df85SFlorian Fainelli #include <linux/etherdevice.h>
30967dd82fSFlorian Fainelli #include <net/dsa.h>
31*1da6df85SFlorian Fainelli #include <net/switchdev.h>
32967dd82fSFlorian Fainelli 
33967dd82fSFlorian Fainelli #include "b53_regs.h"
34967dd82fSFlorian Fainelli #include "b53_priv.h"
35967dd82fSFlorian Fainelli 
36967dd82fSFlorian Fainelli struct b53_mib_desc {
37967dd82fSFlorian Fainelli 	u8 size;
38967dd82fSFlorian Fainelli 	u8 offset;
39967dd82fSFlorian Fainelli 	const char *name;
40967dd82fSFlorian Fainelli };
41967dd82fSFlorian Fainelli 
42967dd82fSFlorian Fainelli /* BCM5365 MIB counters */
43967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs_65[] = {
44967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
45967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
46967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
47967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
48967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
49967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
50967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
51967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
52967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
53967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
54967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
55967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
56967dd82fSFlorian Fainelli 	{ 8, 0x44, "RxOctets" },
57967dd82fSFlorian Fainelli 	{ 4, 0x4c, "RxUndersizePkts" },
58967dd82fSFlorian Fainelli 	{ 4, 0x50, "RxPausePkts" },
59967dd82fSFlorian Fainelli 	{ 4, 0x54, "Pkts64Octets" },
60967dd82fSFlorian Fainelli 	{ 4, 0x58, "Pkts65to127Octets" },
61967dd82fSFlorian Fainelli 	{ 4, 0x5c, "Pkts128to255Octets" },
62967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts256to511Octets" },
63967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts512to1023Octets" },
64967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts1024to1522Octets" },
65967dd82fSFlorian Fainelli 	{ 4, 0x6c, "RxOversizePkts" },
66967dd82fSFlorian Fainelli 	{ 4, 0x70, "RxJabbers" },
67967dd82fSFlorian Fainelli 	{ 4, 0x74, "RxAlignmentErrors" },
68967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxFCSErrors" },
69967dd82fSFlorian Fainelli 	{ 8, 0x7c, "RxGoodOctets" },
70967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxDropPkts" },
71967dd82fSFlorian Fainelli 	{ 4, 0x88, "RxUnicastPkts" },
72967dd82fSFlorian Fainelli 	{ 4, 0x8c, "RxMulticastPkts" },
73967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxBroadcastPkts" },
74967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxSAChanges" },
75967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxFragments" },
76967dd82fSFlorian Fainelli };
77967dd82fSFlorian Fainelli 
78967dd82fSFlorian Fainelli #define B53_MIBS_65_SIZE	ARRAY_SIZE(b53_mibs_65)
79967dd82fSFlorian Fainelli 
80967dd82fSFlorian Fainelli /* BCM63xx MIB counters */
81967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs_63xx[] = {
82967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
83967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
84967dd82fSFlorian Fainelli 	{ 4, 0x0c, "TxQoSPkts" },
85967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
86967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
87967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
88967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
89967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
90967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
91967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
92967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
93967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
94967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
95967dd82fSFlorian Fainelli 	{ 8, 0x3c, "TxQoSOctets" },
96967dd82fSFlorian Fainelli 	{ 8, 0x44, "RxOctets" },
97967dd82fSFlorian Fainelli 	{ 4, 0x4c, "RxUndersizePkts" },
98967dd82fSFlorian Fainelli 	{ 4, 0x50, "RxPausePkts" },
99967dd82fSFlorian Fainelli 	{ 4, 0x54, "Pkts64Octets" },
100967dd82fSFlorian Fainelli 	{ 4, 0x58, "Pkts65to127Octets" },
101967dd82fSFlorian Fainelli 	{ 4, 0x5c, "Pkts128to255Octets" },
102967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts256to511Octets" },
103967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts512to1023Octets" },
104967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts1024to1522Octets" },
105967dd82fSFlorian Fainelli 	{ 4, 0x6c, "RxOversizePkts" },
106967dd82fSFlorian Fainelli 	{ 4, 0x70, "RxJabbers" },
107967dd82fSFlorian Fainelli 	{ 4, 0x74, "RxAlignmentErrors" },
108967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxFCSErrors" },
109967dd82fSFlorian Fainelli 	{ 8, 0x7c, "RxGoodOctets" },
110967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxDropPkts" },
111967dd82fSFlorian Fainelli 	{ 4, 0x88, "RxUnicastPkts" },
112967dd82fSFlorian Fainelli 	{ 4, 0x8c, "RxMulticastPkts" },
113967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxBroadcastPkts" },
114967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxSAChanges" },
115967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxFragments" },
116967dd82fSFlorian Fainelli 	{ 4, 0xa0, "RxSymbolErrors" },
117967dd82fSFlorian Fainelli 	{ 4, 0xa4, "RxQoSPkts" },
118967dd82fSFlorian Fainelli 	{ 8, 0xa8, "RxQoSOctets" },
119967dd82fSFlorian Fainelli 	{ 4, 0xb0, "Pkts1523to2047Octets" },
120967dd82fSFlorian Fainelli 	{ 4, 0xb4, "Pkts2048to4095Octets" },
121967dd82fSFlorian Fainelli 	{ 4, 0xb8, "Pkts4096to8191Octets" },
122967dd82fSFlorian Fainelli 	{ 4, 0xbc, "Pkts8192to9728Octets" },
123967dd82fSFlorian Fainelli 	{ 4, 0xc0, "RxDiscarded" },
124967dd82fSFlorian Fainelli };
125967dd82fSFlorian Fainelli 
126967dd82fSFlorian Fainelli #define B53_MIBS_63XX_SIZE	ARRAY_SIZE(b53_mibs_63xx)
127967dd82fSFlorian Fainelli 
128967dd82fSFlorian Fainelli /* MIB counters */
129967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs[] = {
130967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
131967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
132967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
133967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
134967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
135967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
136967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
137967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
138967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
139967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
140967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
141967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
142967dd82fSFlorian Fainelli 	{ 8, 0x50, "RxOctets" },
143967dd82fSFlorian Fainelli 	{ 4, 0x58, "RxUndersizePkts" },
144967dd82fSFlorian Fainelli 	{ 4, 0x5c, "RxPausePkts" },
145967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts64Octets" },
146967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts65to127Octets" },
147967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts128to255Octets" },
148967dd82fSFlorian Fainelli 	{ 4, 0x6c, "Pkts256to511Octets" },
149967dd82fSFlorian Fainelli 	{ 4, 0x70, "Pkts512to1023Octets" },
150967dd82fSFlorian Fainelli 	{ 4, 0x74, "Pkts1024to1522Octets" },
151967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxOversizePkts" },
152967dd82fSFlorian Fainelli 	{ 4, 0x7c, "RxJabbers" },
153967dd82fSFlorian Fainelli 	{ 4, 0x80, "RxAlignmentErrors" },
154967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxFCSErrors" },
155967dd82fSFlorian Fainelli 	{ 8, 0x88, "RxGoodOctets" },
156967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxDropPkts" },
157967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxUnicastPkts" },
158967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxMulticastPkts" },
159967dd82fSFlorian Fainelli 	{ 4, 0x9c, "RxBroadcastPkts" },
160967dd82fSFlorian Fainelli 	{ 4, 0xa0, "RxSAChanges" },
161967dd82fSFlorian Fainelli 	{ 4, 0xa4, "RxFragments" },
162967dd82fSFlorian Fainelli 	{ 4, 0xa8, "RxJumboPkts" },
163967dd82fSFlorian Fainelli 	{ 4, 0xac, "RxSymbolErrors" },
164967dd82fSFlorian Fainelli 	{ 4, 0xc0, "RxDiscarded" },
165967dd82fSFlorian Fainelli };
166967dd82fSFlorian Fainelli 
167967dd82fSFlorian Fainelli #define B53_MIBS_SIZE	ARRAY_SIZE(b53_mibs)
168967dd82fSFlorian Fainelli 
169967dd82fSFlorian Fainelli static int b53_do_vlan_op(struct b53_device *dev, u8 op)
170967dd82fSFlorian Fainelli {
171967dd82fSFlorian Fainelli 	unsigned int i;
172967dd82fSFlorian Fainelli 
173967dd82fSFlorian Fainelli 	b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
174967dd82fSFlorian Fainelli 
175967dd82fSFlorian Fainelli 	for (i = 0; i < 10; i++) {
176967dd82fSFlorian Fainelli 		u8 vta;
177967dd82fSFlorian Fainelli 
178967dd82fSFlorian Fainelli 		b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
179967dd82fSFlorian Fainelli 		if (!(vta & VTA_START_CMD))
180967dd82fSFlorian Fainelli 			return 0;
181967dd82fSFlorian Fainelli 
182967dd82fSFlorian Fainelli 		usleep_range(100, 200);
183967dd82fSFlorian Fainelli 	}
184967dd82fSFlorian Fainelli 
185967dd82fSFlorian Fainelli 	return -EIO;
186967dd82fSFlorian Fainelli }
187967dd82fSFlorian Fainelli 
188967dd82fSFlorian Fainelli static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
189967dd82fSFlorian Fainelli 			       u16 untag)
190967dd82fSFlorian Fainelli {
191967dd82fSFlorian Fainelli 	if (is5325(dev)) {
192967dd82fSFlorian Fainelli 		u32 entry = 0;
193967dd82fSFlorian Fainelli 
194967dd82fSFlorian Fainelli 		if (members) {
195967dd82fSFlorian Fainelli 			entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
196967dd82fSFlorian Fainelli 				members;
197967dd82fSFlorian Fainelli 			if (dev->core_rev >= 3)
198967dd82fSFlorian Fainelli 				entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
199967dd82fSFlorian Fainelli 			else
200967dd82fSFlorian Fainelli 				entry |= VA_VALID_25;
201967dd82fSFlorian Fainelli 		}
202967dd82fSFlorian Fainelli 
203967dd82fSFlorian Fainelli 		b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
204967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
205967dd82fSFlorian Fainelli 			    VTA_RW_STATE_WR | VTA_RW_OP_EN);
206967dd82fSFlorian Fainelli 	} else if (is5365(dev)) {
207967dd82fSFlorian Fainelli 		u16 entry = 0;
208967dd82fSFlorian Fainelli 
209967dd82fSFlorian Fainelli 		if (members)
210967dd82fSFlorian Fainelli 			entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
211967dd82fSFlorian Fainelli 				members | VA_VALID_65;
212967dd82fSFlorian Fainelli 
213967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
214967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
215967dd82fSFlorian Fainelli 			    VTA_RW_STATE_WR | VTA_RW_OP_EN);
216967dd82fSFlorian Fainelli 	} else {
217967dd82fSFlorian Fainelli 		b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
218967dd82fSFlorian Fainelli 		b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
219967dd82fSFlorian Fainelli 			    (untag << VTE_UNTAG_S) | members);
220967dd82fSFlorian Fainelli 
221967dd82fSFlorian Fainelli 		b53_do_vlan_op(dev, VTA_CMD_WRITE);
222967dd82fSFlorian Fainelli 	}
223967dd82fSFlorian Fainelli }
224967dd82fSFlorian Fainelli 
225967dd82fSFlorian Fainelli void b53_set_forwarding(struct b53_device *dev, int enable)
226967dd82fSFlorian Fainelli {
227967dd82fSFlorian Fainelli 	u8 mgmt;
228967dd82fSFlorian Fainelli 
229967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
230967dd82fSFlorian Fainelli 
231967dd82fSFlorian Fainelli 	if (enable)
232967dd82fSFlorian Fainelli 		mgmt |= SM_SW_FWD_EN;
233967dd82fSFlorian Fainelli 	else
234967dd82fSFlorian Fainelli 		mgmt &= ~SM_SW_FWD_EN;
235967dd82fSFlorian Fainelli 
236967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
237967dd82fSFlorian Fainelli }
238967dd82fSFlorian Fainelli 
239967dd82fSFlorian Fainelli static void b53_enable_vlan(struct b53_device *dev, int enable)
240967dd82fSFlorian Fainelli {
241967dd82fSFlorian Fainelli 	u8 mgmt, vc0, vc1, vc4 = 0, vc5;
242967dd82fSFlorian Fainelli 
243967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
244967dd82fSFlorian Fainelli 	b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
245967dd82fSFlorian Fainelli 	b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
246967dd82fSFlorian Fainelli 
247967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
248967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
249967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
250967dd82fSFlorian Fainelli 	} else if (is63xx(dev)) {
251967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
252967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
253967dd82fSFlorian Fainelli 	} else {
254967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
255967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
256967dd82fSFlorian Fainelli 	}
257967dd82fSFlorian Fainelli 
258967dd82fSFlorian Fainelli 	mgmt &= ~SM_SW_FWD_MODE;
259967dd82fSFlorian Fainelli 
260967dd82fSFlorian Fainelli 	if (enable) {
261967dd82fSFlorian Fainelli 		vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
262967dd82fSFlorian Fainelli 		vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
263967dd82fSFlorian Fainelli 		vc4 &= ~VC4_ING_VID_CHECK_MASK;
264967dd82fSFlorian Fainelli 		vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
265967dd82fSFlorian Fainelli 		vc5 |= VC5_DROP_VTABLE_MISS;
266967dd82fSFlorian Fainelli 
267967dd82fSFlorian Fainelli 		if (is5325(dev))
268967dd82fSFlorian Fainelli 			vc0 &= ~VC0_RESERVED_1;
269967dd82fSFlorian Fainelli 
270967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
271967dd82fSFlorian Fainelli 			vc1 |= VC1_RX_MCST_TAG_EN;
272967dd82fSFlorian Fainelli 
273967dd82fSFlorian Fainelli 		if (!is5325(dev) && !is5365(dev)) {
274967dd82fSFlorian Fainelli 			if (dev->allow_vid_4095)
275967dd82fSFlorian Fainelli 				vc5 |= VC5_VID_FFF_EN;
276967dd82fSFlorian Fainelli 			else
277967dd82fSFlorian Fainelli 				vc5 &= ~VC5_VID_FFF_EN;
278967dd82fSFlorian Fainelli 		}
279967dd82fSFlorian Fainelli 	} else {
280967dd82fSFlorian Fainelli 		vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
281967dd82fSFlorian Fainelli 		vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
282967dd82fSFlorian Fainelli 		vc4 &= ~VC4_ING_VID_CHECK_MASK;
283967dd82fSFlorian Fainelli 		vc5 &= ~VC5_DROP_VTABLE_MISS;
284967dd82fSFlorian Fainelli 
285967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
286967dd82fSFlorian Fainelli 			vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
287967dd82fSFlorian Fainelli 		else
288967dd82fSFlorian Fainelli 			vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
289967dd82fSFlorian Fainelli 
290967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
291967dd82fSFlorian Fainelli 			vc1 &= ~VC1_RX_MCST_TAG_EN;
292967dd82fSFlorian Fainelli 
293967dd82fSFlorian Fainelli 		if (!is5325(dev) && !is5365(dev))
294967dd82fSFlorian Fainelli 			vc5 &= ~VC5_VID_FFF_EN;
295967dd82fSFlorian Fainelli 	}
296967dd82fSFlorian Fainelli 
297967dd82fSFlorian Fainelli 	b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
298967dd82fSFlorian Fainelli 	b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
299967dd82fSFlorian Fainelli 
300967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
301967dd82fSFlorian Fainelli 		/* enable the high 8 bit vid check on 5325 */
302967dd82fSFlorian Fainelli 		if (is5325(dev) && enable)
303967dd82fSFlorian Fainelli 			b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
304967dd82fSFlorian Fainelli 				   VC3_HIGH_8BIT_EN);
305967dd82fSFlorian Fainelli 		else
306967dd82fSFlorian Fainelli 			b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
307967dd82fSFlorian Fainelli 
308967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
309967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
310967dd82fSFlorian Fainelli 	} else if (is63xx(dev)) {
311967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
312967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
313967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
314967dd82fSFlorian Fainelli 	} else {
315967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
316967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
317967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
318967dd82fSFlorian Fainelli 	}
319967dd82fSFlorian Fainelli 
320967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
321967dd82fSFlorian Fainelli }
322967dd82fSFlorian Fainelli 
323967dd82fSFlorian Fainelli static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100)
324967dd82fSFlorian Fainelli {
325967dd82fSFlorian Fainelli 	u32 port_mask = 0;
326967dd82fSFlorian Fainelli 	u16 max_size = JMS_MIN_SIZE;
327967dd82fSFlorian Fainelli 
328967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev))
329967dd82fSFlorian Fainelli 		return -EINVAL;
330967dd82fSFlorian Fainelli 
331967dd82fSFlorian Fainelli 	if (enable) {
332967dd82fSFlorian Fainelli 		port_mask = dev->enabled_ports;
333967dd82fSFlorian Fainelli 		max_size = JMS_MAX_SIZE;
334967dd82fSFlorian Fainelli 		if (allow_10_100)
335967dd82fSFlorian Fainelli 			port_mask |= JPM_10_100_JUMBO_EN;
336967dd82fSFlorian Fainelli 	}
337967dd82fSFlorian Fainelli 
338967dd82fSFlorian Fainelli 	b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
339967dd82fSFlorian Fainelli 	return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
340967dd82fSFlorian Fainelli }
341967dd82fSFlorian Fainelli 
342967dd82fSFlorian Fainelli static int b53_flush_arl(struct b53_device *dev)
343967dd82fSFlorian Fainelli {
344967dd82fSFlorian Fainelli 	unsigned int i;
345967dd82fSFlorian Fainelli 
346967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
347967dd82fSFlorian Fainelli 		   FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
348967dd82fSFlorian Fainelli 
349967dd82fSFlorian Fainelli 	for (i = 0; i < 10; i++) {
350967dd82fSFlorian Fainelli 		u8 fast_age_ctrl;
351967dd82fSFlorian Fainelli 
352967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
353967dd82fSFlorian Fainelli 			  &fast_age_ctrl);
354967dd82fSFlorian Fainelli 
355967dd82fSFlorian Fainelli 		if (!(fast_age_ctrl & FAST_AGE_DONE))
356967dd82fSFlorian Fainelli 			goto out;
357967dd82fSFlorian Fainelli 
358967dd82fSFlorian Fainelli 		msleep(1);
359967dd82fSFlorian Fainelli 	}
360967dd82fSFlorian Fainelli 
361967dd82fSFlorian Fainelli 	return -ETIMEDOUT;
362967dd82fSFlorian Fainelli out:
363967dd82fSFlorian Fainelli 	/* Only age dynamic entries (default behavior) */
364967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DYNAMIC);
365967dd82fSFlorian Fainelli 	return 0;
366967dd82fSFlorian Fainelli }
367967dd82fSFlorian Fainelli 
368967dd82fSFlorian Fainelli static int b53_enable_port(struct dsa_switch *ds, int port,
369967dd82fSFlorian Fainelli 			   struct phy_device *phy)
370967dd82fSFlorian Fainelli {
371967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
372967dd82fSFlorian Fainelli 
373967dd82fSFlorian Fainelli 	/* Clear the Rx and Tx disable bits and set to no spanning tree */
374967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0);
375967dd82fSFlorian Fainelli 
376967dd82fSFlorian Fainelli 	return 0;
377967dd82fSFlorian Fainelli }
378967dd82fSFlorian Fainelli 
379967dd82fSFlorian Fainelli static void b53_disable_port(struct dsa_switch *ds, int port,
380967dd82fSFlorian Fainelli 			     struct phy_device *phy)
381967dd82fSFlorian Fainelli {
382967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
383967dd82fSFlorian Fainelli 	u8 reg;
384967dd82fSFlorian Fainelli 
385967dd82fSFlorian Fainelli 	/* Disable Tx/Rx for the port */
386967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), &reg);
387967dd82fSFlorian Fainelli 	reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
388967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg);
389967dd82fSFlorian Fainelli }
390967dd82fSFlorian Fainelli 
391967dd82fSFlorian Fainelli static void b53_enable_cpu_port(struct b53_device *dev)
392967dd82fSFlorian Fainelli {
393967dd82fSFlorian Fainelli 	unsigned int cpu_port = dev->cpu_port;
394967dd82fSFlorian Fainelli 	u8 port_ctrl;
395967dd82fSFlorian Fainelli 
396967dd82fSFlorian Fainelli 	/* BCM5325 CPU port is at 8 */
397967dd82fSFlorian Fainelli 	if ((is5325(dev) || is5365(dev)) && cpu_port == B53_CPU_PORT_25)
398967dd82fSFlorian Fainelli 		cpu_port = B53_CPU_PORT;
399967dd82fSFlorian Fainelli 
400967dd82fSFlorian Fainelli 	port_ctrl = PORT_CTRL_RX_BCST_EN |
401967dd82fSFlorian Fainelli 		    PORT_CTRL_RX_MCST_EN |
402967dd82fSFlorian Fainelli 		    PORT_CTRL_RX_UCST_EN;
403967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(cpu_port), port_ctrl);
404967dd82fSFlorian Fainelli }
405967dd82fSFlorian Fainelli 
406967dd82fSFlorian Fainelli static void b53_enable_mib(struct b53_device *dev)
407967dd82fSFlorian Fainelli {
408967dd82fSFlorian Fainelli 	u8 gc;
409967dd82fSFlorian Fainelli 
410967dd82fSFlorian Fainelli 	b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
411967dd82fSFlorian Fainelli 	gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
412967dd82fSFlorian Fainelli 	b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
413967dd82fSFlorian Fainelli }
414967dd82fSFlorian Fainelli 
415967dd82fSFlorian Fainelli static int b53_configure_vlan(struct b53_device *dev)
416967dd82fSFlorian Fainelli {
417967dd82fSFlorian Fainelli 	int i;
418967dd82fSFlorian Fainelli 
419967dd82fSFlorian Fainelli 	/* clear all vlan entries */
420967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
421967dd82fSFlorian Fainelli 		for (i = 1; i < dev->num_vlans; i++)
422967dd82fSFlorian Fainelli 			b53_set_vlan_entry(dev, i, 0, 0);
423967dd82fSFlorian Fainelli 	} else {
424967dd82fSFlorian Fainelli 		b53_do_vlan_op(dev, VTA_CMD_CLEAR);
425967dd82fSFlorian Fainelli 	}
426967dd82fSFlorian Fainelli 
427967dd82fSFlorian Fainelli 	b53_enable_vlan(dev, false);
428967dd82fSFlorian Fainelli 
429967dd82fSFlorian Fainelli 	b53_for_each_port(dev, i)
430967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE,
431967dd82fSFlorian Fainelli 			    B53_VLAN_PORT_DEF_TAG(i), 1);
432967dd82fSFlorian Fainelli 
433967dd82fSFlorian Fainelli 	if (!is5325(dev) && !is5365(dev))
434967dd82fSFlorian Fainelli 		b53_set_jumbo(dev, dev->enable_jumbo, false);
435967dd82fSFlorian Fainelli 
436967dd82fSFlorian Fainelli 	return 0;
437967dd82fSFlorian Fainelli }
438967dd82fSFlorian Fainelli 
439967dd82fSFlorian Fainelli static void b53_switch_reset_gpio(struct b53_device *dev)
440967dd82fSFlorian Fainelli {
441967dd82fSFlorian Fainelli 	int gpio = dev->reset_gpio;
442967dd82fSFlorian Fainelli 
443967dd82fSFlorian Fainelli 	if (gpio < 0)
444967dd82fSFlorian Fainelli 		return;
445967dd82fSFlorian Fainelli 
446967dd82fSFlorian Fainelli 	/* Reset sequence: RESET low(50ms)->high(20ms)
447967dd82fSFlorian Fainelli 	 */
448967dd82fSFlorian Fainelli 	gpio_set_value(gpio, 0);
449967dd82fSFlorian Fainelli 	mdelay(50);
450967dd82fSFlorian Fainelli 
451967dd82fSFlorian Fainelli 	gpio_set_value(gpio, 1);
452967dd82fSFlorian Fainelli 	mdelay(20);
453967dd82fSFlorian Fainelli 
454967dd82fSFlorian Fainelli 	dev->current_page = 0xff;
455967dd82fSFlorian Fainelli }
456967dd82fSFlorian Fainelli 
457967dd82fSFlorian Fainelli static int b53_switch_reset(struct b53_device *dev)
458967dd82fSFlorian Fainelli {
459967dd82fSFlorian Fainelli 	u8 mgmt;
460967dd82fSFlorian Fainelli 
461967dd82fSFlorian Fainelli 	b53_switch_reset_gpio(dev);
462967dd82fSFlorian Fainelli 
463967dd82fSFlorian Fainelli 	if (is539x(dev)) {
464967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
465967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
466967dd82fSFlorian Fainelli 	}
467967dd82fSFlorian Fainelli 
468967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
469967dd82fSFlorian Fainelli 
470967dd82fSFlorian Fainelli 	if (!(mgmt & SM_SW_FWD_EN)) {
471967dd82fSFlorian Fainelli 		mgmt &= ~SM_SW_FWD_MODE;
472967dd82fSFlorian Fainelli 		mgmt |= SM_SW_FWD_EN;
473967dd82fSFlorian Fainelli 
474967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
475967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
476967dd82fSFlorian Fainelli 
477967dd82fSFlorian Fainelli 		if (!(mgmt & SM_SW_FWD_EN)) {
478967dd82fSFlorian Fainelli 			dev_err(dev->dev, "Failed to enable switch!\n");
479967dd82fSFlorian Fainelli 			return -EINVAL;
480967dd82fSFlorian Fainelli 		}
481967dd82fSFlorian Fainelli 	}
482967dd82fSFlorian Fainelli 
483967dd82fSFlorian Fainelli 	b53_enable_mib(dev);
484967dd82fSFlorian Fainelli 
485967dd82fSFlorian Fainelli 	return b53_flush_arl(dev);
486967dd82fSFlorian Fainelli }
487967dd82fSFlorian Fainelli 
488967dd82fSFlorian Fainelli static int b53_phy_read16(struct dsa_switch *ds, int addr, int reg)
489967dd82fSFlorian Fainelli {
490967dd82fSFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
491967dd82fSFlorian Fainelli 	u16 value = 0;
492967dd82fSFlorian Fainelli 	int ret;
493967dd82fSFlorian Fainelli 
494967dd82fSFlorian Fainelli 	if (priv->ops->phy_read16)
495967dd82fSFlorian Fainelli 		ret = priv->ops->phy_read16(priv, addr, reg, &value);
496967dd82fSFlorian Fainelli 	else
497967dd82fSFlorian Fainelli 		ret = b53_read16(priv, B53_PORT_MII_PAGE(addr),
498967dd82fSFlorian Fainelli 				 reg * 2, &value);
499967dd82fSFlorian Fainelli 
500967dd82fSFlorian Fainelli 	return ret ? ret : value;
501967dd82fSFlorian Fainelli }
502967dd82fSFlorian Fainelli 
503967dd82fSFlorian Fainelli static int b53_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
504967dd82fSFlorian Fainelli {
505967dd82fSFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
506967dd82fSFlorian Fainelli 
507967dd82fSFlorian Fainelli 	if (priv->ops->phy_write16)
508967dd82fSFlorian Fainelli 		return priv->ops->phy_write16(priv, addr, reg, val);
509967dd82fSFlorian Fainelli 
510967dd82fSFlorian Fainelli 	return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg * 2, val);
511967dd82fSFlorian Fainelli }
512967dd82fSFlorian Fainelli 
513967dd82fSFlorian Fainelli static int b53_reset_switch(struct b53_device *priv)
514967dd82fSFlorian Fainelli {
515967dd82fSFlorian Fainelli 	/* reset vlans */
516967dd82fSFlorian Fainelli 	priv->enable_jumbo = false;
517967dd82fSFlorian Fainelli 
518967dd82fSFlorian Fainelli 	memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports);
519967dd82fSFlorian Fainelli 
520967dd82fSFlorian Fainelli 	return b53_switch_reset(priv);
521967dd82fSFlorian Fainelli }
522967dd82fSFlorian Fainelli 
523967dd82fSFlorian Fainelli static int b53_apply_config(struct b53_device *priv)
524967dd82fSFlorian Fainelli {
525967dd82fSFlorian Fainelli 	/* disable switching */
526967dd82fSFlorian Fainelli 	b53_set_forwarding(priv, 0);
527967dd82fSFlorian Fainelli 
528967dd82fSFlorian Fainelli 	b53_configure_vlan(priv);
529967dd82fSFlorian Fainelli 
530967dd82fSFlorian Fainelli 	/* enable switching */
531967dd82fSFlorian Fainelli 	b53_set_forwarding(priv, 1);
532967dd82fSFlorian Fainelli 
533967dd82fSFlorian Fainelli 	return 0;
534967dd82fSFlorian Fainelli }
535967dd82fSFlorian Fainelli 
536967dd82fSFlorian Fainelli static void b53_reset_mib(struct b53_device *priv)
537967dd82fSFlorian Fainelli {
538967dd82fSFlorian Fainelli 	u8 gc;
539967dd82fSFlorian Fainelli 
540967dd82fSFlorian Fainelli 	b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
541967dd82fSFlorian Fainelli 
542967dd82fSFlorian Fainelli 	b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
543967dd82fSFlorian Fainelli 	msleep(1);
544967dd82fSFlorian Fainelli 	b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
545967dd82fSFlorian Fainelli 	msleep(1);
546967dd82fSFlorian Fainelli }
547967dd82fSFlorian Fainelli 
548967dd82fSFlorian Fainelli static const struct b53_mib_desc *b53_get_mib(struct b53_device *dev)
549967dd82fSFlorian Fainelli {
550967dd82fSFlorian Fainelli 	if (is5365(dev))
551967dd82fSFlorian Fainelli 		return b53_mibs_65;
552967dd82fSFlorian Fainelli 	else if (is63xx(dev))
553967dd82fSFlorian Fainelli 		return b53_mibs_63xx;
554967dd82fSFlorian Fainelli 	else
555967dd82fSFlorian Fainelli 		return b53_mibs;
556967dd82fSFlorian Fainelli }
557967dd82fSFlorian Fainelli 
558967dd82fSFlorian Fainelli static unsigned int b53_get_mib_size(struct b53_device *dev)
559967dd82fSFlorian Fainelli {
560967dd82fSFlorian Fainelli 	if (is5365(dev))
561967dd82fSFlorian Fainelli 		return B53_MIBS_65_SIZE;
562967dd82fSFlorian Fainelli 	else if (is63xx(dev))
563967dd82fSFlorian Fainelli 		return B53_MIBS_63XX_SIZE;
564967dd82fSFlorian Fainelli 	else
565967dd82fSFlorian Fainelli 		return B53_MIBS_SIZE;
566967dd82fSFlorian Fainelli }
567967dd82fSFlorian Fainelli 
568967dd82fSFlorian Fainelli static void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
569967dd82fSFlorian Fainelli {
570967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
571967dd82fSFlorian Fainelli 	const struct b53_mib_desc *mibs = b53_get_mib(dev);
572967dd82fSFlorian Fainelli 	unsigned int mib_size = b53_get_mib_size(dev);
573967dd82fSFlorian Fainelli 	unsigned int i;
574967dd82fSFlorian Fainelli 
575967dd82fSFlorian Fainelli 	for (i = 0; i < mib_size; i++)
576967dd82fSFlorian Fainelli 		memcpy(data + i * ETH_GSTRING_LEN,
577967dd82fSFlorian Fainelli 		       mibs[i].name, ETH_GSTRING_LEN);
578967dd82fSFlorian Fainelli }
579967dd82fSFlorian Fainelli 
580967dd82fSFlorian Fainelli static void b53_get_ethtool_stats(struct dsa_switch *ds, int port,
581967dd82fSFlorian Fainelli 				  uint64_t *data)
582967dd82fSFlorian Fainelli {
583967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
584967dd82fSFlorian Fainelli 	const struct b53_mib_desc *mibs = b53_get_mib(dev);
585967dd82fSFlorian Fainelli 	unsigned int mib_size = b53_get_mib_size(dev);
586967dd82fSFlorian Fainelli 	const struct b53_mib_desc *s;
587967dd82fSFlorian Fainelli 	unsigned int i;
588967dd82fSFlorian Fainelli 	u64 val = 0;
589967dd82fSFlorian Fainelli 
590967dd82fSFlorian Fainelli 	if (is5365(dev) && port == 5)
591967dd82fSFlorian Fainelli 		port = 8;
592967dd82fSFlorian Fainelli 
593967dd82fSFlorian Fainelli 	mutex_lock(&dev->stats_mutex);
594967dd82fSFlorian Fainelli 
595967dd82fSFlorian Fainelli 	for (i = 0; i < mib_size; i++) {
596967dd82fSFlorian Fainelli 		s = &mibs[i];
597967dd82fSFlorian Fainelli 
598967dd82fSFlorian Fainelli 		if (mibs->size == 8) {
599967dd82fSFlorian Fainelli 			b53_read64(dev, B53_MIB_PAGE(port), s->offset, &val);
600967dd82fSFlorian Fainelli 		} else {
601967dd82fSFlorian Fainelli 			u32 val32;
602967dd82fSFlorian Fainelli 
603967dd82fSFlorian Fainelli 			b53_read32(dev, B53_MIB_PAGE(port), s->offset,
604967dd82fSFlorian Fainelli 				   &val32);
605967dd82fSFlorian Fainelli 			val = val32;
606967dd82fSFlorian Fainelli 		}
607967dd82fSFlorian Fainelli 		data[i] = (u64)val;
608967dd82fSFlorian Fainelli 	}
609967dd82fSFlorian Fainelli 
610967dd82fSFlorian Fainelli 	mutex_unlock(&dev->stats_mutex);
611967dd82fSFlorian Fainelli }
612967dd82fSFlorian Fainelli 
613967dd82fSFlorian Fainelli static int b53_get_sset_count(struct dsa_switch *ds)
614967dd82fSFlorian Fainelli {
615967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
616967dd82fSFlorian Fainelli 
617967dd82fSFlorian Fainelli 	return b53_get_mib_size(dev);
618967dd82fSFlorian Fainelli }
619967dd82fSFlorian Fainelli 
620967dd82fSFlorian Fainelli static int b53_set_addr(struct dsa_switch *ds, u8 *addr)
621967dd82fSFlorian Fainelli {
622967dd82fSFlorian Fainelli 	return 0;
623967dd82fSFlorian Fainelli }
624967dd82fSFlorian Fainelli 
625967dd82fSFlorian Fainelli static int b53_setup(struct dsa_switch *ds)
626967dd82fSFlorian Fainelli {
627967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
628967dd82fSFlorian Fainelli 	unsigned int port;
629967dd82fSFlorian Fainelli 	int ret;
630967dd82fSFlorian Fainelli 
631967dd82fSFlorian Fainelli 	ret = b53_reset_switch(dev);
632967dd82fSFlorian Fainelli 	if (ret) {
633967dd82fSFlorian Fainelli 		dev_err(ds->dev, "failed to reset switch\n");
634967dd82fSFlorian Fainelli 		return ret;
635967dd82fSFlorian Fainelli 	}
636967dd82fSFlorian Fainelli 
637967dd82fSFlorian Fainelli 	b53_reset_mib(dev);
638967dd82fSFlorian Fainelli 
639967dd82fSFlorian Fainelli 	ret = b53_apply_config(dev);
640967dd82fSFlorian Fainelli 	if (ret)
641967dd82fSFlorian Fainelli 		dev_err(ds->dev, "failed to apply configuration\n");
642967dd82fSFlorian Fainelli 
643967dd82fSFlorian Fainelli 	for (port = 0; port < dev->num_ports; port++) {
644967dd82fSFlorian Fainelli 		if (BIT(port) & ds->enabled_port_mask)
645967dd82fSFlorian Fainelli 			b53_enable_port(ds, port, NULL);
646967dd82fSFlorian Fainelli 		else if (dsa_is_cpu_port(ds, port))
647967dd82fSFlorian Fainelli 			b53_enable_cpu_port(dev);
648967dd82fSFlorian Fainelli 		else
649967dd82fSFlorian Fainelli 			b53_disable_port(ds, port, NULL);
650967dd82fSFlorian Fainelli 	}
651967dd82fSFlorian Fainelli 
652967dd82fSFlorian Fainelli 	return ret;
653967dd82fSFlorian Fainelli }
654967dd82fSFlorian Fainelli 
655967dd82fSFlorian Fainelli static void b53_adjust_link(struct dsa_switch *ds, int port,
656967dd82fSFlorian Fainelli 			    struct phy_device *phydev)
657967dd82fSFlorian Fainelli {
658967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
659967dd82fSFlorian Fainelli 	u8 rgmii_ctrl = 0, reg = 0, off;
660967dd82fSFlorian Fainelli 
661967dd82fSFlorian Fainelli 	if (!phy_is_pseudo_fixed_link(phydev))
662967dd82fSFlorian Fainelli 		return;
663967dd82fSFlorian Fainelli 
664967dd82fSFlorian Fainelli 	/* Override the port settings */
665967dd82fSFlorian Fainelli 	if (port == dev->cpu_port) {
666967dd82fSFlorian Fainelli 		off = B53_PORT_OVERRIDE_CTRL;
667967dd82fSFlorian Fainelli 		reg = PORT_OVERRIDE_EN;
668967dd82fSFlorian Fainelli 	} else {
669967dd82fSFlorian Fainelli 		off = B53_GMII_PORT_OVERRIDE_CTRL(port);
670967dd82fSFlorian Fainelli 		reg = GMII_PO_EN;
671967dd82fSFlorian Fainelli 	}
672967dd82fSFlorian Fainelli 
673967dd82fSFlorian Fainelli 	/* Set the link UP */
674967dd82fSFlorian Fainelli 	if (phydev->link)
675967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_LINK;
676967dd82fSFlorian Fainelli 
677967dd82fSFlorian Fainelli 	if (phydev->duplex == DUPLEX_FULL)
678967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_FULL_DUPLEX;
679967dd82fSFlorian Fainelli 
680967dd82fSFlorian Fainelli 	switch (phydev->speed) {
681967dd82fSFlorian Fainelli 	case 2000:
682967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_2000M;
683967dd82fSFlorian Fainelli 		/* fallthrough */
684967dd82fSFlorian Fainelli 	case SPEED_1000:
685967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_1000M;
686967dd82fSFlorian Fainelli 		break;
687967dd82fSFlorian Fainelli 	case SPEED_100:
688967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_100M;
689967dd82fSFlorian Fainelli 		break;
690967dd82fSFlorian Fainelli 	case SPEED_10:
691967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_10M;
692967dd82fSFlorian Fainelli 		break;
693967dd82fSFlorian Fainelli 	default:
694967dd82fSFlorian Fainelli 		dev_err(ds->dev, "unknown speed: %d\n", phydev->speed);
695967dd82fSFlorian Fainelli 		return;
696967dd82fSFlorian Fainelli 	}
697967dd82fSFlorian Fainelli 
698967dd82fSFlorian Fainelli 	/* Enable flow control on BCM5301x's CPU port */
699967dd82fSFlorian Fainelli 	if (is5301x(dev) && port == dev->cpu_port)
700967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_RX_FLOW | PORT_OVERRIDE_TX_FLOW;
701967dd82fSFlorian Fainelli 
702967dd82fSFlorian Fainelli 	if (phydev->pause) {
703967dd82fSFlorian Fainelli 		if (phydev->asym_pause)
704967dd82fSFlorian Fainelli 			reg |= PORT_OVERRIDE_TX_FLOW;
705967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_RX_FLOW;
706967dd82fSFlorian Fainelli 	}
707967dd82fSFlorian Fainelli 
708967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, off, reg);
709967dd82fSFlorian Fainelli 
710967dd82fSFlorian Fainelli 	if (is531x5(dev) && phy_interface_is_rgmii(phydev)) {
711967dd82fSFlorian Fainelli 		if (port == 8)
712967dd82fSFlorian Fainelli 			off = B53_RGMII_CTRL_IMP;
713967dd82fSFlorian Fainelli 		else
714967dd82fSFlorian Fainelli 			off = B53_RGMII_CTRL_P(port);
715967dd82fSFlorian Fainelli 
716967dd82fSFlorian Fainelli 		/* Configure the port RGMII clock delay by DLL disabled and
717967dd82fSFlorian Fainelli 		 * tx_clk aligned timing (restoring to reset defaults)
718967dd82fSFlorian Fainelli 		 */
719967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl);
720967dd82fSFlorian Fainelli 		rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC |
721967dd82fSFlorian Fainelli 				RGMII_CTRL_TIMING_SEL);
722967dd82fSFlorian Fainelli 
723967dd82fSFlorian Fainelli 		/* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make
724967dd82fSFlorian Fainelli 		 * sure that we enable the port TX clock internal delay to
725967dd82fSFlorian Fainelli 		 * account for this internal delay that is inserted, otherwise
726967dd82fSFlorian Fainelli 		 * the switch won't be able to receive correctly.
727967dd82fSFlorian Fainelli 		 *
728967dd82fSFlorian Fainelli 		 * PHY_INTERFACE_MODE_RGMII means that we are not introducing
729967dd82fSFlorian Fainelli 		 * any delay neither on transmission nor reception, so the
730967dd82fSFlorian Fainelli 		 * BCM53125 must also be configured accordingly to account for
731967dd82fSFlorian Fainelli 		 * the lack of delay and introduce
732967dd82fSFlorian Fainelli 		 *
733967dd82fSFlorian Fainelli 		 * The BCM53125 switch has its RX clock and TX clock control
734967dd82fSFlorian Fainelli 		 * swapped, hence the reason why we modify the TX clock path in
735967dd82fSFlorian Fainelli 		 * the "RGMII" case
736967dd82fSFlorian Fainelli 		 */
737967dd82fSFlorian Fainelli 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
738967dd82fSFlorian Fainelli 			rgmii_ctrl |= RGMII_CTRL_DLL_TXC;
739967dd82fSFlorian Fainelli 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
740967dd82fSFlorian Fainelli 			rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC;
741967dd82fSFlorian Fainelli 		rgmii_ctrl |= RGMII_CTRL_TIMING_SEL;
742967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
743967dd82fSFlorian Fainelli 
744967dd82fSFlorian Fainelli 		dev_info(ds->dev, "Configured port %d for %s\n", port,
745967dd82fSFlorian Fainelli 			 phy_modes(phydev->interface));
746967dd82fSFlorian Fainelli 	}
747967dd82fSFlorian Fainelli 
748967dd82fSFlorian Fainelli 	/* configure MII port if necessary */
749967dd82fSFlorian Fainelli 	if (is5325(dev)) {
750967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
751967dd82fSFlorian Fainelli 			  &reg);
752967dd82fSFlorian Fainelli 
753967dd82fSFlorian Fainelli 		/* reverse mii needs to be enabled */
754967dd82fSFlorian Fainelli 		if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
755967dd82fSFlorian Fainelli 			b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
756967dd82fSFlorian Fainelli 				   reg | PORT_OVERRIDE_RV_MII_25);
757967dd82fSFlorian Fainelli 			b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
758967dd82fSFlorian Fainelli 				  &reg);
759967dd82fSFlorian Fainelli 
760967dd82fSFlorian Fainelli 			if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
761967dd82fSFlorian Fainelli 				dev_err(ds->dev,
762967dd82fSFlorian Fainelli 					"Failed to enable reverse MII mode\n");
763967dd82fSFlorian Fainelli 				return;
764967dd82fSFlorian Fainelli 			}
765967dd82fSFlorian Fainelli 		}
766967dd82fSFlorian Fainelli 	} else if (is5301x(dev)) {
767967dd82fSFlorian Fainelli 		if (port != dev->cpu_port) {
768967dd82fSFlorian Fainelli 			u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(dev->cpu_port);
769967dd82fSFlorian Fainelli 			u8 gmii_po;
770967dd82fSFlorian Fainelli 
771967dd82fSFlorian Fainelli 			b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
772967dd82fSFlorian Fainelli 			gmii_po |= GMII_PO_LINK |
773967dd82fSFlorian Fainelli 				   GMII_PO_RX_FLOW |
774967dd82fSFlorian Fainelli 				   GMII_PO_TX_FLOW |
775967dd82fSFlorian Fainelli 				   GMII_PO_EN |
776967dd82fSFlorian Fainelli 				   GMII_PO_SPEED_2000M;
777967dd82fSFlorian Fainelli 			b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
778967dd82fSFlorian Fainelli 		}
779967dd82fSFlorian Fainelli 	}
780967dd82fSFlorian Fainelli }
781967dd82fSFlorian Fainelli 
782*1da6df85SFlorian Fainelli /* Address Resolution Logic routines */
783*1da6df85SFlorian Fainelli static int b53_arl_op_wait(struct b53_device *dev)
784*1da6df85SFlorian Fainelli {
785*1da6df85SFlorian Fainelli 	unsigned int timeout = 10;
786*1da6df85SFlorian Fainelli 	u8 reg;
787*1da6df85SFlorian Fainelli 
788*1da6df85SFlorian Fainelli 	do {
789*1da6df85SFlorian Fainelli 		b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &reg);
790*1da6df85SFlorian Fainelli 		if (!(reg & ARLTBL_START_DONE))
791*1da6df85SFlorian Fainelli 			return 0;
792*1da6df85SFlorian Fainelli 
793*1da6df85SFlorian Fainelli 		usleep_range(1000, 2000);
794*1da6df85SFlorian Fainelli 	} while (timeout--);
795*1da6df85SFlorian Fainelli 
796*1da6df85SFlorian Fainelli 	dev_warn(dev->dev, "timeout waiting for ARL to finish: 0x%02x\n", reg);
797*1da6df85SFlorian Fainelli 
798*1da6df85SFlorian Fainelli 	return -ETIMEDOUT;
799*1da6df85SFlorian Fainelli }
800*1da6df85SFlorian Fainelli 
801*1da6df85SFlorian Fainelli static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)
802*1da6df85SFlorian Fainelli {
803*1da6df85SFlorian Fainelli 	u8 reg;
804*1da6df85SFlorian Fainelli 
805*1da6df85SFlorian Fainelli 	if (op > ARLTBL_RW)
806*1da6df85SFlorian Fainelli 		return -EINVAL;
807*1da6df85SFlorian Fainelli 
808*1da6df85SFlorian Fainelli 	b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, &reg);
809*1da6df85SFlorian Fainelli 	reg |= ARLTBL_START_DONE;
810*1da6df85SFlorian Fainelli 	if (op)
811*1da6df85SFlorian Fainelli 		reg |= ARLTBL_RW;
812*1da6df85SFlorian Fainelli 	else
813*1da6df85SFlorian Fainelli 		reg &= ~ARLTBL_RW;
814*1da6df85SFlorian Fainelli 	b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_RW_CTRL, reg);
815*1da6df85SFlorian Fainelli 
816*1da6df85SFlorian Fainelli 	return b53_arl_op_wait(dev);
817*1da6df85SFlorian Fainelli }
818*1da6df85SFlorian Fainelli 
819*1da6df85SFlorian Fainelli static int b53_arl_read(struct b53_device *dev, u64 mac,
820*1da6df85SFlorian Fainelli 			u16 vid, struct b53_arl_entry *ent, u8 *idx,
821*1da6df85SFlorian Fainelli 			bool is_valid)
822*1da6df85SFlorian Fainelli {
823*1da6df85SFlorian Fainelli 	unsigned int i;
824*1da6df85SFlorian Fainelli 	int ret;
825*1da6df85SFlorian Fainelli 
826*1da6df85SFlorian Fainelli 	ret = b53_arl_op_wait(dev);
827*1da6df85SFlorian Fainelli 	if (ret)
828*1da6df85SFlorian Fainelli 		return ret;
829*1da6df85SFlorian Fainelli 
830*1da6df85SFlorian Fainelli 	/* Read the bins */
831*1da6df85SFlorian Fainelli 	for (i = 0; i < dev->num_arl_entries; i++) {
832*1da6df85SFlorian Fainelli 		u64 mac_vid;
833*1da6df85SFlorian Fainelli 		u32 fwd_entry;
834*1da6df85SFlorian Fainelli 
835*1da6df85SFlorian Fainelli 		b53_read64(dev, B53_ARLIO_PAGE,
836*1da6df85SFlorian Fainelli 			   B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid);
837*1da6df85SFlorian Fainelli 		b53_read32(dev, B53_ARLIO_PAGE,
838*1da6df85SFlorian Fainelli 			   B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);
839*1da6df85SFlorian Fainelli 		b53_arl_to_entry(ent, mac_vid, fwd_entry);
840*1da6df85SFlorian Fainelli 
841*1da6df85SFlorian Fainelli 		if (!(fwd_entry & ARLTBL_VALID))
842*1da6df85SFlorian Fainelli 			continue;
843*1da6df85SFlorian Fainelli 		if ((mac_vid & ARLTBL_MAC_MASK) != mac)
844*1da6df85SFlorian Fainelli 			continue;
845*1da6df85SFlorian Fainelli 		*idx = i;
846*1da6df85SFlorian Fainelli 	}
847*1da6df85SFlorian Fainelli 
848*1da6df85SFlorian Fainelli 	return -ENOENT;
849*1da6df85SFlorian Fainelli }
850*1da6df85SFlorian Fainelli 
851*1da6df85SFlorian Fainelli static int b53_arl_op(struct b53_device *dev, int op, int port,
852*1da6df85SFlorian Fainelli 		      const unsigned char *addr, u16 vid, bool is_valid)
853*1da6df85SFlorian Fainelli {
854*1da6df85SFlorian Fainelli 	struct b53_arl_entry ent;
855*1da6df85SFlorian Fainelli 	u32 fwd_entry;
856*1da6df85SFlorian Fainelli 	u64 mac, mac_vid = 0;
857*1da6df85SFlorian Fainelli 	u8 idx = 0;
858*1da6df85SFlorian Fainelli 	int ret;
859*1da6df85SFlorian Fainelli 
860*1da6df85SFlorian Fainelli 	/* Convert the array into a 64-bit MAC */
861*1da6df85SFlorian Fainelli 	mac = b53_mac_to_u64(addr);
862*1da6df85SFlorian Fainelli 
863*1da6df85SFlorian Fainelli 	/* Perform a read for the given MAC and VID */
864*1da6df85SFlorian Fainelli 	b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac);
865*1da6df85SFlorian Fainelli 	b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid);
866*1da6df85SFlorian Fainelli 
867*1da6df85SFlorian Fainelli 	/* Issue a read operation for this MAC */
868*1da6df85SFlorian Fainelli 	ret = b53_arl_rw_op(dev, 1);
869*1da6df85SFlorian Fainelli 	if (ret)
870*1da6df85SFlorian Fainelli 		return ret;
871*1da6df85SFlorian Fainelli 
872*1da6df85SFlorian Fainelli 	ret = b53_arl_read(dev, mac, vid, &ent, &idx, is_valid);
873*1da6df85SFlorian Fainelli 	/* If this is a read, just finish now */
874*1da6df85SFlorian Fainelli 	if (op)
875*1da6df85SFlorian Fainelli 		return ret;
876*1da6df85SFlorian Fainelli 
877*1da6df85SFlorian Fainelli 	/* We could not find a matching MAC, so reset to a new entry */
878*1da6df85SFlorian Fainelli 	if (ret) {
879*1da6df85SFlorian Fainelli 		fwd_entry = 0;
880*1da6df85SFlorian Fainelli 		idx = 1;
881*1da6df85SFlorian Fainelli 	}
882*1da6df85SFlorian Fainelli 
883*1da6df85SFlorian Fainelli 	memset(&ent, 0, sizeof(ent));
884*1da6df85SFlorian Fainelli 	ent.port = port;
885*1da6df85SFlorian Fainelli 	ent.is_valid = is_valid;
886*1da6df85SFlorian Fainelli 	ent.vid = vid;
887*1da6df85SFlorian Fainelli 	ent.is_static = true;
888*1da6df85SFlorian Fainelli 	memcpy(ent.mac, addr, ETH_ALEN);
889*1da6df85SFlorian Fainelli 	b53_arl_from_entry(&mac_vid, &fwd_entry, &ent);
890*1da6df85SFlorian Fainelli 
891*1da6df85SFlorian Fainelli 	b53_write64(dev, B53_ARLIO_PAGE,
892*1da6df85SFlorian Fainelli 		    B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid);
893*1da6df85SFlorian Fainelli 	b53_write32(dev, B53_ARLIO_PAGE,
894*1da6df85SFlorian Fainelli 		    B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);
895*1da6df85SFlorian Fainelli 
896*1da6df85SFlorian Fainelli 	return b53_arl_rw_op(dev, 0);
897*1da6df85SFlorian Fainelli }
898*1da6df85SFlorian Fainelli 
899*1da6df85SFlorian Fainelli static int b53_fdb_prepare(struct dsa_switch *ds, int port,
900*1da6df85SFlorian Fainelli 			   const struct switchdev_obj_port_fdb *fdb,
901*1da6df85SFlorian Fainelli 			   struct switchdev_trans *trans)
902*1da6df85SFlorian Fainelli {
903*1da6df85SFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
904*1da6df85SFlorian Fainelli 
905*1da6df85SFlorian Fainelli 	/* 5325 and 5365 require some more massaging, but could
906*1da6df85SFlorian Fainelli 	 * be supported eventually
907*1da6df85SFlorian Fainelli 	 */
908*1da6df85SFlorian Fainelli 	if (is5325(priv) || is5365(priv))
909*1da6df85SFlorian Fainelli 		return -EOPNOTSUPP;
910*1da6df85SFlorian Fainelli 
911*1da6df85SFlorian Fainelli 	return 0;
912*1da6df85SFlorian Fainelli }
913*1da6df85SFlorian Fainelli 
914*1da6df85SFlorian Fainelli static void b53_fdb_add(struct dsa_switch *ds, int port,
915*1da6df85SFlorian Fainelli 			const struct switchdev_obj_port_fdb *fdb,
916*1da6df85SFlorian Fainelli 			struct switchdev_trans *trans)
917*1da6df85SFlorian Fainelli {
918*1da6df85SFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
919*1da6df85SFlorian Fainelli 
920*1da6df85SFlorian Fainelli 	if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true))
921*1da6df85SFlorian Fainelli 		pr_err("%s: failed to add MAC address\n", __func__);
922*1da6df85SFlorian Fainelli }
923*1da6df85SFlorian Fainelli 
924*1da6df85SFlorian Fainelli static int b53_fdb_del(struct dsa_switch *ds, int port,
925*1da6df85SFlorian Fainelli 		       const struct switchdev_obj_port_fdb *fdb)
926*1da6df85SFlorian Fainelli {
927*1da6df85SFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
928*1da6df85SFlorian Fainelli 
929*1da6df85SFlorian Fainelli 	return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
930*1da6df85SFlorian Fainelli }
931*1da6df85SFlorian Fainelli 
932*1da6df85SFlorian Fainelli static int b53_arl_search_wait(struct b53_device *dev)
933*1da6df85SFlorian Fainelli {
934*1da6df85SFlorian Fainelli 	unsigned int timeout = 1000;
935*1da6df85SFlorian Fainelli 	u8 reg;
936*1da6df85SFlorian Fainelli 
937*1da6df85SFlorian Fainelli 	do {
938*1da6df85SFlorian Fainelli 		b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, &reg);
939*1da6df85SFlorian Fainelli 		if (!(reg & ARL_SRCH_STDN))
940*1da6df85SFlorian Fainelli 			return 0;
941*1da6df85SFlorian Fainelli 
942*1da6df85SFlorian Fainelli 		if (reg & ARL_SRCH_VLID)
943*1da6df85SFlorian Fainelli 			return 0;
944*1da6df85SFlorian Fainelli 
945*1da6df85SFlorian Fainelli 		usleep_range(1000, 2000);
946*1da6df85SFlorian Fainelli 	} while (timeout--);
947*1da6df85SFlorian Fainelli 
948*1da6df85SFlorian Fainelli 	return -ETIMEDOUT;
949*1da6df85SFlorian Fainelli }
950*1da6df85SFlorian Fainelli 
951*1da6df85SFlorian Fainelli static void b53_arl_search_rd(struct b53_device *dev, u8 idx,
952*1da6df85SFlorian Fainelli 			      struct b53_arl_entry *ent)
953*1da6df85SFlorian Fainelli {
954*1da6df85SFlorian Fainelli 	u64 mac_vid;
955*1da6df85SFlorian Fainelli 	u32 fwd_entry;
956*1da6df85SFlorian Fainelli 
957*1da6df85SFlorian Fainelli 	b53_read64(dev, B53_ARLIO_PAGE,
958*1da6df85SFlorian Fainelli 		   B53_ARL_SRCH_RSTL_MACVID(idx), &mac_vid);
959*1da6df85SFlorian Fainelli 	b53_read32(dev, B53_ARLIO_PAGE,
960*1da6df85SFlorian Fainelli 		   B53_ARL_SRCH_RSTL(idx), &fwd_entry);
961*1da6df85SFlorian Fainelli 	b53_arl_to_entry(ent, mac_vid, fwd_entry);
962*1da6df85SFlorian Fainelli }
963*1da6df85SFlorian Fainelli 
964*1da6df85SFlorian Fainelli static int b53_fdb_copy(struct net_device *dev, int port,
965*1da6df85SFlorian Fainelli 			const struct b53_arl_entry *ent,
966*1da6df85SFlorian Fainelli 			struct switchdev_obj_port_fdb *fdb,
967*1da6df85SFlorian Fainelli 			int (*cb)(struct switchdev_obj *obj))
968*1da6df85SFlorian Fainelli {
969*1da6df85SFlorian Fainelli 	if (!ent->is_valid)
970*1da6df85SFlorian Fainelli 		return 0;
971*1da6df85SFlorian Fainelli 
972*1da6df85SFlorian Fainelli 	if (port != ent->port)
973*1da6df85SFlorian Fainelli 		return 0;
974*1da6df85SFlorian Fainelli 
975*1da6df85SFlorian Fainelli 	ether_addr_copy(fdb->addr, ent->mac);
976*1da6df85SFlorian Fainelli 	fdb->vid = ent->vid;
977*1da6df85SFlorian Fainelli 	fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
978*1da6df85SFlorian Fainelli 
979*1da6df85SFlorian Fainelli 	return cb(&fdb->obj);
980*1da6df85SFlorian Fainelli }
981*1da6df85SFlorian Fainelli 
982*1da6df85SFlorian Fainelli static int b53_fdb_dump(struct dsa_switch *ds, int port,
983*1da6df85SFlorian Fainelli 			struct switchdev_obj_port_fdb *fdb,
984*1da6df85SFlorian Fainelli 			int (*cb)(struct switchdev_obj *obj))
985*1da6df85SFlorian Fainelli {
986*1da6df85SFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
987*1da6df85SFlorian Fainelli 	struct net_device *dev = ds->ports[port].netdev;
988*1da6df85SFlorian Fainelli 	struct b53_arl_entry results[2];
989*1da6df85SFlorian Fainelli 	unsigned int count = 0;
990*1da6df85SFlorian Fainelli 	int ret;
991*1da6df85SFlorian Fainelli 	u8 reg;
992*1da6df85SFlorian Fainelli 
993*1da6df85SFlorian Fainelli 	/* Start search operation */
994*1da6df85SFlorian Fainelli 	reg = ARL_SRCH_STDN;
995*1da6df85SFlorian Fainelli 	b53_write8(priv, B53_ARLIO_PAGE, B53_ARL_SRCH_CTL, reg);
996*1da6df85SFlorian Fainelli 
997*1da6df85SFlorian Fainelli 	do {
998*1da6df85SFlorian Fainelli 		ret = b53_arl_search_wait(priv);
999*1da6df85SFlorian Fainelli 		if (ret)
1000*1da6df85SFlorian Fainelli 			return ret;
1001*1da6df85SFlorian Fainelli 
1002*1da6df85SFlorian Fainelli 		b53_arl_search_rd(priv, 0, &results[0]);
1003*1da6df85SFlorian Fainelli 		ret = b53_fdb_copy(dev, port, &results[0], fdb, cb);
1004*1da6df85SFlorian Fainelli 		if (ret)
1005*1da6df85SFlorian Fainelli 			return ret;
1006*1da6df85SFlorian Fainelli 
1007*1da6df85SFlorian Fainelli 		if (priv->num_arl_entries > 2) {
1008*1da6df85SFlorian Fainelli 			b53_arl_search_rd(priv, 1, &results[1]);
1009*1da6df85SFlorian Fainelli 			ret = b53_fdb_copy(dev, port, &results[1], fdb, cb);
1010*1da6df85SFlorian Fainelli 			if (ret)
1011*1da6df85SFlorian Fainelli 				return ret;
1012*1da6df85SFlorian Fainelli 
1013*1da6df85SFlorian Fainelli 			if (!results[0].is_valid && !results[1].is_valid)
1014*1da6df85SFlorian Fainelli 				break;
1015*1da6df85SFlorian Fainelli 		}
1016*1da6df85SFlorian Fainelli 
1017*1da6df85SFlorian Fainelli 	} while (count++ < 1024);
1018*1da6df85SFlorian Fainelli 
1019*1da6df85SFlorian Fainelli 	return 0;
1020*1da6df85SFlorian Fainelli }
1021*1da6df85SFlorian Fainelli 
1022967dd82fSFlorian Fainelli static struct dsa_switch_driver b53_switch_ops = {
1023967dd82fSFlorian Fainelli 	.tag_protocol		= DSA_TAG_PROTO_NONE,
1024967dd82fSFlorian Fainelli 	.setup			= b53_setup,
1025967dd82fSFlorian Fainelli 	.set_addr		= b53_set_addr,
1026967dd82fSFlorian Fainelli 	.get_strings		= b53_get_strings,
1027967dd82fSFlorian Fainelli 	.get_ethtool_stats	= b53_get_ethtool_stats,
1028967dd82fSFlorian Fainelli 	.get_sset_count		= b53_get_sset_count,
1029967dd82fSFlorian Fainelli 	.phy_read		= b53_phy_read16,
1030967dd82fSFlorian Fainelli 	.phy_write		= b53_phy_write16,
1031967dd82fSFlorian Fainelli 	.adjust_link		= b53_adjust_link,
1032967dd82fSFlorian Fainelli 	.port_enable		= b53_enable_port,
1033967dd82fSFlorian Fainelli 	.port_disable		= b53_disable_port,
1034*1da6df85SFlorian Fainelli 	.port_fdb_prepare	= b53_fdb_prepare,
1035*1da6df85SFlorian Fainelli 	.port_fdb_dump		= b53_fdb_dump,
1036*1da6df85SFlorian Fainelli 	.port_fdb_add		= b53_fdb_add,
1037*1da6df85SFlorian Fainelli 	.port_fdb_del		= b53_fdb_del,
1038967dd82fSFlorian Fainelli };
1039967dd82fSFlorian Fainelli 
1040967dd82fSFlorian Fainelli struct b53_chip_data {
1041967dd82fSFlorian Fainelli 	u32 chip_id;
1042967dd82fSFlorian Fainelli 	const char *dev_name;
1043967dd82fSFlorian Fainelli 	u16 vlans;
1044967dd82fSFlorian Fainelli 	u16 enabled_ports;
1045967dd82fSFlorian Fainelli 	u8 cpu_port;
1046967dd82fSFlorian Fainelli 	u8 vta_regs[3];
1047*1da6df85SFlorian Fainelli 	u8 arl_entries;
1048967dd82fSFlorian Fainelli 	u8 duplex_reg;
1049967dd82fSFlorian Fainelli 	u8 jumbo_pm_reg;
1050967dd82fSFlorian Fainelli 	u8 jumbo_size_reg;
1051967dd82fSFlorian Fainelli };
1052967dd82fSFlorian Fainelli 
1053967dd82fSFlorian Fainelli #define B53_VTA_REGS	\
1054967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
1055967dd82fSFlorian Fainelli #define B53_VTA_REGS_9798 \
1056967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
1057967dd82fSFlorian Fainelli #define B53_VTA_REGS_63XX \
1058967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
1059967dd82fSFlorian Fainelli 
1060967dd82fSFlorian Fainelli static const struct b53_chip_data b53_switch_chips[] = {
1061967dd82fSFlorian Fainelli 	{
1062967dd82fSFlorian Fainelli 		.chip_id = BCM5325_DEVICE_ID,
1063967dd82fSFlorian Fainelli 		.dev_name = "BCM5325",
1064967dd82fSFlorian Fainelli 		.vlans = 16,
1065967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1066*1da6df85SFlorian Fainelli 		.arl_entries = 2,
1067967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25,
1068967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_FE,
1069967dd82fSFlorian Fainelli 	},
1070967dd82fSFlorian Fainelli 	{
1071967dd82fSFlorian Fainelli 		.chip_id = BCM5365_DEVICE_ID,
1072967dd82fSFlorian Fainelli 		.dev_name = "BCM5365",
1073967dd82fSFlorian Fainelli 		.vlans = 256,
1074967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1075*1da6df85SFlorian Fainelli 		.arl_entries = 2,
1076967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25,
1077967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_FE,
1078967dd82fSFlorian Fainelli 	},
1079967dd82fSFlorian Fainelli 	{
1080967dd82fSFlorian Fainelli 		.chip_id = BCM5395_DEVICE_ID,
1081967dd82fSFlorian Fainelli 		.dev_name = "BCM5395",
1082967dd82fSFlorian Fainelli 		.vlans = 4096,
1083967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1084*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1085967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1086967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1087967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1088967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1089967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1090967dd82fSFlorian Fainelli 	},
1091967dd82fSFlorian Fainelli 	{
1092967dd82fSFlorian Fainelli 		.chip_id = BCM5397_DEVICE_ID,
1093967dd82fSFlorian Fainelli 		.dev_name = "BCM5397",
1094967dd82fSFlorian Fainelli 		.vlans = 4096,
1095967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1096*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1097967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1098967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_9798,
1099967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1100967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1101967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1102967dd82fSFlorian Fainelli 	},
1103967dd82fSFlorian Fainelli 	{
1104967dd82fSFlorian Fainelli 		.chip_id = BCM5398_DEVICE_ID,
1105967dd82fSFlorian Fainelli 		.dev_name = "BCM5398",
1106967dd82fSFlorian Fainelli 		.vlans = 4096,
1107967dd82fSFlorian Fainelli 		.enabled_ports = 0x7f,
1108*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1109967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1110967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_9798,
1111967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1112967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1113967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1114967dd82fSFlorian Fainelli 	},
1115967dd82fSFlorian Fainelli 	{
1116967dd82fSFlorian Fainelli 		.chip_id = BCM53115_DEVICE_ID,
1117967dd82fSFlorian Fainelli 		.dev_name = "BCM53115",
1118967dd82fSFlorian Fainelli 		.vlans = 4096,
1119967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1120*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1121967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1122967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1123967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1124967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1125967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1126967dd82fSFlorian Fainelli 	},
1127967dd82fSFlorian Fainelli 	{
1128967dd82fSFlorian Fainelli 		.chip_id = BCM53125_DEVICE_ID,
1129967dd82fSFlorian Fainelli 		.dev_name = "BCM53125",
1130967dd82fSFlorian Fainelli 		.vlans = 4096,
1131967dd82fSFlorian Fainelli 		.enabled_ports = 0xff,
1132967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1133967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1134967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1135967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1136967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1137967dd82fSFlorian Fainelli 	},
1138967dd82fSFlorian Fainelli 	{
1139967dd82fSFlorian Fainelli 		.chip_id = BCM53128_DEVICE_ID,
1140967dd82fSFlorian Fainelli 		.dev_name = "BCM53128",
1141967dd82fSFlorian Fainelli 		.vlans = 4096,
1142967dd82fSFlorian Fainelli 		.enabled_ports = 0x1ff,
1143*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1144967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1145967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1146967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1147967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1148967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1149967dd82fSFlorian Fainelli 	},
1150967dd82fSFlorian Fainelli 	{
1151967dd82fSFlorian Fainelli 		.chip_id = BCM63XX_DEVICE_ID,
1152967dd82fSFlorian Fainelli 		.dev_name = "BCM63xx",
1153967dd82fSFlorian Fainelli 		.vlans = 4096,
1154967dd82fSFlorian Fainelli 		.enabled_ports = 0, /* pdata must provide them */
1155*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1156967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
1157967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_63XX,
1158967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_63XX,
1159967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
1160967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
1161967dd82fSFlorian Fainelli 	},
1162967dd82fSFlorian Fainelli 	{
1163967dd82fSFlorian Fainelli 		.chip_id = BCM53010_DEVICE_ID,
1164967dd82fSFlorian Fainelli 		.dev_name = "BCM53010",
1165967dd82fSFlorian Fainelli 		.vlans = 4096,
1166967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1167*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1168967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
1169967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1170967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1171967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1172967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1173967dd82fSFlorian Fainelli 	},
1174967dd82fSFlorian Fainelli 	{
1175967dd82fSFlorian Fainelli 		.chip_id = BCM53011_DEVICE_ID,
1176967dd82fSFlorian Fainelli 		.dev_name = "BCM53011",
1177967dd82fSFlorian Fainelli 		.vlans = 4096,
1178967dd82fSFlorian Fainelli 		.enabled_ports = 0x1bf,
1179*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1180967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
1181967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1182967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1183967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1184967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1185967dd82fSFlorian Fainelli 	},
1186967dd82fSFlorian Fainelli 	{
1187967dd82fSFlorian Fainelli 		.chip_id = BCM53012_DEVICE_ID,
1188967dd82fSFlorian Fainelli 		.dev_name = "BCM53012",
1189967dd82fSFlorian Fainelli 		.vlans = 4096,
1190967dd82fSFlorian Fainelli 		.enabled_ports = 0x1bf,
1191*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1192967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
1193967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1194967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1195967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1196967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1197967dd82fSFlorian Fainelli 	},
1198967dd82fSFlorian Fainelli 	{
1199967dd82fSFlorian Fainelli 		.chip_id = BCM53018_DEVICE_ID,
1200967dd82fSFlorian Fainelli 		.dev_name = "BCM53018",
1201967dd82fSFlorian Fainelli 		.vlans = 4096,
1202967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1203*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1204967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
1205967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1206967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1207967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1208967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1209967dd82fSFlorian Fainelli 	},
1210967dd82fSFlorian Fainelli 	{
1211967dd82fSFlorian Fainelli 		.chip_id = BCM53019_DEVICE_ID,
1212967dd82fSFlorian Fainelli 		.dev_name = "BCM53019",
1213967dd82fSFlorian Fainelli 		.vlans = 4096,
1214967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
1215*1da6df85SFlorian Fainelli 		.arl_entries = 4,
1216967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
1217967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
1218967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
1219967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
1220967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
1221967dd82fSFlorian Fainelli 	},
1222967dd82fSFlorian Fainelli };
1223967dd82fSFlorian Fainelli 
1224967dd82fSFlorian Fainelli static int b53_switch_init(struct b53_device *dev)
1225967dd82fSFlorian Fainelli {
1226967dd82fSFlorian Fainelli 	struct dsa_switch *ds = dev->ds;
1227967dd82fSFlorian Fainelli 	unsigned int i;
1228967dd82fSFlorian Fainelli 	int ret;
1229967dd82fSFlorian Fainelli 
1230967dd82fSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
1231967dd82fSFlorian Fainelli 		const struct b53_chip_data *chip = &b53_switch_chips[i];
1232967dd82fSFlorian Fainelli 
1233967dd82fSFlorian Fainelli 		if (chip->chip_id == dev->chip_id) {
1234967dd82fSFlorian Fainelli 			if (!dev->enabled_ports)
1235967dd82fSFlorian Fainelli 				dev->enabled_ports = chip->enabled_ports;
1236967dd82fSFlorian Fainelli 			dev->name = chip->dev_name;
1237967dd82fSFlorian Fainelli 			dev->duplex_reg = chip->duplex_reg;
1238967dd82fSFlorian Fainelli 			dev->vta_regs[0] = chip->vta_regs[0];
1239967dd82fSFlorian Fainelli 			dev->vta_regs[1] = chip->vta_regs[1];
1240967dd82fSFlorian Fainelli 			dev->vta_regs[2] = chip->vta_regs[2];
1241967dd82fSFlorian Fainelli 			dev->jumbo_pm_reg = chip->jumbo_pm_reg;
1242967dd82fSFlorian Fainelli 			ds->drv = &b53_switch_ops;
1243967dd82fSFlorian Fainelli 			dev->cpu_port = chip->cpu_port;
1244967dd82fSFlorian Fainelli 			dev->num_vlans = chip->vlans;
1245*1da6df85SFlorian Fainelli 			dev->num_arl_entries = chip->arl_entries;
1246967dd82fSFlorian Fainelli 			break;
1247967dd82fSFlorian Fainelli 		}
1248967dd82fSFlorian Fainelli 	}
1249967dd82fSFlorian Fainelli 
1250967dd82fSFlorian Fainelli 	/* check which BCM5325x version we have */
1251967dd82fSFlorian Fainelli 	if (is5325(dev)) {
1252967dd82fSFlorian Fainelli 		u8 vc4;
1253967dd82fSFlorian Fainelli 
1254967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
1255967dd82fSFlorian Fainelli 
1256967dd82fSFlorian Fainelli 		/* check reserved bits */
1257967dd82fSFlorian Fainelli 		switch (vc4 & 3) {
1258967dd82fSFlorian Fainelli 		case 1:
1259967dd82fSFlorian Fainelli 			/* BCM5325E */
1260967dd82fSFlorian Fainelli 			break;
1261967dd82fSFlorian Fainelli 		case 3:
1262967dd82fSFlorian Fainelli 			/* BCM5325F - do not use port 4 */
1263967dd82fSFlorian Fainelli 			dev->enabled_ports &= ~BIT(4);
1264967dd82fSFlorian Fainelli 			break;
1265967dd82fSFlorian Fainelli 		default:
1266967dd82fSFlorian Fainelli /* On the BCM47XX SoCs this is the supported internal switch.*/
1267967dd82fSFlorian Fainelli #ifndef CONFIG_BCM47XX
1268967dd82fSFlorian Fainelli 			/* BCM5325M */
1269967dd82fSFlorian Fainelli 			return -EINVAL;
1270967dd82fSFlorian Fainelli #else
1271967dd82fSFlorian Fainelli 			break;
1272967dd82fSFlorian Fainelli #endif
1273967dd82fSFlorian Fainelli 		}
1274967dd82fSFlorian Fainelli 	} else if (dev->chip_id == BCM53115_DEVICE_ID) {
1275967dd82fSFlorian Fainelli 		u64 strap_value;
1276967dd82fSFlorian Fainelli 
1277967dd82fSFlorian Fainelli 		b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
1278967dd82fSFlorian Fainelli 		/* use second IMP port if GMII is enabled */
1279967dd82fSFlorian Fainelli 		if (strap_value & SV_GMII_CTRL_115)
1280967dd82fSFlorian Fainelli 			dev->cpu_port = 5;
1281967dd82fSFlorian Fainelli 	}
1282967dd82fSFlorian Fainelli 
1283967dd82fSFlorian Fainelli 	/* cpu port is always last */
1284967dd82fSFlorian Fainelli 	dev->num_ports = dev->cpu_port + 1;
1285967dd82fSFlorian Fainelli 	dev->enabled_ports |= BIT(dev->cpu_port);
1286967dd82fSFlorian Fainelli 
1287967dd82fSFlorian Fainelli 	dev->ports = devm_kzalloc(dev->dev,
1288967dd82fSFlorian Fainelli 				  sizeof(struct b53_port) * dev->num_ports,
1289967dd82fSFlorian Fainelli 				  GFP_KERNEL);
1290967dd82fSFlorian Fainelli 	if (!dev->ports)
1291967dd82fSFlorian Fainelli 		return -ENOMEM;
1292967dd82fSFlorian Fainelli 
1293967dd82fSFlorian Fainelli 	dev->reset_gpio = b53_switch_get_reset_gpio(dev);
1294967dd82fSFlorian Fainelli 	if (dev->reset_gpio >= 0) {
1295967dd82fSFlorian Fainelli 		ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
1296967dd82fSFlorian Fainelli 					    GPIOF_OUT_INIT_HIGH, "robo_reset");
1297967dd82fSFlorian Fainelli 		if (ret)
1298967dd82fSFlorian Fainelli 			return ret;
1299967dd82fSFlorian Fainelli 	}
1300967dd82fSFlorian Fainelli 
1301967dd82fSFlorian Fainelli 	return 0;
1302967dd82fSFlorian Fainelli }
1303967dd82fSFlorian Fainelli 
1304967dd82fSFlorian Fainelli struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
1305967dd82fSFlorian Fainelli 				    void *priv)
1306967dd82fSFlorian Fainelli {
1307967dd82fSFlorian Fainelli 	struct dsa_switch *ds;
1308967dd82fSFlorian Fainelli 	struct b53_device *dev;
1309967dd82fSFlorian Fainelli 
1310967dd82fSFlorian Fainelli 	ds = devm_kzalloc(base, sizeof(*ds) + sizeof(*dev), GFP_KERNEL);
1311967dd82fSFlorian Fainelli 	if (!ds)
1312967dd82fSFlorian Fainelli 		return NULL;
1313967dd82fSFlorian Fainelli 
1314967dd82fSFlorian Fainelli 	dev = (struct b53_device *)(ds + 1);
1315967dd82fSFlorian Fainelli 
1316967dd82fSFlorian Fainelli 	ds->priv = dev;
1317967dd82fSFlorian Fainelli 	ds->dev = base;
1318967dd82fSFlorian Fainelli 	dev->dev = base;
1319967dd82fSFlorian Fainelli 
1320967dd82fSFlorian Fainelli 	dev->ds = ds;
1321967dd82fSFlorian Fainelli 	dev->priv = priv;
1322967dd82fSFlorian Fainelli 	dev->ops = ops;
1323967dd82fSFlorian Fainelli 	mutex_init(&dev->reg_mutex);
1324967dd82fSFlorian Fainelli 	mutex_init(&dev->stats_mutex);
1325967dd82fSFlorian Fainelli 
1326967dd82fSFlorian Fainelli 	return dev;
1327967dd82fSFlorian Fainelli }
1328967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_alloc);
1329967dd82fSFlorian Fainelli 
1330967dd82fSFlorian Fainelli int b53_switch_detect(struct b53_device *dev)
1331967dd82fSFlorian Fainelli {
1332967dd82fSFlorian Fainelli 	u32 id32;
1333967dd82fSFlorian Fainelli 	u16 tmp;
1334967dd82fSFlorian Fainelli 	u8 id8;
1335967dd82fSFlorian Fainelli 	int ret;
1336967dd82fSFlorian Fainelli 
1337967dd82fSFlorian Fainelli 	ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
1338967dd82fSFlorian Fainelli 	if (ret)
1339967dd82fSFlorian Fainelli 		return ret;
1340967dd82fSFlorian Fainelli 
1341967dd82fSFlorian Fainelli 	switch (id8) {
1342967dd82fSFlorian Fainelli 	case 0:
1343967dd82fSFlorian Fainelli 		/* BCM5325 and BCM5365 do not have this register so reads
1344967dd82fSFlorian Fainelli 		 * return 0. But the read operation did succeed, so assume this
1345967dd82fSFlorian Fainelli 		 * is one of them.
1346967dd82fSFlorian Fainelli 		 *
1347967dd82fSFlorian Fainelli 		 * Next check if we can write to the 5325's VTA register; for
1348967dd82fSFlorian Fainelli 		 * 5365 it is read only.
1349967dd82fSFlorian Fainelli 		 */
1350967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
1351967dd82fSFlorian Fainelli 		b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
1352967dd82fSFlorian Fainelli 
1353967dd82fSFlorian Fainelli 		if (tmp == 0xf)
1354967dd82fSFlorian Fainelli 			dev->chip_id = BCM5325_DEVICE_ID;
1355967dd82fSFlorian Fainelli 		else
1356967dd82fSFlorian Fainelli 			dev->chip_id = BCM5365_DEVICE_ID;
1357967dd82fSFlorian Fainelli 		break;
1358967dd82fSFlorian Fainelli 	case BCM5395_DEVICE_ID:
1359967dd82fSFlorian Fainelli 	case BCM5397_DEVICE_ID:
1360967dd82fSFlorian Fainelli 	case BCM5398_DEVICE_ID:
1361967dd82fSFlorian Fainelli 		dev->chip_id = id8;
1362967dd82fSFlorian Fainelli 		break;
1363967dd82fSFlorian Fainelli 	default:
1364967dd82fSFlorian Fainelli 		ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
1365967dd82fSFlorian Fainelli 		if (ret)
1366967dd82fSFlorian Fainelli 			return ret;
1367967dd82fSFlorian Fainelli 
1368967dd82fSFlorian Fainelli 		switch (id32) {
1369967dd82fSFlorian Fainelli 		case BCM53115_DEVICE_ID:
1370967dd82fSFlorian Fainelli 		case BCM53125_DEVICE_ID:
1371967dd82fSFlorian Fainelli 		case BCM53128_DEVICE_ID:
1372967dd82fSFlorian Fainelli 		case BCM53010_DEVICE_ID:
1373967dd82fSFlorian Fainelli 		case BCM53011_DEVICE_ID:
1374967dd82fSFlorian Fainelli 		case BCM53012_DEVICE_ID:
1375967dd82fSFlorian Fainelli 		case BCM53018_DEVICE_ID:
1376967dd82fSFlorian Fainelli 		case BCM53019_DEVICE_ID:
1377967dd82fSFlorian Fainelli 			dev->chip_id = id32;
1378967dd82fSFlorian Fainelli 			break;
1379967dd82fSFlorian Fainelli 		default:
1380967dd82fSFlorian Fainelli 			pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
1381967dd82fSFlorian Fainelli 			       id8, id32);
1382967dd82fSFlorian Fainelli 			return -ENODEV;
1383967dd82fSFlorian Fainelli 		}
1384967dd82fSFlorian Fainelli 	}
1385967dd82fSFlorian Fainelli 
1386967dd82fSFlorian Fainelli 	if (dev->chip_id == BCM5325_DEVICE_ID)
1387967dd82fSFlorian Fainelli 		return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
1388967dd82fSFlorian Fainelli 				 &dev->core_rev);
1389967dd82fSFlorian Fainelli 	else
1390967dd82fSFlorian Fainelli 		return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
1391967dd82fSFlorian Fainelli 				 &dev->core_rev);
1392967dd82fSFlorian Fainelli }
1393967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_detect);
1394967dd82fSFlorian Fainelli 
1395967dd82fSFlorian Fainelli int b53_switch_register(struct b53_device *dev)
1396967dd82fSFlorian Fainelli {
1397967dd82fSFlorian Fainelli 	int ret;
1398967dd82fSFlorian Fainelli 
1399967dd82fSFlorian Fainelli 	if (dev->pdata) {
1400967dd82fSFlorian Fainelli 		dev->chip_id = dev->pdata->chip_id;
1401967dd82fSFlorian Fainelli 		dev->enabled_ports = dev->pdata->enabled_ports;
1402967dd82fSFlorian Fainelli 	}
1403967dd82fSFlorian Fainelli 
1404967dd82fSFlorian Fainelli 	if (!dev->chip_id && b53_switch_detect(dev))
1405967dd82fSFlorian Fainelli 		return -EINVAL;
1406967dd82fSFlorian Fainelli 
1407967dd82fSFlorian Fainelli 	ret = b53_switch_init(dev);
1408967dd82fSFlorian Fainelli 	if (ret)
1409967dd82fSFlorian Fainelli 		return ret;
1410967dd82fSFlorian Fainelli 
1411967dd82fSFlorian Fainelli 	pr_info("found switch: %s, rev %i\n", dev->name, dev->core_rev);
1412967dd82fSFlorian Fainelli 
1413967dd82fSFlorian Fainelli 	return dsa_register_switch(dev->ds, dev->ds->dev->of_node);
1414967dd82fSFlorian Fainelli }
1415967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_register);
1416967dd82fSFlorian Fainelli 
1417967dd82fSFlorian Fainelli MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
1418967dd82fSFlorian Fainelli MODULE_DESCRIPTION("B53 switch library");
1419967dd82fSFlorian Fainelli MODULE_LICENSE("Dual BSD/GPL");
1420