1*765c39a4SLuiz Angelo Daros de Luca // SPDX-License-Identifier: GPL-2.0
2*765c39a4SLuiz Angelo Daros de Luca /* Realtek SMI library helpers for the RTL8366x variants
3*765c39a4SLuiz Angelo Daros de Luca  * RTL8366RB and RTL8366S
4*765c39a4SLuiz Angelo Daros de Luca  *
5*765c39a4SLuiz Angelo Daros de Luca  * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
6*765c39a4SLuiz Angelo Daros de Luca  * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
7*765c39a4SLuiz Angelo Daros de Luca  * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
8*765c39a4SLuiz Angelo Daros de Luca  * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
9*765c39a4SLuiz Angelo Daros de Luca  * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
10*765c39a4SLuiz Angelo Daros de Luca  */
11*765c39a4SLuiz Angelo Daros de Luca #include <linux/if_bridge.h>
12*765c39a4SLuiz Angelo Daros de Luca #include <net/dsa.h>
13*765c39a4SLuiz Angelo Daros de Luca 
14*765c39a4SLuiz Angelo Daros de Luca #include "realtek.h"
15*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_mc_is_used(struct realtek_priv * priv,int mc_index,int * used)16*765c39a4SLuiz Angelo Daros de Luca int rtl8366_mc_is_used(struct realtek_priv *priv, int mc_index, int *used)
17*765c39a4SLuiz Angelo Daros de Luca {
18*765c39a4SLuiz Angelo Daros de Luca 	int ret;
19*765c39a4SLuiz Angelo Daros de Luca 	int i;
20*765c39a4SLuiz Angelo Daros de Luca 
21*765c39a4SLuiz Angelo Daros de Luca 	*used = 0;
22*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_ports; i++) {
23*765c39a4SLuiz Angelo Daros de Luca 		int index = 0;
24*765c39a4SLuiz Angelo Daros de Luca 
25*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->get_mc_index(priv, i, &index);
26*765c39a4SLuiz Angelo Daros de Luca 		if (ret)
27*765c39a4SLuiz Angelo Daros de Luca 			return ret;
28*765c39a4SLuiz Angelo Daros de Luca 
29*765c39a4SLuiz Angelo Daros de Luca 		if (mc_index == index) {
30*765c39a4SLuiz Angelo Daros de Luca 			*used = 1;
31*765c39a4SLuiz Angelo Daros de Luca 			break;
32*765c39a4SLuiz Angelo Daros de Luca 		}
33*765c39a4SLuiz Angelo Daros de Luca 	}
34*765c39a4SLuiz Angelo Daros de Luca 
35*765c39a4SLuiz Angelo Daros de Luca 	return 0;
36*765c39a4SLuiz Angelo Daros de Luca }
37*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_mc_is_used);
38*765c39a4SLuiz Angelo Daros de Luca 
39*765c39a4SLuiz Angelo Daros de Luca /**
40*765c39a4SLuiz Angelo Daros de Luca  * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration
41*765c39a4SLuiz Angelo Daros de Luca  * @priv: the Realtek SMI device instance
42*765c39a4SLuiz Angelo Daros de Luca  * @vid: the VLAN ID to look up or allocate
43*765c39a4SLuiz Angelo Daros de Luca  * @vlanmc: the pointer will be assigned to a pointer to a valid member config
44*765c39a4SLuiz Angelo Daros de Luca  * if successful
45*765c39a4SLuiz Angelo Daros de Luca  * @return: index of a new member config or negative error number
46*765c39a4SLuiz Angelo Daros de Luca  */
rtl8366_obtain_mc(struct realtek_priv * priv,int vid,struct rtl8366_vlan_mc * vlanmc)47*765c39a4SLuiz Angelo Daros de Luca static int rtl8366_obtain_mc(struct realtek_priv *priv, int vid,
48*765c39a4SLuiz Angelo Daros de Luca 			     struct rtl8366_vlan_mc *vlanmc)
49*765c39a4SLuiz Angelo Daros de Luca {
50*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_vlan_4k vlan4k;
51*765c39a4SLuiz Angelo Daros de Luca 	int ret;
52*765c39a4SLuiz Angelo Daros de Luca 	int i;
53*765c39a4SLuiz Angelo Daros de Luca 
54*765c39a4SLuiz Angelo Daros de Luca 	/* Try to find an existing member config entry for this VID */
55*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_vlan_mc; i++) {
56*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
57*765c39a4SLuiz Angelo Daros de Luca 		if (ret) {
58*765c39a4SLuiz Angelo Daros de Luca 			dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n",
59*765c39a4SLuiz Angelo Daros de Luca 				i, vid);
60*765c39a4SLuiz Angelo Daros de Luca 			return ret;
61*765c39a4SLuiz Angelo Daros de Luca 		}
62*765c39a4SLuiz Angelo Daros de Luca 
63*765c39a4SLuiz Angelo Daros de Luca 		if (vid == vlanmc->vid)
64*765c39a4SLuiz Angelo Daros de Luca 			return i;
65*765c39a4SLuiz Angelo Daros de Luca 	}
66*765c39a4SLuiz Angelo Daros de Luca 
67*765c39a4SLuiz Angelo Daros de Luca 	/* We have no MC entry for this VID, try to find an empty one */
68*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_vlan_mc; i++) {
69*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->get_vlan_mc(priv, i, vlanmc);
70*765c39a4SLuiz Angelo Daros de Luca 		if (ret) {
71*765c39a4SLuiz Angelo Daros de Luca 			dev_err(priv->dev, "error searching for VLAN MC %d for VID %d\n",
72*765c39a4SLuiz Angelo Daros de Luca 				i, vid);
73*765c39a4SLuiz Angelo Daros de Luca 			return ret;
74*765c39a4SLuiz Angelo Daros de Luca 		}
75*765c39a4SLuiz Angelo Daros de Luca 
76*765c39a4SLuiz Angelo Daros de Luca 		if (vlanmc->vid == 0 && vlanmc->member == 0) {
77*765c39a4SLuiz Angelo Daros de Luca 			/* Update the entry from the 4K table */
78*765c39a4SLuiz Angelo Daros de Luca 			ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
79*765c39a4SLuiz Angelo Daros de Luca 			if (ret) {
80*765c39a4SLuiz Angelo Daros de Luca 				dev_err(priv->dev, "error looking for 4K VLAN MC %d for VID %d\n",
81*765c39a4SLuiz Angelo Daros de Luca 					i, vid);
82*765c39a4SLuiz Angelo Daros de Luca 				return ret;
83*765c39a4SLuiz Angelo Daros de Luca 			}
84*765c39a4SLuiz Angelo Daros de Luca 
85*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->vid = vid;
86*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->member = vlan4k.member;
87*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->untag = vlan4k.untag;
88*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->fid = vlan4k.fid;
89*765c39a4SLuiz Angelo Daros de Luca 			ret = priv->ops->set_vlan_mc(priv, i, vlanmc);
90*765c39a4SLuiz Angelo Daros de Luca 			if (ret) {
91*765c39a4SLuiz Angelo Daros de Luca 				dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
92*765c39a4SLuiz Angelo Daros de Luca 					i, vid);
93*765c39a4SLuiz Angelo Daros de Luca 				return ret;
94*765c39a4SLuiz Angelo Daros de Luca 			}
95*765c39a4SLuiz Angelo Daros de Luca 
96*765c39a4SLuiz Angelo Daros de Luca 			dev_dbg(priv->dev, "created new MC at index %d for VID %d\n",
97*765c39a4SLuiz Angelo Daros de Luca 				i, vid);
98*765c39a4SLuiz Angelo Daros de Luca 			return i;
99*765c39a4SLuiz Angelo Daros de Luca 		}
100*765c39a4SLuiz Angelo Daros de Luca 	}
101*765c39a4SLuiz Angelo Daros de Luca 
102*765c39a4SLuiz Angelo Daros de Luca 	/* MC table is full, try to find an unused entry and replace it */
103*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_vlan_mc; i++) {
104*765c39a4SLuiz Angelo Daros de Luca 		int used;
105*765c39a4SLuiz Angelo Daros de Luca 
106*765c39a4SLuiz Angelo Daros de Luca 		ret = rtl8366_mc_is_used(priv, i, &used);
107*765c39a4SLuiz Angelo Daros de Luca 		if (ret)
108*765c39a4SLuiz Angelo Daros de Luca 			return ret;
109*765c39a4SLuiz Angelo Daros de Luca 
110*765c39a4SLuiz Angelo Daros de Luca 		if (!used) {
111*765c39a4SLuiz Angelo Daros de Luca 			/* Update the entry from the 4K table */
112*765c39a4SLuiz Angelo Daros de Luca 			ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
113*765c39a4SLuiz Angelo Daros de Luca 			if (ret)
114*765c39a4SLuiz Angelo Daros de Luca 				return ret;
115*765c39a4SLuiz Angelo Daros de Luca 
116*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->vid = vid;
117*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->member = vlan4k.member;
118*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->untag = vlan4k.untag;
119*765c39a4SLuiz Angelo Daros de Luca 			vlanmc->fid = vlan4k.fid;
120*765c39a4SLuiz Angelo Daros de Luca 			ret = priv->ops->set_vlan_mc(priv, i, vlanmc);
121*765c39a4SLuiz Angelo Daros de Luca 			if (ret) {
122*765c39a4SLuiz Angelo Daros de Luca 				dev_err(priv->dev, "unable to set/update VLAN MC %d for VID %d\n",
123*765c39a4SLuiz Angelo Daros de Luca 					i, vid);
124*765c39a4SLuiz Angelo Daros de Luca 				return ret;
125*765c39a4SLuiz Angelo Daros de Luca 			}
126*765c39a4SLuiz Angelo Daros de Luca 			dev_dbg(priv->dev, "recycled MC at index %i for VID %d\n",
127*765c39a4SLuiz Angelo Daros de Luca 				i, vid);
128*765c39a4SLuiz Angelo Daros de Luca 			return i;
129*765c39a4SLuiz Angelo Daros de Luca 		}
130*765c39a4SLuiz Angelo Daros de Luca 	}
131*765c39a4SLuiz Angelo Daros de Luca 
132*765c39a4SLuiz Angelo Daros de Luca 	dev_err(priv->dev, "all VLAN member configurations are in use\n");
133*765c39a4SLuiz Angelo Daros de Luca 	return -ENOSPC;
134*765c39a4SLuiz Angelo Daros de Luca }
135*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_set_vlan(struct realtek_priv * priv,int vid,u32 member,u32 untag,u32 fid)136*765c39a4SLuiz Angelo Daros de Luca int rtl8366_set_vlan(struct realtek_priv *priv, int vid, u32 member,
137*765c39a4SLuiz Angelo Daros de Luca 		     u32 untag, u32 fid)
138*765c39a4SLuiz Angelo Daros de Luca {
139*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_vlan_mc vlanmc;
140*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_vlan_4k vlan4k;
141*765c39a4SLuiz Angelo Daros de Luca 	int mc;
142*765c39a4SLuiz Angelo Daros de Luca 	int ret;
143*765c39a4SLuiz Angelo Daros de Luca 
144*765c39a4SLuiz Angelo Daros de Luca 	if (!priv->ops->is_vlan_valid(priv, vid))
145*765c39a4SLuiz Angelo Daros de Luca 		return -EINVAL;
146*765c39a4SLuiz Angelo Daros de Luca 
147*765c39a4SLuiz Angelo Daros de Luca 	dev_dbg(priv->dev,
148*765c39a4SLuiz Angelo Daros de Luca 		"setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
149*765c39a4SLuiz Angelo Daros de Luca 		vid, member, untag);
150*765c39a4SLuiz Angelo Daros de Luca 
151*765c39a4SLuiz Angelo Daros de Luca 	/* Update the 4K table */
152*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->get_vlan_4k(priv, vid, &vlan4k);
153*765c39a4SLuiz Angelo Daros de Luca 	if (ret)
154*765c39a4SLuiz Angelo Daros de Luca 		return ret;
155*765c39a4SLuiz Angelo Daros de Luca 
156*765c39a4SLuiz Angelo Daros de Luca 	vlan4k.member |= member;
157*765c39a4SLuiz Angelo Daros de Luca 	vlan4k.untag |= untag;
158*765c39a4SLuiz Angelo Daros de Luca 	vlan4k.fid = fid;
159*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->set_vlan_4k(priv, &vlan4k);
160*765c39a4SLuiz Angelo Daros de Luca 	if (ret)
161*765c39a4SLuiz Angelo Daros de Luca 		return ret;
162*765c39a4SLuiz Angelo Daros de Luca 
163*765c39a4SLuiz Angelo Daros de Luca 	dev_dbg(priv->dev,
164*765c39a4SLuiz Angelo Daros de Luca 		"resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",
165*765c39a4SLuiz Angelo Daros de Luca 		vid, vlan4k.member, vlan4k.untag);
166*765c39a4SLuiz Angelo Daros de Luca 
167*765c39a4SLuiz Angelo Daros de Luca 	/* Find or allocate a member config for this VID */
168*765c39a4SLuiz Angelo Daros de Luca 	ret = rtl8366_obtain_mc(priv, vid, &vlanmc);
169*765c39a4SLuiz Angelo Daros de Luca 	if (ret < 0)
170*765c39a4SLuiz Angelo Daros de Luca 		return ret;
171*765c39a4SLuiz Angelo Daros de Luca 	mc = ret;
172*765c39a4SLuiz Angelo Daros de Luca 
173*765c39a4SLuiz Angelo Daros de Luca 	/* Update the MC entry */
174*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.member |= member;
175*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.untag |= untag;
176*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.fid = fid;
177*765c39a4SLuiz Angelo Daros de Luca 
178*765c39a4SLuiz Angelo Daros de Luca 	/* Commit updates to the MC entry */
179*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->set_vlan_mc(priv, mc, &vlanmc);
180*765c39a4SLuiz Angelo Daros de Luca 	if (ret)
181*765c39a4SLuiz Angelo Daros de Luca 		dev_err(priv->dev, "failed to commit changes to VLAN MC index %d for VID %d\n",
182*765c39a4SLuiz Angelo Daros de Luca 			mc, vid);
183*765c39a4SLuiz Angelo Daros de Luca 	else
184*765c39a4SLuiz Angelo Daros de Luca 		dev_dbg(priv->dev,
185*765c39a4SLuiz Angelo Daros de Luca 			"resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n",
186*765c39a4SLuiz Angelo Daros de Luca 			vid, vlanmc.member, vlanmc.untag);
187*765c39a4SLuiz Angelo Daros de Luca 
188*765c39a4SLuiz Angelo Daros de Luca 	return ret;
189*765c39a4SLuiz Angelo Daros de Luca }
190*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_set_vlan);
191*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_set_pvid(struct realtek_priv * priv,unsigned int port,unsigned int vid)192*765c39a4SLuiz Angelo Daros de Luca int rtl8366_set_pvid(struct realtek_priv *priv, unsigned int port,
193*765c39a4SLuiz Angelo Daros de Luca 		     unsigned int vid)
194*765c39a4SLuiz Angelo Daros de Luca {
195*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_vlan_mc vlanmc;
196*765c39a4SLuiz Angelo Daros de Luca 	int mc;
197*765c39a4SLuiz Angelo Daros de Luca 	int ret;
198*765c39a4SLuiz Angelo Daros de Luca 
199*765c39a4SLuiz Angelo Daros de Luca 	if (!priv->ops->is_vlan_valid(priv, vid))
200*765c39a4SLuiz Angelo Daros de Luca 		return -EINVAL;
201*765c39a4SLuiz Angelo Daros de Luca 
202*765c39a4SLuiz Angelo Daros de Luca 	/* Find or allocate a member config for this VID */
203*765c39a4SLuiz Angelo Daros de Luca 	ret = rtl8366_obtain_mc(priv, vid, &vlanmc);
204*765c39a4SLuiz Angelo Daros de Luca 	if (ret < 0)
205*765c39a4SLuiz Angelo Daros de Luca 		return ret;
206*765c39a4SLuiz Angelo Daros de Luca 	mc = ret;
207*765c39a4SLuiz Angelo Daros de Luca 
208*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->set_mc_index(priv, port, mc);
209*765c39a4SLuiz Angelo Daros de Luca 	if (ret) {
210*765c39a4SLuiz Angelo Daros de Luca 		dev_err(priv->dev, "set PVID: failed to set MC index %d for port %d\n",
211*765c39a4SLuiz Angelo Daros de Luca 			mc, port);
212*765c39a4SLuiz Angelo Daros de Luca 		return ret;
213*765c39a4SLuiz Angelo Daros de Luca 	}
214*765c39a4SLuiz Angelo Daros de Luca 
215*765c39a4SLuiz Angelo Daros de Luca 	dev_dbg(priv->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n",
216*765c39a4SLuiz Angelo Daros de Luca 		port, vid, mc);
217*765c39a4SLuiz Angelo Daros de Luca 
218*765c39a4SLuiz Angelo Daros de Luca 	return 0;
219*765c39a4SLuiz Angelo Daros de Luca }
220*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_set_pvid);
221*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_enable_vlan4k(struct realtek_priv * priv,bool enable)222*765c39a4SLuiz Angelo Daros de Luca int rtl8366_enable_vlan4k(struct realtek_priv *priv, bool enable)
223*765c39a4SLuiz Angelo Daros de Luca {
224*765c39a4SLuiz Angelo Daros de Luca 	int ret;
225*765c39a4SLuiz Angelo Daros de Luca 
226*765c39a4SLuiz Angelo Daros de Luca 	/* To enable 4k VLAN, ordinary VLAN must be enabled first,
227*765c39a4SLuiz Angelo Daros de Luca 	 * but if we disable 4k VLAN it is fine to leave ordinary
228*765c39a4SLuiz Angelo Daros de Luca 	 * VLAN enabled.
229*765c39a4SLuiz Angelo Daros de Luca 	 */
230*765c39a4SLuiz Angelo Daros de Luca 	if (enable) {
231*765c39a4SLuiz Angelo Daros de Luca 		/* Make sure VLAN is ON */
232*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->enable_vlan(priv, true);
233*765c39a4SLuiz Angelo Daros de Luca 		if (ret)
234*765c39a4SLuiz Angelo Daros de Luca 			return ret;
235*765c39a4SLuiz Angelo Daros de Luca 
236*765c39a4SLuiz Angelo Daros de Luca 		priv->vlan_enabled = true;
237*765c39a4SLuiz Angelo Daros de Luca 	}
238*765c39a4SLuiz Angelo Daros de Luca 
239*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->enable_vlan4k(priv, enable);
240*765c39a4SLuiz Angelo Daros de Luca 	if (ret)
241*765c39a4SLuiz Angelo Daros de Luca 		return ret;
242*765c39a4SLuiz Angelo Daros de Luca 
243*765c39a4SLuiz Angelo Daros de Luca 	priv->vlan4k_enabled = enable;
244*765c39a4SLuiz Angelo Daros de Luca 	return 0;
245*765c39a4SLuiz Angelo Daros de Luca }
246*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_enable_vlan4k);
247*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_enable_vlan(struct realtek_priv * priv,bool enable)248*765c39a4SLuiz Angelo Daros de Luca int rtl8366_enable_vlan(struct realtek_priv *priv, bool enable)
249*765c39a4SLuiz Angelo Daros de Luca {
250*765c39a4SLuiz Angelo Daros de Luca 	int ret;
251*765c39a4SLuiz Angelo Daros de Luca 
252*765c39a4SLuiz Angelo Daros de Luca 	ret = priv->ops->enable_vlan(priv, enable);
253*765c39a4SLuiz Angelo Daros de Luca 	if (ret)
254*765c39a4SLuiz Angelo Daros de Luca 		return ret;
255*765c39a4SLuiz Angelo Daros de Luca 
256*765c39a4SLuiz Angelo Daros de Luca 	priv->vlan_enabled = enable;
257*765c39a4SLuiz Angelo Daros de Luca 
258*765c39a4SLuiz Angelo Daros de Luca 	/* If we turn VLAN off, make sure that we turn off
259*765c39a4SLuiz Angelo Daros de Luca 	 * 4k VLAN as well, if that happened to be on.
260*765c39a4SLuiz Angelo Daros de Luca 	 */
261*765c39a4SLuiz Angelo Daros de Luca 	if (!enable) {
262*765c39a4SLuiz Angelo Daros de Luca 		priv->vlan4k_enabled = false;
263*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->enable_vlan4k(priv, false);
264*765c39a4SLuiz Angelo Daros de Luca 	}
265*765c39a4SLuiz Angelo Daros de Luca 
266*765c39a4SLuiz Angelo Daros de Luca 	return ret;
267*765c39a4SLuiz Angelo Daros de Luca }
268*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
269*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_reset_vlan(struct realtek_priv * priv)270*765c39a4SLuiz Angelo Daros de Luca int rtl8366_reset_vlan(struct realtek_priv *priv)
271*765c39a4SLuiz Angelo Daros de Luca {
272*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_vlan_mc vlanmc;
273*765c39a4SLuiz Angelo Daros de Luca 	int ret;
274*765c39a4SLuiz Angelo Daros de Luca 	int i;
275*765c39a4SLuiz Angelo Daros de Luca 
276*765c39a4SLuiz Angelo Daros de Luca 	rtl8366_enable_vlan(priv, false);
277*765c39a4SLuiz Angelo Daros de Luca 	rtl8366_enable_vlan4k(priv, false);
278*765c39a4SLuiz Angelo Daros de Luca 
279*765c39a4SLuiz Angelo Daros de Luca 	/* Clear the 16 VLAN member configurations */
280*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.vid = 0;
281*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.priority = 0;
282*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.member = 0;
283*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.untag = 0;
284*765c39a4SLuiz Angelo Daros de Luca 	vlanmc.fid = 0;
285*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_vlan_mc; i++) {
286*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
287*765c39a4SLuiz Angelo Daros de Luca 		if (ret)
288*765c39a4SLuiz Angelo Daros de Luca 			return ret;
289*765c39a4SLuiz Angelo Daros de Luca 	}
290*765c39a4SLuiz Angelo Daros de Luca 
291*765c39a4SLuiz Angelo Daros de Luca 	return 0;
292*765c39a4SLuiz Angelo Daros de Luca }
293*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
294*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_vlan_add(struct dsa_switch * ds,int port,const struct switchdev_obj_port_vlan * vlan,struct netlink_ext_ack * extack)295*765c39a4SLuiz Angelo Daros de Luca int rtl8366_vlan_add(struct dsa_switch *ds, int port,
296*765c39a4SLuiz Angelo Daros de Luca 		     const struct switchdev_obj_port_vlan *vlan,
297*765c39a4SLuiz Angelo Daros de Luca 		     struct netlink_ext_ack *extack)
298*765c39a4SLuiz Angelo Daros de Luca {
299*765c39a4SLuiz Angelo Daros de Luca 	bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
300*765c39a4SLuiz Angelo Daros de Luca 	bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
301*765c39a4SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ds->priv;
302*765c39a4SLuiz Angelo Daros de Luca 	u32 member = 0;
303*765c39a4SLuiz Angelo Daros de Luca 	u32 untag = 0;
304*765c39a4SLuiz Angelo Daros de Luca 	int ret;
305*765c39a4SLuiz Angelo Daros de Luca 
306*765c39a4SLuiz Angelo Daros de Luca 	if (!priv->ops->is_vlan_valid(priv, vlan->vid)) {
307*765c39a4SLuiz Angelo Daros de Luca 		NL_SET_ERR_MSG_MOD(extack, "VLAN ID not valid");
308*765c39a4SLuiz Angelo Daros de Luca 		return -EINVAL;
309*765c39a4SLuiz Angelo Daros de Luca 	}
310*765c39a4SLuiz Angelo Daros de Luca 
311*765c39a4SLuiz Angelo Daros de Luca 	/* Enable VLAN in the hardware
312*765c39a4SLuiz Angelo Daros de Luca 	 * FIXME: what's with this 4k business?
313*765c39a4SLuiz Angelo Daros de Luca 	 * Just rtl8366_enable_vlan() seems inconclusive.
314*765c39a4SLuiz Angelo Daros de Luca 	 */
315*765c39a4SLuiz Angelo Daros de Luca 	ret = rtl8366_enable_vlan4k(priv, true);
316*765c39a4SLuiz Angelo Daros de Luca 	if (ret) {
317*765c39a4SLuiz Angelo Daros de Luca 		NL_SET_ERR_MSG_MOD(extack, "Failed to enable VLAN 4K");
318*765c39a4SLuiz Angelo Daros de Luca 		return ret;
319*765c39a4SLuiz Angelo Daros de Luca 	}
320*765c39a4SLuiz Angelo Daros de Luca 
321*765c39a4SLuiz Angelo Daros de Luca 	dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n",
322*765c39a4SLuiz Angelo Daros de Luca 		vlan->vid, port, untagged ? "untagged" : "tagged",
323*765c39a4SLuiz Angelo Daros de Luca 		pvid ? "PVID" : "no PVID");
324*765c39a4SLuiz Angelo Daros de Luca 
325*765c39a4SLuiz Angelo Daros de Luca 	member |= BIT(port);
326*765c39a4SLuiz Angelo Daros de Luca 
327*765c39a4SLuiz Angelo Daros de Luca 	if (untagged)
328*765c39a4SLuiz Angelo Daros de Luca 		untag |= BIT(port);
329*765c39a4SLuiz Angelo Daros de Luca 
330*765c39a4SLuiz Angelo Daros de Luca 	ret = rtl8366_set_vlan(priv, vlan->vid, member, untag, 0);
331*765c39a4SLuiz Angelo Daros de Luca 	if (ret) {
332*765c39a4SLuiz Angelo Daros de Luca 		dev_err(priv->dev, "failed to set up VLAN %04x", vlan->vid);
333*765c39a4SLuiz Angelo Daros de Luca 		return ret;
334*765c39a4SLuiz Angelo Daros de Luca 	}
335*765c39a4SLuiz Angelo Daros de Luca 
336*765c39a4SLuiz Angelo Daros de Luca 	if (!pvid)
337*765c39a4SLuiz Angelo Daros de Luca 		return 0;
338*765c39a4SLuiz Angelo Daros de Luca 
339*765c39a4SLuiz Angelo Daros de Luca 	ret = rtl8366_set_pvid(priv, port, vlan->vid);
340*765c39a4SLuiz Angelo Daros de Luca 	if (ret) {
341*765c39a4SLuiz Angelo Daros de Luca 		dev_err(priv->dev, "failed to set PVID on port %d to VLAN %04x",
342*765c39a4SLuiz Angelo Daros de Luca 			port, vlan->vid);
343*765c39a4SLuiz Angelo Daros de Luca 		return ret;
344*765c39a4SLuiz Angelo Daros de Luca 	}
345*765c39a4SLuiz Angelo Daros de Luca 
346*765c39a4SLuiz Angelo Daros de Luca 	return 0;
347*765c39a4SLuiz Angelo Daros de Luca }
348*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
349*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_vlan_del(struct dsa_switch * ds,int port,const struct switchdev_obj_port_vlan * vlan)350*765c39a4SLuiz Angelo Daros de Luca int rtl8366_vlan_del(struct dsa_switch *ds, int port,
351*765c39a4SLuiz Angelo Daros de Luca 		     const struct switchdev_obj_port_vlan *vlan)
352*765c39a4SLuiz Angelo Daros de Luca {
353*765c39a4SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ds->priv;
354*765c39a4SLuiz Angelo Daros de Luca 	int ret, i;
355*765c39a4SLuiz Angelo Daros de Luca 
356*765c39a4SLuiz Angelo Daros de Luca 	dev_dbg(priv->dev, "del VLAN %d on port %d\n", vlan->vid, port);
357*765c39a4SLuiz Angelo Daros de Luca 
358*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_vlan_mc; i++) {
359*765c39a4SLuiz Angelo Daros de Luca 		struct rtl8366_vlan_mc vlanmc;
360*765c39a4SLuiz Angelo Daros de Luca 
361*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->get_vlan_mc(priv, i, &vlanmc);
362*765c39a4SLuiz Angelo Daros de Luca 		if (ret)
363*765c39a4SLuiz Angelo Daros de Luca 			return ret;
364*765c39a4SLuiz Angelo Daros de Luca 
365*765c39a4SLuiz Angelo Daros de Luca 		if (vlan->vid == vlanmc.vid) {
366*765c39a4SLuiz Angelo Daros de Luca 			/* Remove this port from the VLAN */
367*765c39a4SLuiz Angelo Daros de Luca 			vlanmc.member &= ~BIT(port);
368*765c39a4SLuiz Angelo Daros de Luca 			vlanmc.untag &= ~BIT(port);
369*765c39a4SLuiz Angelo Daros de Luca 			/*
370*765c39a4SLuiz Angelo Daros de Luca 			 * If no ports are members of this VLAN
371*765c39a4SLuiz Angelo Daros de Luca 			 * anymore then clear the whole member
372*765c39a4SLuiz Angelo Daros de Luca 			 * config so it can be reused.
373*765c39a4SLuiz Angelo Daros de Luca 			 */
374*765c39a4SLuiz Angelo Daros de Luca 			if (!vlanmc.member) {
375*765c39a4SLuiz Angelo Daros de Luca 				vlanmc.vid = 0;
376*765c39a4SLuiz Angelo Daros de Luca 				vlanmc.priority = 0;
377*765c39a4SLuiz Angelo Daros de Luca 				vlanmc.fid = 0;
378*765c39a4SLuiz Angelo Daros de Luca 			}
379*765c39a4SLuiz Angelo Daros de Luca 			ret = priv->ops->set_vlan_mc(priv, i, &vlanmc);
380*765c39a4SLuiz Angelo Daros de Luca 			if (ret) {
381*765c39a4SLuiz Angelo Daros de Luca 				dev_err(priv->dev,
382*765c39a4SLuiz Angelo Daros de Luca 					"failed to remove VLAN %04x\n",
383*765c39a4SLuiz Angelo Daros de Luca 					vlan->vid);
384*765c39a4SLuiz Angelo Daros de Luca 				return ret;
385*765c39a4SLuiz Angelo Daros de Luca 			}
386*765c39a4SLuiz Angelo Daros de Luca 			break;
387*765c39a4SLuiz Angelo Daros de Luca 		}
388*765c39a4SLuiz Angelo Daros de Luca 	}
389*765c39a4SLuiz Angelo Daros de Luca 
390*765c39a4SLuiz Angelo Daros de Luca 	return 0;
391*765c39a4SLuiz Angelo Daros de Luca }
392*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_vlan_del);
393*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_get_strings(struct dsa_switch * ds,int port,u32 stringset,uint8_t * data)394*765c39a4SLuiz Angelo Daros de Luca void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
395*765c39a4SLuiz Angelo Daros de Luca 			 uint8_t *data)
396*765c39a4SLuiz Angelo Daros de Luca {
397*765c39a4SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ds->priv;
398*765c39a4SLuiz Angelo Daros de Luca 	struct rtl8366_mib_counter *mib;
399*765c39a4SLuiz Angelo Daros de Luca 	int i;
400*765c39a4SLuiz Angelo Daros de Luca 
401*765c39a4SLuiz Angelo Daros de Luca 	if (port >= priv->num_ports)
402*765c39a4SLuiz Angelo Daros de Luca 		return;
403*765c39a4SLuiz Angelo Daros de Luca 
404*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_mib_counters; i++) {
405*765c39a4SLuiz Angelo Daros de Luca 		mib = &priv->mib_counters[i];
406*765c39a4SLuiz Angelo Daros de Luca 		strncpy(data + i * ETH_GSTRING_LEN,
407*765c39a4SLuiz Angelo Daros de Luca 			mib->name, ETH_GSTRING_LEN);
408*765c39a4SLuiz Angelo Daros de Luca 	}
409*765c39a4SLuiz Angelo Daros de Luca }
410*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_get_strings);
411*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_get_sset_count(struct dsa_switch * ds,int port,int sset)412*765c39a4SLuiz Angelo Daros de Luca int rtl8366_get_sset_count(struct dsa_switch *ds, int port, int sset)
413*765c39a4SLuiz Angelo Daros de Luca {
414*765c39a4SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ds->priv;
415*765c39a4SLuiz Angelo Daros de Luca 
416*765c39a4SLuiz Angelo Daros de Luca 	/* We only support SS_STATS */
417*765c39a4SLuiz Angelo Daros de Luca 	if (sset != ETH_SS_STATS)
418*765c39a4SLuiz Angelo Daros de Luca 		return 0;
419*765c39a4SLuiz Angelo Daros de Luca 	if (port >= priv->num_ports)
420*765c39a4SLuiz Angelo Daros de Luca 		return -EINVAL;
421*765c39a4SLuiz Angelo Daros de Luca 
422*765c39a4SLuiz Angelo Daros de Luca 	return priv->num_mib_counters;
423*765c39a4SLuiz Angelo Daros de Luca }
424*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_get_sset_count);
425*765c39a4SLuiz Angelo Daros de Luca 
rtl8366_get_ethtool_stats(struct dsa_switch * ds,int port,uint64_t * data)426*765c39a4SLuiz Angelo Daros de Luca void rtl8366_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
427*765c39a4SLuiz Angelo Daros de Luca {
428*765c39a4SLuiz Angelo Daros de Luca 	struct realtek_priv *priv = ds->priv;
429*765c39a4SLuiz Angelo Daros de Luca 	int i;
430*765c39a4SLuiz Angelo Daros de Luca 	int ret;
431*765c39a4SLuiz Angelo Daros de Luca 
432*765c39a4SLuiz Angelo Daros de Luca 	if (port >= priv->num_ports)
433*765c39a4SLuiz Angelo Daros de Luca 		return;
434*765c39a4SLuiz Angelo Daros de Luca 
435*765c39a4SLuiz Angelo Daros de Luca 	for (i = 0; i < priv->num_mib_counters; i++) {
436*765c39a4SLuiz Angelo Daros de Luca 		struct rtl8366_mib_counter *mib;
437*765c39a4SLuiz Angelo Daros de Luca 		u64 mibvalue = 0;
438*765c39a4SLuiz Angelo Daros de Luca 
439*765c39a4SLuiz Angelo Daros de Luca 		mib = &priv->mib_counters[i];
440*765c39a4SLuiz Angelo Daros de Luca 		ret = priv->ops->get_mib_counter(priv, port, mib, &mibvalue);
441*765c39a4SLuiz Angelo Daros de Luca 		if (ret) {
442*765c39a4SLuiz Angelo Daros de Luca 			dev_err(priv->dev, "error reading MIB counter %s\n",
443*765c39a4SLuiz Angelo Daros de Luca 				mib->name);
444*765c39a4SLuiz Angelo Daros de Luca 		}
445*765c39a4SLuiz Angelo Daros de Luca 		data[i] = mibvalue;
446*765c39a4SLuiz Angelo Daros de Luca 	}
447*765c39a4SLuiz Angelo Daros de Luca }
448*765c39a4SLuiz Angelo Daros de Luca EXPORT_SYMBOL_GPL(rtl8366_get_ethtool_stats);
449