xref: /linux/drivers/net/dsa/realtek/rtl8365mb_l2.c (revision 336e3e4a1ab37b6826fae27e53cd2ac43c9a96ca)
1*336e3e4aSAlvin Šipraga // SPDX-License-Identifier: GPL-2.0
2*336e3e4aSAlvin Šipraga /* Forwarding and multicast database interface for the rtl8365mb switch family
3*336e3e4aSAlvin Šipraga  *
4*336e3e4aSAlvin Šipraga  * Copyright (C) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
5*336e3e4aSAlvin Šipraga  */
6*336e3e4aSAlvin Šipraga 
7*336e3e4aSAlvin Šipraga #include <linux/etherdevice.h>
8*336e3e4aSAlvin Šipraga 
9*336e3e4aSAlvin Šipraga #include "rtl8365mb_l2.h"
10*336e3e4aSAlvin Šipraga #include "rtl8365mb_table.h"
11*336e3e4aSAlvin Šipraga #include <linux/regmap.h>
12*336e3e4aSAlvin Šipraga 
13*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_ENTRY_SIZE			6
14*336e3e4aSAlvin Šipraga 
15*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D0_MAC5_MSK		GENMASK(7, 0)
16*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D0_MAC4_MSK		GENMASK(15, 8)
17*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D1_MAC3_MSK		GENMASK(7, 0)
18*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D1_MAC2_MSK		GENMASK(15, 8)
19*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D2_MAC1_MSK		GENMASK(7, 0)
20*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D2_MAC0_MSK		GENMASK(15, 8)
21*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_VID_MSK		GENMASK(11, 0)
22*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_IVL_MSK		GENMASK(13, 13)
23*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D3_PORT_EXT_MSK	GENMASK(15, 15)
24*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_UC_PORT_HI_MSK		GENMASK(3, 3)
25*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_EFID_MSK		GENMASK(2, 0)
26*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_FID_MSK		GENMASK(6, 3)
27*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_SA_PRI_MSK		GENMASK(7, 7)
28*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_PORT_MSK		GENMASK(10, 8)
29*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_UC_PORT_LO_MSK		GENMASK(2, 0)
30*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_AGE_MSK		GENMASK(13, 11)
31*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_AUTH_MSK		GENMASK(14, 14)
32*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D4_SA_BLOCK_MSK	GENMASK(15, 15)
33*336e3e4aSAlvin Šipraga 
34*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_DA_BLOCK_MSK	GENMASK(0, 0)
35*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_PRIORITY_MSK	GENMASK(3, 1)
36*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_FWD_PRI_MSK		GENMASK(4, 4)
37*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_UC_D5_STATIC_MSK		GENMASK(5, 5)
38*336e3e4aSAlvin Šipraga 
39*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D0_MAC5_MSK		GENMASK(7, 0)
40*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D0_MAC4_MSK		GENMASK(15, 8)
41*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D1_MAC3_MSK		GENMASK(7, 0)
42*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D1_MAC2_MSK		GENMASK(15, 8)
43*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D2_MAC1_MSK		GENMASK(7, 0)
44*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D2_MAC0_MSK		GENMASK(15, 8)
45*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_VID_MSK		GENMASK(11, 0)
46*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_IVL_MSK		GENMASK(13, 13)
47*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D3_MBR_HI1_MSK		GENMASK(15, 14)
48*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_MC_MBR_HI1_MSK		GENMASK(9, 8)
49*336e3e4aSAlvin Šipraga 
50*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D4_MBR_MSK		GENMASK(7, 0)
51*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_MC_MBR_LO_MSK		GENMASK(7, 0)
52*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D4_IGMPIDX_MSK		GENMASK(15, 8)
53*336e3e4aSAlvin Šipraga 
54*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK	GENMASK(0, 0)
55*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_PRIORITY_MSK	GENMASK(3, 1)
56*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_FWD_PRI_MSK		GENMASK(4, 4)
57*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_STATIC_MSK		GENMASK(5, 5)
58*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_MC_D5_MBR_HI2_MSK		GENMASK(7, 7)
59*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_MC_MBR_HI2_MSK		GENMASK(10, 10)
60*336e3e4aSAlvin Šipraga 
61*336e3e4aSAlvin Šipraga /* Port flush command registers - writing a 1 to the port's MASK bit will
62*336e3e4aSAlvin Šipraga  * initiate the flush procedure. Completion is signalled when the corresponding
63*336e3e4aSAlvin Šipraga  * BUSY bit is 0.
64*336e3e4aSAlvin Šipraga  */
65*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_REG		0x0A36
66*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_PORT_MSK_MSK	GENMASK(7, 0)
67*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_PORT_BUSY_MSK	GENMASK(15, 8)
68*336e3e4aSAlvin Šipraga 
69*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_PORT_EXT_REG		0x0A35
70*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK	GENMASK(2, 0)
71*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK	GENMASK(5, 3)
72*336e3e4aSAlvin Šipraga 
73*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL1_REG		0x0A37
74*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL1_VID_MSK	GENMASK(11, 0)
75*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL1_FID_MSK	GENMASK(15, 12)
76*336e3e4aSAlvin Šipraga 
77*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL2_REG		0x0A38
78*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK	GENMASK(1, 0)
79*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT	0
80*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID 1
81*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_FID 2
82*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK	GENMASK(2, 2)
83*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC	0
84*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL2_TYPE_BOTH	1
85*336e3e4aSAlvin Šipraga 
86*336e3e4aSAlvin Šipraga /* This flushes the entire LUT, reading it back it will turn 0 when the
87*336e3e4aSAlvin Šipraga  * operation is complete
88*336e3e4aSAlvin Šipraga  */
89*336e3e4aSAlvin Šipraga #define RTL8365MB_L2_FLUSH_CTRL3_REG		0x0A39
90*336e3e4aSAlvin Šipraga #define   RTL8365MB_L2_FLUSH_CTRL3_MSK		GENMASK(0, 0)
91*336e3e4aSAlvin Šipraga 
92*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc_key {
93*336e3e4aSAlvin Šipraga 	u8 mac_addr[ETH_ALEN];
94*336e3e4aSAlvin Šipraga 	u16 vid;
95*336e3e4aSAlvin Šipraga 	u16 fid;
96*336e3e4aSAlvin Šipraga 	bool ivl;
97*336e3e4aSAlvin Šipraga 	u16 efid;
98*336e3e4aSAlvin Šipraga };
99*336e3e4aSAlvin Šipraga 
100*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_uc {
101*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_uc_key key;
102*336e3e4aSAlvin Šipraga 	u8 port;
103*336e3e4aSAlvin Šipraga 	u8 age;
104*336e3e4aSAlvin Šipraga 	u8 priority;
105*336e3e4aSAlvin Šipraga 
106*336e3e4aSAlvin Šipraga 	bool sa_block;
107*336e3e4aSAlvin Šipraga 	bool da_block;
108*336e3e4aSAlvin Šipraga 	bool auth;
109*336e3e4aSAlvin Šipraga 	bool is_static;
110*336e3e4aSAlvin Šipraga 	bool sa_pri;
111*336e3e4aSAlvin Šipraga 	bool fwd_pri;
112*336e3e4aSAlvin Šipraga };
113*336e3e4aSAlvin Šipraga 
114*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc_key {
115*336e3e4aSAlvin Šipraga 	u8 mac_addr[ETH_ALEN];
116*336e3e4aSAlvin Šipraga 	union {
117*336e3e4aSAlvin Šipraga 		u16 vid; /* IVL */
118*336e3e4aSAlvin Šipraga 		u16 fid; /* SVL */
119*336e3e4aSAlvin Šipraga 	};
120*336e3e4aSAlvin Šipraga 	bool ivl;
121*336e3e4aSAlvin Šipraga };
122*336e3e4aSAlvin Šipraga 
123*336e3e4aSAlvin Šipraga struct rtl8365mb_l2_mc {
124*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_mc_key key;
125*336e3e4aSAlvin Šipraga 	u16 member;
126*336e3e4aSAlvin Šipraga 	u8 priority;
127*336e3e4aSAlvin Šipraga 	u8 igmpidx;
128*336e3e4aSAlvin Šipraga 
129*336e3e4aSAlvin Šipraga 	bool is_static;
130*336e3e4aSAlvin Šipraga 	bool fwd_pri;
131*336e3e4aSAlvin Šipraga 	bool igmp_asic;
132*336e3e4aSAlvin Šipraga };
133*336e3e4aSAlvin Šipraga 
134*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_data_to_uc(const u16 *data, struct rtl8365mb_l2_uc *uc)
135*336e3e4aSAlvin Šipraga {
136*336e3e4aSAlvin Šipraga 	u32 val;
137*336e3e4aSAlvin Šipraga 
138*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC5_MSK, data[0]);
139*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_UC_D0_MAC4_MSK, data[0]);
140*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC3_MSK, data[1]);
141*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_UC_D1_MAC2_MSK, data[1]);
142*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC1_MSK, data[2]);
143*336e3e4aSAlvin Šipraga 	uc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_UC_D2_MAC0_MSK, data[2]);
144*336e3e4aSAlvin Šipraga 	uc->key.efid = FIELD_GET(RTL8365MB_L2_UC_D4_EFID_MSK, data[4]);
145*336e3e4aSAlvin Šipraga 	uc->key.vid = FIELD_GET(RTL8365MB_L2_UC_D3_VID_MSK, data[3]);
146*336e3e4aSAlvin Šipraga 	uc->key.ivl = FIELD_GET(RTL8365MB_L2_UC_D3_IVL_MSK, data[3]);
147*336e3e4aSAlvin Šipraga 	uc->key.fid = FIELD_GET(RTL8365MB_L2_UC_D4_FID_MSK, data[4]);
148*336e3e4aSAlvin Šipraga 	uc->age = FIELD_GET(RTL8365MB_L2_UC_D4_AGE_MSK, data[4]);
149*336e3e4aSAlvin Šipraga 	uc->auth = FIELD_GET(RTL8365MB_L2_UC_D4_AUTH_MSK, data[4]);
150*336e3e4aSAlvin Šipraga 
151*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_UC_D4_PORT_MSK, data[4]);
152*336e3e4aSAlvin Šipraga 	uc->port = FIELD_PREP(RTL8365MB_L2_UC_PORT_LO_MSK, val);
153*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, data[3]);
154*336e3e4aSAlvin Šipraga 	uc->port |= FIELD_PREP(RTL8365MB_L2_UC_PORT_HI_MSK, val);
155*336e3e4aSAlvin Šipraga 
156*336e3e4aSAlvin Šipraga 	uc->sa_pri = FIELD_GET(RTL8365MB_L2_UC_D4_SA_PRI_MSK, data[4]);
157*336e3e4aSAlvin Šipraga 	uc->fwd_pri = FIELD_GET(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, data[5]);
158*336e3e4aSAlvin Šipraga 	uc->sa_block = FIELD_GET(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, data[4]);
159*336e3e4aSAlvin Šipraga 	uc->da_block = FIELD_GET(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, data[5]);
160*336e3e4aSAlvin Šipraga 	uc->priority = FIELD_GET(RTL8365MB_L2_UC_D5_PRIORITY_MSK, data[5]);
161*336e3e4aSAlvin Šipraga 	uc->is_static = FIELD_GET(RTL8365MB_L2_UC_D5_STATIC_MSK, data[5]);
162*336e3e4aSAlvin Šipraga }
163*336e3e4aSAlvin Šipraga 
164*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_uc_to_data(const struct rtl8365mb_l2_uc *uc, u16 *data)
165*336e3e4aSAlvin Šipraga {
166*336e3e4aSAlvin Šipraga 	u32 val;
167*336e3e4aSAlvin Šipraga 
168*336e3e4aSAlvin Šipraga 	memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2);
169*336e3e4aSAlvin Šipraga 	data[0] |=
170*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D0_MAC5_MSK, uc->key.mac_addr[5]);
171*336e3e4aSAlvin Šipraga 	data[0] |=
172*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D0_MAC4_MSK, uc->key.mac_addr[4]);
173*336e3e4aSAlvin Šipraga 	data[1] |=
174*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D1_MAC3_MSK, uc->key.mac_addr[3]);
175*336e3e4aSAlvin Šipraga 	data[1] |=
176*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D1_MAC2_MSK, uc->key.mac_addr[2]);
177*336e3e4aSAlvin Šipraga 	data[2] |=
178*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D2_MAC1_MSK, uc->key.mac_addr[1]);
179*336e3e4aSAlvin Šipraga 	data[2] |=
180*336e3e4aSAlvin Šipraga 		FIELD_PREP(RTL8365MB_L2_UC_D2_MAC0_MSK, uc->key.mac_addr[0]);
181*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_VID_MSK, uc->key.vid);
182*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_IVL_MSK, uc->key.ivl);
183*336e3e4aSAlvin Šipraga 
184*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_UC_PORT_HI_MSK, uc->port);
185*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_UC_D3_PORT_EXT_MSK, val);
186*336e3e4aSAlvin Šipraga 
187*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_FID_MSK, uc->key.fid);
188*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_EFID_MSK, uc->key.efid);
189*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AGE_MSK, uc->age);
190*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_AUTH_MSK, uc->auth);
191*336e3e4aSAlvin Šipraga 
192*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_UC_PORT_LO_MSK, uc->port);
193*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_PORT_MSK, val);
194*336e3e4aSAlvin Šipraga 
195*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_PRI_MSK, uc->sa_pri);
196*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_UC_D4_SA_BLOCK_MSK, uc->sa_block);
197*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_FWD_PRI_MSK, uc->fwd_pri);
198*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_DA_BLOCK_MSK, uc->da_block);
199*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_PRIORITY_MSK, uc->priority);
200*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_UC_D5_STATIC_MSK, uc->is_static);
201*336e3e4aSAlvin Šipraga }
202*336e3e4aSAlvin Šipraga 
203*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_data_to_mc(const u16 *data, struct rtl8365mb_l2_mc *mc)
204*336e3e4aSAlvin Šipraga {
205*336e3e4aSAlvin Šipraga 	u32 val;
206*336e3e4aSAlvin Šipraga 
207*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[5] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC5_MSK, data[0]);
208*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[4] = FIELD_GET(RTL8365MB_L2_MC_D0_MAC4_MSK, data[0]);
209*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[3] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC3_MSK, data[1]);
210*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[2] = FIELD_GET(RTL8365MB_L2_MC_D1_MAC2_MSK, data[1]);
211*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[1] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC1_MSK, data[2]);
212*336e3e4aSAlvin Šipraga 	mc->key.mac_addr[0] = FIELD_GET(RTL8365MB_L2_MC_D2_MAC0_MSK, data[2]);
213*336e3e4aSAlvin Šipraga 	/* key.vid,key.fid shares the same memory space */
214*336e3e4aSAlvin Šipraga 	mc->key.vid = FIELD_GET(RTL8365MB_L2_MC_D3_VID_MSK, data[3]);
215*336e3e4aSAlvin Šipraga 	mc->key.ivl = FIELD_GET(RTL8365MB_L2_MC_D3_IVL_MSK, data[3]);
216*336e3e4aSAlvin Šipraga 	mc->priority = FIELD_GET(RTL8365MB_L2_MC_D5_PRIORITY_MSK, data[5]);
217*336e3e4aSAlvin Šipraga 	mc->fwd_pri = FIELD_GET(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, data[5]);
218*336e3e4aSAlvin Šipraga 	mc->is_static = FIELD_GET(RTL8365MB_L2_MC_D5_STATIC_MSK, data[5]);
219*336e3e4aSAlvin Šipraga 
220*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_D4_MBR_MSK, data[4]);
221*336e3e4aSAlvin Šipraga 	mc->member = FIELD_PREP(RTL8365MB_L2_MC_MBR_LO_MSK, val);
222*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, data[3]);
223*336e3e4aSAlvin Šipraga 	mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI1_MSK, val);
224*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, data[5]);
225*336e3e4aSAlvin Šipraga 	mc->member |= FIELD_PREP(RTL8365MB_L2_MC_MBR_HI2_MSK, val);
226*336e3e4aSAlvin Šipraga 
227*336e3e4aSAlvin Šipraga 	mc->igmpidx = FIELD_GET(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, data[4]);
228*336e3e4aSAlvin Šipraga 	mc->igmp_asic = FIELD_GET(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, data[5]);
229*336e3e4aSAlvin Šipraga }
230*336e3e4aSAlvin Šipraga 
231*336e3e4aSAlvin Šipraga static void rtl8365mb_l2_mc_to_data(const struct rtl8365mb_l2_mc *mc, u16 *data)
232*336e3e4aSAlvin Šipraga {
233*336e3e4aSAlvin Šipraga 	u32 val;
234*336e3e4aSAlvin Šipraga 
235*336e3e4aSAlvin Šipraga 	memset(data, 0, RTL8365MB_L2_ENTRY_SIZE * 2);
236*336e3e4aSAlvin Šipraga 	data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC5_MSK, mc->key.mac_addr[5]);
237*336e3e4aSAlvin Šipraga 	data[0] |= FIELD_PREP(RTL8365MB_L2_MC_D0_MAC4_MSK, mc->key.mac_addr[4]);
238*336e3e4aSAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC3_MSK, mc->key.mac_addr[3]);
239*336e3e4aSAlvin Šipraga 	data[1] |= FIELD_PREP(RTL8365MB_L2_MC_D1_MAC2_MSK, mc->key.mac_addr[2]);
240*336e3e4aSAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC1_MSK, mc->key.mac_addr[1]);
241*336e3e4aSAlvin Šipraga 	data[2] |= FIELD_PREP(RTL8365MB_L2_MC_D2_MAC0_MSK, mc->key.mac_addr[0]);
242*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_VID_MSK, mc->key.vid);
243*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_IVL_MSK, mc->key.ivl);
244*336e3e4aSAlvin Šipraga 
245*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI1_MSK, mc->member);
246*336e3e4aSAlvin Šipraga 	data[3] |= FIELD_PREP(RTL8365MB_L2_MC_D3_MBR_HI1_MSK, val);
247*336e3e4aSAlvin Šipraga 
248*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_MBR_LO_MSK, mc->member);
249*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_MBR_MSK, val);
250*336e3e4aSAlvin Šipraga 
251*336e3e4aSAlvin Šipraga 	data[4] |= FIELD_PREP(RTL8365MB_L2_MC_D4_IGMPIDX_MSK, mc->igmpidx);
252*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_IGMP_ASIC_MSK, mc->igmp_asic);
253*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_PRIORITY_MSK, mc->priority);
254*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_FWD_PRI_MSK, mc->fwd_pri);
255*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_STATIC_MSK, mc->is_static);
256*336e3e4aSAlvin Šipraga 
257*336e3e4aSAlvin Šipraga 	val = FIELD_GET(RTL8365MB_L2_MC_MBR_HI2_MSK, mc->member);
258*336e3e4aSAlvin Šipraga 	data[5] |= FIELD_PREP(RTL8365MB_L2_MC_D5_MBR_HI2_MSK, val);
259*336e3e4aSAlvin Šipraga }
260*336e3e4aSAlvin Šipraga 
261*336e3e4aSAlvin Šipraga /*
262*336e3e4aSAlvin Šipraga  * rtl8365mb_l2_get_next_uc() - get the next Unicast L2 entry
263*336e3e4aSAlvin Šipraga  * @priv: realtek_priv pointer
264*336e3e4aSAlvin Šipraga  * @addr: as input, the table index to start the walk
265*336e3e4aSAlvin Šipraga  *        as output, the found table index
266*336e3e4aSAlvin Šipraga  * @port: restrict the walk on entries related to port
267*336e3e4aSAlvin Šipraga  * @entry: returned L2 Unicast table entry
268*336e3e4aSAlvin Šipraga  *
269*336e3e4aSAlvin Šipraga  * This function gets the next unicast L2 table entry starting from @addr
270*336e3e4aSAlvin Šipraga  * and checking exclusively entries related to @port.
271*336e3e4aSAlvin Šipraga  *
272*336e3e4aSAlvin Šipraga  * On success, it returns 0, updates @addr to the index of the found entry,
273*336e3e4aSAlvin Šipraga  * and populates @entry. If the search reaches the end of the table and
274*336e3e4aSAlvin Šipraga  * wraps around and @addr will be strictly lower than the input @addr.
275*336e3e4aSAlvin Šipraga  * Callers must detect this wrap-around condition to prevent infinite loops.
276*336e3e4aSAlvin Šipraga  *
277*336e3e4aSAlvin Šipraga  * If the table contains no matching entries at all, it returns -ENOENT
278*336e3e4aSAlvin Šipraga  * and leaves @addr and @entry unmodified.
279*336e3e4aSAlvin Šipraga  *
280*336e3e4aSAlvin Šipraga  * Return: Returns 0 on success, a negative error on failure.
281*336e3e4aSAlvin Šipraga  **/
282*336e3e4aSAlvin Šipraga int rtl8365mb_l2_get_next_uc(struct realtek_priv *priv, u16 *addr, int port,
283*336e3e4aSAlvin Šipraga 			     struct realtek_fdb_entry *entry)
284*336e3e4aSAlvin Šipraga {
285*336e3e4aSAlvin Šipraga 	u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };
286*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_uc uc;
287*336e3e4aSAlvin Šipraga 	int ret;
288*336e3e4aSAlvin Šipraga 
289*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
290*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_READ, addr,
291*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_L2_METHOD_ADDR_NEXT_UC_PORT,
292*336e3e4aSAlvin Šipraga 				    port, data, RTL8365MB_L2_ENTRY_SIZE);
293*336e3e4aSAlvin Šipraga 	if (ret)
294*336e3e4aSAlvin Šipraga 		return ret;
295*336e3e4aSAlvin Šipraga 
296*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_data_to_uc(data, &uc);
297*336e3e4aSAlvin Šipraga 
298*336e3e4aSAlvin Šipraga 	ether_addr_copy(entry->mac_addr, uc.key.mac_addr);
299*336e3e4aSAlvin Šipraga 	entry->vid = uc.key.vid;
300*336e3e4aSAlvin Šipraga 	entry->is_static = uc.is_static;
301*336e3e4aSAlvin Šipraga 
302*336e3e4aSAlvin Šipraga 	return 0;
303*336e3e4aSAlvin Šipraga }
304*336e3e4aSAlvin Šipraga 
305*336e3e4aSAlvin Šipraga int rtl8365mb_l2_add_uc(struct realtek_priv *priv, int port,
306*336e3e4aSAlvin Šipraga 			const unsigned char mac_addr[static ETH_ALEN],
307*336e3e4aSAlvin Šipraga 			u16 efid, u16 vid)
308*336e3e4aSAlvin Šipraga {
309*336e3e4aSAlvin Šipraga 	u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };
310*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_uc uc = { 0 };
311*336e3e4aSAlvin Šipraga 	u16 addr;
312*336e3e4aSAlvin Šipraga 	int ret;
313*336e3e4aSAlvin Šipraga 
314*336e3e4aSAlvin Šipraga 	memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN);
315*336e3e4aSAlvin Šipraga 	uc.key.efid = efid;
316*336e3e4aSAlvin Šipraga 	uc.key.fid = 0;
317*336e3e4aSAlvin Šipraga 	uc.key.ivl = true;
318*336e3e4aSAlvin Šipraga 	uc.key.vid = vid;
319*336e3e4aSAlvin Šipraga 
320*336e3e4aSAlvin Šipraga 	uc.port = port;
321*336e3e4aSAlvin Šipraga 	/* Entries programmed by DSA (including those dynamically learned by
322*336e3e4aSAlvin Šipraga 	 * the software bridge and injected into the CPU port via assisted
323*336e3e4aSAlvin Šipraga 	 * learning) must be static. We do not let HW decrease age behind the
324*336e3e4aSAlvin Šipraga 	 * OS's back. As a trade-off, these will show up as permanent to users.
325*336e3e4aSAlvin Šipraga 	 */
326*336e3e4aSAlvin Šipraga 	uc.is_static = true;
327*336e3e4aSAlvin Šipraga 	/* age greater than 0 adds/updates entries */
328*336e3e4aSAlvin Šipraga 	uc.age = 1;
329*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_uc_to_data(&uc, data);
330*336e3e4aSAlvin Šipraga 
331*336e3e4aSAlvin Šipraga 	/* add the new entry or update an existing one */
332*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
333*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_WRITE, &addr,
334*336e3e4aSAlvin Šipraga 				    0, 0,
335*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
336*336e3e4aSAlvin Šipraga 
337*336e3e4aSAlvin Šipraga 	/* Assume the missing new entry as the table is full */
338*336e3e4aSAlvin Šipraga 	if (ret == -ENOENT)
339*336e3e4aSAlvin Šipraga 		return -ENOSPC;
340*336e3e4aSAlvin Šipraga 
341*336e3e4aSAlvin Šipraga 	/* addr will hold the table index, but it is not used here */
342*336e3e4aSAlvin Šipraga 	return ret;
343*336e3e4aSAlvin Šipraga }
344*336e3e4aSAlvin Šipraga 
345*336e3e4aSAlvin Šipraga int rtl8365mb_l2_del_uc(struct realtek_priv *priv, int port,
346*336e3e4aSAlvin Šipraga 			const unsigned char mac_addr[static ETH_ALEN],
347*336e3e4aSAlvin Šipraga 			u16 efid, u16 vid)
348*336e3e4aSAlvin Šipraga {
349*336e3e4aSAlvin Šipraga 	u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };
350*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_uc uc = { 0 };
351*336e3e4aSAlvin Šipraga 	u16 addr;
352*336e3e4aSAlvin Šipraga 	int ret;
353*336e3e4aSAlvin Šipraga 
354*336e3e4aSAlvin Šipraga 	memcpy(uc.key.mac_addr, mac_addr, ETH_ALEN);
355*336e3e4aSAlvin Šipraga 	uc.key.efid = efid;
356*336e3e4aSAlvin Šipraga 	uc.key.fid = 0;
357*336e3e4aSAlvin Šipraga 	uc.key.ivl = true;
358*336e3e4aSAlvin Šipraga 	uc.key.vid = vid;
359*336e3e4aSAlvin Šipraga 	/* age 0 deletes the entry */
360*336e3e4aSAlvin Šipraga 	uc.age = 0;
361*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_uc_to_data(&uc, data);
362*336e3e4aSAlvin Šipraga 
363*336e3e4aSAlvin Šipraga 	/* it looks like the switch will always add/update the entry,
364*336e3e4aSAlvin Šipraga 	 * even when age is 0 or uc.key did not match an existing entry,
365*336e3e4aSAlvin Šipraga 	 * just to immediately drop it because age is zero. You can still
366*336e3e4aSAlvin Šipraga 	 * get the added/updated address from @addr
367*336e3e4aSAlvin Šipraga 	 */
368*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
369*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_WRITE, &addr,
370*336e3e4aSAlvin Šipraga 				    0, 0,
371*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
372*336e3e4aSAlvin Šipraga 
373*336e3e4aSAlvin Šipraga 	if (ret == -ENOENT) {
374*336e3e4aSAlvin Šipraga 		dev_dbg(priv->dev, "%s: %pM vid=%d efid=%d missing\n",
375*336e3e4aSAlvin Šipraga 			__func__, mac_addr, vid, efid);
376*336e3e4aSAlvin Šipraga 		/* Silently return success */
377*336e3e4aSAlvin Šipraga 		return 0;
378*336e3e4aSAlvin Šipraga 	}
379*336e3e4aSAlvin Šipraga 
380*336e3e4aSAlvin Šipraga 	/* addr will hold the table index, but it is not used here */
381*336e3e4aSAlvin Šipraga 	return ret;
382*336e3e4aSAlvin Šipraga }
383*336e3e4aSAlvin Šipraga 
384*336e3e4aSAlvin Šipraga int rtl8365mb_l2_flush(struct realtek_priv *priv, int port, u16 vid)
385*336e3e4aSAlvin Šipraga {
386*336e3e4aSAlvin Šipraga 	int mode = vid ? RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT_VID :
387*336e3e4aSAlvin Šipraga 			 RTL8365MB_L2_FLUSH_CTRL2_MODE_PORT;
388*336e3e4aSAlvin Šipraga 	u32 val, mask;
389*336e3e4aSAlvin Šipraga 	int ret;
390*336e3e4aSAlvin Šipraga 
391*336e3e4aSAlvin Šipraga 	mutex_lock(&priv->map_lock);
392*336e3e4aSAlvin Šipraga 
393*336e3e4aSAlvin Šipraga 	/* Configure flushing mode; only flush dynamic entries */
394*336e3e4aSAlvin Šipraga 	ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL2_REG,
395*336e3e4aSAlvin Šipraga 			   FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_MODE_MSK,
396*336e3e4aSAlvin Šipraga 				      mode) |
397*336e3e4aSAlvin Šipraga 			   FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL2_TYPE_MSK,
398*336e3e4aSAlvin Šipraga 				      RTL8365MB_L2_FLUSH_CTRL2_TYPE_DYNAMIC));
399*336e3e4aSAlvin Šipraga 	if (ret)
400*336e3e4aSAlvin Šipraga 		goto out;
401*336e3e4aSAlvin Šipraga 
402*336e3e4aSAlvin Šipraga 	ret = regmap_write(priv->map_nolock, RTL8365MB_L2_FLUSH_CTRL1_REG,
403*336e3e4aSAlvin Šipraga 			   FIELD_PREP(RTL8365MB_L2_FLUSH_CTRL1_VID_MSK, vid));
404*336e3e4aSAlvin Šipraga 
405*336e3e4aSAlvin Šipraga 	if (ret)
406*336e3e4aSAlvin Šipraga 		goto out;
407*336e3e4aSAlvin Šipraga 	/* Now issue the flush command and wait for its completion. There are
408*336e3e4aSAlvin Šipraga 	 * two registers for this purpose, and which one to use depends on the
409*336e3e4aSAlvin Šipraga 	 * port number. The _EXT register is for ports 8 or higher.
410*336e3e4aSAlvin Šipraga 	 */
411*336e3e4aSAlvin Šipraga 	if (port < 8) {
412*336e3e4aSAlvin Šipraga 		val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_MSK_MSK,
413*336e3e4aSAlvin Šipraga 				 BIT(port) & 0xFF);
414*336e3e4aSAlvin Šipraga 		ret = regmap_write(priv->map_nolock,
415*336e3e4aSAlvin Šipraga 				   RTL8365MB_L2_FLUSH_PORT_REG, val);
416*336e3e4aSAlvin Šipraga 		if (ret)
417*336e3e4aSAlvin Šipraga 			goto out;
418*336e3e4aSAlvin Šipraga 
419*336e3e4aSAlvin Šipraga 		mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_BUSY_MSK,
420*336e3e4aSAlvin Šipraga 				  BIT(port) & 0xFF);
421*336e3e4aSAlvin Šipraga 		ret = regmap_read_poll_timeout(priv->map_nolock,
422*336e3e4aSAlvin Šipraga 					       RTL8365MB_L2_FLUSH_PORT_REG,
423*336e3e4aSAlvin Šipraga 					       val, !(val & mask), 10, 10000);
424*336e3e4aSAlvin Šipraga 		if (ret)
425*336e3e4aSAlvin Šipraga 			goto out;
426*336e3e4aSAlvin Šipraga 	} else {
427*336e3e4aSAlvin Šipraga 		val = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_MSK_MSK,
428*336e3e4aSAlvin Šipraga 				 BIT(port) >> 8);
429*336e3e4aSAlvin Šipraga 		ret = regmap_write(priv->map_nolock,
430*336e3e4aSAlvin Šipraga 				   RTL8365MB_L2_FLUSH_PORT_EXT_REG, val);
431*336e3e4aSAlvin Šipraga 		if (ret)
432*336e3e4aSAlvin Šipraga 			goto out;
433*336e3e4aSAlvin Šipraga 
434*336e3e4aSAlvin Šipraga 		mask = FIELD_PREP(RTL8365MB_L2_FLUSH_PORT_EXT_BUSY_MSK,
435*336e3e4aSAlvin Šipraga 				  BIT(port) >> 8);
436*336e3e4aSAlvin Šipraga 		ret = regmap_read_poll_timeout(priv->map_nolock,
437*336e3e4aSAlvin Šipraga 					       RTL8365MB_L2_FLUSH_PORT_EXT_REG,
438*336e3e4aSAlvin Šipraga 					       val, !(val & mask), 10, 10000);
439*336e3e4aSAlvin Šipraga 		if (ret)
440*336e3e4aSAlvin Šipraga 			goto out;
441*336e3e4aSAlvin Šipraga 	}
442*336e3e4aSAlvin Šipraga 
443*336e3e4aSAlvin Šipraga out:
444*336e3e4aSAlvin Šipraga 	mutex_unlock(&priv->map_lock);
445*336e3e4aSAlvin Šipraga 
446*336e3e4aSAlvin Šipraga 	return ret;
447*336e3e4aSAlvin Šipraga }
448*336e3e4aSAlvin Šipraga 
449*336e3e4aSAlvin Šipraga int rtl8365mb_l2_add_mc(struct realtek_priv *priv, int port,
450*336e3e4aSAlvin Šipraga 			const unsigned char mac_addr[static ETH_ALEN],
451*336e3e4aSAlvin Šipraga 			u16 vid)
452*336e3e4aSAlvin Šipraga {
453*336e3e4aSAlvin Šipraga 	u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };
454*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_mc mc = { 0 };
455*336e3e4aSAlvin Šipraga 	u16 addr;
456*336e3e4aSAlvin Šipraga 	int ret;
457*336e3e4aSAlvin Šipraga 
458*336e3e4aSAlvin Šipraga 	memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN);
459*336e3e4aSAlvin Šipraga 	mc.key.vid = vid;
460*336e3e4aSAlvin Šipraga 	mc.key.ivl = true;
461*336e3e4aSAlvin Šipraga 	/* Already set the port and is_static, although not used in OP_READ,
462*336e3e4aSAlvin Šipraga 	 * data will be ready for OP_WRITE if it is a new entry.
463*336e3e4aSAlvin Šipraga 	 */
464*336e3e4aSAlvin Šipraga 	mc.member |= BIT(port);
465*336e3e4aSAlvin Šipraga 	mc.is_static = 1;
466*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_mc_to_data(&mc, data);
467*336e3e4aSAlvin Šipraga 
468*336e3e4aSAlvin Šipraga 	/* First look for an existing entry (to get existing port members) */
469*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
470*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_READ, &addr,
471*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_L2_METHOD_MAC, 0,
472*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
473*336e3e4aSAlvin Šipraga 	if (!ret) {
474*336e3e4aSAlvin Šipraga 		/* There is already an entry... */
475*336e3e4aSAlvin Šipraga 		rtl8365mb_l2_data_to_mc(data, &mc);
476*336e3e4aSAlvin Šipraga 		dev_dbg(priv->dev,
477*336e3e4aSAlvin Šipraga 			"%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n",
478*336e3e4aSAlvin Šipraga 			__func__, mac_addr, addr, mc.member, mc.igmpidx,
479*336e3e4aSAlvin Šipraga 			mc.is_static ? "static" : "dynamic");
480*336e3e4aSAlvin Šipraga 		/* the port must be added as a member */
481*336e3e4aSAlvin Šipraga 		mc.member |= BIT(port);
482*336e3e4aSAlvin Šipraga 
483*336e3e4aSAlvin Šipraga 		if (!mc.is_static) {
484*336e3e4aSAlvin Šipraga 			dev_dbg(priv->dev,
485*336e3e4aSAlvin Šipraga 				"%s: promoting addr=%d group to static\n",
486*336e3e4aSAlvin Šipraga 				__func__, addr);
487*336e3e4aSAlvin Šipraga 			mc.is_static = 1;
488*336e3e4aSAlvin Šipraga 		}
489*336e3e4aSAlvin Šipraga 
490*336e3e4aSAlvin Šipraga 		rtl8365mb_l2_mc_to_data(&mc, data);
491*336e3e4aSAlvin Šipraga 	} else if (ret == -ENOENT) {
492*336e3e4aSAlvin Šipraga 		/* New entry, no need to update data again as it already
493*336e3e4aSAlvin Šipraga 		 * includes the member.
494*336e3e4aSAlvin Šipraga 		 *
495*336e3e4aSAlvin Šipraga 		 * Multicast hardware entries do not support EFID (bridge
496*336e3e4aSAlvin Šipraga 		 * isolation). However, traffic isolation is still maintained
497*336e3e4aSAlvin Šipraga 		 * because the hardware applies the port isolation masks
498*336e3e4aSAlvin Šipraga 		 * (pmasks) configured in bridge_join after the L2 lookup.
499*336e3e4aSAlvin Šipraga 		 * Entries from different bridges will collide on the same
500*336e3e4aSAlvin Šipraga 		 * MAC+VID slot with an OR'ed member mask, but packets will
501*336e3e4aSAlvin Šipraga 		 * only exit through ports allowed by the source port's pmask.
502*336e3e4aSAlvin Šipraga 		 */
503*336e3e4aSAlvin Šipraga 	} else {
504*336e3e4aSAlvin Šipraga 		return ret;
505*336e3e4aSAlvin Šipraga 	}
506*336e3e4aSAlvin Šipraga 
507*336e3e4aSAlvin Šipraga 	/* add the new entry or update an existing one */
508*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
509*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_WRITE, &addr,
510*336e3e4aSAlvin Šipraga 				    0, 0,
511*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
512*336e3e4aSAlvin Šipraga 
513*336e3e4aSAlvin Šipraga 	/* Assume the missing new entry as the table is full */
514*336e3e4aSAlvin Šipraga 	if (ret == -ENOENT)
515*336e3e4aSAlvin Šipraga 		return -ENOSPC;
516*336e3e4aSAlvin Šipraga 
517*336e3e4aSAlvin Šipraga 	return ret;
518*336e3e4aSAlvin Šipraga }
519*336e3e4aSAlvin Šipraga 
520*336e3e4aSAlvin Šipraga int rtl8365mb_l2_del_mc(struct realtek_priv *priv, int port,
521*336e3e4aSAlvin Šipraga 			const unsigned char mac_addr[static ETH_ALEN],
522*336e3e4aSAlvin Šipraga 			u16 vid)
523*336e3e4aSAlvin Šipraga {
524*336e3e4aSAlvin Šipraga 	u16 data[RTL8365MB_L2_ENTRY_SIZE] = { 0 };
525*336e3e4aSAlvin Šipraga 	struct rtl8365mb_l2_mc mc = { 0 };
526*336e3e4aSAlvin Šipraga 	u16 addr;
527*336e3e4aSAlvin Šipraga 	int ret;
528*336e3e4aSAlvin Šipraga 
529*336e3e4aSAlvin Šipraga 	memcpy(mc.key.mac_addr, mac_addr, ETH_ALEN);
530*336e3e4aSAlvin Šipraga 	mc.key.vid = vid;
531*336e3e4aSAlvin Šipraga 	mc.key.ivl = true;
532*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_mc_to_data(&mc, data);
533*336e3e4aSAlvin Šipraga 
534*336e3e4aSAlvin Šipraga 	/* First look for an existing entry (to get existing port members) */
535*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
536*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_READ, &addr,
537*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_L2_METHOD_MAC, 0,
538*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
539*336e3e4aSAlvin Šipraga 	if (ret == -ENOENT) {
540*336e3e4aSAlvin Šipraga 		dev_dbg(priv->dev, "%s: %pM vid=%d missing\n",
541*336e3e4aSAlvin Šipraga 			__func__, mac_addr, vid);
542*336e3e4aSAlvin Šipraga 		/* Silently return success */
543*336e3e4aSAlvin Šipraga 		return 0;
544*336e3e4aSAlvin Šipraga 	}
545*336e3e4aSAlvin Šipraga 
546*336e3e4aSAlvin Šipraga 	if (ret)
547*336e3e4aSAlvin Šipraga 		/* Return on any other error */
548*336e3e4aSAlvin Šipraga 		return ret;
549*336e3e4aSAlvin Šipraga 
550*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_data_to_mc(data, &mc);
551*336e3e4aSAlvin Šipraga 	dev_dbg(priv->dev,
552*336e3e4aSAlvin Šipraga 		"%s: found %pM addr=%d member=0x%x igmpidx=0x%x %s\n",
553*336e3e4aSAlvin Šipraga 		__func__, mac_addr, addr, mc.member, mc.igmpidx,
554*336e3e4aSAlvin Šipraga 		mc.is_static ? "static" : "dynamic");
555*336e3e4aSAlvin Šipraga 	/* the port must be removed as a member */
556*336e3e4aSAlvin Šipraga 	mc.member &= ~BIT(port);
557*336e3e4aSAlvin Šipraga 	if (!mc.member) {
558*336e3e4aSAlvin Šipraga 		/* Multicast entries do not have an age field. Clearing both
559*336e3e4aSAlvin Šipraga 		 * the member portmask and is_static flags is the hardware
560*336e3e4aSAlvin Šipraga 		 * signal to invalidate and reclaim the L2 table slot.
561*336e3e4aSAlvin Šipraga 		 */
562*336e3e4aSAlvin Šipraga 		mc.is_static = 0;
563*336e3e4aSAlvin Šipraga 		mc.igmpidx = 0;
564*336e3e4aSAlvin Šipraga 		mc.priority = 0;
565*336e3e4aSAlvin Šipraga 		mc.fwd_pri = 0;
566*336e3e4aSAlvin Šipraga 		mc.igmp_asic = 0;
567*336e3e4aSAlvin Šipraga 	}
568*336e3e4aSAlvin Šipraga 	rtl8365mb_l2_mc_to_data(&mc, data);
569*336e3e4aSAlvin Šipraga 
570*336e3e4aSAlvin Šipraga 	/* update the existing entry. */
571*336e3e4aSAlvin Šipraga 	ret = rtl8365mb_table_query(priv, RTL8365MB_TABLE_L2,
572*336e3e4aSAlvin Šipraga 				    RTL8365MB_TABLE_OP_WRITE, &addr,
573*336e3e4aSAlvin Šipraga 				    0, 0,
574*336e3e4aSAlvin Šipraga 				    data, RTL8365MB_L2_ENTRY_SIZE);
575*336e3e4aSAlvin Šipraga 	return ret;
576*336e3e4aSAlvin Šipraga }
577