xref: /openbmc/linux/drivers/net/dsa/b53/b53_common.c (revision 967dd82ffc52e9d8ea0defde094f9a39a3f4eeed)
1*967dd82fSFlorian Fainelli /*
2*967dd82fSFlorian Fainelli  * B53 switch driver main logic
3*967dd82fSFlorian Fainelli  *
4*967dd82fSFlorian Fainelli  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
5*967dd82fSFlorian Fainelli  * Copyright (C) 2016 Florian Fainelli <f.fainelli@gmail.com>
6*967dd82fSFlorian Fainelli  *
7*967dd82fSFlorian Fainelli  * Permission to use, copy, modify, and/or distribute this software for any
8*967dd82fSFlorian Fainelli  * purpose with or without fee is hereby granted, provided that the above
9*967dd82fSFlorian Fainelli  * copyright notice and this permission notice appear in all copies.
10*967dd82fSFlorian Fainelli  *
11*967dd82fSFlorian Fainelli  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*967dd82fSFlorian Fainelli  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*967dd82fSFlorian Fainelli  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*967dd82fSFlorian Fainelli  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*967dd82fSFlorian Fainelli  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*967dd82fSFlorian Fainelli  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*967dd82fSFlorian Fainelli  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*967dd82fSFlorian Fainelli  */
19*967dd82fSFlorian Fainelli 
20*967dd82fSFlorian Fainelli #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
21*967dd82fSFlorian Fainelli 
22*967dd82fSFlorian Fainelli #include <linux/delay.h>
23*967dd82fSFlorian Fainelli #include <linux/export.h>
24*967dd82fSFlorian Fainelli #include <linux/gpio.h>
25*967dd82fSFlorian Fainelli #include <linux/kernel.h>
26*967dd82fSFlorian Fainelli #include <linux/module.h>
27*967dd82fSFlorian Fainelli #include <linux/platform_data/b53.h>
28*967dd82fSFlorian Fainelli #include <linux/phy.h>
29*967dd82fSFlorian Fainelli #include <net/dsa.h>
30*967dd82fSFlorian Fainelli 
31*967dd82fSFlorian Fainelli #include "b53_regs.h"
32*967dd82fSFlorian Fainelli #include "b53_priv.h"
33*967dd82fSFlorian Fainelli 
34*967dd82fSFlorian Fainelli struct b53_mib_desc {
35*967dd82fSFlorian Fainelli 	u8 size;
36*967dd82fSFlorian Fainelli 	u8 offset;
37*967dd82fSFlorian Fainelli 	const char *name;
38*967dd82fSFlorian Fainelli };
39*967dd82fSFlorian Fainelli 
40*967dd82fSFlorian Fainelli /* BCM5365 MIB counters */
41*967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs_65[] = {
42*967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
43*967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
44*967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
45*967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
46*967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
47*967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
48*967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
49*967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
50*967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
51*967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
52*967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
53*967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
54*967dd82fSFlorian Fainelli 	{ 8, 0x44, "RxOctets" },
55*967dd82fSFlorian Fainelli 	{ 4, 0x4c, "RxUndersizePkts" },
56*967dd82fSFlorian Fainelli 	{ 4, 0x50, "RxPausePkts" },
57*967dd82fSFlorian Fainelli 	{ 4, 0x54, "Pkts64Octets" },
58*967dd82fSFlorian Fainelli 	{ 4, 0x58, "Pkts65to127Octets" },
59*967dd82fSFlorian Fainelli 	{ 4, 0x5c, "Pkts128to255Octets" },
60*967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts256to511Octets" },
61*967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts512to1023Octets" },
62*967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts1024to1522Octets" },
63*967dd82fSFlorian Fainelli 	{ 4, 0x6c, "RxOversizePkts" },
64*967dd82fSFlorian Fainelli 	{ 4, 0x70, "RxJabbers" },
65*967dd82fSFlorian Fainelli 	{ 4, 0x74, "RxAlignmentErrors" },
66*967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxFCSErrors" },
67*967dd82fSFlorian Fainelli 	{ 8, 0x7c, "RxGoodOctets" },
68*967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxDropPkts" },
69*967dd82fSFlorian Fainelli 	{ 4, 0x88, "RxUnicastPkts" },
70*967dd82fSFlorian Fainelli 	{ 4, 0x8c, "RxMulticastPkts" },
71*967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxBroadcastPkts" },
72*967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxSAChanges" },
73*967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxFragments" },
74*967dd82fSFlorian Fainelli };
75*967dd82fSFlorian Fainelli 
76*967dd82fSFlorian Fainelli #define B53_MIBS_65_SIZE	ARRAY_SIZE(b53_mibs_65)
77*967dd82fSFlorian Fainelli 
78*967dd82fSFlorian Fainelli /* BCM63xx MIB counters */
79*967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs_63xx[] = {
80*967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
81*967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
82*967dd82fSFlorian Fainelli 	{ 4, 0x0c, "TxQoSPkts" },
83*967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
84*967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
85*967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
86*967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
87*967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
88*967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
89*967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
90*967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
91*967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
92*967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
93*967dd82fSFlorian Fainelli 	{ 8, 0x3c, "TxQoSOctets" },
94*967dd82fSFlorian Fainelli 	{ 8, 0x44, "RxOctets" },
95*967dd82fSFlorian Fainelli 	{ 4, 0x4c, "RxUndersizePkts" },
96*967dd82fSFlorian Fainelli 	{ 4, 0x50, "RxPausePkts" },
97*967dd82fSFlorian Fainelli 	{ 4, 0x54, "Pkts64Octets" },
98*967dd82fSFlorian Fainelli 	{ 4, 0x58, "Pkts65to127Octets" },
99*967dd82fSFlorian Fainelli 	{ 4, 0x5c, "Pkts128to255Octets" },
100*967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts256to511Octets" },
101*967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts512to1023Octets" },
102*967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts1024to1522Octets" },
103*967dd82fSFlorian Fainelli 	{ 4, 0x6c, "RxOversizePkts" },
104*967dd82fSFlorian Fainelli 	{ 4, 0x70, "RxJabbers" },
105*967dd82fSFlorian Fainelli 	{ 4, 0x74, "RxAlignmentErrors" },
106*967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxFCSErrors" },
107*967dd82fSFlorian Fainelli 	{ 8, 0x7c, "RxGoodOctets" },
108*967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxDropPkts" },
109*967dd82fSFlorian Fainelli 	{ 4, 0x88, "RxUnicastPkts" },
110*967dd82fSFlorian Fainelli 	{ 4, 0x8c, "RxMulticastPkts" },
111*967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxBroadcastPkts" },
112*967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxSAChanges" },
113*967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxFragments" },
114*967dd82fSFlorian Fainelli 	{ 4, 0xa0, "RxSymbolErrors" },
115*967dd82fSFlorian Fainelli 	{ 4, 0xa4, "RxQoSPkts" },
116*967dd82fSFlorian Fainelli 	{ 8, 0xa8, "RxQoSOctets" },
117*967dd82fSFlorian Fainelli 	{ 4, 0xb0, "Pkts1523to2047Octets" },
118*967dd82fSFlorian Fainelli 	{ 4, 0xb4, "Pkts2048to4095Octets" },
119*967dd82fSFlorian Fainelli 	{ 4, 0xb8, "Pkts4096to8191Octets" },
120*967dd82fSFlorian Fainelli 	{ 4, 0xbc, "Pkts8192to9728Octets" },
121*967dd82fSFlorian Fainelli 	{ 4, 0xc0, "RxDiscarded" },
122*967dd82fSFlorian Fainelli };
123*967dd82fSFlorian Fainelli 
124*967dd82fSFlorian Fainelli #define B53_MIBS_63XX_SIZE	ARRAY_SIZE(b53_mibs_63xx)
125*967dd82fSFlorian Fainelli 
126*967dd82fSFlorian Fainelli /* MIB counters */
127*967dd82fSFlorian Fainelli static const struct b53_mib_desc b53_mibs[] = {
128*967dd82fSFlorian Fainelli 	{ 8, 0x00, "TxOctets" },
129*967dd82fSFlorian Fainelli 	{ 4, 0x08, "TxDropPkts" },
130*967dd82fSFlorian Fainelli 	{ 4, 0x10, "TxBroadcastPkts" },
131*967dd82fSFlorian Fainelli 	{ 4, 0x14, "TxMulticastPkts" },
132*967dd82fSFlorian Fainelli 	{ 4, 0x18, "TxUnicastPkts" },
133*967dd82fSFlorian Fainelli 	{ 4, 0x1c, "TxCollisions" },
134*967dd82fSFlorian Fainelli 	{ 4, 0x20, "TxSingleCollision" },
135*967dd82fSFlorian Fainelli 	{ 4, 0x24, "TxMultipleCollision" },
136*967dd82fSFlorian Fainelli 	{ 4, 0x28, "TxDeferredTransmit" },
137*967dd82fSFlorian Fainelli 	{ 4, 0x2c, "TxLateCollision" },
138*967dd82fSFlorian Fainelli 	{ 4, 0x30, "TxExcessiveCollision" },
139*967dd82fSFlorian Fainelli 	{ 4, 0x38, "TxPausePkts" },
140*967dd82fSFlorian Fainelli 	{ 8, 0x50, "RxOctets" },
141*967dd82fSFlorian Fainelli 	{ 4, 0x58, "RxUndersizePkts" },
142*967dd82fSFlorian Fainelli 	{ 4, 0x5c, "RxPausePkts" },
143*967dd82fSFlorian Fainelli 	{ 4, 0x60, "Pkts64Octets" },
144*967dd82fSFlorian Fainelli 	{ 4, 0x64, "Pkts65to127Octets" },
145*967dd82fSFlorian Fainelli 	{ 4, 0x68, "Pkts128to255Octets" },
146*967dd82fSFlorian Fainelli 	{ 4, 0x6c, "Pkts256to511Octets" },
147*967dd82fSFlorian Fainelli 	{ 4, 0x70, "Pkts512to1023Octets" },
148*967dd82fSFlorian Fainelli 	{ 4, 0x74, "Pkts1024to1522Octets" },
149*967dd82fSFlorian Fainelli 	{ 4, 0x78, "RxOversizePkts" },
150*967dd82fSFlorian Fainelli 	{ 4, 0x7c, "RxJabbers" },
151*967dd82fSFlorian Fainelli 	{ 4, 0x80, "RxAlignmentErrors" },
152*967dd82fSFlorian Fainelli 	{ 4, 0x84, "RxFCSErrors" },
153*967dd82fSFlorian Fainelli 	{ 8, 0x88, "RxGoodOctets" },
154*967dd82fSFlorian Fainelli 	{ 4, 0x90, "RxDropPkts" },
155*967dd82fSFlorian Fainelli 	{ 4, 0x94, "RxUnicastPkts" },
156*967dd82fSFlorian Fainelli 	{ 4, 0x98, "RxMulticastPkts" },
157*967dd82fSFlorian Fainelli 	{ 4, 0x9c, "RxBroadcastPkts" },
158*967dd82fSFlorian Fainelli 	{ 4, 0xa0, "RxSAChanges" },
159*967dd82fSFlorian Fainelli 	{ 4, 0xa4, "RxFragments" },
160*967dd82fSFlorian Fainelli 	{ 4, 0xa8, "RxJumboPkts" },
161*967dd82fSFlorian Fainelli 	{ 4, 0xac, "RxSymbolErrors" },
162*967dd82fSFlorian Fainelli 	{ 4, 0xc0, "RxDiscarded" },
163*967dd82fSFlorian Fainelli };
164*967dd82fSFlorian Fainelli 
165*967dd82fSFlorian Fainelli #define B53_MIBS_SIZE	ARRAY_SIZE(b53_mibs)
166*967dd82fSFlorian Fainelli 
167*967dd82fSFlorian Fainelli static int b53_do_vlan_op(struct b53_device *dev, u8 op)
168*967dd82fSFlorian Fainelli {
169*967dd82fSFlorian Fainelli 	unsigned int i;
170*967dd82fSFlorian Fainelli 
171*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
172*967dd82fSFlorian Fainelli 
173*967dd82fSFlorian Fainelli 	for (i = 0; i < 10; i++) {
174*967dd82fSFlorian Fainelli 		u8 vta;
175*967dd82fSFlorian Fainelli 
176*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
177*967dd82fSFlorian Fainelli 		if (!(vta & VTA_START_CMD))
178*967dd82fSFlorian Fainelli 			return 0;
179*967dd82fSFlorian Fainelli 
180*967dd82fSFlorian Fainelli 		usleep_range(100, 200);
181*967dd82fSFlorian Fainelli 	}
182*967dd82fSFlorian Fainelli 
183*967dd82fSFlorian Fainelli 	return -EIO;
184*967dd82fSFlorian Fainelli }
185*967dd82fSFlorian Fainelli 
186*967dd82fSFlorian Fainelli static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
187*967dd82fSFlorian Fainelli 			       u16 untag)
188*967dd82fSFlorian Fainelli {
189*967dd82fSFlorian Fainelli 	if (is5325(dev)) {
190*967dd82fSFlorian Fainelli 		u32 entry = 0;
191*967dd82fSFlorian Fainelli 
192*967dd82fSFlorian Fainelli 		if (members) {
193*967dd82fSFlorian Fainelli 			entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
194*967dd82fSFlorian Fainelli 				members;
195*967dd82fSFlorian Fainelli 			if (dev->core_rev >= 3)
196*967dd82fSFlorian Fainelli 				entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
197*967dd82fSFlorian Fainelli 			else
198*967dd82fSFlorian Fainelli 				entry |= VA_VALID_25;
199*967dd82fSFlorian Fainelli 		}
200*967dd82fSFlorian Fainelli 
201*967dd82fSFlorian Fainelli 		b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
202*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
203*967dd82fSFlorian Fainelli 			    VTA_RW_STATE_WR | VTA_RW_OP_EN);
204*967dd82fSFlorian Fainelli 	} else if (is5365(dev)) {
205*967dd82fSFlorian Fainelli 		u16 entry = 0;
206*967dd82fSFlorian Fainelli 
207*967dd82fSFlorian Fainelli 		if (members)
208*967dd82fSFlorian Fainelli 			entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
209*967dd82fSFlorian Fainelli 				members | VA_VALID_65;
210*967dd82fSFlorian Fainelli 
211*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
212*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
213*967dd82fSFlorian Fainelli 			    VTA_RW_STATE_WR | VTA_RW_OP_EN);
214*967dd82fSFlorian Fainelli 	} else {
215*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
216*967dd82fSFlorian Fainelli 		b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
217*967dd82fSFlorian Fainelli 			    (untag << VTE_UNTAG_S) | members);
218*967dd82fSFlorian Fainelli 
219*967dd82fSFlorian Fainelli 		b53_do_vlan_op(dev, VTA_CMD_WRITE);
220*967dd82fSFlorian Fainelli 	}
221*967dd82fSFlorian Fainelli }
222*967dd82fSFlorian Fainelli 
223*967dd82fSFlorian Fainelli void b53_set_forwarding(struct b53_device *dev, int enable)
224*967dd82fSFlorian Fainelli {
225*967dd82fSFlorian Fainelli 	u8 mgmt;
226*967dd82fSFlorian Fainelli 
227*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
228*967dd82fSFlorian Fainelli 
229*967dd82fSFlorian Fainelli 	if (enable)
230*967dd82fSFlorian Fainelli 		mgmt |= SM_SW_FWD_EN;
231*967dd82fSFlorian Fainelli 	else
232*967dd82fSFlorian Fainelli 		mgmt &= ~SM_SW_FWD_EN;
233*967dd82fSFlorian Fainelli 
234*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
235*967dd82fSFlorian Fainelli }
236*967dd82fSFlorian Fainelli 
237*967dd82fSFlorian Fainelli static void b53_enable_vlan(struct b53_device *dev, int enable)
238*967dd82fSFlorian Fainelli {
239*967dd82fSFlorian Fainelli 	u8 mgmt, vc0, vc1, vc4 = 0, vc5;
240*967dd82fSFlorian Fainelli 
241*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
242*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
243*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
244*967dd82fSFlorian Fainelli 
245*967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
246*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
247*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
248*967dd82fSFlorian Fainelli 	} else if (is63xx(dev)) {
249*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
250*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
251*967dd82fSFlorian Fainelli 	} else {
252*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
253*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
254*967dd82fSFlorian Fainelli 	}
255*967dd82fSFlorian Fainelli 
256*967dd82fSFlorian Fainelli 	mgmt &= ~SM_SW_FWD_MODE;
257*967dd82fSFlorian Fainelli 
258*967dd82fSFlorian Fainelli 	if (enable) {
259*967dd82fSFlorian Fainelli 		vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
260*967dd82fSFlorian Fainelli 		vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
261*967dd82fSFlorian Fainelli 		vc4 &= ~VC4_ING_VID_CHECK_MASK;
262*967dd82fSFlorian Fainelli 		vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
263*967dd82fSFlorian Fainelli 		vc5 |= VC5_DROP_VTABLE_MISS;
264*967dd82fSFlorian Fainelli 
265*967dd82fSFlorian Fainelli 		if (is5325(dev))
266*967dd82fSFlorian Fainelli 			vc0 &= ~VC0_RESERVED_1;
267*967dd82fSFlorian Fainelli 
268*967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
269*967dd82fSFlorian Fainelli 			vc1 |= VC1_RX_MCST_TAG_EN;
270*967dd82fSFlorian Fainelli 
271*967dd82fSFlorian Fainelli 		if (!is5325(dev) && !is5365(dev)) {
272*967dd82fSFlorian Fainelli 			if (dev->allow_vid_4095)
273*967dd82fSFlorian Fainelli 				vc5 |= VC5_VID_FFF_EN;
274*967dd82fSFlorian Fainelli 			else
275*967dd82fSFlorian Fainelli 				vc5 &= ~VC5_VID_FFF_EN;
276*967dd82fSFlorian Fainelli 		}
277*967dd82fSFlorian Fainelli 	} else {
278*967dd82fSFlorian Fainelli 		vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
279*967dd82fSFlorian Fainelli 		vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
280*967dd82fSFlorian Fainelli 		vc4 &= ~VC4_ING_VID_CHECK_MASK;
281*967dd82fSFlorian Fainelli 		vc5 &= ~VC5_DROP_VTABLE_MISS;
282*967dd82fSFlorian Fainelli 
283*967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
284*967dd82fSFlorian Fainelli 			vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
285*967dd82fSFlorian Fainelli 		else
286*967dd82fSFlorian Fainelli 			vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
287*967dd82fSFlorian Fainelli 
288*967dd82fSFlorian Fainelli 		if (is5325(dev) || is5365(dev))
289*967dd82fSFlorian Fainelli 			vc1 &= ~VC1_RX_MCST_TAG_EN;
290*967dd82fSFlorian Fainelli 
291*967dd82fSFlorian Fainelli 		if (!is5325(dev) && !is5365(dev))
292*967dd82fSFlorian Fainelli 			vc5 &= ~VC5_VID_FFF_EN;
293*967dd82fSFlorian Fainelli 	}
294*967dd82fSFlorian Fainelli 
295*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
296*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
297*967dd82fSFlorian Fainelli 
298*967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
299*967dd82fSFlorian Fainelli 		/* enable the high 8 bit vid check on 5325 */
300*967dd82fSFlorian Fainelli 		if (is5325(dev) && enable)
301*967dd82fSFlorian Fainelli 			b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
302*967dd82fSFlorian Fainelli 				   VC3_HIGH_8BIT_EN);
303*967dd82fSFlorian Fainelli 		else
304*967dd82fSFlorian Fainelli 			b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
305*967dd82fSFlorian Fainelli 
306*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
307*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
308*967dd82fSFlorian Fainelli 	} else if (is63xx(dev)) {
309*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
310*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
311*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
312*967dd82fSFlorian Fainelli 	} else {
313*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
314*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
315*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
316*967dd82fSFlorian Fainelli 	}
317*967dd82fSFlorian Fainelli 
318*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
319*967dd82fSFlorian Fainelli }
320*967dd82fSFlorian Fainelli 
321*967dd82fSFlorian Fainelli static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100)
322*967dd82fSFlorian Fainelli {
323*967dd82fSFlorian Fainelli 	u32 port_mask = 0;
324*967dd82fSFlorian Fainelli 	u16 max_size = JMS_MIN_SIZE;
325*967dd82fSFlorian Fainelli 
326*967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev))
327*967dd82fSFlorian Fainelli 		return -EINVAL;
328*967dd82fSFlorian Fainelli 
329*967dd82fSFlorian Fainelli 	if (enable) {
330*967dd82fSFlorian Fainelli 		port_mask = dev->enabled_ports;
331*967dd82fSFlorian Fainelli 		max_size = JMS_MAX_SIZE;
332*967dd82fSFlorian Fainelli 		if (allow_10_100)
333*967dd82fSFlorian Fainelli 			port_mask |= JPM_10_100_JUMBO_EN;
334*967dd82fSFlorian Fainelli 	}
335*967dd82fSFlorian Fainelli 
336*967dd82fSFlorian Fainelli 	b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
337*967dd82fSFlorian Fainelli 	return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
338*967dd82fSFlorian Fainelli }
339*967dd82fSFlorian Fainelli 
340*967dd82fSFlorian Fainelli static int b53_flush_arl(struct b53_device *dev)
341*967dd82fSFlorian Fainelli {
342*967dd82fSFlorian Fainelli 	unsigned int i;
343*967dd82fSFlorian Fainelli 
344*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
345*967dd82fSFlorian Fainelli 		   FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
346*967dd82fSFlorian Fainelli 
347*967dd82fSFlorian Fainelli 	for (i = 0; i < 10; i++) {
348*967dd82fSFlorian Fainelli 		u8 fast_age_ctrl;
349*967dd82fSFlorian Fainelli 
350*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
351*967dd82fSFlorian Fainelli 			  &fast_age_ctrl);
352*967dd82fSFlorian Fainelli 
353*967dd82fSFlorian Fainelli 		if (!(fast_age_ctrl & FAST_AGE_DONE))
354*967dd82fSFlorian Fainelli 			goto out;
355*967dd82fSFlorian Fainelli 
356*967dd82fSFlorian Fainelli 		msleep(1);
357*967dd82fSFlorian Fainelli 	}
358*967dd82fSFlorian Fainelli 
359*967dd82fSFlorian Fainelli 	return -ETIMEDOUT;
360*967dd82fSFlorian Fainelli out:
361*967dd82fSFlorian Fainelli 	/* Only age dynamic entries (default behavior) */
362*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL, FAST_AGE_DYNAMIC);
363*967dd82fSFlorian Fainelli 	return 0;
364*967dd82fSFlorian Fainelli }
365*967dd82fSFlorian Fainelli 
366*967dd82fSFlorian Fainelli static int b53_enable_port(struct dsa_switch *ds, int port,
367*967dd82fSFlorian Fainelli 			   struct phy_device *phy)
368*967dd82fSFlorian Fainelli {
369*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
370*967dd82fSFlorian Fainelli 
371*967dd82fSFlorian Fainelli 	/* Clear the Rx and Tx disable bits and set to no spanning tree */
372*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), 0);
373*967dd82fSFlorian Fainelli 
374*967dd82fSFlorian Fainelli 	return 0;
375*967dd82fSFlorian Fainelli }
376*967dd82fSFlorian Fainelli 
377*967dd82fSFlorian Fainelli static void b53_disable_port(struct dsa_switch *ds, int port,
378*967dd82fSFlorian Fainelli 			     struct phy_device *phy)
379*967dd82fSFlorian Fainelli {
380*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
381*967dd82fSFlorian Fainelli 	u8 reg;
382*967dd82fSFlorian Fainelli 
383*967dd82fSFlorian Fainelli 	/* Disable Tx/Rx for the port */
384*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), &reg);
385*967dd82fSFlorian Fainelli 	reg |= PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
386*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(port), reg);
387*967dd82fSFlorian Fainelli }
388*967dd82fSFlorian Fainelli 
389*967dd82fSFlorian Fainelli static void b53_enable_cpu_port(struct b53_device *dev)
390*967dd82fSFlorian Fainelli {
391*967dd82fSFlorian Fainelli 	unsigned int cpu_port = dev->cpu_port;
392*967dd82fSFlorian Fainelli 	u8 port_ctrl;
393*967dd82fSFlorian Fainelli 
394*967dd82fSFlorian Fainelli 	/* BCM5325 CPU port is at 8 */
395*967dd82fSFlorian Fainelli 	if ((is5325(dev) || is5365(dev)) && cpu_port == B53_CPU_PORT_25)
396*967dd82fSFlorian Fainelli 		cpu_port = B53_CPU_PORT;
397*967dd82fSFlorian Fainelli 
398*967dd82fSFlorian Fainelli 	port_ctrl = PORT_CTRL_RX_BCST_EN |
399*967dd82fSFlorian Fainelli 		    PORT_CTRL_RX_MCST_EN |
400*967dd82fSFlorian Fainelli 		    PORT_CTRL_RX_UCST_EN;
401*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(cpu_port), port_ctrl);
402*967dd82fSFlorian Fainelli }
403*967dd82fSFlorian Fainelli 
404*967dd82fSFlorian Fainelli static void b53_enable_mib(struct b53_device *dev)
405*967dd82fSFlorian Fainelli {
406*967dd82fSFlorian Fainelli 	u8 gc;
407*967dd82fSFlorian Fainelli 
408*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
409*967dd82fSFlorian Fainelli 	gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
410*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
411*967dd82fSFlorian Fainelli }
412*967dd82fSFlorian Fainelli 
413*967dd82fSFlorian Fainelli static int b53_configure_vlan(struct b53_device *dev)
414*967dd82fSFlorian Fainelli {
415*967dd82fSFlorian Fainelli 	int i;
416*967dd82fSFlorian Fainelli 
417*967dd82fSFlorian Fainelli 	/* clear all vlan entries */
418*967dd82fSFlorian Fainelli 	if (is5325(dev) || is5365(dev)) {
419*967dd82fSFlorian Fainelli 		for (i = 1; i < dev->num_vlans; i++)
420*967dd82fSFlorian Fainelli 			b53_set_vlan_entry(dev, i, 0, 0);
421*967dd82fSFlorian Fainelli 	} else {
422*967dd82fSFlorian Fainelli 		b53_do_vlan_op(dev, VTA_CMD_CLEAR);
423*967dd82fSFlorian Fainelli 	}
424*967dd82fSFlorian Fainelli 
425*967dd82fSFlorian Fainelli 	b53_enable_vlan(dev, false);
426*967dd82fSFlorian Fainelli 
427*967dd82fSFlorian Fainelli 	b53_for_each_port(dev, i)
428*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE,
429*967dd82fSFlorian Fainelli 			    B53_VLAN_PORT_DEF_TAG(i), 1);
430*967dd82fSFlorian Fainelli 
431*967dd82fSFlorian Fainelli 	if (!is5325(dev) && !is5365(dev))
432*967dd82fSFlorian Fainelli 		b53_set_jumbo(dev, dev->enable_jumbo, false);
433*967dd82fSFlorian Fainelli 
434*967dd82fSFlorian Fainelli 	return 0;
435*967dd82fSFlorian Fainelli }
436*967dd82fSFlorian Fainelli 
437*967dd82fSFlorian Fainelli static void b53_switch_reset_gpio(struct b53_device *dev)
438*967dd82fSFlorian Fainelli {
439*967dd82fSFlorian Fainelli 	int gpio = dev->reset_gpio;
440*967dd82fSFlorian Fainelli 
441*967dd82fSFlorian Fainelli 	if (gpio < 0)
442*967dd82fSFlorian Fainelli 		return;
443*967dd82fSFlorian Fainelli 
444*967dd82fSFlorian Fainelli 	/* Reset sequence: RESET low(50ms)->high(20ms)
445*967dd82fSFlorian Fainelli 	 */
446*967dd82fSFlorian Fainelli 	gpio_set_value(gpio, 0);
447*967dd82fSFlorian Fainelli 	mdelay(50);
448*967dd82fSFlorian Fainelli 
449*967dd82fSFlorian Fainelli 	gpio_set_value(gpio, 1);
450*967dd82fSFlorian Fainelli 	mdelay(20);
451*967dd82fSFlorian Fainelli 
452*967dd82fSFlorian Fainelli 	dev->current_page = 0xff;
453*967dd82fSFlorian Fainelli }
454*967dd82fSFlorian Fainelli 
455*967dd82fSFlorian Fainelli static int b53_switch_reset(struct b53_device *dev)
456*967dd82fSFlorian Fainelli {
457*967dd82fSFlorian Fainelli 	u8 mgmt;
458*967dd82fSFlorian Fainelli 
459*967dd82fSFlorian Fainelli 	b53_switch_reset_gpio(dev);
460*967dd82fSFlorian Fainelli 
461*967dd82fSFlorian Fainelli 	if (is539x(dev)) {
462*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
463*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
464*967dd82fSFlorian Fainelli 	}
465*967dd82fSFlorian Fainelli 
466*967dd82fSFlorian Fainelli 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
467*967dd82fSFlorian Fainelli 
468*967dd82fSFlorian Fainelli 	if (!(mgmt & SM_SW_FWD_EN)) {
469*967dd82fSFlorian Fainelli 		mgmt &= ~SM_SW_FWD_MODE;
470*967dd82fSFlorian Fainelli 		mgmt |= SM_SW_FWD_EN;
471*967dd82fSFlorian Fainelli 
472*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
473*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
474*967dd82fSFlorian Fainelli 
475*967dd82fSFlorian Fainelli 		if (!(mgmt & SM_SW_FWD_EN)) {
476*967dd82fSFlorian Fainelli 			dev_err(dev->dev, "Failed to enable switch!\n");
477*967dd82fSFlorian Fainelli 			return -EINVAL;
478*967dd82fSFlorian Fainelli 		}
479*967dd82fSFlorian Fainelli 	}
480*967dd82fSFlorian Fainelli 
481*967dd82fSFlorian Fainelli 	b53_enable_mib(dev);
482*967dd82fSFlorian Fainelli 
483*967dd82fSFlorian Fainelli 	return b53_flush_arl(dev);
484*967dd82fSFlorian Fainelli }
485*967dd82fSFlorian Fainelli 
486*967dd82fSFlorian Fainelli static int b53_phy_read16(struct dsa_switch *ds, int addr, int reg)
487*967dd82fSFlorian Fainelli {
488*967dd82fSFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
489*967dd82fSFlorian Fainelli 	u16 value = 0;
490*967dd82fSFlorian Fainelli 	int ret;
491*967dd82fSFlorian Fainelli 
492*967dd82fSFlorian Fainelli 	if (priv->ops->phy_read16)
493*967dd82fSFlorian Fainelli 		ret = priv->ops->phy_read16(priv, addr, reg, &value);
494*967dd82fSFlorian Fainelli 	else
495*967dd82fSFlorian Fainelli 		ret = b53_read16(priv, B53_PORT_MII_PAGE(addr),
496*967dd82fSFlorian Fainelli 				 reg * 2, &value);
497*967dd82fSFlorian Fainelli 
498*967dd82fSFlorian Fainelli 	return ret ? ret : value;
499*967dd82fSFlorian Fainelli }
500*967dd82fSFlorian Fainelli 
501*967dd82fSFlorian Fainelli static int b53_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val)
502*967dd82fSFlorian Fainelli {
503*967dd82fSFlorian Fainelli 	struct b53_device *priv = ds_to_priv(ds);
504*967dd82fSFlorian Fainelli 
505*967dd82fSFlorian Fainelli 	if (priv->ops->phy_write16)
506*967dd82fSFlorian Fainelli 		return priv->ops->phy_write16(priv, addr, reg, val);
507*967dd82fSFlorian Fainelli 
508*967dd82fSFlorian Fainelli 	return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg * 2, val);
509*967dd82fSFlorian Fainelli }
510*967dd82fSFlorian Fainelli 
511*967dd82fSFlorian Fainelli static int b53_reset_switch(struct b53_device *priv)
512*967dd82fSFlorian Fainelli {
513*967dd82fSFlorian Fainelli 	/* reset vlans */
514*967dd82fSFlorian Fainelli 	priv->enable_jumbo = false;
515*967dd82fSFlorian Fainelli 
516*967dd82fSFlorian Fainelli 	memset(priv->ports, 0, sizeof(*priv->ports) * priv->num_ports);
517*967dd82fSFlorian Fainelli 
518*967dd82fSFlorian Fainelli 	return b53_switch_reset(priv);
519*967dd82fSFlorian Fainelli }
520*967dd82fSFlorian Fainelli 
521*967dd82fSFlorian Fainelli static int b53_apply_config(struct b53_device *priv)
522*967dd82fSFlorian Fainelli {
523*967dd82fSFlorian Fainelli 	/* disable switching */
524*967dd82fSFlorian Fainelli 	b53_set_forwarding(priv, 0);
525*967dd82fSFlorian Fainelli 
526*967dd82fSFlorian Fainelli 	b53_configure_vlan(priv);
527*967dd82fSFlorian Fainelli 
528*967dd82fSFlorian Fainelli 	/* enable switching */
529*967dd82fSFlorian Fainelli 	b53_set_forwarding(priv, 1);
530*967dd82fSFlorian Fainelli 
531*967dd82fSFlorian Fainelli 	return 0;
532*967dd82fSFlorian Fainelli }
533*967dd82fSFlorian Fainelli 
534*967dd82fSFlorian Fainelli static void b53_reset_mib(struct b53_device *priv)
535*967dd82fSFlorian Fainelli {
536*967dd82fSFlorian Fainelli 	u8 gc;
537*967dd82fSFlorian Fainelli 
538*967dd82fSFlorian Fainelli 	b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
539*967dd82fSFlorian Fainelli 
540*967dd82fSFlorian Fainelli 	b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
541*967dd82fSFlorian Fainelli 	msleep(1);
542*967dd82fSFlorian Fainelli 	b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
543*967dd82fSFlorian Fainelli 	msleep(1);
544*967dd82fSFlorian Fainelli }
545*967dd82fSFlorian Fainelli 
546*967dd82fSFlorian Fainelli static const struct b53_mib_desc *b53_get_mib(struct b53_device *dev)
547*967dd82fSFlorian Fainelli {
548*967dd82fSFlorian Fainelli 	if (is5365(dev))
549*967dd82fSFlorian Fainelli 		return b53_mibs_65;
550*967dd82fSFlorian Fainelli 	else if (is63xx(dev))
551*967dd82fSFlorian Fainelli 		return b53_mibs_63xx;
552*967dd82fSFlorian Fainelli 	else
553*967dd82fSFlorian Fainelli 		return b53_mibs;
554*967dd82fSFlorian Fainelli }
555*967dd82fSFlorian Fainelli 
556*967dd82fSFlorian Fainelli static unsigned int b53_get_mib_size(struct b53_device *dev)
557*967dd82fSFlorian Fainelli {
558*967dd82fSFlorian Fainelli 	if (is5365(dev))
559*967dd82fSFlorian Fainelli 		return B53_MIBS_65_SIZE;
560*967dd82fSFlorian Fainelli 	else if (is63xx(dev))
561*967dd82fSFlorian Fainelli 		return B53_MIBS_63XX_SIZE;
562*967dd82fSFlorian Fainelli 	else
563*967dd82fSFlorian Fainelli 		return B53_MIBS_SIZE;
564*967dd82fSFlorian Fainelli }
565*967dd82fSFlorian Fainelli 
566*967dd82fSFlorian Fainelli static void b53_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
567*967dd82fSFlorian Fainelli {
568*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
569*967dd82fSFlorian Fainelli 	const struct b53_mib_desc *mibs = b53_get_mib(dev);
570*967dd82fSFlorian Fainelli 	unsigned int mib_size = b53_get_mib_size(dev);
571*967dd82fSFlorian Fainelli 	unsigned int i;
572*967dd82fSFlorian Fainelli 
573*967dd82fSFlorian Fainelli 	for (i = 0; i < mib_size; i++)
574*967dd82fSFlorian Fainelli 		memcpy(data + i * ETH_GSTRING_LEN,
575*967dd82fSFlorian Fainelli 		       mibs[i].name, ETH_GSTRING_LEN);
576*967dd82fSFlorian Fainelli }
577*967dd82fSFlorian Fainelli 
578*967dd82fSFlorian Fainelli static void b53_get_ethtool_stats(struct dsa_switch *ds, int port,
579*967dd82fSFlorian Fainelli 				  uint64_t *data)
580*967dd82fSFlorian Fainelli {
581*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
582*967dd82fSFlorian Fainelli 	const struct b53_mib_desc *mibs = b53_get_mib(dev);
583*967dd82fSFlorian Fainelli 	unsigned int mib_size = b53_get_mib_size(dev);
584*967dd82fSFlorian Fainelli 	const struct b53_mib_desc *s;
585*967dd82fSFlorian Fainelli 	unsigned int i;
586*967dd82fSFlorian Fainelli 	u64 val = 0;
587*967dd82fSFlorian Fainelli 
588*967dd82fSFlorian Fainelli 	if (is5365(dev) && port == 5)
589*967dd82fSFlorian Fainelli 		port = 8;
590*967dd82fSFlorian Fainelli 
591*967dd82fSFlorian Fainelli 	mutex_lock(&dev->stats_mutex);
592*967dd82fSFlorian Fainelli 
593*967dd82fSFlorian Fainelli 	for (i = 0; i < mib_size; i++) {
594*967dd82fSFlorian Fainelli 		s = &mibs[i];
595*967dd82fSFlorian Fainelli 
596*967dd82fSFlorian Fainelli 		if (mibs->size == 8) {
597*967dd82fSFlorian Fainelli 			b53_read64(dev, B53_MIB_PAGE(port), s->offset, &val);
598*967dd82fSFlorian Fainelli 		} else {
599*967dd82fSFlorian Fainelli 			u32 val32;
600*967dd82fSFlorian Fainelli 
601*967dd82fSFlorian Fainelli 			b53_read32(dev, B53_MIB_PAGE(port), s->offset,
602*967dd82fSFlorian Fainelli 				   &val32);
603*967dd82fSFlorian Fainelli 			val = val32;
604*967dd82fSFlorian Fainelli 		}
605*967dd82fSFlorian Fainelli 		data[i] = (u64)val;
606*967dd82fSFlorian Fainelli 	}
607*967dd82fSFlorian Fainelli 
608*967dd82fSFlorian Fainelli 	mutex_unlock(&dev->stats_mutex);
609*967dd82fSFlorian Fainelli }
610*967dd82fSFlorian Fainelli 
611*967dd82fSFlorian Fainelli static int b53_get_sset_count(struct dsa_switch *ds)
612*967dd82fSFlorian Fainelli {
613*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
614*967dd82fSFlorian Fainelli 
615*967dd82fSFlorian Fainelli 	return b53_get_mib_size(dev);
616*967dd82fSFlorian Fainelli }
617*967dd82fSFlorian Fainelli 
618*967dd82fSFlorian Fainelli static int b53_set_addr(struct dsa_switch *ds, u8 *addr)
619*967dd82fSFlorian Fainelli {
620*967dd82fSFlorian Fainelli 	return 0;
621*967dd82fSFlorian Fainelli }
622*967dd82fSFlorian Fainelli 
623*967dd82fSFlorian Fainelli static int b53_setup(struct dsa_switch *ds)
624*967dd82fSFlorian Fainelli {
625*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
626*967dd82fSFlorian Fainelli 	unsigned int port;
627*967dd82fSFlorian Fainelli 	int ret;
628*967dd82fSFlorian Fainelli 
629*967dd82fSFlorian Fainelli 	ret = b53_reset_switch(dev);
630*967dd82fSFlorian Fainelli 	if (ret) {
631*967dd82fSFlorian Fainelli 		dev_err(ds->dev, "failed to reset switch\n");
632*967dd82fSFlorian Fainelli 		return ret;
633*967dd82fSFlorian Fainelli 	}
634*967dd82fSFlorian Fainelli 
635*967dd82fSFlorian Fainelli 	b53_reset_mib(dev);
636*967dd82fSFlorian Fainelli 
637*967dd82fSFlorian Fainelli 	ret = b53_apply_config(dev);
638*967dd82fSFlorian Fainelli 	if (ret)
639*967dd82fSFlorian Fainelli 		dev_err(ds->dev, "failed to apply configuration\n");
640*967dd82fSFlorian Fainelli 
641*967dd82fSFlorian Fainelli 	for (port = 0; port < dev->num_ports; port++) {
642*967dd82fSFlorian Fainelli 		if (BIT(port) & ds->enabled_port_mask)
643*967dd82fSFlorian Fainelli 			b53_enable_port(ds, port, NULL);
644*967dd82fSFlorian Fainelli 		else if (dsa_is_cpu_port(ds, port))
645*967dd82fSFlorian Fainelli 			b53_enable_cpu_port(dev);
646*967dd82fSFlorian Fainelli 		else
647*967dd82fSFlorian Fainelli 			b53_disable_port(ds, port, NULL);
648*967dd82fSFlorian Fainelli 	}
649*967dd82fSFlorian Fainelli 
650*967dd82fSFlorian Fainelli 	return ret;
651*967dd82fSFlorian Fainelli }
652*967dd82fSFlorian Fainelli 
653*967dd82fSFlorian Fainelli static void b53_adjust_link(struct dsa_switch *ds, int port,
654*967dd82fSFlorian Fainelli 			    struct phy_device *phydev)
655*967dd82fSFlorian Fainelli {
656*967dd82fSFlorian Fainelli 	struct b53_device *dev = ds_to_priv(ds);
657*967dd82fSFlorian Fainelli 	u8 rgmii_ctrl = 0, reg = 0, off;
658*967dd82fSFlorian Fainelli 
659*967dd82fSFlorian Fainelli 	if (!phy_is_pseudo_fixed_link(phydev))
660*967dd82fSFlorian Fainelli 		return;
661*967dd82fSFlorian Fainelli 
662*967dd82fSFlorian Fainelli 	/* Override the port settings */
663*967dd82fSFlorian Fainelli 	if (port == dev->cpu_port) {
664*967dd82fSFlorian Fainelli 		off = B53_PORT_OVERRIDE_CTRL;
665*967dd82fSFlorian Fainelli 		reg = PORT_OVERRIDE_EN;
666*967dd82fSFlorian Fainelli 	} else {
667*967dd82fSFlorian Fainelli 		off = B53_GMII_PORT_OVERRIDE_CTRL(port);
668*967dd82fSFlorian Fainelli 		reg = GMII_PO_EN;
669*967dd82fSFlorian Fainelli 	}
670*967dd82fSFlorian Fainelli 
671*967dd82fSFlorian Fainelli 	/* Set the link UP */
672*967dd82fSFlorian Fainelli 	if (phydev->link)
673*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_LINK;
674*967dd82fSFlorian Fainelli 
675*967dd82fSFlorian Fainelli 	if (phydev->duplex == DUPLEX_FULL)
676*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_FULL_DUPLEX;
677*967dd82fSFlorian Fainelli 
678*967dd82fSFlorian Fainelli 	switch (phydev->speed) {
679*967dd82fSFlorian Fainelli 	case 2000:
680*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_2000M;
681*967dd82fSFlorian Fainelli 		/* fallthrough */
682*967dd82fSFlorian Fainelli 	case SPEED_1000:
683*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_1000M;
684*967dd82fSFlorian Fainelli 		break;
685*967dd82fSFlorian Fainelli 	case SPEED_100:
686*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_100M;
687*967dd82fSFlorian Fainelli 		break;
688*967dd82fSFlorian Fainelli 	case SPEED_10:
689*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_SPEED_10M;
690*967dd82fSFlorian Fainelli 		break;
691*967dd82fSFlorian Fainelli 	default:
692*967dd82fSFlorian Fainelli 		dev_err(ds->dev, "unknown speed: %d\n", phydev->speed);
693*967dd82fSFlorian Fainelli 		return;
694*967dd82fSFlorian Fainelli 	}
695*967dd82fSFlorian Fainelli 
696*967dd82fSFlorian Fainelli 	/* Enable flow control on BCM5301x's CPU port */
697*967dd82fSFlorian Fainelli 	if (is5301x(dev) && port == dev->cpu_port)
698*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_RX_FLOW | PORT_OVERRIDE_TX_FLOW;
699*967dd82fSFlorian Fainelli 
700*967dd82fSFlorian Fainelli 	if (phydev->pause) {
701*967dd82fSFlorian Fainelli 		if (phydev->asym_pause)
702*967dd82fSFlorian Fainelli 			reg |= PORT_OVERRIDE_TX_FLOW;
703*967dd82fSFlorian Fainelli 		reg |= PORT_OVERRIDE_RX_FLOW;
704*967dd82fSFlorian Fainelli 	}
705*967dd82fSFlorian Fainelli 
706*967dd82fSFlorian Fainelli 	b53_write8(dev, B53_CTRL_PAGE, off, reg);
707*967dd82fSFlorian Fainelli 
708*967dd82fSFlorian Fainelli 	if (is531x5(dev) && phy_interface_is_rgmii(phydev)) {
709*967dd82fSFlorian Fainelli 		if (port == 8)
710*967dd82fSFlorian Fainelli 			off = B53_RGMII_CTRL_IMP;
711*967dd82fSFlorian Fainelli 		else
712*967dd82fSFlorian Fainelli 			off = B53_RGMII_CTRL_P(port);
713*967dd82fSFlorian Fainelli 
714*967dd82fSFlorian Fainelli 		/* Configure the port RGMII clock delay by DLL disabled and
715*967dd82fSFlorian Fainelli 		 * tx_clk aligned timing (restoring to reset defaults)
716*967dd82fSFlorian Fainelli 		 */
717*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, off, &rgmii_ctrl);
718*967dd82fSFlorian Fainelli 		rgmii_ctrl &= ~(RGMII_CTRL_DLL_RXC | RGMII_CTRL_DLL_TXC |
719*967dd82fSFlorian Fainelli 				RGMII_CTRL_TIMING_SEL);
720*967dd82fSFlorian Fainelli 
721*967dd82fSFlorian Fainelli 		/* PHY_INTERFACE_MODE_RGMII_TXID means TX internal delay, make
722*967dd82fSFlorian Fainelli 		 * sure that we enable the port TX clock internal delay to
723*967dd82fSFlorian Fainelli 		 * account for this internal delay that is inserted, otherwise
724*967dd82fSFlorian Fainelli 		 * the switch won't be able to receive correctly.
725*967dd82fSFlorian Fainelli 		 *
726*967dd82fSFlorian Fainelli 		 * PHY_INTERFACE_MODE_RGMII means that we are not introducing
727*967dd82fSFlorian Fainelli 		 * any delay neither on transmission nor reception, so the
728*967dd82fSFlorian Fainelli 		 * BCM53125 must also be configured accordingly to account for
729*967dd82fSFlorian Fainelli 		 * the lack of delay and introduce
730*967dd82fSFlorian Fainelli 		 *
731*967dd82fSFlorian Fainelli 		 * The BCM53125 switch has its RX clock and TX clock control
732*967dd82fSFlorian Fainelli 		 * swapped, hence the reason why we modify the TX clock path in
733*967dd82fSFlorian Fainelli 		 * the "RGMII" case
734*967dd82fSFlorian Fainelli 		 */
735*967dd82fSFlorian Fainelli 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
736*967dd82fSFlorian Fainelli 			rgmii_ctrl |= RGMII_CTRL_DLL_TXC;
737*967dd82fSFlorian Fainelli 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII)
738*967dd82fSFlorian Fainelli 			rgmii_ctrl |= RGMII_CTRL_DLL_TXC | RGMII_CTRL_DLL_RXC;
739*967dd82fSFlorian Fainelli 		rgmii_ctrl |= RGMII_CTRL_TIMING_SEL;
740*967dd82fSFlorian Fainelli 		b53_write8(dev, B53_CTRL_PAGE, off, rgmii_ctrl);
741*967dd82fSFlorian Fainelli 
742*967dd82fSFlorian Fainelli 		dev_info(ds->dev, "Configured port %d for %s\n", port,
743*967dd82fSFlorian Fainelli 			 phy_modes(phydev->interface));
744*967dd82fSFlorian Fainelli 	}
745*967dd82fSFlorian Fainelli 
746*967dd82fSFlorian Fainelli 	/* configure MII port if necessary */
747*967dd82fSFlorian Fainelli 	if (is5325(dev)) {
748*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
749*967dd82fSFlorian Fainelli 			  &reg);
750*967dd82fSFlorian Fainelli 
751*967dd82fSFlorian Fainelli 		/* reverse mii needs to be enabled */
752*967dd82fSFlorian Fainelli 		if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
753*967dd82fSFlorian Fainelli 			b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
754*967dd82fSFlorian Fainelli 				   reg | PORT_OVERRIDE_RV_MII_25);
755*967dd82fSFlorian Fainelli 			b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
756*967dd82fSFlorian Fainelli 				  &reg);
757*967dd82fSFlorian Fainelli 
758*967dd82fSFlorian Fainelli 			if (!(reg & PORT_OVERRIDE_RV_MII_25)) {
759*967dd82fSFlorian Fainelli 				dev_err(ds->dev,
760*967dd82fSFlorian Fainelli 					"Failed to enable reverse MII mode\n");
761*967dd82fSFlorian Fainelli 				return;
762*967dd82fSFlorian Fainelli 			}
763*967dd82fSFlorian Fainelli 		}
764*967dd82fSFlorian Fainelli 	} else if (is5301x(dev)) {
765*967dd82fSFlorian Fainelli 		if (port != dev->cpu_port) {
766*967dd82fSFlorian Fainelli 			u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(dev->cpu_port);
767*967dd82fSFlorian Fainelli 			u8 gmii_po;
768*967dd82fSFlorian Fainelli 
769*967dd82fSFlorian Fainelli 			b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
770*967dd82fSFlorian Fainelli 			gmii_po |= GMII_PO_LINK |
771*967dd82fSFlorian Fainelli 				   GMII_PO_RX_FLOW |
772*967dd82fSFlorian Fainelli 				   GMII_PO_TX_FLOW |
773*967dd82fSFlorian Fainelli 				   GMII_PO_EN |
774*967dd82fSFlorian Fainelli 				   GMII_PO_SPEED_2000M;
775*967dd82fSFlorian Fainelli 			b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
776*967dd82fSFlorian Fainelli 		}
777*967dd82fSFlorian Fainelli 	}
778*967dd82fSFlorian Fainelli }
779*967dd82fSFlorian Fainelli 
780*967dd82fSFlorian Fainelli static struct dsa_switch_driver b53_switch_ops = {
781*967dd82fSFlorian Fainelli 	.tag_protocol		= DSA_TAG_PROTO_NONE,
782*967dd82fSFlorian Fainelli 	.setup			= b53_setup,
783*967dd82fSFlorian Fainelli 	.set_addr		= b53_set_addr,
784*967dd82fSFlorian Fainelli 	.get_strings		= b53_get_strings,
785*967dd82fSFlorian Fainelli 	.get_ethtool_stats	= b53_get_ethtool_stats,
786*967dd82fSFlorian Fainelli 	.get_sset_count		= b53_get_sset_count,
787*967dd82fSFlorian Fainelli 	.phy_read		= b53_phy_read16,
788*967dd82fSFlorian Fainelli 	.phy_write		= b53_phy_write16,
789*967dd82fSFlorian Fainelli 	.adjust_link		= b53_adjust_link,
790*967dd82fSFlorian Fainelli 	.port_enable		= b53_enable_port,
791*967dd82fSFlorian Fainelli 	.port_disable		= b53_disable_port,
792*967dd82fSFlorian Fainelli };
793*967dd82fSFlorian Fainelli 
794*967dd82fSFlorian Fainelli struct b53_chip_data {
795*967dd82fSFlorian Fainelli 	u32 chip_id;
796*967dd82fSFlorian Fainelli 	const char *dev_name;
797*967dd82fSFlorian Fainelli 	u16 vlans;
798*967dd82fSFlorian Fainelli 	u16 enabled_ports;
799*967dd82fSFlorian Fainelli 	u8 cpu_port;
800*967dd82fSFlorian Fainelli 	u8 vta_regs[3];
801*967dd82fSFlorian Fainelli 	u8 duplex_reg;
802*967dd82fSFlorian Fainelli 	u8 jumbo_pm_reg;
803*967dd82fSFlorian Fainelli 	u8 jumbo_size_reg;
804*967dd82fSFlorian Fainelli };
805*967dd82fSFlorian Fainelli 
806*967dd82fSFlorian Fainelli #define B53_VTA_REGS	\
807*967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
808*967dd82fSFlorian Fainelli #define B53_VTA_REGS_9798 \
809*967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
810*967dd82fSFlorian Fainelli #define B53_VTA_REGS_63XX \
811*967dd82fSFlorian Fainelli 	{ B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
812*967dd82fSFlorian Fainelli 
813*967dd82fSFlorian Fainelli static const struct b53_chip_data b53_switch_chips[] = {
814*967dd82fSFlorian Fainelli 	{
815*967dd82fSFlorian Fainelli 		.chip_id = BCM5325_DEVICE_ID,
816*967dd82fSFlorian Fainelli 		.dev_name = "BCM5325",
817*967dd82fSFlorian Fainelli 		.vlans = 16,
818*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
819*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25,
820*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_FE,
821*967dd82fSFlorian Fainelli 	},
822*967dd82fSFlorian Fainelli 	{
823*967dd82fSFlorian Fainelli 		.chip_id = BCM5365_DEVICE_ID,
824*967dd82fSFlorian Fainelli 		.dev_name = "BCM5365",
825*967dd82fSFlorian Fainelli 		.vlans = 256,
826*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
827*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25,
828*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_FE,
829*967dd82fSFlorian Fainelli 	},
830*967dd82fSFlorian Fainelli 	{
831*967dd82fSFlorian Fainelli 		.chip_id = BCM5395_DEVICE_ID,
832*967dd82fSFlorian Fainelli 		.dev_name = "BCM5395",
833*967dd82fSFlorian Fainelli 		.vlans = 4096,
834*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
835*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
836*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
837*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
838*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
839*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
840*967dd82fSFlorian Fainelli 	},
841*967dd82fSFlorian Fainelli 	{
842*967dd82fSFlorian Fainelli 		.chip_id = BCM5397_DEVICE_ID,
843*967dd82fSFlorian Fainelli 		.dev_name = "BCM5397",
844*967dd82fSFlorian Fainelli 		.vlans = 4096,
845*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
846*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
847*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_9798,
848*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
849*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
850*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
851*967dd82fSFlorian Fainelli 	},
852*967dd82fSFlorian Fainelli 	{
853*967dd82fSFlorian Fainelli 		.chip_id = BCM5398_DEVICE_ID,
854*967dd82fSFlorian Fainelli 		.dev_name = "BCM5398",
855*967dd82fSFlorian Fainelli 		.vlans = 4096,
856*967dd82fSFlorian Fainelli 		.enabled_ports = 0x7f,
857*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
858*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_9798,
859*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
860*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
861*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
862*967dd82fSFlorian Fainelli 	},
863*967dd82fSFlorian Fainelli 	{
864*967dd82fSFlorian Fainelli 		.chip_id = BCM53115_DEVICE_ID,
865*967dd82fSFlorian Fainelli 		.dev_name = "BCM53115",
866*967dd82fSFlorian Fainelli 		.vlans = 4096,
867*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
868*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
869*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
870*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
871*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
872*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
873*967dd82fSFlorian Fainelli 	},
874*967dd82fSFlorian Fainelli 	{
875*967dd82fSFlorian Fainelli 		.chip_id = BCM53125_DEVICE_ID,
876*967dd82fSFlorian Fainelli 		.dev_name = "BCM53125",
877*967dd82fSFlorian Fainelli 		.vlans = 4096,
878*967dd82fSFlorian Fainelli 		.enabled_ports = 0xff,
879*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
880*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
881*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
882*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
883*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
884*967dd82fSFlorian Fainelli 	},
885*967dd82fSFlorian Fainelli 	{
886*967dd82fSFlorian Fainelli 		.chip_id = BCM53128_DEVICE_ID,
887*967dd82fSFlorian Fainelli 		.dev_name = "BCM53128",
888*967dd82fSFlorian Fainelli 		.vlans = 4096,
889*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1ff,
890*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
891*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
892*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
893*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
894*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
895*967dd82fSFlorian Fainelli 	},
896*967dd82fSFlorian Fainelli 	{
897*967dd82fSFlorian Fainelli 		.chip_id = BCM63XX_DEVICE_ID,
898*967dd82fSFlorian Fainelli 		.dev_name = "BCM63xx",
899*967dd82fSFlorian Fainelli 		.vlans = 4096,
900*967dd82fSFlorian Fainelli 		.enabled_ports = 0, /* pdata must provide them */
901*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT,
902*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS_63XX,
903*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_63XX,
904*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
905*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
906*967dd82fSFlorian Fainelli 	},
907*967dd82fSFlorian Fainelli 	{
908*967dd82fSFlorian Fainelli 		.chip_id = BCM53010_DEVICE_ID,
909*967dd82fSFlorian Fainelli 		.dev_name = "BCM53010",
910*967dd82fSFlorian Fainelli 		.vlans = 4096,
911*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
912*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
913*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
914*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
915*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
916*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
917*967dd82fSFlorian Fainelli 	},
918*967dd82fSFlorian Fainelli 	{
919*967dd82fSFlorian Fainelli 		.chip_id = BCM53011_DEVICE_ID,
920*967dd82fSFlorian Fainelli 		.dev_name = "BCM53011",
921*967dd82fSFlorian Fainelli 		.vlans = 4096,
922*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1bf,
923*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
924*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
925*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
926*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
927*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
928*967dd82fSFlorian Fainelli 	},
929*967dd82fSFlorian Fainelli 	{
930*967dd82fSFlorian Fainelli 		.chip_id = BCM53012_DEVICE_ID,
931*967dd82fSFlorian Fainelli 		.dev_name = "BCM53012",
932*967dd82fSFlorian Fainelli 		.vlans = 4096,
933*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1bf,
934*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
935*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
936*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
937*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
938*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
939*967dd82fSFlorian Fainelli 	},
940*967dd82fSFlorian Fainelli 	{
941*967dd82fSFlorian Fainelli 		.chip_id = BCM53018_DEVICE_ID,
942*967dd82fSFlorian Fainelli 		.dev_name = "BCM53018",
943*967dd82fSFlorian Fainelli 		.vlans = 4096,
944*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
945*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
946*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
947*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
948*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
949*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
950*967dd82fSFlorian Fainelli 	},
951*967dd82fSFlorian Fainelli 	{
952*967dd82fSFlorian Fainelli 		.chip_id = BCM53019_DEVICE_ID,
953*967dd82fSFlorian Fainelli 		.dev_name = "BCM53019",
954*967dd82fSFlorian Fainelli 		.vlans = 4096,
955*967dd82fSFlorian Fainelli 		.enabled_ports = 0x1f,
956*967dd82fSFlorian Fainelli 		.cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
957*967dd82fSFlorian Fainelli 		.vta_regs = B53_VTA_REGS,
958*967dd82fSFlorian Fainelli 		.duplex_reg = B53_DUPLEX_STAT_GE,
959*967dd82fSFlorian Fainelli 		.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
960*967dd82fSFlorian Fainelli 		.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
961*967dd82fSFlorian Fainelli 	},
962*967dd82fSFlorian Fainelli };
963*967dd82fSFlorian Fainelli 
964*967dd82fSFlorian Fainelli static int b53_switch_init(struct b53_device *dev)
965*967dd82fSFlorian Fainelli {
966*967dd82fSFlorian Fainelli 	struct dsa_switch *ds = dev->ds;
967*967dd82fSFlorian Fainelli 	unsigned int i;
968*967dd82fSFlorian Fainelli 	int ret;
969*967dd82fSFlorian Fainelli 
970*967dd82fSFlorian Fainelli 	for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
971*967dd82fSFlorian Fainelli 		const struct b53_chip_data *chip = &b53_switch_chips[i];
972*967dd82fSFlorian Fainelli 
973*967dd82fSFlorian Fainelli 		if (chip->chip_id == dev->chip_id) {
974*967dd82fSFlorian Fainelli 			if (!dev->enabled_ports)
975*967dd82fSFlorian Fainelli 				dev->enabled_ports = chip->enabled_ports;
976*967dd82fSFlorian Fainelli 			dev->name = chip->dev_name;
977*967dd82fSFlorian Fainelli 			dev->duplex_reg = chip->duplex_reg;
978*967dd82fSFlorian Fainelli 			dev->vta_regs[0] = chip->vta_regs[0];
979*967dd82fSFlorian Fainelli 			dev->vta_regs[1] = chip->vta_regs[1];
980*967dd82fSFlorian Fainelli 			dev->vta_regs[2] = chip->vta_regs[2];
981*967dd82fSFlorian Fainelli 			dev->jumbo_pm_reg = chip->jumbo_pm_reg;
982*967dd82fSFlorian Fainelli 			ds->drv = &b53_switch_ops;
983*967dd82fSFlorian Fainelli 			dev->cpu_port = chip->cpu_port;
984*967dd82fSFlorian Fainelli 			dev->num_vlans = chip->vlans;
985*967dd82fSFlorian Fainelli 			break;
986*967dd82fSFlorian Fainelli 		}
987*967dd82fSFlorian Fainelli 	}
988*967dd82fSFlorian Fainelli 
989*967dd82fSFlorian Fainelli 	/* check which BCM5325x version we have */
990*967dd82fSFlorian Fainelli 	if (is5325(dev)) {
991*967dd82fSFlorian Fainelli 		u8 vc4;
992*967dd82fSFlorian Fainelli 
993*967dd82fSFlorian Fainelli 		b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
994*967dd82fSFlorian Fainelli 
995*967dd82fSFlorian Fainelli 		/* check reserved bits */
996*967dd82fSFlorian Fainelli 		switch (vc4 & 3) {
997*967dd82fSFlorian Fainelli 		case 1:
998*967dd82fSFlorian Fainelli 			/* BCM5325E */
999*967dd82fSFlorian Fainelli 			break;
1000*967dd82fSFlorian Fainelli 		case 3:
1001*967dd82fSFlorian Fainelli 			/* BCM5325F - do not use port 4 */
1002*967dd82fSFlorian Fainelli 			dev->enabled_ports &= ~BIT(4);
1003*967dd82fSFlorian Fainelli 			break;
1004*967dd82fSFlorian Fainelli 		default:
1005*967dd82fSFlorian Fainelli /* On the BCM47XX SoCs this is the supported internal switch.*/
1006*967dd82fSFlorian Fainelli #ifndef CONFIG_BCM47XX
1007*967dd82fSFlorian Fainelli 			/* BCM5325M */
1008*967dd82fSFlorian Fainelli 			return -EINVAL;
1009*967dd82fSFlorian Fainelli #else
1010*967dd82fSFlorian Fainelli 			break;
1011*967dd82fSFlorian Fainelli #endif
1012*967dd82fSFlorian Fainelli 		}
1013*967dd82fSFlorian Fainelli 	} else if (dev->chip_id == BCM53115_DEVICE_ID) {
1014*967dd82fSFlorian Fainelli 		u64 strap_value;
1015*967dd82fSFlorian Fainelli 
1016*967dd82fSFlorian Fainelli 		b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
1017*967dd82fSFlorian Fainelli 		/* use second IMP port if GMII is enabled */
1018*967dd82fSFlorian Fainelli 		if (strap_value & SV_GMII_CTRL_115)
1019*967dd82fSFlorian Fainelli 			dev->cpu_port = 5;
1020*967dd82fSFlorian Fainelli 	}
1021*967dd82fSFlorian Fainelli 
1022*967dd82fSFlorian Fainelli 	/* cpu port is always last */
1023*967dd82fSFlorian Fainelli 	dev->num_ports = dev->cpu_port + 1;
1024*967dd82fSFlorian Fainelli 	dev->enabled_ports |= BIT(dev->cpu_port);
1025*967dd82fSFlorian Fainelli 
1026*967dd82fSFlorian Fainelli 	dev->ports = devm_kzalloc(dev->dev,
1027*967dd82fSFlorian Fainelli 				  sizeof(struct b53_port) * dev->num_ports,
1028*967dd82fSFlorian Fainelli 				  GFP_KERNEL);
1029*967dd82fSFlorian Fainelli 	if (!dev->ports)
1030*967dd82fSFlorian Fainelli 		return -ENOMEM;
1031*967dd82fSFlorian Fainelli 
1032*967dd82fSFlorian Fainelli 	dev->reset_gpio = b53_switch_get_reset_gpio(dev);
1033*967dd82fSFlorian Fainelli 	if (dev->reset_gpio >= 0) {
1034*967dd82fSFlorian Fainelli 		ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
1035*967dd82fSFlorian Fainelli 					    GPIOF_OUT_INIT_HIGH, "robo_reset");
1036*967dd82fSFlorian Fainelli 		if (ret)
1037*967dd82fSFlorian Fainelli 			return ret;
1038*967dd82fSFlorian Fainelli 	}
1039*967dd82fSFlorian Fainelli 
1040*967dd82fSFlorian Fainelli 	return 0;
1041*967dd82fSFlorian Fainelli }
1042*967dd82fSFlorian Fainelli 
1043*967dd82fSFlorian Fainelli struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
1044*967dd82fSFlorian Fainelli 				    void *priv)
1045*967dd82fSFlorian Fainelli {
1046*967dd82fSFlorian Fainelli 	struct dsa_switch *ds;
1047*967dd82fSFlorian Fainelli 	struct b53_device *dev;
1048*967dd82fSFlorian Fainelli 
1049*967dd82fSFlorian Fainelli 	ds = devm_kzalloc(base, sizeof(*ds) + sizeof(*dev), GFP_KERNEL);
1050*967dd82fSFlorian Fainelli 	if (!ds)
1051*967dd82fSFlorian Fainelli 		return NULL;
1052*967dd82fSFlorian Fainelli 
1053*967dd82fSFlorian Fainelli 	dev = (struct b53_device *)(ds + 1);
1054*967dd82fSFlorian Fainelli 
1055*967dd82fSFlorian Fainelli 	ds->priv = dev;
1056*967dd82fSFlorian Fainelli 	ds->dev = base;
1057*967dd82fSFlorian Fainelli 	dev->dev = base;
1058*967dd82fSFlorian Fainelli 
1059*967dd82fSFlorian Fainelli 	dev->ds = ds;
1060*967dd82fSFlorian Fainelli 	dev->priv = priv;
1061*967dd82fSFlorian Fainelli 	dev->ops = ops;
1062*967dd82fSFlorian Fainelli 	mutex_init(&dev->reg_mutex);
1063*967dd82fSFlorian Fainelli 	mutex_init(&dev->stats_mutex);
1064*967dd82fSFlorian Fainelli 
1065*967dd82fSFlorian Fainelli 	return dev;
1066*967dd82fSFlorian Fainelli }
1067*967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_alloc);
1068*967dd82fSFlorian Fainelli 
1069*967dd82fSFlorian Fainelli int b53_switch_detect(struct b53_device *dev)
1070*967dd82fSFlorian Fainelli {
1071*967dd82fSFlorian Fainelli 	u32 id32;
1072*967dd82fSFlorian Fainelli 	u16 tmp;
1073*967dd82fSFlorian Fainelli 	u8 id8;
1074*967dd82fSFlorian Fainelli 	int ret;
1075*967dd82fSFlorian Fainelli 
1076*967dd82fSFlorian Fainelli 	ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
1077*967dd82fSFlorian Fainelli 	if (ret)
1078*967dd82fSFlorian Fainelli 		return ret;
1079*967dd82fSFlorian Fainelli 
1080*967dd82fSFlorian Fainelli 	switch (id8) {
1081*967dd82fSFlorian Fainelli 	case 0:
1082*967dd82fSFlorian Fainelli 		/* BCM5325 and BCM5365 do not have this register so reads
1083*967dd82fSFlorian Fainelli 		 * return 0. But the read operation did succeed, so assume this
1084*967dd82fSFlorian Fainelli 		 * is one of them.
1085*967dd82fSFlorian Fainelli 		 *
1086*967dd82fSFlorian Fainelli 		 * Next check if we can write to the 5325's VTA register; for
1087*967dd82fSFlorian Fainelli 		 * 5365 it is read only.
1088*967dd82fSFlorian Fainelli 		 */
1089*967dd82fSFlorian Fainelli 		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
1090*967dd82fSFlorian Fainelli 		b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
1091*967dd82fSFlorian Fainelli 
1092*967dd82fSFlorian Fainelli 		if (tmp == 0xf)
1093*967dd82fSFlorian Fainelli 			dev->chip_id = BCM5325_DEVICE_ID;
1094*967dd82fSFlorian Fainelli 		else
1095*967dd82fSFlorian Fainelli 			dev->chip_id = BCM5365_DEVICE_ID;
1096*967dd82fSFlorian Fainelli 		break;
1097*967dd82fSFlorian Fainelli 	case BCM5395_DEVICE_ID:
1098*967dd82fSFlorian Fainelli 	case BCM5397_DEVICE_ID:
1099*967dd82fSFlorian Fainelli 	case BCM5398_DEVICE_ID:
1100*967dd82fSFlorian Fainelli 		dev->chip_id = id8;
1101*967dd82fSFlorian Fainelli 		break;
1102*967dd82fSFlorian Fainelli 	default:
1103*967dd82fSFlorian Fainelli 		ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
1104*967dd82fSFlorian Fainelli 		if (ret)
1105*967dd82fSFlorian Fainelli 			return ret;
1106*967dd82fSFlorian Fainelli 
1107*967dd82fSFlorian Fainelli 		switch (id32) {
1108*967dd82fSFlorian Fainelli 		case BCM53115_DEVICE_ID:
1109*967dd82fSFlorian Fainelli 		case BCM53125_DEVICE_ID:
1110*967dd82fSFlorian Fainelli 		case BCM53128_DEVICE_ID:
1111*967dd82fSFlorian Fainelli 		case BCM53010_DEVICE_ID:
1112*967dd82fSFlorian Fainelli 		case BCM53011_DEVICE_ID:
1113*967dd82fSFlorian Fainelli 		case BCM53012_DEVICE_ID:
1114*967dd82fSFlorian Fainelli 		case BCM53018_DEVICE_ID:
1115*967dd82fSFlorian Fainelli 		case BCM53019_DEVICE_ID:
1116*967dd82fSFlorian Fainelli 			dev->chip_id = id32;
1117*967dd82fSFlorian Fainelli 			break;
1118*967dd82fSFlorian Fainelli 		default:
1119*967dd82fSFlorian Fainelli 			pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
1120*967dd82fSFlorian Fainelli 			       id8, id32);
1121*967dd82fSFlorian Fainelli 			return -ENODEV;
1122*967dd82fSFlorian Fainelli 		}
1123*967dd82fSFlorian Fainelli 	}
1124*967dd82fSFlorian Fainelli 
1125*967dd82fSFlorian Fainelli 	if (dev->chip_id == BCM5325_DEVICE_ID)
1126*967dd82fSFlorian Fainelli 		return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
1127*967dd82fSFlorian Fainelli 				 &dev->core_rev);
1128*967dd82fSFlorian Fainelli 	else
1129*967dd82fSFlorian Fainelli 		return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
1130*967dd82fSFlorian Fainelli 				 &dev->core_rev);
1131*967dd82fSFlorian Fainelli }
1132*967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_detect);
1133*967dd82fSFlorian Fainelli 
1134*967dd82fSFlorian Fainelli int b53_switch_register(struct b53_device *dev)
1135*967dd82fSFlorian Fainelli {
1136*967dd82fSFlorian Fainelli 	int ret;
1137*967dd82fSFlorian Fainelli 
1138*967dd82fSFlorian Fainelli 	if (dev->pdata) {
1139*967dd82fSFlorian Fainelli 		dev->chip_id = dev->pdata->chip_id;
1140*967dd82fSFlorian Fainelli 		dev->enabled_ports = dev->pdata->enabled_ports;
1141*967dd82fSFlorian Fainelli 	}
1142*967dd82fSFlorian Fainelli 
1143*967dd82fSFlorian Fainelli 	if (!dev->chip_id && b53_switch_detect(dev))
1144*967dd82fSFlorian Fainelli 		return -EINVAL;
1145*967dd82fSFlorian Fainelli 
1146*967dd82fSFlorian Fainelli 	ret = b53_switch_init(dev);
1147*967dd82fSFlorian Fainelli 	if (ret)
1148*967dd82fSFlorian Fainelli 		return ret;
1149*967dd82fSFlorian Fainelli 
1150*967dd82fSFlorian Fainelli 	pr_info("found switch: %s, rev %i\n", dev->name, dev->core_rev);
1151*967dd82fSFlorian Fainelli 
1152*967dd82fSFlorian Fainelli 	return dsa_register_switch(dev->ds, dev->ds->dev->of_node);
1153*967dd82fSFlorian Fainelli }
1154*967dd82fSFlorian Fainelli EXPORT_SYMBOL(b53_switch_register);
1155*967dd82fSFlorian Fainelli 
1156*967dd82fSFlorian Fainelli MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
1157*967dd82fSFlorian Fainelli MODULE_DESCRIPTION("B53 switch library");
1158*967dd82fSFlorian Fainelli MODULE_LICENSE("Dual BSD/GPL");
1159