xref: /linux/drivers/net/dsa/realtek/rtl8365mb_vlan.c (revision 9da2c8672f77108a1f09232320f22225ab53dde9)
1*9da2c867SAlvin Šipraga // SPDX-License-Identifier: GPL-2.0
2*9da2c867SAlvin Šipraga /* VLAN configuration interface for the rtl8365mb switch family
3*9da2c867SAlvin Šipraga  *
4*9da2c867SAlvin Šipraga  * Copyright (C) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
5*9da2c867SAlvin Šipraga  *
6*9da2c867SAlvin Šipraga  * VLAN configuration takes place in two separate domains of the switch: the
7*9da2c867SAlvin Šipraga  * VLAN4k table and the VLAN membership configuration (MC) database. While the
8*9da2c867SAlvin Šipraga  * VLAN4k table is exhaustive and can be fully populated with 4096 VLAN
9*9da2c867SAlvin Šipraga  * configurations, the same does not hold for the VLAN membership configuration
10*9da2c867SAlvin Šipraga  * database, which is limited to 32 entries.
11*9da2c867SAlvin Šipraga  *
12*9da2c867SAlvin Šipraga  * The switch will normally only use the VLAN4k table when making forwarding
13*9da2c867SAlvin Šipraga  * decisions. The VLAN membership configuration database is a vestigial ASIC
14*9da2c867SAlvin Šipraga  * design and is only used for a few specific features in the rtl8365mb
15*9da2c867SAlvin Šipraga  * family. This means that the limit of 32 entries should not hinder us in
16*9da2c867SAlvin Šipraga  * programming a huge number of VLANs into the switch.
17*9da2c867SAlvin Šipraga  *
18*9da2c867SAlvin Šipraga  * One necessary use of the VLAN membership configuration database is for the
19*9da2c867SAlvin Šipraga  * programming of a port-based VLAN ID (PVID). The PVID is programmed on a
20*9da2c867SAlvin Šipraga  * per-port basis via register field, which refers to a specific VLAN membership
21*9da2c867SAlvin Šipraga  * configuration via an index 0~31. In order to maintain coherent behaviour on a
22*9da2c867SAlvin Šipraga  * port with a PVID, it is necessary to keep the VLAN configuration synchronized
23*9da2c867SAlvin Šipraga  * between the VLAN4k table and the VLAN membership configuration database.
24*9da2c867SAlvin Šipraga  *
25*9da2c867SAlvin Šipraga  * Since VLAN membership configs are a scarce resource, it will only be used
26*9da2c867SAlvin Šipraga  * when strictly needed (i.e. a VLAN with members using PVID). Otherwise, the
27*9da2c867SAlvin Šipraga  * VLAN4k will be enough.
28*9da2c867SAlvin Šipraga  *
29*9da2c867SAlvin Šipraga  * With some exceptions, the entries in both the VLAN4k table and the VLAN
30*9da2c867SAlvin Šipraga  * membership configuration database offer the same configuration options. The
31*9da2c867SAlvin Šipraga  * differences are as follows:
32*9da2c867SAlvin Šipraga  *
33*9da2c867SAlvin Šipraga  * 1. VLAN4k entries can specify whether to use Independent or Shared VLAN
34*9da2c867SAlvin Šipraga  *    Learning (IVL or SVL respectively). VLAN membership config entries
35*9da2c867SAlvin Šipraga  *    cannot. This underscores the fact that VLAN membership configs are not
36*9da2c867SAlvin Šipraga  *    involved in the learning process of the ASIC.
37*9da2c867SAlvin Šipraga  *
38*9da2c867SAlvin Šipraga  * 2. VLAN membership config entries use an "enhanced VLAN ID" (efid), which has
39*9da2c867SAlvin Šipraga  *    a range 0~8191 compared with the standard 0~4095 range of the VLAN4k
40*9da2c867SAlvin Šipraga  *    table. This underscores the fact that VLAN membership configs can be used
41*9da2c867SAlvin Šipraga  *    to group ports on a layer beyond the standard VLAN configuration, which
42*9da2c867SAlvin Šipraga  *    may be useful for ACL rules which specify alternative forwarding
43*9da2c867SAlvin Šipraga  *    decisions.
44*9da2c867SAlvin Šipraga  *
45*9da2c867SAlvin Šipraga  * VLANMC index 0 is reserved as a neutral PVID, used for standalone ports.
46*9da2c867SAlvin Šipraga  *
47*9da2c867SAlvin Šipraga  */
48*9da2c867SAlvin Šipraga 
49*9da2c867SAlvin Šipraga #include "rtl8365mb_vlan.h"
50*9da2c867SAlvin Šipraga #include "rtl8365mb_table.h"
51*9da2c867SAlvin Šipraga #include <linux/if_bridge.h>
52*9da2c867SAlvin Šipraga #include <linux/lockdep.h>
53*9da2c867SAlvin Šipraga #include <linux/regmap.h>
54*9da2c867SAlvin Šipraga 
55*9da2c867SAlvin Šipraga /* CVLAN (i.e. VLAN4k) table entry layout, u16[3] */
56*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_SIZE			3 /* 48-bits */
57*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK		GENMASK(7, 0)
58*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_MBR_LO_MASK			GENMASK(7, 0)
59*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK		GENMASK(15, 8)
60*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_UNTAG_LO_MASK			GENMASK(7, 0)
61*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_FID_MASK		GENMASK(3, 0)
62*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK		GENMASK(4, 4)
63*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK		GENMASK(7, 5)
64*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK		GENMASK(8, 8)
65*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK		GENMASK(13, 9)
66*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_METERIDX_LO_MASK		GENMASK(4, 0)
67*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK		GENMASK(14, 14)
68*9da2c867SAlvin Šipraga /* extends RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK */
69*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK		GENMASK(2, 0)
70*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_MBR_HI_MASK			GENMASK(10, 8)
71*9da2c867SAlvin Šipraga /* extends RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK */
72*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK		GENMASK(5, 3)
73*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_UNTAG_HI_MASK			GENMASK(10, 8)
74*9da2c867SAlvin Šipraga /* extends RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK */
75*9da2c867SAlvin Šipraga #define RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK	GENMASK(6, 6)
76*9da2c867SAlvin Šipraga #define   RTL8365MB_CVLAN_METERIDX_HI_MASK		GENMASK(5, 5)
77*9da2c867SAlvin Šipraga 
78*9da2c867SAlvin Šipraga /* VLAN member configuration registers 0~31, u16[3] */
79*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_MC_BASE				0x0728
80*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_MC_ENTRY_SIZE			4 /* 64-bit */
81*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_MC_REG(index) \
82*9da2c867SAlvin Šipraga 		(RTL8365MB_VLAN_MC_BASE + \
83*9da2c867SAlvin Šipraga 		 (RTL8365MB_VLAN_MC_ENTRY_SIZE * (index)))
84*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D0_MBR_MASK			GENMASK(10, 0)
85*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D1_FID_MASK			GENMASK(3, 0)
86*9da2c867SAlvin Šipraga 
87*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D2_VBPEN_MASK		GENMASK(0, 0)
88*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D2_VBPRI_MASK		GENMASK(3, 1)
89*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK		GENMASK(4, 4)
90*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D2_METERIDX_MASK		GENMASK(10, 5)
91*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_MC_D3_EVID_MASK		GENMASK(12, 0)
92*9da2c867SAlvin Šipraga 
93*9da2c867SAlvin Šipraga /* Some limits for VLAN4k/VLAN membership config entries */
94*9da2c867SAlvin Šipraga #define RTL8365MB_PRIORITYMAX	7
95*9da2c867SAlvin Šipraga #define RTL8365MB_FIDMAX	15
96*9da2c867SAlvin Šipraga #define RTL8365MB_METERMAX	63
97*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_MCMAX	31
98*9da2c867SAlvin Šipraga 
99*9da2c867SAlvin Šipraga /* RTL8367S supports 4k vlans (vid<=4095) and 32 enhanced vlans
100*9da2c867SAlvin Šipraga  * for VIDs up to 8191
101*9da2c867SAlvin Šipraga  */
102*9da2c867SAlvin Šipraga #define RTL8365MB_MAX_4K_VID	0x0FFF /* 4095 */
103*9da2c867SAlvin Šipraga #define RTL8365MB_MAX_MC_VID	0x1FFF /* 8191 */
104*9da2c867SAlvin Šipraga 
105*9da2c867SAlvin Šipraga  /* Port-based VID registers 0~5 - each one holds an MC index for two ports */
106*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_PVID_CTRL_BASE			0x0700
107*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_PVID_CTRL_REG(_p) \
108*9da2c867SAlvin Šipraga 		(RTL8365MB_VLAN_PVID_CTRL_BASE + ((_p) >> 1))
109*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK	0x001F
110*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK	0x1F00
111*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p) \
112*9da2c867SAlvin Šipraga 		(((_p) & 1) << 3)
113*9da2c867SAlvin Šipraga #define   RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \
114*9da2c867SAlvin Šipraga 		(0x1F << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p))
115*9da2c867SAlvin Šipraga 
116*9da2c867SAlvin Šipraga /* Frame type filtering registers */
117*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE	0x07aa
118*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port) \
119*9da2c867SAlvin Šipraga 		(RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_BASE + ((port) >> 3))
120*9da2c867SAlvin Šipraga /* required as FIELD_PREP cannot use non-constant masks */
121*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port) \
122*9da2c867SAlvin Šipraga 		(0x3 << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port))
123*9da2c867SAlvin Šipraga #define RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port) \
124*9da2c867SAlvin Šipraga 		(((port) & 0x7) << 1)
125*9da2c867SAlvin Šipraga 
126*9da2c867SAlvin Šipraga /*
127*9da2c867SAlvin Šipraga  * struct rtl8365mb_vlan4k - VLAN4k table entry
128*9da2c867SAlvin Šipraga  * @vid: VLAN ID (0~4095)
129*9da2c867SAlvin Šipraga  * @member: port mask of ports in this VLAN
130*9da2c867SAlvin Šipraga  * @untag: port mask of ports which untag on egress
131*9da2c867SAlvin Šipraga  * @fid: filter ID - only used with SVL (unused)
132*9da2c867SAlvin Šipraga  * @priority: priority classification (unused)
133*9da2c867SAlvin Šipraga  * @priority_en: enable priority (unused)
134*9da2c867SAlvin Šipraga  * @policing_en: enable policing (unused)
135*9da2c867SAlvin Šipraga  * @ivl_en: enable IVL instead of default SVL
136*9da2c867SAlvin Šipraga  * @meteridx: metering index (unused)
137*9da2c867SAlvin Šipraga  *
138*9da2c867SAlvin Šipraga  * This structure is used to get/set entries in the VLAN4k table. The
139*9da2c867SAlvin Šipraga  * VLAN4k table dictates the VLAN configuration for the switch for the
140*9da2c867SAlvin Šipraga  * vast majority of features.
141*9da2c867SAlvin Šipraga  */
142*9da2c867SAlvin Šipraga struct rtl8365mb_vlan4k {
143*9da2c867SAlvin Šipraga 	u16 vid;
144*9da2c867SAlvin Šipraga 	u16 member;
145*9da2c867SAlvin Šipraga 	u16 untag;
146*9da2c867SAlvin Šipraga 	u8 fid : 4;
147*9da2c867SAlvin Šipraga 	u8 priority : 3;
148*9da2c867SAlvin Šipraga 	u8 priority_en : 1;
149*9da2c867SAlvin Šipraga 	u8 policing_en : 1;
150*9da2c867SAlvin Šipraga 	u8 ivl_en : 1;
151*9da2c867SAlvin Šipraga 	u8 meteridx : 6;
152*9da2c867SAlvin Šipraga };
153*9da2c867SAlvin Šipraga 
154*9da2c867SAlvin Šipraga /*
155*9da2c867SAlvin Šipraga  * struct rtl8365mb_vlanmc - VLAN membership config
156*9da2c867SAlvin Šipraga  * @evid: Enhanced VLAN ID (0~8191)
157*9da2c867SAlvin Šipraga  * @member: port mask of ports in this VLAN
158*9da2c867SAlvin Šipraga  * @fid: filter ID - only used with SVL (unused)
159*9da2c867SAlvin Šipraga  * @priority: priority classification (unused)
160*9da2c867SAlvin Šipraga  * @priority_en: enable priority (unused)
161*9da2c867SAlvin Šipraga  * @policing_en: enable policing (unused)
162*9da2c867SAlvin Šipraga  * @meteridx: metering index (unused)
163*9da2c867SAlvin Šipraga  *
164*9da2c867SAlvin Šipraga  * This structure is used to get/set entries in the VLAN membership
165*9da2c867SAlvin Šipraga  * configuration database. This feature is largely vestigial, but
166*9da2c867SAlvin Šipraga  * still needed for at least the following features:
167*9da2c867SAlvin Šipraga  *   - PVID configuration
168*9da2c867SAlvin Šipraga  *   - ACL configuration
169*9da2c867SAlvin Šipraga  *   - selection of VLAN by the CPU tag when VSEL=1, although the switch
170*9da2c867SAlvin Šipraga  *     can also select VLAN based on the VLAN tag if VSEL=0
171*9da2c867SAlvin Šipraga  *
172*9da2c867SAlvin Šipraga  * This is a low-level structure and it is recommended to interface with
173*9da2c867SAlvin Šipraga  * the VLAN membership config database via &struct rtl8365mb_vlanmc_entry.
174*9da2c867SAlvin Šipraga  */
175*9da2c867SAlvin Šipraga struct rtl8365mb_vlanmc {
176*9da2c867SAlvin Šipraga 	u16 evid;
177*9da2c867SAlvin Šipraga 	u16 member;
178*9da2c867SAlvin Šipraga 	u8 fid : 4;
179*9da2c867SAlvin Šipraga 	u8 priority : 3;
180*9da2c867SAlvin Šipraga 	u8 priority_en : 1;
181*9da2c867SAlvin Šipraga 	u8 policing_en : 1;
182*9da2c867SAlvin Šipraga 	u8 meteridx : 6;
183*9da2c867SAlvin Šipraga };
184*9da2c867SAlvin Šipraga 
185*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_4k_read(struct realtek_priv *priv, u16 vid,
186*9da2c867SAlvin Šipraga 				  struct rtl8365mb_vlan4k *vlan4k)
187*9da2c867SAlvin Šipraga {
188*9da2c867SAlvin Šipraga 	u16 data[RTL8365MB_CVLAN_ENTRY_SIZE];
189*9da2c867SAlvin Šipraga 	int val;
190*9da2c867SAlvin Šipraga 	int ret;
191*9da2c867SAlvin Šipraga 
192*9da2c867SAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
193*9da2c867SAlvin Šipraga 				    RTL8365MB_TABLE_OP_READ, &vid, 0, 0,
194*9da2c867SAlvin Šipraga 				    data, ARRAY_SIZE(data));
195*9da2c867SAlvin Šipraga 	if (ret)
196*9da2c867SAlvin Šipraga 		return ret;
197*9da2c867SAlvin Šipraga 
198*9da2c867SAlvin Šipraga 	/* Unpack table entry */
199*9da2c867SAlvin Šipraga 	memset(vlan4k, 0, sizeof(*vlan4k));
200*9da2c867SAlvin Šipraga 	vlan4k->vid = vid;
201*9da2c867SAlvin Šipraga 
202*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, data[0]);
203*9da2c867SAlvin Šipraga 	vlan4k->member = FIELD_PREP(RTL8365MB_CVLAN_MBR_LO_MASK, val);
204*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, data[2]);
205*9da2c867SAlvin Šipraga 	vlan4k->member |= FIELD_PREP(RTL8365MB_CVLAN_MBR_HI_MASK, val);
206*9da2c867SAlvin Šipraga 
207*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, data[0]);
208*9da2c867SAlvin Šipraga 	vlan4k->untag = FIELD_PREP(RTL8365MB_CVLAN_UNTAG_LO_MASK, val);
209*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, data[2]);
210*9da2c867SAlvin Šipraga 	vlan4k->untag |= FIELD_PREP(RTL8365MB_CVLAN_UNTAG_HI_MASK, val);
211*9da2c867SAlvin Šipraga 
212*9da2c867SAlvin Šipraga 	vlan4k->fid = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, data[1]);
213*9da2c867SAlvin Šipraga 	vlan4k->priority_en =
214*9da2c867SAlvin Šipraga 		FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK, data[1]);
215*9da2c867SAlvin Šipraga 	vlan4k->priority =
216*9da2c867SAlvin Šipraga 		FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK, data[1]);
217*9da2c867SAlvin Šipraga 	vlan4k->policing_en =
218*9da2c867SAlvin Šipraga 		FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK, data[1]);
219*9da2c867SAlvin Šipraga 
220*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, data[1]);
221*9da2c867SAlvin Šipraga 	val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
222*9da2c867SAlvin Šipraga 	vlan4k->meteridx = val;
223*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, data[2]);
224*9da2c867SAlvin Šipraga 	val = FIELD_PREP(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
225*9da2c867SAlvin Šipraga 	vlan4k->meteridx |= val;
226*9da2c867SAlvin Šipraga 
227*9da2c867SAlvin Šipraga 	vlan4k->ivl_en =
228*9da2c867SAlvin Šipraga 		FIELD_GET(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK, data[1]);
229*9da2c867SAlvin Šipraga 
230*9da2c867SAlvin Šipraga 	return 0;
231*9da2c867SAlvin Šipraga }
232*9da2c867SAlvin Šipraga 
233*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_4k_write(struct realtek_priv *priv,
234*9da2c867SAlvin Šipraga 				   const struct rtl8365mb_vlan4k *vlan4k)
235*9da2c867SAlvin Šipraga {
236*9da2c867SAlvin Šipraga 	u16 data[RTL8365MB_CVLAN_ENTRY_SIZE] = { 0 };
237*9da2c867SAlvin Šipraga 	u16 vid;
238*9da2c867SAlvin Šipraga 	int val;
239*9da2c867SAlvin Šipraga 
240*9da2c867SAlvin Šipraga 	/* Pack table entry value */
241*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_MBR_LO_MASK, vlan4k->member);
242*9da2c867SAlvin Šipraga 	data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_MBR_MASK, val);
243*9da2c867SAlvin Šipraga 
244*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_LO_MASK, vlan4k->untag);
245*9da2c867SAlvin Šipraga 	data[0] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D0_UNTAG_MASK, val);
246*9da2c867SAlvin Šipraga 
247*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_FID_MASK, vlan4k->fid);
248*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPEN_MASK,
249*9da2c867SAlvin Šipraga 			      vlan4k->priority_en);
250*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_VBPRI_MASK,
251*9da2c867SAlvin Šipraga 			      vlan4k->priority);
252*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_ENVLANPOL_MASK,
253*9da2c867SAlvin Šipraga 			      vlan4k->policing_en);
254*9da2c867SAlvin Šipraga 
255*9da2c867SAlvin Šipraga 	/* FIELD_* does not play nice with struct bitfield. */
256*9da2c867SAlvin Šipraga 	val = vlan4k->meteridx;
257*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_LO_MASK, val);
258*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_METERIDX_MASK, val);
259*9da2c867SAlvin Šipraga 
260*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D1_IVL_SVL_MASK,
261*9da2c867SAlvin Šipraga 			      vlan4k->ivl_en);
262*9da2c867SAlvin Šipraga 
263*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_MBR_HI_MASK, vlan4k->member);
264*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_MBR_EXT_MASK, val);
265*9da2c867SAlvin Šipraga 
266*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_UNTAG_HI_MASK, vlan4k->untag);
267*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_UNTAG_EXT_MASK, val);
268*9da2c867SAlvin Šipraga 
269*9da2c867SAlvin Šipraga 	val = vlan4k->meteridx;
270*9da2c867SAlvin Šipraga 	val = FIELD_GET(RTL8365MB_CVLAN_METERIDX_HI_MASK, val);
271*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_CVLAN_ENTRY_D2_METERIDX_EXT_MASK, val);
272*9da2c867SAlvin Šipraga 
273*9da2c867SAlvin Šipraga 	vid = vlan4k->vid;
274*9da2c867SAlvin Šipraga 	return rtl8365mb_table_query(priv, RTL8365MB_TABLE_CVLAN,
275*9da2c867SAlvin Šipraga 				     RTL8365MB_TABLE_OP_WRITE, &vid, 0, 0,
276*9da2c867SAlvin Šipraga 				     data, ARRAY_SIZE(data));
277*9da2c867SAlvin Šipraga }
278*9da2c867SAlvin Šipraga 
279*9da2c867SAlvin Šipraga static int
280*9da2c867SAlvin Šipraga rtl8365mb_vlan_4k_port_set(struct dsa_switch *ds, int port,
281*9da2c867SAlvin Šipraga 			   const struct switchdev_obj_port_vlan *vlan,
282*9da2c867SAlvin Šipraga 			   struct netlink_ext_ack *extack,
283*9da2c867SAlvin Šipraga 			   bool include)
284*9da2c867SAlvin Šipraga {
285*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
286*9da2c867SAlvin Šipraga 	struct rtl8365mb_vlan4k vlan4k = {0};
287*9da2c867SAlvin Šipraga 	int ret;
288*9da2c867SAlvin Šipraga 
289*9da2c867SAlvin Šipraga 	dev_dbg(priv->dev, "%s VLAN %d 4K on port %d\n",
290*9da2c867SAlvin Šipraga 		include ? "add" : "del",
291*9da2c867SAlvin Šipraga 		vlan->vid, port);
292*9da2c867SAlvin Šipraga 
293*9da2c867SAlvin Šipraga 	if (vlan->vid > RTL8365MB_MAX_4K_VID) {
294*9da2c867SAlvin Šipraga 		NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than "
295*9da2c867SAlvin Šipraga 				   __stringify(RTL8365MB_MAX_4K_VID));
296*9da2c867SAlvin Šipraga 		return -EINVAL;
297*9da2c867SAlvin Šipraga 	}
298*9da2c867SAlvin Šipraga 
299*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_4k_read(priv, vlan->vid, &vlan4k);
300*9da2c867SAlvin Šipraga 	if (ret) {
301*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to read VLAN 4k table\n");
302*9da2c867SAlvin Šipraga 		return ret;
303*9da2c867SAlvin Šipraga 	}
304*9da2c867SAlvin Šipraga 
305*9da2c867SAlvin Šipraga 	if (include)
306*9da2c867SAlvin Šipraga 		vlan4k.member |= BIT(port);
307*9da2c867SAlvin Šipraga 	else
308*9da2c867SAlvin Šipraga 		vlan4k.member &= ~BIT(port);
309*9da2c867SAlvin Šipraga 
310*9da2c867SAlvin Šipraga 	if (include && (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED))
311*9da2c867SAlvin Šipraga 		vlan4k.untag |= BIT(port);
312*9da2c867SAlvin Šipraga 	else
313*9da2c867SAlvin Šipraga 		vlan4k.untag &= ~BIT(port);
314*9da2c867SAlvin Šipraga 	vlan4k.ivl_en = true; /* always use Independent VLAN Learning */
315*9da2c867SAlvin Šipraga 
316*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_4k_write(priv, &vlan4k);
317*9da2c867SAlvin Šipraga 	if (ret) {
318*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to write VLAN 4k table\n");
319*9da2c867SAlvin Šipraga 		return ret;
320*9da2c867SAlvin Šipraga 	}
321*9da2c867SAlvin Šipraga 
322*9da2c867SAlvin Šipraga 	return 0;
323*9da2c867SAlvin Šipraga }
324*9da2c867SAlvin Šipraga 
325*9da2c867SAlvin Šipraga /*
326*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_4k_port_add() - Add a port to a VLAN 4K table entry
327*9da2c867SAlvin Šipraga  * @ds: dsa switch instance
328*9da2c867SAlvin Šipraga  * @port: port index
329*9da2c867SAlvin Šipraga  * @vlan: switchdev VLAN object containing the target VID and flags
330*9da2c867SAlvin Šipraga  * @extack: netlink extended ACK for error reporting
331*9da2c867SAlvin Šipraga  *
332*9da2c867SAlvin Šipraga  * Adds the specified port to the hardware VLAN 4K membership table.
333*9da2c867SAlvin Šipraga  *
334*9da2c867SAlvin Šipraga  * Context: Can sleep. Must be called with &priv->vlan_lock held.
335*9da2c867SAlvin Šipraga  * Takes and releases &priv->map_lock.
336*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
337*9da2c867SAlvin Šipraga  */
338*9da2c867SAlvin Šipraga int rtl8365mb_vlan_4k_port_add(struct dsa_switch *ds, int port,
339*9da2c867SAlvin Šipraga 			       const struct switchdev_obj_port_vlan *vlan,
340*9da2c867SAlvin Šipraga 			       struct netlink_ext_ack *extack)
341*9da2c867SAlvin Šipraga {
342*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
343*9da2c867SAlvin Šipraga 
344*9da2c867SAlvin Šipraga 	lockdep_assert_held(&priv->vlan_lock);
345*9da2c867SAlvin Šipraga 
346*9da2c867SAlvin Šipraga 	return rtl8365mb_vlan_4k_port_set(ds, port, vlan, extack, true);
347*9da2c867SAlvin Šipraga }
348*9da2c867SAlvin Šipraga 
349*9da2c867SAlvin Šipraga /*
350*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_4k_port_del() - Remove a port from a VLAN 4K table entry
351*9da2c867SAlvin Šipraga  * @ds: dsa switch instance
352*9da2c867SAlvin Šipraga  * @port: port index
353*9da2c867SAlvin Šipraga  * @vlan: switchdev VLAN object containing the target VID
354*9da2c867SAlvin Šipraga  *
355*9da2c867SAlvin Šipraga  * Removes the specified port from the hardware VLAN 4K membership table.
356*9da2c867SAlvin Šipraga  *
357*9da2c867SAlvin Šipraga  * Context: Can sleep. Must be called with &priv->vlan_lock held.
358*9da2c867SAlvin Šipraga  * Takes and releases &priv->map_lock.
359*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
360*9da2c867SAlvin Šipraga  */
361*9da2c867SAlvin Šipraga int rtl8365mb_vlan_4k_port_del(struct dsa_switch *ds, int port,
362*9da2c867SAlvin Šipraga 			       const struct switchdev_obj_port_vlan *vlan)
363*9da2c867SAlvin Šipraga {
364*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
365*9da2c867SAlvin Šipraga 
366*9da2c867SAlvin Šipraga 	lockdep_assert_held(&priv->vlan_lock);
367*9da2c867SAlvin Šipraga 
368*9da2c867SAlvin Šipraga 	return rtl8365mb_vlan_4k_port_set(ds, port, vlan, NULL, false);
369*9da2c867SAlvin Šipraga }
370*9da2c867SAlvin Šipraga 
371*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_mc_read(struct realtek_priv *priv, u32 index,
372*9da2c867SAlvin Šipraga 				  struct rtl8365mb_vlanmc *vlanmc)
373*9da2c867SAlvin Šipraga {
374*9da2c867SAlvin Šipraga 	u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE];
375*9da2c867SAlvin Šipraga 	int ret;
376*9da2c867SAlvin Šipraga 
377*9da2c867SAlvin Šipraga 	ret = regmap_bulk_read(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
378*9da2c867SAlvin Šipraga 			       RTL8365MB_VLAN_MC_ENTRY_SIZE);
379*9da2c867SAlvin Šipraga 	if (ret)
380*9da2c867SAlvin Šipraga 		return ret;
381*9da2c867SAlvin Šipraga 
382*9da2c867SAlvin Šipraga 	vlanmc->member = FIELD_GET(RTL8365MB_VLAN_MC_D0_MBR_MASK, data[0]);
383*9da2c867SAlvin Šipraga 	vlanmc->fid = FIELD_GET(RTL8365MB_VLAN_MC_D1_FID_MASK, data[1]);
384*9da2c867SAlvin Šipraga 	vlanmc->meteridx = FIELD_GET(RTL8365MB_VLAN_MC_D2_METERIDX_MASK,
385*9da2c867SAlvin Šipraga 				     data[2]);
386*9da2c867SAlvin Šipraga 	vlanmc->policing_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK,
387*9da2c867SAlvin Šipraga 					data[2]);
388*9da2c867SAlvin Šipraga 	vlanmc->priority = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, data[2]);
389*9da2c867SAlvin Šipraga 	vlanmc->priority_en = FIELD_GET(RTL8365MB_VLAN_MC_D2_VBPEN_MASK,
390*9da2c867SAlvin Šipraga 					data[2]);
391*9da2c867SAlvin Šipraga 	vlanmc->evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, data[3]);
392*9da2c867SAlvin Šipraga 
393*9da2c867SAlvin Šipraga 	return 0;
394*9da2c867SAlvin Šipraga }
395*9da2c867SAlvin Šipraga 
396*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_mc_write(struct realtek_priv *priv, u32 index,
397*9da2c867SAlvin Šipraga 				   const struct rtl8365mb_vlanmc *vlanmc)
398*9da2c867SAlvin Šipraga {
399*9da2c867SAlvin Šipraga 	u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 };
400*9da2c867SAlvin Šipraga 	int ret;
401*9da2c867SAlvin Šipraga 
402*9da2c867SAlvin Šipraga 	data[0] |= FIELD_PREP(RTL8365MB_VLAN_MC_D0_MBR_MASK, vlanmc->member);
403*9da2c867SAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_VLAN_MC_D1_FID_MASK, vlanmc->fid);
404*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_METERIDX_MASK,
405*9da2c867SAlvin Šipraga 			      vlanmc->meteridx);
406*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_ENVLANPOL_MASK,
407*9da2c867SAlvin Šipraga 			      vlanmc->policing_en);
408*9da2c867SAlvin Šipraga 	data[2] |=
409*9da2c867SAlvin Šipraga 		FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPRI_MASK, vlanmc->priority);
410*9da2c867SAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_VLAN_MC_D2_VBPEN_MASK,
411*9da2c867SAlvin Šipraga 			      vlanmc->priority_en);
412*9da2c867SAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlanmc->evid);
413*9da2c867SAlvin Šipraga 
414*9da2c867SAlvin Šipraga 	ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
415*9da2c867SAlvin Šipraga 				RTL8365MB_VLAN_MC_ENTRY_SIZE);
416*9da2c867SAlvin Šipraga 
417*9da2c867SAlvin Šipraga 	return ret;
418*9da2c867SAlvin Šipraga }
419*9da2c867SAlvin Šipraga 
420*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_mc_erase(struct realtek_priv *priv, u32 index)
421*9da2c867SAlvin Šipraga {
422*9da2c867SAlvin Šipraga 	u16 data[RTL8365MB_VLAN_MC_ENTRY_SIZE] = { 0 };
423*9da2c867SAlvin Šipraga 	int ret;
424*9da2c867SAlvin Šipraga 
425*9da2c867SAlvin Šipraga 	ret = regmap_bulk_write(priv->map, RTL8365MB_VLAN_MC_REG(index), &data,
426*9da2c867SAlvin Šipraga 				RTL8365MB_VLAN_MC_ENTRY_SIZE);
427*9da2c867SAlvin Šipraga 
428*9da2c867SAlvin Šipraga 	return ret;
429*9da2c867SAlvin Šipraga }
430*9da2c867SAlvin Šipraga 
431*9da2c867SAlvin Šipraga /*
432*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_mc_find() - find VLANMC index by VID or the first free index
433*9da2c867SAlvin Šipraga  *
434*9da2c867SAlvin Šipraga  * @priv: realtek_priv pointer
435*9da2c867SAlvin Šipraga  * @vid: VLAN ID
436*9da2c867SAlvin Šipraga  * @index: found index
437*9da2c867SAlvin Šipraga  * @first_free: found free index
438*9da2c867SAlvin Šipraga  *
439*9da2c867SAlvin Šipraga  * If a VLAN MC entry using @vid was found, @index will return the matched index
440*9da2c867SAlvin Šipraga  * and @first_free is undefined. If not found, @index will return 0 and
441*9da2c867SAlvin Šipraga  * @first_free will return the first found free index in VLAN MC or 0 if the
442*9da2c867SAlvin Šipraga  * table is full.
443*9da2c867SAlvin Šipraga  *
444*9da2c867SAlvin Šipraga  * Although 0 is a valid VLAN MC index, it is reserved for ports without PVID,
445*9da2c867SAlvin Šipraga  * including standalone, non-member ports. It uses VID == 0.
446*9da2c867SAlvin Šipraga  *
447*9da2c867SAlvin Šipraga  * Both @index and @first_free will be in the * 1..@RTL8365MB_VLAN_MCMAX range.
448*9da2c867SAlvin Šipraga  *
449*9da2c867SAlvin Šipraga  * Return: Returns 0 on success, a negative error on failure.
450*9da2c867SAlvin Šipraga  */
451*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_mc_find(struct realtek_priv *priv, u16 vid,
452*9da2c867SAlvin Šipraga 				  u8 *index, u8 *first_free)
453*9da2c867SAlvin Šipraga {
454*9da2c867SAlvin Šipraga 	u32 vlan_entry_d3;
455*9da2c867SAlvin Šipraga 	u8 vlanmc_idx;
456*9da2c867SAlvin Šipraga 	u16 evid;
457*9da2c867SAlvin Šipraga 	int ret;
458*9da2c867SAlvin Šipraga 
459*9da2c867SAlvin Šipraga 	*index = 0;
460*9da2c867SAlvin Šipraga 	*first_free = 0;
461*9da2c867SAlvin Šipraga 
462*9da2c867SAlvin Šipraga 	/* look for existing entry or an empty one */
463*9da2c867SAlvin Šipraga 	/* By design, VlanMC[0] is reserved as a neutral PVID value for
464*9da2c867SAlvin Šipraga 	 * standalone ports. It always has EVID == 0. That way, we assume that
465*9da2c867SAlvin Šipraga 	 * all entries after index 0 with VID == 0 are empty.
466*9da2c867SAlvin Šipraga 	 **/
467*9da2c867SAlvin Šipraga 	for (vlanmc_idx = 1; vlanmc_idx <= RTL8365MB_VLAN_MCMAX; vlanmc_idx++) {
468*9da2c867SAlvin Šipraga 		/* just read the 4th word, where the evid is */
469*9da2c867SAlvin Šipraga 		ret = regmap_read(priv->map,
470*9da2c867SAlvin Šipraga 				  RTL8365MB_VLAN_MC_REG(vlanmc_idx) + 3,
471*9da2c867SAlvin Šipraga 				  &vlan_entry_d3);
472*9da2c867SAlvin Šipraga 		if (ret)
473*9da2c867SAlvin Šipraga 			return ret;
474*9da2c867SAlvin Šipraga 
475*9da2c867SAlvin Šipraga 		evid = FIELD_GET(RTL8365MB_VLAN_MC_D3_EVID_MASK, vlan_entry_d3);
476*9da2c867SAlvin Šipraga 
477*9da2c867SAlvin Šipraga 		if (evid == vid) {
478*9da2c867SAlvin Šipraga 			*index = vlanmc_idx;
479*9da2c867SAlvin Šipraga 			return 0;
480*9da2c867SAlvin Šipraga 		}
481*9da2c867SAlvin Šipraga 
482*9da2c867SAlvin Šipraga 		if (evid == 0x0 && *first_free < 1)
483*9da2c867SAlvin Šipraga 			*first_free = vlanmc_idx;
484*9da2c867SAlvin Šipraga 	}
485*9da2c867SAlvin Šipraga 	return 0;
486*9da2c867SAlvin Šipraga }
487*9da2c867SAlvin Šipraga 
488*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_port_get_pvid_idx(struct realtek_priv *priv,
489*9da2c867SAlvin Šipraga 					    int port, u8 *vlanmc_idx)
490*9da2c867SAlvin Šipraga {
491*9da2c867SAlvin Šipraga 	u32 data;
492*9da2c867SAlvin Šipraga 	int ret;
493*9da2c867SAlvin Šipraga 
494*9da2c867SAlvin Šipraga 	ret = regmap_read(priv->map, RTL8365MB_VLAN_PVID_CTRL_REG(port), &data);
495*9da2c867SAlvin Šipraga 	if (ret)
496*9da2c867SAlvin Šipraga 		return ret;
497*9da2c867SAlvin Šipraga 
498*9da2c867SAlvin Šipraga 	*vlanmc_idx = (data & RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port))
499*9da2c867SAlvin Šipraga 		      >> RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port);
500*9da2c867SAlvin Šipraga 
501*9da2c867SAlvin Šipraga 	return 0;
502*9da2c867SAlvin Šipraga }
503*9da2c867SAlvin Šipraga 
504*9da2c867SAlvin Šipraga /*
505*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_mc_port_set() - include or exclude a port from VlanMC
506*9da2c867SAlvin Šipraga  * @ds: dsa switch
507*9da2c867SAlvin Šipraga  * @port: the port number
508*9da2c867SAlvin Šipraga  * @vid: the vlan VID to include/exclude @port
509*9da2c867SAlvin Šipraga  * @pvid: inform if vid is used as pvid in @port
510*9da2c867SAlvin Šipraga  * @extack: optional extack to return errors
511*9da2c867SAlvin Šipraga  * @include: whether to include or exclude @port
512*9da2c867SAlvin Šipraga  *
513*9da2c867SAlvin Šipraga  * This function is used to include/exclude ports to the VlanMC table.
514*9da2c867SAlvin Šipraga  *
515*9da2c867SAlvin Šipraga  * VlanMC stands for VLAN membership config and it is used exclusively for
516*9da2c867SAlvin Šipraga  * PVID. If @vlan members are not using PVID, this function will either
517*9da2c867SAlvin Šipraga  * remove or not create a new VlanMC entry.
518*9da2c867SAlvin Šipraga  *
519*9da2c867SAlvin Šipraga  * VlanMC members are used as a reference port map, cleaning the entry once
520*9da2c867SAlvin Šipraga  * no port is using it.
521*9da2c867SAlvin Šipraga  *
522*9da2c867SAlvin Šipraga  * Port PVID and accepted frame type are updated as well.
523*9da2c867SAlvin Šipraga  *
524*9da2c867SAlvin Šipraga  * Context: Can sleep. Must be called with &priv->vlan_lock held.
525*9da2c867SAlvin Šipraga  * Takes and releases &priv->map_lock.
526*9da2c867SAlvin Šipraga  * Return: Returns 0 on success, a negative error on failure.
527*9da2c867SAlvin Šipraga  */
528*9da2c867SAlvin Šipraga static
529*9da2c867SAlvin Šipraga int rtl8365mb_vlan_mc_port_set(struct dsa_switch *ds, int port,
530*9da2c867SAlvin Šipraga 			       u16 vid, bool pvid,
531*9da2c867SAlvin Šipraga 			       struct netlink_ext_ack *extack,
532*9da2c867SAlvin Šipraga 			       bool include)
533*9da2c867SAlvin Šipraga {
534*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
535*9da2c867SAlvin Šipraga 	struct rtl8365mb_vlanmc vlanmc = {0};
536*9da2c867SAlvin Šipraga 	u8 first_unused = 0;
537*9da2c867SAlvin Šipraga 	u8 vlanmc_idx = 0;
538*9da2c867SAlvin Šipraga 	int ret;
539*9da2c867SAlvin Šipraga 
540*9da2c867SAlvin Šipraga 	dev_dbg(priv->dev, "%s VLAN %d MC on port %d\n",
541*9da2c867SAlvin Šipraga 		include ? "add" : "del",
542*9da2c867SAlvin Šipraga 		vid, port);
543*9da2c867SAlvin Šipraga 
544*9da2c867SAlvin Šipraga 	if (vid > RTL8365MB_MAX_MC_VID) {
545*9da2c867SAlvin Šipraga 		NL_SET_ERR_MSG_MOD(extack, "VLAN ID greater than "
546*9da2c867SAlvin Šipraga 				   __stringify(RTL8365MB_MAX_MC_VID));
547*9da2c867SAlvin Šipraga 		return -EINVAL;
548*9da2c867SAlvin Šipraga 	}
549*9da2c867SAlvin Šipraga 
550*9da2c867SAlvin Šipraga 	/* look for existing entry or an empty slot */
551*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx,
552*9da2c867SAlvin Šipraga 				     &first_unused);
553*9da2c867SAlvin Šipraga 	if (ret) {
554*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to find a VLAN MC table index\n");
555*9da2c867SAlvin Šipraga 		return ret;
556*9da2c867SAlvin Šipraga 	}
557*9da2c867SAlvin Šipraga 
558*9da2c867SAlvin Šipraga 	if (vlanmc_idx) {
559*9da2c867SAlvin Šipraga 		ret = rtl8365mb_vlan_mc_read(priv, vlanmc_idx, &vlanmc);
560*9da2c867SAlvin Šipraga 		if (ret) {
561*9da2c867SAlvin Šipraga 			dev_err(priv->dev, "Failed to read VLAN MC table\n");
562*9da2c867SAlvin Šipraga 			return ret;
563*9da2c867SAlvin Šipraga 		}
564*9da2c867SAlvin Šipraga 	} else if (include) {
565*9da2c867SAlvin Šipraga 		/* for now, vlan_mc is only required for PVID. Defer allocation
566*9da2c867SAlvin Šipraga 		 * until at least one port uses PVID.
567*9da2c867SAlvin Šipraga 		 */
568*9da2c867SAlvin Šipraga 		if (!pvid) {
569*9da2c867SAlvin Šipraga 			dev_dbg(priv->dev,
570*9da2c867SAlvin Šipraga 				"Not creating VlanMC for vlan %d until a port uses PVID (%d does not)\n",
571*9da2c867SAlvin Šipraga 				vid, port);
572*9da2c867SAlvin Šipraga 			return 0;
573*9da2c867SAlvin Šipraga 		}
574*9da2c867SAlvin Šipraga 
575*9da2c867SAlvin Šipraga 		if (!first_unused) {
576*9da2c867SAlvin Šipraga 			NL_SET_ERR_MSG_MOD(extack, "All VLAN MC entries (0.."
577*9da2c867SAlvin Šipraga 					   __stringify(RTL8365MB_VLAN_MCMAX)
578*9da2c867SAlvin Šipraga 					   ") are in use.");
579*9da2c867SAlvin Šipraga 			return -ENOSPC;
580*9da2c867SAlvin Šipraga 		}
581*9da2c867SAlvin Šipraga 
582*9da2c867SAlvin Šipraga 		vlanmc_idx = first_unused;
583*9da2c867SAlvin Šipraga 		vlanmc.evid = vid;
584*9da2c867SAlvin Šipraga 
585*9da2c867SAlvin Šipraga 	} else /* excluding and VLANMC not found */ {
586*9da2c867SAlvin Šipraga 		return 0;
587*9da2c867SAlvin Šipraga 	}
588*9da2c867SAlvin Šipraga 
589*9da2c867SAlvin Šipraga 	dev_dbg(priv->dev,
590*9da2c867SAlvin Šipraga 		"VLAN %d (idx: %d) PVID curr members: %08x\n",
591*9da2c867SAlvin Šipraga 		vid, vlanmc_idx, vlanmc.member);
592*9da2c867SAlvin Šipraga 
593*9da2c867SAlvin Šipraga 	/* here we either have an existing VLANMC (with PVID members) or the
594*9da2c867SAlvin Šipraga 	 * added port is using this VLAN as PVID
595*9da2c867SAlvin Šipraga 	 */
596*9da2c867SAlvin Šipraga 	if (include)
597*9da2c867SAlvin Šipraga 		vlanmc.member |= BIT(port);
598*9da2c867SAlvin Šipraga 	else
599*9da2c867SAlvin Šipraga 		vlanmc.member &= ~BIT(port);
600*9da2c867SAlvin Šipraga 
601*9da2c867SAlvin Šipraga 	/* just like we don't need to create a VLAN_MC when there is no port
602*9da2c867SAlvin Šipraga 	 * using it as PVID, we can erase it when there is no more port using
603*9da2c867SAlvin Šipraga 	 * it as PVID.
604*9da2c867SAlvin Šipraga 	 */
605*9da2c867SAlvin Šipraga 	if (!vlanmc.member) {
606*9da2c867SAlvin Šipraga 		dev_dbg(priv->dev,
607*9da2c867SAlvin Šipraga 			"Clearing VlanMC index %d previously used by VID %d\n",
608*9da2c867SAlvin Šipraga 			vlanmc_idx, vid);
609*9da2c867SAlvin Šipraga 		ret = rtl8365mb_vlan_mc_erase(priv, vlanmc_idx);
610*9da2c867SAlvin Šipraga 	} else {
611*9da2c867SAlvin Šipraga 		dev_dbg(priv->dev,
612*9da2c867SAlvin Šipraga 			"Saving VlanMC index %d with VID %d\n",
613*9da2c867SAlvin Šipraga 			vlanmc_idx, vid);
614*9da2c867SAlvin Šipraga 		ret = rtl8365mb_vlan_mc_write(priv, vlanmc_idx, &vlanmc);
615*9da2c867SAlvin Šipraga 	}
616*9da2c867SAlvin Šipraga 	if (ret) {
617*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to write vlan MC entry\n");
618*9da2c867SAlvin Šipraga 		return ret;
619*9da2c867SAlvin Šipraga 	}
620*9da2c867SAlvin Šipraga 
621*9da2c867SAlvin Šipraga 	return 0;
622*9da2c867SAlvin Šipraga }
623*9da2c867SAlvin Šipraga 
624*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_port_set_pvid(struct realtek_priv *priv,
625*9da2c867SAlvin Šipraga 					int port, u16 vlanmc_idx)
626*9da2c867SAlvin Šipraga {
627*9da2c867SAlvin Šipraga 	int ret;
628*9da2c867SAlvin Šipraga 	u32 val;
629*9da2c867SAlvin Šipraga 
630*9da2c867SAlvin Šipraga 	dev_dbg(priv->dev, "set PVID IDX %d on port %d\n", vlanmc_idx, port);
631*9da2c867SAlvin Šipraga 
632*9da2c867SAlvin Šipraga 	val = vlanmc_idx << RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(port);
633*9da2c867SAlvin Šipraga 	ret = regmap_update_bits(priv->map,
634*9da2c867SAlvin Šipraga 				 RTL8365MB_VLAN_PVID_CTRL_REG(port),
635*9da2c867SAlvin Šipraga 				 RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(port),
636*9da2c867SAlvin Šipraga 				 val);
637*9da2c867SAlvin Šipraga 	if (ret)
638*9da2c867SAlvin Šipraga 		return ret;
639*9da2c867SAlvin Šipraga 
640*9da2c867SAlvin Šipraga 	return 0;
641*9da2c867SAlvin Šipraga }
642*9da2c867SAlvin Šipraga 
643*9da2c867SAlvin Šipraga static int rtl8365mb_vlan_get_pvid_mc(struct realtek_priv *priv,
644*9da2c867SAlvin Šipraga 				      int port, u8 *vlanmc_idx,
645*9da2c867SAlvin Šipraga 				      struct rtl8365mb_vlanmc *vlanmc)
646*9da2c867SAlvin Šipraga {
647*9da2c867SAlvin Šipraga 	int ret;
648*9da2c867SAlvin Šipraga 
649*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_get_pvid_idx(priv, port, vlanmc_idx);
650*9da2c867SAlvin Šipraga 	if (ret)
651*9da2c867SAlvin Šipraga 		return ret;
652*9da2c867SAlvin Šipraga 
653*9da2c867SAlvin Šipraga 	memset(vlanmc, 0, sizeof(*vlanmc));
654*9da2c867SAlvin Šipraga 
655*9da2c867SAlvin Šipraga 	if (!*vlanmc_idx)
656*9da2c867SAlvin Šipraga 		return 0;
657*9da2c867SAlvin Šipraga 
658*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_mc_read(priv, *vlanmc_idx, vlanmc);
659*9da2c867SAlvin Šipraga 	if (ret)
660*9da2c867SAlvin Šipraga 		return ret;
661*9da2c867SAlvin Šipraga 
662*9da2c867SAlvin Šipraga 	return 0;
663*9da2c867SAlvin Šipraga }
664*9da2c867SAlvin Šipraga 
665*9da2c867SAlvin Šipraga /*
666*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_port_get_pvid - Retrieve the port PVID
667*9da2c867SAlvin Šipraga  * @priv: realtek switch private structure
668*9da2c867SAlvin Šipraga  * @port: port index
669*9da2c867SAlvin Šipraga  * @pvid: pointer to store the retrieved VLAN ID
670*9da2c867SAlvin Šipraga  *
671*9da2c867SAlvin Šipraga  * Returns the port PVID if defined or 0 if not.
672*9da2c867SAlvin Šipraga  *
673*9da2c867SAlvin Šipraga  * Context: Can sleep. Takes and releases &priv->map_lock.
674*9da2c867SAlvin Šipraga  * Return: 0 on success or a negative error code on failure.
675*9da2c867SAlvin Šipraga  */
676*9da2c867SAlvin Šipraga int rtl8365mb_vlan_port_get_pvid(struct realtek_priv *priv, int port, u16 *pvid)
677*9da2c867SAlvin Šipraga {
678*9da2c867SAlvin Šipraga 	struct rtl8365mb_vlanmc vlanmc;
679*9da2c867SAlvin Šipraga 	u8 vlanmc_idx;
680*9da2c867SAlvin Šipraga 	int ret;
681*9da2c867SAlvin Šipraga 
682*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx, &vlanmc);
683*9da2c867SAlvin Šipraga 	if (ret)
684*9da2c867SAlvin Šipraga 		return ret;
685*9da2c867SAlvin Šipraga 
686*9da2c867SAlvin Šipraga 	*pvid = vlanmc.evid;
687*9da2c867SAlvin Šipraga 	return 0;
688*9da2c867SAlvin Šipraga }
689*9da2c867SAlvin Šipraga 
690*9da2c867SAlvin Šipraga /*
691*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_port_get_framefilter() - Get the ingress frame filtering mode
692*9da2c867SAlvin Šipraga  * for a port
693*9da2c867SAlvin Šipraga  * @priv: realtek switch private structure
694*9da2c867SAlvin Šipraga  * @port: port index
695*9da2c867SAlvin Šipraga  * @frame_type: pointer to store the retrieved ingress frame filter type
696*9da2c867SAlvin Šipraga  *
697*9da2c867SAlvin Šipraga  * Context: Can sleep. Takes and releases &priv->map_lock.
698*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
699*9da2c867SAlvin Šipraga  */
700*9da2c867SAlvin Šipraga int
701*9da2c867SAlvin Šipraga rtl8365mb_vlan_port_get_framefilter(struct realtek_priv *priv,
702*9da2c867SAlvin Šipraga 				    int port,
703*9da2c867SAlvin Šipraga 				    enum rtl8365mb_frame_ingress *frame_type)
704*9da2c867SAlvin Šipraga {
705*9da2c867SAlvin Šipraga 	u32 val;
706*9da2c867SAlvin Šipraga 	int ret;
707*9da2c867SAlvin Šipraga 
708*9da2c867SAlvin Šipraga 	/* Even if ACCEPT_FRAME_TYPE_ANY, the switch will still check if the
709*9da2c867SAlvin Šipraga 	 * port is a member of vlan PVID
710*9da2c867SAlvin Šipraga 	 */
711*9da2c867SAlvin Šipraga 
712*9da2c867SAlvin Šipraga 	ret = regmap_read(priv->map, RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port),
713*9da2c867SAlvin Šipraga 			  &val);
714*9da2c867SAlvin Šipraga 	if (ret)
715*9da2c867SAlvin Šipraga 		return ret;
716*9da2c867SAlvin Šipraga 
717*9da2c867SAlvin Šipraga 	*frame_type = field_get(RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port),
718*9da2c867SAlvin Šipraga 				val);
719*9da2c867SAlvin Šipraga 
720*9da2c867SAlvin Šipraga 	return 0;
721*9da2c867SAlvin Šipraga }
722*9da2c867SAlvin Šipraga 
723*9da2c867SAlvin Šipraga /*
724*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_port_set_framefilter() - Set the ingress frame filtering mode
725*9da2c867SAlvin Šipraga  * for a port
726*9da2c867SAlvin Šipraga  * @priv: realtek switch private structure
727*9da2c867SAlvin Šipraga  * @port: port index
728*9da2c867SAlvin Šipraga  * @frame_type: the ingress frame filter type to configure
729*9da2c867SAlvin Šipraga  *
730*9da2c867SAlvin Šipraga  * Context: Can sleep. Takes and releases &priv->map_lock.
731*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
732*9da2c867SAlvin Šipraga  */
733*9da2c867SAlvin Šipraga int
734*9da2c867SAlvin Šipraga rtl8365mb_vlan_port_set_framefilter(struct realtek_priv *priv,
735*9da2c867SAlvin Šipraga 				    int port,
736*9da2c867SAlvin Šipraga 				    enum rtl8365mb_frame_ingress frame_type)
737*9da2c867SAlvin Šipraga {
738*9da2c867SAlvin Šipraga 	u32 val;
739*9da2c867SAlvin Šipraga 
740*9da2c867SAlvin Šipraga 	/* Even if ACCEPT_FRAME_TYPE_ANY, the switch will still check if the
741*9da2c867SAlvin Šipraga 	 * port is a member of vlan PVID
742*9da2c867SAlvin Šipraga 	 */
743*9da2c867SAlvin Šipraga 	val = frame_type << RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_OFFSET(port);
744*9da2c867SAlvin Šipraga 
745*9da2c867SAlvin Šipraga 	return regmap_update_bits(priv->map,
746*9da2c867SAlvin Šipraga 				  RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_REG(port),
747*9da2c867SAlvin Šipraga 				  RTL8365MB_VLAN_ACCEPT_FRAME_TYPE_MASK(port),
748*9da2c867SAlvin Šipraga 				  val);
749*9da2c867SAlvin Šipraga }
750*9da2c867SAlvin Šipraga 
751*9da2c867SAlvin Šipraga /*
752*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_pvid_port_set() - Configure a port's PVID and associated
753*9da2c867SAlvin Šipraga  * VLANMC entry
754*9da2c867SAlvin Šipraga  * @ds: dsa switch instance
755*9da2c867SAlvin Šipraga  * @port: port index
756*9da2c867SAlvin Šipraga  * @vid: target VID
757*9da2c867SAlvin Šipraga  * @extack: netlink extended ACK for error reporting
758*9da2c867SAlvin Šipraga  *
759*9da2c867SAlvin Šipraga  * Allocates or reuses a hardware VLANMC entry to map the given port to its new
760*9da2c867SAlvin Šipraga  * PVID. Gracefully unwinds and restores previous configuration if a hardware
761*9da2c867SAlvin Šipraga  * write operation fails during execution.
762*9da2c867SAlvin Šipraga  *
763*9da2c867SAlvin Šipraga  * Context: Can sleep. Must be called with &priv->vlan_lock held.
764*9da2c867SAlvin Šipraga  * Takes and releases &priv->map_lock.
765*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
766*9da2c867SAlvin Šipraga  */
767*9da2c867SAlvin Šipraga int rtl8365mb_vlan_pvid_port_set(struct dsa_switch *ds, int port, u16 vid,
768*9da2c867SAlvin Šipraga 				 struct netlink_ext_ack *extack)
769*9da2c867SAlvin Šipraga {
770*9da2c867SAlvin Šipraga 	enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame;
771*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
772*9da2c867SAlvin Šipraga 	struct rtl8365mb_vlanmc prev_vlanmc = {0};
773*9da2c867SAlvin Šipraga 	u8 _unused_first_free_idx;
774*9da2c867SAlvin Šipraga 	u8 prev_vlanmc_idx;
775*9da2c867SAlvin Šipraga 	u8 vlanmc_idx;
776*9da2c867SAlvin Šipraga 	int ret;
777*9da2c867SAlvin Šipraga 
778*9da2c867SAlvin Šipraga 	lockdep_assert_held(&priv->vlan_lock);
779*9da2c867SAlvin Šipraga 
780*9da2c867SAlvin Šipraga 	/* Read the old PVID exclusively to undo in case of error */
781*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &prev_vlanmc_idx,
782*9da2c867SAlvin Šipraga 					 &prev_vlanmc);
783*9da2c867SAlvin Šipraga 	if (ret) {
784*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to read current VLAN MC\n");
785*9da2c867SAlvin Šipraga 		return ret;
786*9da2c867SAlvin Šipraga 	}
787*9da2c867SAlvin Šipraga 
788*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_get_framefilter(priv, port,
789*9da2c867SAlvin Šipraga 						  &prev_accepted_frame);
790*9da2c867SAlvin Šipraga 	if (ret) {
791*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to get current framefilter\n");
792*9da2c867SAlvin Šipraga 		return ret;
793*9da2c867SAlvin Šipraga 	}
794*9da2c867SAlvin Šipraga 
795*9da2c867SAlvin Šipraga 	/* Find or allocate a new vlan MC and add port to members,
796*9da2c867SAlvin Šipraga 	 * although members are not checked by the HW in vlan MC.
797*9da2c867SAlvin Šipraga 	 */
798*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_mc_port_set(ds, port, vid, true, extack, true);
799*9da2c867SAlvin Šipraga 	if (ret)
800*9da2c867SAlvin Šipraga 		return ret;
801*9da2c867SAlvin Šipraga 
802*9da2c867SAlvin Šipraga 	/* look for existing entry */
803*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_mc_find(priv, vid, &vlanmc_idx,
804*9da2c867SAlvin Šipraga 				     &_unused_first_free_idx);
805*9da2c867SAlvin Šipraga 	if (ret) {
806*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to find a VLAN MC table index\n");
807*9da2c867SAlvin Šipraga 		goto undo_vlan_mc_port_set;
808*9da2c867SAlvin Šipraga 	}
809*9da2c867SAlvin Šipraga 
810*9da2c867SAlvin Šipraga 	if (!vlanmc_idx) {
811*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "VLAN should already exist in VLAN MC\n");
812*9da2c867SAlvin Šipraga 		ret = -ENOENT;
813*9da2c867SAlvin Šipraga 		goto undo_vlan_mc_port_set;
814*9da2c867SAlvin Šipraga 	}
815*9da2c867SAlvin Šipraga 
816*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx);
817*9da2c867SAlvin Šipraga 	if (ret) {
818*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to set port PVID\n");
819*9da2c867SAlvin Šipraga 		goto undo_vlan_mc_port_set;
820*9da2c867SAlvin Šipraga 	}
821*9da2c867SAlvin Šipraga 
822*9da2c867SAlvin Šipraga 	/* Changing accept frame is what enables PVID (if not enabled before) */
823*9da2c867SAlvin Šipraga 	accepted_frame = RTL8365MB_FRAME_TYPE_ANY_FRAME;
824*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_set_framefilter(priv, port, accepted_frame);
825*9da2c867SAlvin Šipraga 	if (ret) {
826*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to set port frame filter\n");
827*9da2c867SAlvin Šipraga 		goto undo_vlan_port_set_pvid;
828*9da2c867SAlvin Šipraga 	}
829*9da2c867SAlvin Šipraga 
830*9da2c867SAlvin Šipraga 	/* A VLAN can be added with PVID without removing from the old
831*9da2c867SAlvin Šipraga 	 * PVID VLAN. Clear PVID from the old VLAN MC (if needed).
832*9da2c867SAlvin Šipraga 	 */
833*9da2c867SAlvin Šipraga 	if (prev_vlanmc_idx && (prev_vlanmc.evid != vid)) {
834*9da2c867SAlvin Šipraga 		ret = rtl8365mb_vlan_mc_port_set(ds, port, prev_vlanmc.evid,
835*9da2c867SAlvin Šipraga 						 false, NULL, false);
836*9da2c867SAlvin Šipraga 		if (ret) {
837*9da2c867SAlvin Šipraga 			dev_err(priv->dev, "Failed to clear old VLAN MC\n");
838*9da2c867SAlvin Šipraga 			goto undo_set_framefilter;
839*9da2c867SAlvin Šipraga 		}
840*9da2c867SAlvin Šipraga 	}
841*9da2c867SAlvin Šipraga 
842*9da2c867SAlvin Šipraga 	return 0;
843*9da2c867SAlvin Šipraga 
844*9da2c867SAlvin Šipraga undo_set_framefilter:
845*9da2c867SAlvin Šipraga 	(void)rtl8365mb_vlan_port_set_framefilter(priv, port,
846*9da2c867SAlvin Šipraga 						  prev_accepted_frame);
847*9da2c867SAlvin Šipraga 
848*9da2c867SAlvin Šipraga undo_vlan_port_set_pvid:
849*9da2c867SAlvin Šipraga 	(void)rtl8365mb_vlan_port_set_pvid(priv, port, prev_vlanmc_idx);
850*9da2c867SAlvin Šipraga 
851*9da2c867SAlvin Šipraga undo_vlan_mc_port_set:
852*9da2c867SAlvin Šipraga 	if (prev_vlanmc.evid != vid)
853*9da2c867SAlvin Šipraga 		(void)rtl8365mb_vlan_mc_port_set(ds, port, vid, false, NULL,
854*9da2c867SAlvin Šipraga 						 false);
855*9da2c867SAlvin Šipraga 
856*9da2c867SAlvin Šipraga 	return ret;
857*9da2c867SAlvin Šipraga }
858*9da2c867SAlvin Šipraga 
859*9da2c867SAlvin Šipraga /*
860*9da2c867SAlvin Šipraga  * rtl8365mb_vlan_pvid_port_clear() - Remove a port's PVID configuration
861*9da2c867SAlvin Šipraga  * @ds: dsa switch instance
862*9da2c867SAlvin Šipraga  * @port: port index
863*9da2c867SAlvin Šipraga  * @vid:  VLAN VID for PVID
864*9da2c867SAlvin Šipraga  *
865*9da2c867SAlvin Šipraga  * Resets the target port's hardware PVID allocation to 0. Cleans up and frees
866*9da2c867SAlvin Šipraga  * the associated VLANMC entry if no other ports are referencing it.
867*9da2c867SAlvin Šipraga  *
868*9da2c867SAlvin Šipraga  * Context: Can sleep. Must be called with &priv->vlan_lock held.
869*9da2c867SAlvin Šipraga  * Takes and releases &priv->map_lock.
870*9da2c867SAlvin Šipraga  * Return: 0 on success, or a negative error code on failure.
871*9da2c867SAlvin Šipraga  */
872*9da2c867SAlvin Šipraga int rtl8365mb_vlan_pvid_port_clear(struct dsa_switch *ds, int port, u16 vid)
873*9da2c867SAlvin Šipraga {
874*9da2c867SAlvin Šipraga 	enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame;
875*9da2c867SAlvin Šipraga 	struct realtek_priv *priv = ds->priv;
876*9da2c867SAlvin Šipraga 	struct rtl8365mb_vlanmc vlanmc = {0};
877*9da2c867SAlvin Šipraga 	u8 vlanmc_idx;
878*9da2c867SAlvin Šipraga 	int ret;
879*9da2c867SAlvin Šipraga 
880*9da2c867SAlvin Šipraga 	lockdep_assert_held(&priv->vlan_lock);
881*9da2c867SAlvin Šipraga 
882*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_get_pvid_mc(priv, port, &vlanmc_idx,
883*9da2c867SAlvin Šipraga 					 &vlanmc);
884*9da2c867SAlvin Šipraga 	if (ret) {
885*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to read current VLAN MC\n");
886*9da2c867SAlvin Šipraga 		return ret;
887*9da2c867SAlvin Šipraga 	}
888*9da2c867SAlvin Šipraga 
889*9da2c867SAlvin Šipraga 	/* Port is not using PVID. Nothing to remove. */
890*9da2c867SAlvin Šipraga 	if (!vlanmc_idx)
891*9da2c867SAlvin Šipraga 		return 0;
892*9da2c867SAlvin Šipraga 
893*9da2c867SAlvin Šipraga 	/* We are leaving a non PVID vlan, Nothing to remove. */
894*9da2c867SAlvin Šipraga 	if (vlanmc.evid != vid)
895*9da2c867SAlvin Šipraga 		return 0;
896*9da2c867SAlvin Šipraga 
897*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_get_framefilter(priv, port,
898*9da2c867SAlvin Šipraga 						  &prev_accepted_frame);
899*9da2c867SAlvin Šipraga 	if (ret) {
900*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to get current framefilter\n");
901*9da2c867SAlvin Šipraga 		return ret;
902*9da2c867SAlvin Šipraga 	}
903*9da2c867SAlvin Šipraga 
904*9da2c867SAlvin Šipraga 	/* Changing accept frame is what really removes PVID. But only do
905*9da2c867SAlvin Šipraga 	 * that if we are filtering vlan
906*9da2c867SAlvin Šipraga 	 */
907*9da2c867SAlvin Šipraga 	if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) {
908*9da2c867SAlvin Šipraga 		accepted_frame = RTL8365MB_FRAME_TYPE_TAGGED_ONLY;
909*9da2c867SAlvin Šipraga 
910*9da2c867SAlvin Šipraga 		ret = rtl8365mb_vlan_port_set_framefilter(priv, port,
911*9da2c867SAlvin Šipraga 							  accepted_frame);
912*9da2c867SAlvin Šipraga 		if (ret) {
913*9da2c867SAlvin Šipraga 			dev_err(priv->dev, "Failed to set port frame filter\n");
914*9da2c867SAlvin Šipraga 			return ret;
915*9da2c867SAlvin Šipraga 		}
916*9da2c867SAlvin Šipraga 	} else {
917*9da2c867SAlvin Šipraga 		/* skip undo_set_framefilter */
918*9da2c867SAlvin Šipraga 		accepted_frame = prev_accepted_frame;
919*9da2c867SAlvin Šipraga 	}
920*9da2c867SAlvin Šipraga 
921*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_port_set_pvid(priv, port, 0);
922*9da2c867SAlvin Šipraga 	if (ret) {
923*9da2c867SAlvin Šipraga 		dev_err(priv->dev, "Failed to set port PVID to 0\n");
924*9da2c867SAlvin Šipraga 		goto undo_set_framefilter;
925*9da2c867SAlvin Šipraga 	}
926*9da2c867SAlvin Šipraga 
927*9da2c867SAlvin Šipraga 	/* Clears the VLAN MC membership and maybe VLAN MC entry if empty */
928*9da2c867SAlvin Šipraga 	ret = rtl8365mb_vlan_mc_port_set(ds, port, vlanmc.evid,
929*9da2c867SAlvin Šipraga 					 false, NULL, false);
930*9da2c867SAlvin Šipraga 	if (ret)
931*9da2c867SAlvin Šipraga 		goto undo_port_set_pvid;
932*9da2c867SAlvin Šipraga 
933*9da2c867SAlvin Šipraga 	return 0;
934*9da2c867SAlvin Šipraga 
935*9da2c867SAlvin Šipraga undo_port_set_pvid:
936*9da2c867SAlvin Šipraga 	(void)rtl8365mb_vlan_port_set_pvid(priv, port, vlanmc_idx);
937*9da2c867SAlvin Šipraga 
938*9da2c867SAlvin Šipraga undo_set_framefilter:
939*9da2c867SAlvin Šipraga 	if (prev_accepted_frame != accepted_frame)
940*9da2c867SAlvin Šipraga 		(void)rtl8365mb_vlan_port_set_framefilter(priv, port,
941*9da2c867SAlvin Šipraga 							  prev_accepted_frame);
942*9da2c867SAlvin Šipraga 
943*9da2c867SAlvin Šipraga 	return ret;
944*9da2c867SAlvin Šipraga }
945