xref: /linux/drivers/net/dsa/yt921x.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1186623f4SDavid Yang // SPDX-License-Identifier: GPL-2.0-or-later
2186623f4SDavid Yang /*
3186623f4SDavid Yang  * Driver for Motorcomm YT921x Switch
4186623f4SDavid Yang  *
5186623f4SDavid Yang  * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII,
6186623f4SDavid Yang  * be sure to do your own checks before porting to another chip.
7186623f4SDavid Yang  *
8186623f4SDavid Yang  * Copyright (c) 2025 David Yang
9186623f4SDavid Yang  */
10186623f4SDavid Yang 
11186623f4SDavid Yang #include <linux/etherdevice.h>
12186623f4SDavid Yang #include <linux/if_bridge.h>
13186623f4SDavid Yang #include <linux/if_hsr.h>
14186623f4SDavid Yang #include <linux/if_vlan.h>
15186623f4SDavid Yang #include <linux/iopoll.h>
16186623f4SDavid Yang #include <linux/mdio.h>
17186623f4SDavid Yang #include <linux/module.h>
18186623f4SDavid Yang #include <linux/of.h>
19186623f4SDavid Yang #include <linux/of_mdio.h>
20186623f4SDavid Yang #include <linux/of_net.h>
21186623f4SDavid Yang 
22186623f4SDavid Yang #include <net/dsa.h>
23186623f4SDavid Yang 
24186623f4SDavid Yang #include "yt921x.h"
25186623f4SDavid Yang 
26186623f4SDavid Yang struct yt921x_mib_desc {
27186623f4SDavid Yang 	unsigned int size;
28186623f4SDavid Yang 	unsigned int offset;
29186623f4SDavid Yang 	const char *name;
30186623f4SDavid Yang };
31186623f4SDavid Yang 
32186623f4SDavid Yang #define MIB_DESC(_size, _offset, _name) \
33186623f4SDavid Yang 	{_size, _offset, _name}
34186623f4SDavid Yang 
35186623f4SDavid Yang /* Must agree with yt921x_mib
36186623f4SDavid Yang  *
37186623f4SDavid Yang  * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
38186623f4SDavid Yang  * structured go to their *_stats() methods, but we need their sizes and offsets
39186623f4SDavid Yang  * to perform 32bit MIB overflow wraparound.
40186623f4SDavid Yang  */
41186623f4SDavid Yang static const struct yt921x_mib_desc yt921x_mib_descs[] = {
42fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
43fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
44fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
45fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
46186623f4SDavid Yang 
47fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
48fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
49fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
50fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
51186623f4SDavid Yang 
52fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
53fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
54fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
55fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
56186623f4SDavid Yang 
57fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
58fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
59fbce7b36SDavid Yang 	MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
60186623f4SDavid Yang 
61fbce7b36SDavid Yang 	MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
62fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
63186623f4SDavid Yang 
64fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
65fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
66fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
67fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
68186623f4SDavid Yang 
69fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
70fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
71fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
72fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
73186623f4SDavid Yang 
74fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
75fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
76fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
77fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
78186623f4SDavid Yang 
79fbce7b36SDavid Yang 	MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
80fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
81186623f4SDavid Yang 
82fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
83fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
84fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
85fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
86186623f4SDavid Yang 
87fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
88fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
89fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
90fbce7b36SDavid Yang 	MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
91186623f4SDavid Yang };
92186623f4SDavid Yang 
93186623f4SDavid Yang struct yt921x_info {
94186623f4SDavid Yang 	const char *name;
95186623f4SDavid Yang 	u16 major;
96186623f4SDavid Yang 	/* Unknown, seems to be plain enumeration */
97186623f4SDavid Yang 	u8 mode;
98186623f4SDavid Yang 	u8 extmode;
99186623f4SDavid Yang 	/* Ports with integral GbE PHYs, not including MCU Port 10 */
100186623f4SDavid Yang 	u16 internal_mask;
101186623f4SDavid Yang 	/* TODO: see comments in yt921x_dsa_phylink_get_caps() */
102186623f4SDavid Yang 	u16 external_mask;
103186623f4SDavid Yang };
104186623f4SDavid Yang 
105186623f4SDavid Yang #define YT921X_PORT_MASK_INTn(port)	BIT(port)
106186623f4SDavid Yang #define YT921X_PORT_MASK_INT0_n(n)	GENMASK((n) - 1, 0)
107186623f4SDavid Yang #define YT921X_PORT_MASK_EXT0		BIT(8)
108186623f4SDavid Yang #define YT921X_PORT_MASK_EXT1		BIT(9)
109186623f4SDavid Yang 
110186623f4SDavid Yang static const struct yt921x_info yt921x_infos[] = {
111186623f4SDavid Yang 	{
112186623f4SDavid Yang 		"YT9215SC", YT9215_MAJOR, 1, 0,
113186623f4SDavid Yang 		YT921X_PORT_MASK_INT0_n(5),
114186623f4SDavid Yang 		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
115186623f4SDavid Yang 	},
116186623f4SDavid Yang 	{
117186623f4SDavid Yang 		"YT9215S", YT9215_MAJOR, 2, 0,
118186623f4SDavid Yang 		YT921X_PORT_MASK_INT0_n(5),
119186623f4SDavid Yang 		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
120186623f4SDavid Yang 	},
121186623f4SDavid Yang 	{
122186623f4SDavid Yang 		"YT9215RB", YT9215_MAJOR, 3, 0,
123186623f4SDavid Yang 		YT921X_PORT_MASK_INT0_n(5),
124186623f4SDavid Yang 		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
125186623f4SDavid Yang 	},
126186623f4SDavid Yang 	{
127186623f4SDavid Yang 		"YT9214NB", YT9215_MAJOR, 3, 2,
128186623f4SDavid Yang 		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
129186623f4SDavid Yang 		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
130186623f4SDavid Yang 	},
131186623f4SDavid Yang 	{
132186623f4SDavid Yang 		"YT9213NB", YT9215_MAJOR, 3, 3,
133186623f4SDavid Yang 		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
134186623f4SDavid Yang 		YT921X_PORT_MASK_EXT1,
135186623f4SDavid Yang 	},
136186623f4SDavid Yang 	{
137186623f4SDavid Yang 		"YT9218N", YT9218_MAJOR, 0, 0,
138186623f4SDavid Yang 		YT921X_PORT_MASK_INT0_n(8),
139186623f4SDavid Yang 		0,
140186623f4SDavid Yang 	},
141186623f4SDavid Yang 	{
142186623f4SDavid Yang 		"YT9218MB", YT9218_MAJOR, 1, 0,
143186623f4SDavid Yang 		YT921X_PORT_MASK_INT0_n(8),
144186623f4SDavid Yang 		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
145186623f4SDavid Yang 	},
146186623f4SDavid Yang 	{}
147186623f4SDavid Yang };
148186623f4SDavid Yang 
149186623f4SDavid Yang #define YT921X_NAME	"yt921x"
150186623f4SDavid Yang 
151186623f4SDavid Yang #define YT921X_VID_UNWARE	4095
152186623f4SDavid Yang 
153186623f4SDavid Yang #define YT921X_POLL_SLEEP_US	10000
154186623f4SDavid Yang #define YT921X_POLL_TIMEOUT_US	100000
155186623f4SDavid Yang 
156186623f4SDavid Yang /* The interval should be small enough to avoid overflow of 32bit MIBs.
157186623f4SDavid Yang  *
158186623f4SDavid Yang  * Until we can read MIBs from stats64 call directly (i.e. sleep
159186623f4SDavid Yang  * there), we have to poll stats more frequently then it is actually needed.
160186623f4SDavid Yang  * For overflow protection, normally, 100 sec interval should have been OK.
161186623f4SDavid Yang  */
162186623f4SDavid Yang #define YT921X_STATS_INTERVAL_JIFFIES	(3 * HZ)
163186623f4SDavid Yang 
164186623f4SDavid Yang struct yt921x_reg_mdio {
165186623f4SDavid Yang 	struct mii_bus *bus;
166186623f4SDavid Yang 	int addr;
167186623f4SDavid Yang 	/* SWITCH_ID_1 / SWITCH_ID_0 of the device
168186623f4SDavid Yang 	 *
169186623f4SDavid Yang 	 * This is a way to multiplex multiple devices on the same MII phyaddr
170186623f4SDavid Yang 	 * and should be configurable in DT. However, MDIO core simply doesn't
171186623f4SDavid Yang 	 * allow multiple devices over one reg addr, so this is a fixed value
172186623f4SDavid Yang 	 * for now until a solution is found.
173186623f4SDavid Yang 	 *
174186623f4SDavid Yang 	 * Keep this because we need switchid to form MII regaddrs anyway.
175186623f4SDavid Yang 	 */
176186623f4SDavid Yang 	unsigned char switchid;
177186623f4SDavid Yang };
178186623f4SDavid Yang 
179186623f4SDavid Yang /* TODO: SPI/I2C */
180186623f4SDavid Yang 
181186623f4SDavid Yang #define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
182186623f4SDavid Yang #define to_device(priv) ((priv)->ds.dev)
183186623f4SDavid Yang 
184186623f4SDavid Yang static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
185186623f4SDavid Yang {
186186623f4SDavid Yang 	WARN_ON(!mutex_is_locked(&priv->reg_lock));
187186623f4SDavid Yang 
188186623f4SDavid Yang 	return priv->reg_ops->read(priv->reg_ctx, reg, valp);
189186623f4SDavid Yang }
190186623f4SDavid Yang 
191186623f4SDavid Yang static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
192186623f4SDavid Yang {
193186623f4SDavid Yang 	WARN_ON(!mutex_is_locked(&priv->reg_lock));
194186623f4SDavid Yang 
195186623f4SDavid Yang 	return priv->reg_ops->write(priv->reg_ctx, reg, val);
196186623f4SDavid Yang }
197186623f4SDavid Yang 
198186623f4SDavid Yang static int
199186623f4SDavid Yang yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
200186623f4SDavid Yang {
201186623f4SDavid Yang 	u32 val;
202186623f4SDavid Yang 	int res;
203186623f4SDavid Yang 	int ret;
204186623f4SDavid Yang 
205186623f4SDavid Yang 	ret = read_poll_timeout(yt921x_reg_read, res,
206186623f4SDavid Yang 				res || (val & mask) == *valp,
207186623f4SDavid Yang 				YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
208186623f4SDavid Yang 				false, priv, reg, &val);
209186623f4SDavid Yang 	if (ret)
210186623f4SDavid Yang 		return ret;
211186623f4SDavid Yang 	if (res)
212186623f4SDavid Yang 		return res;
213186623f4SDavid Yang 
214186623f4SDavid Yang 	*valp = val;
215186623f4SDavid Yang 	return 0;
216186623f4SDavid Yang }
217186623f4SDavid Yang 
218186623f4SDavid Yang static int
219186623f4SDavid Yang yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
220186623f4SDavid Yang {
221186623f4SDavid Yang 	int res;
222186623f4SDavid Yang 	u32 v;
223186623f4SDavid Yang 	u32 u;
224186623f4SDavid Yang 
225186623f4SDavid Yang 	res = yt921x_reg_read(priv, reg, &v);
226186623f4SDavid Yang 	if (res)
227186623f4SDavid Yang 		return res;
228186623f4SDavid Yang 
229186623f4SDavid Yang 	u = v;
230186623f4SDavid Yang 	u &= ~mask;
231186623f4SDavid Yang 	u |= val;
232186623f4SDavid Yang 	if (u == v)
233186623f4SDavid Yang 		return 0;
234186623f4SDavid Yang 
235186623f4SDavid Yang 	return yt921x_reg_write(priv, reg, u);
236186623f4SDavid Yang }
237186623f4SDavid Yang 
238186623f4SDavid Yang static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
239186623f4SDavid Yang {
240186623f4SDavid Yang 	return yt921x_reg_update_bits(priv, reg, 0, mask);
241186623f4SDavid Yang }
242186623f4SDavid Yang 
243186623f4SDavid Yang static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
244186623f4SDavid Yang {
245186623f4SDavid Yang 	return yt921x_reg_update_bits(priv, reg, mask, 0);
246186623f4SDavid Yang }
247186623f4SDavid Yang 
248186623f4SDavid Yang static int
249186623f4SDavid Yang yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
250186623f4SDavid Yang {
251186623f4SDavid Yang 	return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
252186623f4SDavid Yang }
253186623f4SDavid Yang 
254186623f4SDavid Yang /* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if
255186623f4SDavid Yang  * you are to write only the lower / upper 32 bits.
256186623f4SDavid Yang  *
257186623f4SDavid Yang  * There is no such restriction for reading, but we still provide 64-bit read
258186623f4SDavid Yang  * wrappers so that we always handle u64 values.
259186623f4SDavid Yang  */
260186623f4SDavid Yang 
261186623f4SDavid Yang static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
262186623f4SDavid Yang {
263186623f4SDavid Yang 	u32 lo;
264186623f4SDavid Yang 	u32 hi;
265186623f4SDavid Yang 	int res;
266186623f4SDavid Yang 
267186623f4SDavid Yang 	res = yt921x_reg_read(priv, reg, &lo);
268186623f4SDavid Yang 	if (res)
269186623f4SDavid Yang 		return res;
270186623f4SDavid Yang 	res = yt921x_reg_read(priv, reg + 4, &hi);
271186623f4SDavid Yang 	if (res)
272186623f4SDavid Yang 		return res;
273186623f4SDavid Yang 
274186623f4SDavid Yang 	*valp = ((u64)hi << 32) | lo;
275186623f4SDavid Yang 	return 0;
276186623f4SDavid Yang }
277186623f4SDavid Yang 
278186623f4SDavid Yang static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val)
279186623f4SDavid Yang {
280186623f4SDavid Yang 	int res;
281186623f4SDavid Yang 
282186623f4SDavid Yang 	res = yt921x_reg_write(priv, reg, (u32)val);
283186623f4SDavid Yang 	if (res)
284186623f4SDavid Yang 		return res;
285186623f4SDavid Yang 	return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32));
286186623f4SDavid Yang }
287186623f4SDavid Yang 
288186623f4SDavid Yang static int
289186623f4SDavid Yang yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val)
290186623f4SDavid Yang {
291186623f4SDavid Yang 	int res;
292186623f4SDavid Yang 	u64 v;
293186623f4SDavid Yang 	u64 u;
294186623f4SDavid Yang 
295186623f4SDavid Yang 	res = yt921x_reg64_read(priv, reg, &v);
296186623f4SDavid Yang 	if (res)
297186623f4SDavid Yang 		return res;
298186623f4SDavid Yang 
299186623f4SDavid Yang 	u = v;
300186623f4SDavid Yang 	u &= ~mask;
301186623f4SDavid Yang 	u |= val;
302186623f4SDavid Yang 	if (u == v)
303186623f4SDavid Yang 		return 0;
304186623f4SDavid Yang 
305186623f4SDavid Yang 	return yt921x_reg64_write(priv, reg, u);
306186623f4SDavid Yang }
307186623f4SDavid Yang 
308186623f4SDavid Yang static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask)
309186623f4SDavid Yang {
310186623f4SDavid Yang 	return yt921x_reg64_update_bits(priv, reg, mask, 0);
311186623f4SDavid Yang }
312186623f4SDavid Yang 
313186623f4SDavid Yang static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
314186623f4SDavid Yang {
315186623f4SDavid Yang 	struct yt921x_reg_mdio *mdio = context;
316186623f4SDavid Yang 	struct mii_bus *bus = mdio->bus;
317186623f4SDavid Yang 	int addr = mdio->addr;
318186623f4SDavid Yang 	u32 reg_addr;
319186623f4SDavid Yang 	u32 reg_data;
320186623f4SDavid Yang 	u32 val;
321186623f4SDavid Yang 	int res;
322186623f4SDavid Yang 
323186623f4SDavid Yang 	/* Hold the mdio bus lock to avoid (un)locking for 4 times */
324186623f4SDavid Yang 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
325186623f4SDavid Yang 
326186623f4SDavid Yang 	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
327186623f4SDavid Yang 		   YT921X_SMI_READ;
328186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
329186623f4SDavid Yang 	if (res)
330186623f4SDavid Yang 		goto end;
331186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
332186623f4SDavid Yang 	if (res)
333186623f4SDavid Yang 		goto end;
334186623f4SDavid Yang 
335186623f4SDavid Yang 	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
336186623f4SDavid Yang 		   YT921X_SMI_READ;
337186623f4SDavid Yang 	res = __mdiobus_read(bus, addr, reg_data);
338186623f4SDavid Yang 	if (res < 0)
339186623f4SDavid Yang 		goto end;
340186623f4SDavid Yang 	val = (u16)res;
341186623f4SDavid Yang 	res = __mdiobus_read(bus, addr, reg_data);
342186623f4SDavid Yang 	if (res < 0)
343186623f4SDavid Yang 		goto end;
344186623f4SDavid Yang 	val = (val << 16) | (u16)res;
345186623f4SDavid Yang 
346186623f4SDavid Yang 	*valp = val;
347186623f4SDavid Yang 	res = 0;
348186623f4SDavid Yang 
349186623f4SDavid Yang end:
350186623f4SDavid Yang 	mutex_unlock(&bus->mdio_lock);
351186623f4SDavid Yang 	return res;
352186623f4SDavid Yang }
353186623f4SDavid Yang 
354186623f4SDavid Yang static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
355186623f4SDavid Yang {
356186623f4SDavid Yang 	struct yt921x_reg_mdio *mdio = context;
357186623f4SDavid Yang 	struct mii_bus *bus = mdio->bus;
358186623f4SDavid Yang 	int addr = mdio->addr;
359186623f4SDavid Yang 	u32 reg_addr;
360186623f4SDavid Yang 	u32 reg_data;
361186623f4SDavid Yang 	int res;
362186623f4SDavid Yang 
363186623f4SDavid Yang 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
364186623f4SDavid Yang 
365186623f4SDavid Yang 	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
366186623f4SDavid Yang 		   YT921X_SMI_WRITE;
367186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
368186623f4SDavid Yang 	if (res)
369186623f4SDavid Yang 		goto end;
370186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
371186623f4SDavid Yang 	if (res)
372186623f4SDavid Yang 		goto end;
373186623f4SDavid Yang 
374186623f4SDavid Yang 	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
375186623f4SDavid Yang 		   YT921X_SMI_WRITE;
376186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
377186623f4SDavid Yang 	if (res)
378186623f4SDavid Yang 		goto end;
379186623f4SDavid Yang 	res = __mdiobus_write(bus, addr, reg_data, (u16)val);
380186623f4SDavid Yang 	if (res)
381186623f4SDavid Yang 		goto end;
382186623f4SDavid Yang 
383186623f4SDavid Yang 	res = 0;
384186623f4SDavid Yang 
385186623f4SDavid Yang end:
386186623f4SDavid Yang 	mutex_unlock(&bus->mdio_lock);
387186623f4SDavid Yang 	return res;
388186623f4SDavid Yang }
389186623f4SDavid Yang 
390186623f4SDavid Yang static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
391186623f4SDavid Yang 	.read = yt921x_reg_mdio_read,
392186623f4SDavid Yang 	.write = yt921x_reg_mdio_write,
393186623f4SDavid Yang };
394186623f4SDavid Yang 
395186623f4SDavid Yang /* TODO: SPI/I2C */
396186623f4SDavid Yang 
397186623f4SDavid Yang static int yt921x_intif_wait(struct yt921x_priv *priv)
398186623f4SDavid Yang {
399186623f4SDavid Yang 	u32 val = 0;
400186623f4SDavid Yang 
401186623f4SDavid Yang 	return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
402186623f4SDavid Yang 			       &val);
403186623f4SDavid Yang }
404186623f4SDavid Yang 
405186623f4SDavid Yang static int
406186623f4SDavid Yang yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
407186623f4SDavid Yang {
408186623f4SDavid Yang 	struct device *dev = to_device(priv);
409186623f4SDavid Yang 	u32 mask;
410186623f4SDavid Yang 	u32 ctrl;
411186623f4SDavid Yang 	u32 val;
412186623f4SDavid Yang 	int res;
413186623f4SDavid Yang 
414186623f4SDavid Yang 	res = yt921x_intif_wait(priv);
415186623f4SDavid Yang 	if (res)
416186623f4SDavid Yang 		return res;
417186623f4SDavid Yang 
418186623f4SDavid Yang 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
419186623f4SDavid Yang 	       YT921X_MBUS_CTRL_OP_M;
420186623f4SDavid Yang 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
421186623f4SDavid Yang 	       YT921X_MBUS_CTRL_READ;
422186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
423186623f4SDavid Yang 	if (res)
424186623f4SDavid Yang 		return res;
425186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
426186623f4SDavid Yang 	if (res)
427186623f4SDavid Yang 		return res;
428186623f4SDavid Yang 
429186623f4SDavid Yang 	res = yt921x_intif_wait(priv);
430186623f4SDavid Yang 	if (res)
431186623f4SDavid Yang 		return res;
432186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
433186623f4SDavid Yang 	if (res)
434186623f4SDavid Yang 		return res;
435186623f4SDavid Yang 
436186623f4SDavid Yang 	if ((u16)val != val)
437186623f4SDavid Yang 		dev_info(dev,
438186623f4SDavid Yang 			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
439186623f4SDavid Yang 			 __func__, port, reg, val);
440186623f4SDavid Yang 	*valp = (u16)val;
441186623f4SDavid Yang 	return 0;
442186623f4SDavid Yang }
443186623f4SDavid Yang 
444186623f4SDavid Yang static int
445186623f4SDavid Yang yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
446186623f4SDavid Yang {
447186623f4SDavid Yang 	u32 mask;
448186623f4SDavid Yang 	u32 ctrl;
449186623f4SDavid Yang 	int res;
450186623f4SDavid Yang 
451186623f4SDavid Yang 	res = yt921x_intif_wait(priv);
452186623f4SDavid Yang 	if (res)
453186623f4SDavid Yang 		return res;
454186623f4SDavid Yang 
455186623f4SDavid Yang 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
456186623f4SDavid Yang 	       YT921X_MBUS_CTRL_OP_M;
457186623f4SDavid Yang 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
458186623f4SDavid Yang 	       YT921X_MBUS_CTRL_WRITE;
459186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
460186623f4SDavid Yang 	if (res)
461186623f4SDavid Yang 		return res;
462186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
463186623f4SDavid Yang 	if (res)
464186623f4SDavid Yang 		return res;
465186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
466186623f4SDavid Yang 	if (res)
467186623f4SDavid Yang 		return res;
468186623f4SDavid Yang 
469186623f4SDavid Yang 	return yt921x_intif_wait(priv);
470186623f4SDavid Yang }
471186623f4SDavid Yang 
472186623f4SDavid Yang static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
473186623f4SDavid Yang {
474186623f4SDavid Yang 	struct yt921x_priv *priv = mbus->priv;
475186623f4SDavid Yang 	u16 val;
476186623f4SDavid Yang 	int res;
477186623f4SDavid Yang 
478186623f4SDavid Yang 	if (port >= YT921X_PORT_NUM)
479186623f4SDavid Yang 		return U16_MAX;
480186623f4SDavid Yang 
481186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
482186623f4SDavid Yang 	res = yt921x_intif_read(priv, port, reg, &val);
483186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
484186623f4SDavid Yang 
485186623f4SDavid Yang 	if (res)
486186623f4SDavid Yang 		return res;
487186623f4SDavid Yang 	return val;
488186623f4SDavid Yang }
489186623f4SDavid Yang 
490186623f4SDavid Yang static int
491186623f4SDavid Yang yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
492186623f4SDavid Yang {
493186623f4SDavid Yang 	struct yt921x_priv *priv = mbus->priv;
494186623f4SDavid Yang 	int res;
495186623f4SDavid Yang 
496186623f4SDavid Yang 	if (port >= YT921X_PORT_NUM)
497186623f4SDavid Yang 		return -ENODEV;
498186623f4SDavid Yang 
499186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
500186623f4SDavid Yang 	res = yt921x_intif_write(priv, port, reg, data);
501186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
502186623f4SDavid Yang 
503186623f4SDavid Yang 	return res;
504186623f4SDavid Yang }
505186623f4SDavid Yang 
506186623f4SDavid Yang static int
507186623f4SDavid Yang yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
508186623f4SDavid Yang {
509186623f4SDavid Yang 	struct device *dev = to_device(priv);
510186623f4SDavid Yang 	struct mii_bus *mbus;
511186623f4SDavid Yang 	int res;
512186623f4SDavid Yang 
513186623f4SDavid Yang 	mbus = devm_mdiobus_alloc(dev);
514186623f4SDavid Yang 	if (!mbus)
515186623f4SDavid Yang 		return -ENOMEM;
516186623f4SDavid Yang 
517186623f4SDavid Yang 	mbus->name = "YT921x internal MDIO bus";
518186623f4SDavid Yang 	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
519186623f4SDavid Yang 	mbus->priv = priv;
520186623f4SDavid Yang 	mbus->read = yt921x_mbus_int_read;
521186623f4SDavid Yang 	mbus->write = yt921x_mbus_int_write;
522186623f4SDavid Yang 	mbus->parent = dev;
523186623f4SDavid Yang 	mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
524186623f4SDavid Yang 
525186623f4SDavid Yang 	res = devm_of_mdiobus_register(dev, mbus, mnp);
526186623f4SDavid Yang 	if (res)
527186623f4SDavid Yang 		return res;
528186623f4SDavid Yang 
529186623f4SDavid Yang 	priv->mbus_int = mbus;
530186623f4SDavid Yang 
531186623f4SDavid Yang 	return 0;
532186623f4SDavid Yang }
533186623f4SDavid Yang 
534186623f4SDavid Yang static int yt921x_extif_wait(struct yt921x_priv *priv)
535186623f4SDavid Yang {
536186623f4SDavid Yang 	u32 val = 0;
537186623f4SDavid Yang 
538186623f4SDavid Yang 	return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
539186623f4SDavid Yang 			       &val);
540186623f4SDavid Yang }
541186623f4SDavid Yang 
542186623f4SDavid Yang static int
543186623f4SDavid Yang yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
544186623f4SDavid Yang {
545186623f4SDavid Yang 	struct device *dev = to_device(priv);
546186623f4SDavid Yang 	u32 mask;
547186623f4SDavid Yang 	u32 ctrl;
548186623f4SDavid Yang 	u32 val;
549186623f4SDavid Yang 	int res;
550186623f4SDavid Yang 
551186623f4SDavid Yang 	res = yt921x_extif_wait(priv);
552186623f4SDavid Yang 	if (res)
553186623f4SDavid Yang 		return res;
554186623f4SDavid Yang 
555186623f4SDavid Yang 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
556186623f4SDavid Yang 	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
557186623f4SDavid Yang 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
558186623f4SDavid Yang 	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
559186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
560186623f4SDavid Yang 	if (res)
561186623f4SDavid Yang 		return res;
562186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
563186623f4SDavid Yang 	if (res)
564186623f4SDavid Yang 		return res;
565186623f4SDavid Yang 
566186623f4SDavid Yang 	res = yt921x_extif_wait(priv);
567186623f4SDavid Yang 	if (res)
568186623f4SDavid Yang 		return res;
569186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
570186623f4SDavid Yang 	if (res)
571186623f4SDavid Yang 		return res;
572186623f4SDavid Yang 
573186623f4SDavid Yang 	if ((u16)val != val)
574186623f4SDavid Yang 		dev_info(dev,
575186623f4SDavid Yang 			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
576186623f4SDavid Yang 			 __func__, port, reg, val);
577186623f4SDavid Yang 	*valp = (u16)val;
578186623f4SDavid Yang 	return 0;
579186623f4SDavid Yang }
580186623f4SDavid Yang 
581186623f4SDavid Yang static int
582186623f4SDavid Yang yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
583186623f4SDavid Yang {
584186623f4SDavid Yang 	u32 mask;
585186623f4SDavid Yang 	u32 ctrl;
586186623f4SDavid Yang 	int res;
587186623f4SDavid Yang 
588186623f4SDavid Yang 	res = yt921x_extif_wait(priv);
589186623f4SDavid Yang 	if (res)
590186623f4SDavid Yang 		return res;
591186623f4SDavid Yang 
592186623f4SDavid Yang 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
593186623f4SDavid Yang 	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
594186623f4SDavid Yang 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
595186623f4SDavid Yang 	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
596186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
597186623f4SDavid Yang 	if (res)
598186623f4SDavid Yang 		return res;
599186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
600186623f4SDavid Yang 	if (res)
601186623f4SDavid Yang 		return res;
602186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
603186623f4SDavid Yang 	if (res)
604186623f4SDavid Yang 		return res;
605186623f4SDavid Yang 
606186623f4SDavid Yang 	return yt921x_extif_wait(priv);
607186623f4SDavid Yang }
608186623f4SDavid Yang 
609186623f4SDavid Yang static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
610186623f4SDavid Yang {
611186623f4SDavid Yang 	struct yt921x_priv *priv = mbus->priv;
612186623f4SDavid Yang 	u16 val;
613186623f4SDavid Yang 	int res;
614186623f4SDavid Yang 
615186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
616186623f4SDavid Yang 	res = yt921x_extif_read(priv, port, reg, &val);
617186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
618186623f4SDavid Yang 
619186623f4SDavid Yang 	if (res)
620186623f4SDavid Yang 		return res;
621186623f4SDavid Yang 	return val;
622186623f4SDavid Yang }
623186623f4SDavid Yang 
624186623f4SDavid Yang static int
625186623f4SDavid Yang yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
626186623f4SDavid Yang {
627186623f4SDavid Yang 	struct yt921x_priv *priv = mbus->priv;
628186623f4SDavid Yang 	int res;
629186623f4SDavid Yang 
630186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
631186623f4SDavid Yang 	res = yt921x_extif_write(priv, port, reg, data);
632186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
633186623f4SDavid Yang 
634186623f4SDavid Yang 	return res;
635186623f4SDavid Yang }
636186623f4SDavid Yang 
637186623f4SDavid Yang static int
638186623f4SDavid Yang yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
639186623f4SDavid Yang {
640186623f4SDavid Yang 	struct device *dev = to_device(priv);
641186623f4SDavid Yang 	struct mii_bus *mbus;
642186623f4SDavid Yang 	int res;
643186623f4SDavid Yang 
644186623f4SDavid Yang 	mbus = devm_mdiobus_alloc(dev);
645186623f4SDavid Yang 	if (!mbus)
646186623f4SDavid Yang 		return -ENOMEM;
647186623f4SDavid Yang 
648186623f4SDavid Yang 	mbus->name = "YT921x external MDIO bus";
649186623f4SDavid Yang 	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
650186623f4SDavid Yang 	mbus->priv = priv;
651186623f4SDavid Yang 	/* TODO: c45? */
652186623f4SDavid Yang 	mbus->read = yt921x_mbus_ext_read;
653186623f4SDavid Yang 	mbus->write = yt921x_mbus_ext_write;
654186623f4SDavid Yang 	mbus->parent = dev;
655186623f4SDavid Yang 
656186623f4SDavid Yang 	res = devm_of_mdiobus_register(dev, mbus, mnp);
657186623f4SDavid Yang 	if (res)
658186623f4SDavid Yang 		return res;
659186623f4SDavid Yang 
660186623f4SDavid Yang 	priv->mbus_ext = mbus;
661186623f4SDavid Yang 
662186623f4SDavid Yang 	return 0;
663186623f4SDavid Yang }
664186623f4SDavid Yang 
665186623f4SDavid Yang /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
666186623f4SDavid Yang static int yt921x_read_mib(struct yt921x_priv *priv, int port)
667186623f4SDavid Yang {
668186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
669186623f4SDavid Yang 	struct device *dev = to_device(priv);
670186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
671186623f4SDavid Yang 	int res = 0;
672186623f4SDavid Yang 
673186623f4SDavid Yang 	/* Reading of yt921x_port::mib is not protected by a lock and it's vain
674186623f4SDavid Yang 	 * to keep its consistency, since we have to read registers one by one
675186623f4SDavid Yang 	 * and there is no way to make a snapshot of MIB stats.
676186623f4SDavid Yang 	 *
677186623f4SDavid Yang 	 * Writing (by this function only) is and should be protected by
678186623f4SDavid Yang 	 * reg_lock.
679186623f4SDavid Yang 	 */
680186623f4SDavid Yang 
681186623f4SDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
682186623f4SDavid Yang 		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
683186623f4SDavid Yang 		u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
684186623f4SDavid Yang 		u64 *valp = &((u64 *)mib)[i];
685186623f4SDavid Yang 		u64 val = *valp;
686186623f4SDavid Yang 		u32 val0;
687186623f4SDavid Yang 		u32 val1;
688186623f4SDavid Yang 
689186623f4SDavid Yang 		res = yt921x_reg_read(priv, reg, &val0);
690186623f4SDavid Yang 		if (res)
691186623f4SDavid Yang 			break;
692186623f4SDavid Yang 
693186623f4SDavid Yang 		if (desc->size <= 1) {
694186623f4SDavid Yang 			if (val < (u32)val)
695186623f4SDavid Yang 				/* overflow */
696186623f4SDavid Yang 				val += (u64)U32_MAX + 1;
697186623f4SDavid Yang 			val &= ~U32_MAX;
698186623f4SDavid Yang 			val |= val0;
699186623f4SDavid Yang 		} else {
700186623f4SDavid Yang 			res = yt921x_reg_read(priv, reg + 4, &val1);
701186623f4SDavid Yang 			if (res)
702186623f4SDavid Yang 				break;
703510026a3SDavid Yang 			val = ((u64)val1 << 32) | val0;
704186623f4SDavid Yang 		}
705186623f4SDavid Yang 
706186623f4SDavid Yang 		WRITE_ONCE(*valp, val);
707186623f4SDavid Yang 	}
708186623f4SDavid Yang 
709186623f4SDavid Yang 	pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
710186623f4SDavid Yang 			mib->rx_128_255byte + mib->rx_256_511byte +
711186623f4SDavid Yang 			mib->rx_512_1023byte + mib->rx_1024_1518byte +
712186623f4SDavid Yang 			mib->rx_jumbo;
713186623f4SDavid Yang 	pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
714186623f4SDavid Yang 			mib->tx_128_255byte + mib->tx_256_511byte +
715186623f4SDavid Yang 			mib->tx_512_1023byte + mib->tx_1024_1518byte +
716186623f4SDavid Yang 			mib->tx_jumbo;
717186623f4SDavid Yang 
718186623f4SDavid Yang 	if (res)
719186623f4SDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
720186623f4SDavid Yang 			port, res);
721186623f4SDavid Yang 	return res;
722186623f4SDavid Yang }
723186623f4SDavid Yang 
724186623f4SDavid Yang static void yt921x_poll_mib(struct work_struct *work)
725186623f4SDavid Yang {
726186623f4SDavid Yang 	struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
727186623f4SDavid Yang 						    mib_read.work);
728186623f4SDavid Yang 	struct yt921x_priv *priv = (void *)(pp - pp->index) -
729186623f4SDavid Yang 				   offsetof(struct yt921x_priv, ports);
730186623f4SDavid Yang 	unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
731186623f4SDavid Yang 	int port = pp->index;
732186623f4SDavid Yang 	int res;
733186623f4SDavid Yang 
734186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
735186623f4SDavid Yang 	res = yt921x_read_mib(priv, port);
736186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
737186623f4SDavid Yang 	if (res)
738186623f4SDavid Yang 		delay *= 4;
739186623f4SDavid Yang 
740186623f4SDavid Yang 	schedule_delayed_work(&pp->mib_read, delay);
741186623f4SDavid Yang }
742186623f4SDavid Yang 
743186623f4SDavid Yang static void
744186623f4SDavid Yang yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
745186623f4SDavid Yang 		       uint8_t *data)
746186623f4SDavid Yang {
747186623f4SDavid Yang 	if (stringset != ETH_SS_STATS)
748186623f4SDavid Yang 		return;
749186623f4SDavid Yang 
750186623f4SDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
751186623f4SDavid Yang 		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
752186623f4SDavid Yang 
753186623f4SDavid Yang 		if (desc->name)
754186623f4SDavid Yang 			ethtool_puts(&data, desc->name);
755186623f4SDavid Yang 	}
756186623f4SDavid Yang }
757186623f4SDavid Yang 
758186623f4SDavid Yang static void
759186623f4SDavid Yang yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
760186623f4SDavid Yang {
761186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
762186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
763186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
764186623f4SDavid Yang 	size_t j;
765186623f4SDavid Yang 
766186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
767186623f4SDavid Yang 	yt921x_read_mib(priv, port);
768186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
769186623f4SDavid Yang 
770186623f4SDavid Yang 	j = 0;
771186623f4SDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
772186623f4SDavid Yang 		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
773186623f4SDavid Yang 
774186623f4SDavid Yang 		if (!desc->name)
775186623f4SDavid Yang 			continue;
776186623f4SDavid Yang 
777186623f4SDavid Yang 		data[j] = ((u64 *)mib)[i];
778186623f4SDavid Yang 		j++;
779186623f4SDavid Yang 	}
780186623f4SDavid Yang }
781186623f4SDavid Yang 
782186623f4SDavid Yang static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
783186623f4SDavid Yang {
784186623f4SDavid Yang 	int cnt = 0;
785186623f4SDavid Yang 
786186623f4SDavid Yang 	if (sset != ETH_SS_STATS)
787186623f4SDavid Yang 		return 0;
788186623f4SDavid Yang 
789186623f4SDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
790186623f4SDavid Yang 		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
791186623f4SDavid Yang 
792186623f4SDavid Yang 		if (desc->name)
793186623f4SDavid Yang 			cnt++;
794186623f4SDavid Yang 	}
795186623f4SDavid Yang 
796186623f4SDavid Yang 	return cnt;
797186623f4SDavid Yang }
798186623f4SDavid Yang 
799186623f4SDavid Yang static void
800186623f4SDavid Yang yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
801186623f4SDavid Yang 			     struct ethtool_eth_mac_stats *mac_stats)
802186623f4SDavid Yang {
803186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
804186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
805186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
806186623f4SDavid Yang 
807186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
808186623f4SDavid Yang 	yt921x_read_mib(priv, port);
809186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
810186623f4SDavid Yang 
811186623f4SDavid Yang 	mac_stats->FramesTransmittedOK = pp->tx_frames;
812186623f4SDavid Yang 	mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
813186623f4SDavid Yang 	mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
814186623f4SDavid Yang 	mac_stats->FramesReceivedOK = pp->rx_frames;
815186623f4SDavid Yang 	mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
816186623f4SDavid Yang 	mac_stats->AlignmentErrors = mib->rx_alignment_errors;
817186623f4SDavid Yang 	mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
818186623f4SDavid Yang 	mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
819186623f4SDavid Yang 	mac_stats->LateCollisions = mib->tx_late_collisions;
820186623f4SDavid Yang 	mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
821186623f4SDavid Yang 	/* mac_stats->FramesLostDueToIntMACXmitError */
822186623f4SDavid Yang 	/* mac_stats->CarrierSenseErrors */
823186623f4SDavid Yang 	mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
824186623f4SDavid Yang 	/* mac_stats->FramesLostDueToIntMACRcvError */
825186623f4SDavid Yang 	mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
826186623f4SDavid Yang 	mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
827186623f4SDavid Yang 	/* mac_stats->FramesWithExcessiveDeferral */
828186623f4SDavid Yang 	mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
829186623f4SDavid Yang 	mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
830186623f4SDavid Yang 	/* mac_stats->InRangeLengthErrors */
831186623f4SDavid Yang 	/* mac_stats->OutOfRangeLengthField */
832186623f4SDavid Yang 	mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
833186623f4SDavid Yang }
834186623f4SDavid Yang 
835186623f4SDavid Yang static void
836186623f4SDavid Yang yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
837186623f4SDavid Yang 			      struct ethtool_eth_ctrl_stats *ctrl_stats)
838186623f4SDavid Yang {
839186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
840186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
841186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
842186623f4SDavid Yang 
843186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
844186623f4SDavid Yang 	yt921x_read_mib(priv, port);
845186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
846186623f4SDavid Yang 
847186623f4SDavid Yang 	ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
848186623f4SDavid Yang 	ctrl_stats->MACControlFramesReceived = mib->rx_pause;
849186623f4SDavid Yang 	/* ctrl_stats->UnsupportedOpcodesReceived */
850186623f4SDavid Yang }
851186623f4SDavid Yang 
852186623f4SDavid Yang static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
853186623f4SDavid Yang 	{ 0, 64 },
854186623f4SDavid Yang 	{ 65, 127 },
855186623f4SDavid Yang 	{ 128, 255 },
856186623f4SDavid Yang 	{ 256, 511 },
857186623f4SDavid Yang 	{ 512, 1023 },
858186623f4SDavid Yang 	{ 1024, 1518 },
859186623f4SDavid Yang 	{ 1519, YT921X_FRAME_SIZE_MAX },
860186623f4SDavid Yang 	{}
861186623f4SDavid Yang };
862186623f4SDavid Yang 
863186623f4SDavid Yang static void
864186623f4SDavid Yang yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
865186623f4SDavid Yang 			  struct ethtool_rmon_stats *rmon_stats,
866186623f4SDavid Yang 			  const struct ethtool_rmon_hist_range **ranges)
867186623f4SDavid Yang {
868186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
869186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
870186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
871186623f4SDavid Yang 
872186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
873186623f4SDavid Yang 	yt921x_read_mib(priv, port);
874186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
875186623f4SDavid Yang 
876186623f4SDavid Yang 	*ranges = yt921x_rmon_ranges;
877186623f4SDavid Yang 
878186623f4SDavid Yang 	rmon_stats->undersize_pkts = mib->rx_undersize_errors;
879186623f4SDavid Yang 	rmon_stats->oversize_pkts = mib->rx_oversize_errors;
880186623f4SDavid Yang 	rmon_stats->fragments = mib->rx_alignment_errors;
881186623f4SDavid Yang 	/* rmon_stats->jabbers */
882186623f4SDavid Yang 
883186623f4SDavid Yang 	rmon_stats->hist[0] = mib->rx_64byte;
884186623f4SDavid Yang 	rmon_stats->hist[1] = mib->rx_65_127byte;
885186623f4SDavid Yang 	rmon_stats->hist[2] = mib->rx_128_255byte;
886186623f4SDavid Yang 	rmon_stats->hist[3] = mib->rx_256_511byte;
887186623f4SDavid Yang 	rmon_stats->hist[4] = mib->rx_512_1023byte;
888186623f4SDavid Yang 	rmon_stats->hist[5] = mib->rx_1024_1518byte;
889186623f4SDavid Yang 	rmon_stats->hist[6] = mib->rx_jumbo;
890186623f4SDavid Yang 
891186623f4SDavid Yang 	rmon_stats->hist_tx[0] = mib->tx_64byte;
892186623f4SDavid Yang 	rmon_stats->hist_tx[1] = mib->tx_65_127byte;
893186623f4SDavid Yang 	rmon_stats->hist_tx[2] = mib->tx_128_255byte;
894186623f4SDavid Yang 	rmon_stats->hist_tx[3] = mib->tx_256_511byte;
895186623f4SDavid Yang 	rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
896186623f4SDavid Yang 	rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
897186623f4SDavid Yang 	rmon_stats->hist_tx[6] = mib->tx_jumbo;
898186623f4SDavid Yang }
899186623f4SDavid Yang 
900186623f4SDavid Yang static void
901186623f4SDavid Yang yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
902186623f4SDavid Yang 		       struct rtnl_link_stats64 *stats)
903186623f4SDavid Yang {
904186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
905186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
906186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
907186623f4SDavid Yang 
908186623f4SDavid Yang 	stats->rx_length_errors = mib->rx_undersize_errors +
909186623f4SDavid Yang 				  mib->rx_fragment_errors;
910186623f4SDavid Yang 	stats->rx_over_errors = mib->rx_oversize_errors;
911186623f4SDavid Yang 	stats->rx_crc_errors = mib->rx_crc_errors;
912186623f4SDavid Yang 	stats->rx_frame_errors = mib->rx_alignment_errors;
913186623f4SDavid Yang 	/* stats->rx_fifo_errors */
914186623f4SDavid Yang 	/* stats->rx_missed_errors */
915186623f4SDavid Yang 
916186623f4SDavid Yang 	stats->tx_aborted_errors = mib->tx_aborted_errors;
917186623f4SDavid Yang 	/* stats->tx_carrier_errors */
918186623f4SDavid Yang 	stats->tx_fifo_errors = mib->tx_undersize_errors;
919186623f4SDavid Yang 	/* stats->tx_heartbeat_errors */
920186623f4SDavid Yang 	stats->tx_window_errors = mib->tx_late_collisions;
921186623f4SDavid Yang 
922186623f4SDavid Yang 	stats->rx_packets = pp->rx_frames;
923186623f4SDavid Yang 	stats->tx_packets = pp->tx_frames;
924186623f4SDavid Yang 	stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
925186623f4SDavid Yang 	stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
926186623f4SDavid Yang 	stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
927186623f4SDavid Yang 			   stats->rx_crc_errors + stats->rx_frame_errors;
928186623f4SDavid Yang 	stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
929186623f4SDavid Yang 			   stats->tx_window_errors;
930186623f4SDavid Yang 	stats->rx_dropped = mib->rx_dropped;
931186623f4SDavid Yang 	/* stats->tx_dropped */
932186623f4SDavid Yang 	stats->multicast = mib->rx_multicast;
933186623f4SDavid Yang 	stats->collisions = mib->tx_collisions;
934186623f4SDavid Yang }
935186623f4SDavid Yang 
936186623f4SDavid Yang static void
937186623f4SDavid Yang yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
938186623f4SDavid Yang 			   struct ethtool_pause_stats *pause_stats)
939186623f4SDavid Yang {
940186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
941186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
942186623f4SDavid Yang 	struct yt921x_mib *mib = &pp->mib;
943186623f4SDavid Yang 
944186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
945186623f4SDavid Yang 	yt921x_read_mib(priv, port);
946186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
947186623f4SDavid Yang 
948186623f4SDavid Yang 	pause_stats->tx_pause_frames = mib->tx_pause;
949186623f4SDavid Yang 	pause_stats->rx_pause_frames = mib->rx_pause;
950186623f4SDavid Yang }
951186623f4SDavid Yang 
952186623f4SDavid Yang static int
953186623f4SDavid Yang yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
954186623f4SDavid Yang {
955186623f4SDavid Yang 	/* Poor datasheet for EEE operations; don't ask if you are confused */
956186623f4SDavid Yang 
957186623f4SDavid Yang 	bool enable = e->eee_enabled;
958186623f4SDavid Yang 	u16 new_mask;
959186623f4SDavid Yang 	int res;
960186623f4SDavid Yang 
961186623f4SDavid Yang 	/* Enable / disable global EEE */
962186623f4SDavid Yang 	new_mask = priv->eee_ports_mask;
963186623f4SDavid Yang 	new_mask &= ~BIT(port);
964186623f4SDavid Yang 	new_mask |= !enable ? 0 : BIT(port);
965186623f4SDavid Yang 
966186623f4SDavid Yang 	if (!!new_mask != !!priv->eee_ports_mask) {
967186623f4SDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
968186623f4SDavid Yang 					     YT921X_PON_STRAP_EEE, !!new_mask);
969186623f4SDavid Yang 		if (res)
970186623f4SDavid Yang 			return res;
971186623f4SDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
972186623f4SDavid Yang 					     YT921X_PON_STRAP_EEE, !!new_mask);
973186623f4SDavid Yang 		if (res)
974186623f4SDavid Yang 			return res;
975186623f4SDavid Yang 	}
976186623f4SDavid Yang 
977186623f4SDavid Yang 	priv->eee_ports_mask = new_mask;
978186623f4SDavid Yang 
979186623f4SDavid Yang 	/* Enable / disable port EEE */
980186623f4SDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
981186623f4SDavid Yang 				     YT921X_EEE_CTRL_ENn(port), enable);
982186623f4SDavid Yang 	if (res)
983186623f4SDavid Yang 		return res;
984186623f4SDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
985186623f4SDavid Yang 				     YT921X_EEE_VAL_DATA, enable);
986186623f4SDavid Yang 	if (res)
987186623f4SDavid Yang 		return res;
988186623f4SDavid Yang 
989186623f4SDavid Yang 	return 0;
990186623f4SDavid Yang }
991186623f4SDavid Yang 
992186623f4SDavid Yang static int
993186623f4SDavid Yang yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
994186623f4SDavid Yang {
995186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
996186623f4SDavid Yang 	int res;
997186623f4SDavid Yang 
998186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
999186623f4SDavid Yang 	res = yt921x_set_eee(priv, port, e);
1000186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1001186623f4SDavid Yang 
1002186623f4SDavid Yang 	return res;
1003186623f4SDavid Yang }
1004186623f4SDavid Yang 
1005186623f4SDavid Yang static int
1006186623f4SDavid Yang yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
1007186623f4SDavid Yang {
1008186623f4SDavid Yang 	/* Only serves as packet filter, since the frame size is always set to
1009186623f4SDavid Yang 	 * maximum after reset
1010186623f4SDavid Yang 	 */
1011186623f4SDavid Yang 
1012186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1013186623f4SDavid Yang 	struct dsa_port *dp = dsa_to_port(ds, port);
1014186623f4SDavid Yang 	int frame_size;
1015186623f4SDavid Yang 	int res;
1016186623f4SDavid Yang 
1017186623f4SDavid Yang 	frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
1018186623f4SDavid Yang 	if (dsa_port_is_cpu(dp))
1019186623f4SDavid Yang 		frame_size += YT921X_TAG_LEN;
1020186623f4SDavid Yang 
1021186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1022186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
1023186623f4SDavid Yang 				     YT921X_MAC_FRAME_SIZE_M,
1024186623f4SDavid Yang 				     YT921X_MAC_FRAME_SIZE(frame_size));
1025186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1026186623f4SDavid Yang 
1027186623f4SDavid Yang 	return res;
1028186623f4SDavid Yang }
1029186623f4SDavid Yang 
1030186623f4SDavid Yang static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
1031186623f4SDavid Yang {
1032186623f4SDavid Yang 	/* Only called for user ports, exclude tag len here */
1033186623f4SDavid Yang 	return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
1034186623f4SDavid Yang }
1035186623f4SDavid Yang 
1036186623f4SDavid Yang static int
1037186623f4SDavid Yang yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
1038186623f4SDavid Yang {
1039186623f4SDavid Yang 	u32 mask;
1040186623f4SDavid Yang 
1041186623f4SDavid Yang 	if (ingress)
1042186623f4SDavid Yang 		mask = YT921X_MIRROR_IGR_PORTn(port);
1043186623f4SDavid Yang 	else
1044186623f4SDavid Yang 		mask = YT921X_MIRROR_EGR_PORTn(port);
1045186623f4SDavid Yang 	return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
1046186623f4SDavid Yang }
1047186623f4SDavid Yang 
1048186623f4SDavid Yang static int
1049186623f4SDavid Yang yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
1050186623f4SDavid Yang 		  int to_local_port, struct netlink_ext_ack *extack)
1051186623f4SDavid Yang {
1052186623f4SDavid Yang 	u32 srcs;
1053186623f4SDavid Yang 	u32 ctrl;
1054186623f4SDavid Yang 	u32 val;
1055186623f4SDavid Yang 	u32 dst;
1056186623f4SDavid Yang 	int res;
1057186623f4SDavid Yang 
1058186623f4SDavid Yang 	if (ingress)
1059186623f4SDavid Yang 		srcs = YT921X_MIRROR_IGR_PORTn(port);
1060186623f4SDavid Yang 	else
1061186623f4SDavid Yang 		srcs = YT921X_MIRROR_EGR_PORTn(port);
1062186623f4SDavid Yang 	dst = YT921X_MIRROR_PORT(to_local_port);
1063186623f4SDavid Yang 
1064186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
1065186623f4SDavid Yang 	if (res)
1066186623f4SDavid Yang 		return res;
1067186623f4SDavid Yang 
1068186623f4SDavid Yang 	/* other mirror tasks & different dst port -> conflict */
1069186623f4SDavid Yang 	if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
1070186623f4SDavid Yang 			    YT921X_MIRROR_IGR_PORTS_M)) &&
1071186623f4SDavid Yang 	    (val & YT921X_MIRROR_PORT_M) != dst) {
1072186623f4SDavid Yang 		NL_SET_ERR_MSG_MOD(extack,
1073186623f4SDavid Yang 				   "Sniffer port is already configured, delete existing rules & retry");
1074186623f4SDavid Yang 		return -EBUSY;
1075186623f4SDavid Yang 	}
1076186623f4SDavid Yang 
1077186623f4SDavid Yang 	ctrl = val & ~YT921X_MIRROR_PORT_M;
1078186623f4SDavid Yang 	ctrl |= srcs;
1079186623f4SDavid Yang 	ctrl |= dst;
1080186623f4SDavid Yang 
1081186623f4SDavid Yang 	if (ctrl == val)
1082186623f4SDavid Yang 		return 0;
1083186623f4SDavid Yang 
1084186623f4SDavid Yang 	return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
1085186623f4SDavid Yang }
1086186623f4SDavid Yang 
1087186623f4SDavid Yang static void
1088186623f4SDavid Yang yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
1089186623f4SDavid Yang 			   struct dsa_mall_mirror_tc_entry *mirror)
1090186623f4SDavid Yang {
1091186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1092186623f4SDavid Yang 	struct device *dev = to_device(priv);
1093186623f4SDavid Yang 	int res;
1094186623f4SDavid Yang 
1095186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1096186623f4SDavid Yang 	res = yt921x_mirror_del(priv, port, mirror->ingress);
1097186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1098186623f4SDavid Yang 
1099186623f4SDavid Yang 	if (res)
1100186623f4SDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
1101186623f4SDavid Yang 			port, res);
1102186623f4SDavid Yang }
1103186623f4SDavid Yang 
1104186623f4SDavid Yang static int
1105186623f4SDavid Yang yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
1106186623f4SDavid Yang 			   struct dsa_mall_mirror_tc_entry *mirror,
1107186623f4SDavid Yang 			   bool ingress, struct netlink_ext_ack *extack)
1108186623f4SDavid Yang {
1109186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1110186623f4SDavid Yang 	int res;
1111186623f4SDavid Yang 
1112186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1113186623f4SDavid Yang 	res = yt921x_mirror_add(priv, port, ingress,
1114186623f4SDavid Yang 				mirror->to_local_port, extack);
1115186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1116186623f4SDavid Yang 
1117186623f4SDavid Yang 	return res;
1118186623f4SDavid Yang }
1119186623f4SDavid Yang 
1120186623f4SDavid Yang static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
1121186623f4SDavid Yang {
1122186623f4SDavid Yang 	struct device *dev = to_device(priv);
1123186623f4SDavid Yang 	u32 val = YT921X_FDB_RESULT_DONE;
1124186623f4SDavid Yang 	int res;
1125186623f4SDavid Yang 
1126186623f4SDavid Yang 	res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE,
1127186623f4SDavid Yang 			      &val);
1128186623f4SDavid Yang 	if (res) {
112922795871SColin Ian King 		dev_err(dev, "FDB probably stuck\n");
1130186623f4SDavid Yang 		return res;
1131186623f4SDavid Yang 	}
1132186623f4SDavid Yang 
1133186623f4SDavid Yang 	*valp = val;
1134186623f4SDavid Yang 	return 0;
1135186623f4SDavid Yang }
1136186623f4SDavid Yang 
1137186623f4SDavid Yang static int
1138186623f4SDavid Yang yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr,
1139186623f4SDavid Yang 		u16 vid, u32 ctrl1)
1140186623f4SDavid Yang {
1141186623f4SDavid Yang 	u32 ctrl;
1142186623f4SDavid Yang 	int res;
1143186623f4SDavid Yang 
1144186623f4SDavid Yang 	ctrl = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
1145186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl);
1146186623f4SDavid Yang 	if (res)
1147186623f4SDavid Yang 		return res;
1148186623f4SDavid Yang 
1149186623f4SDavid Yang 	ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5];
1150186623f4SDavid Yang 	return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
1151186623f4SDavid Yang }
1152186623f4SDavid Yang 
1153186623f4SDavid Yang static int
1154186623f4SDavid Yang yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
1155186623f4SDavid Yang 	       u16 *indexp)
1156186623f4SDavid Yang {
1157186623f4SDavid Yang 	u32 ctrl;
1158186623f4SDavid Yang 	u32 val;
1159186623f4SDavid Yang 	int res;
1160186623f4SDavid Yang 
1161186623f4SDavid Yang 	res = yt921x_fdb_in01(priv, addr, vid, 0);
1162186623f4SDavid Yang 	if (res)
1163186623f4SDavid Yang 		return res;
1164186623f4SDavid Yang 
1165186623f4SDavid Yang 	ctrl = 0;
1166186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
1167186623f4SDavid Yang 	if (res)
1168186623f4SDavid Yang 		return res;
1169186623f4SDavid Yang 
1170186623f4SDavid Yang 	ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
1171186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1172186623f4SDavid Yang 	if (res)
1173186623f4SDavid Yang 		return res;
1174186623f4SDavid Yang 
1175186623f4SDavid Yang 	res = yt921x_fdb_wait(priv, &val);
1176186623f4SDavid Yang 	if (res)
1177186623f4SDavid Yang 		return res;
1178186623f4SDavid Yang 	if (val & YT921X_FDB_RESULT_NOTFOUND) {
1179186623f4SDavid Yang 		*indexp = YT921X_FDB_NUM;
1180186623f4SDavid Yang 		return 0;
1181186623f4SDavid Yang 	}
1182186623f4SDavid Yang 
1183186623f4SDavid Yang 	*indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
1184186623f4SDavid Yang 	return 0;
1185186623f4SDavid Yang }
1186186623f4SDavid Yang 
1187186623f4SDavid Yang static int
1188186623f4SDavid Yang yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp,
1189186623f4SDavid Yang 		u16 *ports_maskp, u16 *indexp, u8 *statusp)
1190186623f4SDavid Yang {
1191186623f4SDavid Yang 	struct device *dev = to_device(priv);
1192186623f4SDavid Yang 	u16 index;
1193186623f4SDavid Yang 	u32 data0;
1194186623f4SDavid Yang 	u32 data1;
1195186623f4SDavid Yang 	u32 data2;
1196186623f4SDavid Yang 	u32 val;
1197186623f4SDavid Yang 	int res;
1198186623f4SDavid Yang 
1199186623f4SDavid Yang 	res = yt921x_fdb_wait(priv, &val);
1200186623f4SDavid Yang 	if (res)
1201186623f4SDavid Yang 		return res;
1202186623f4SDavid Yang 	if (val & YT921X_FDB_RESULT_NOTFOUND) {
1203186623f4SDavid Yang 		*ports_maskp = 0;
1204186623f4SDavid Yang 		return 0;
1205186623f4SDavid Yang 	}
1206186623f4SDavid Yang 	index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
1207186623f4SDavid Yang 
1208186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1);
1209186623f4SDavid Yang 	if (res)
1210186623f4SDavid Yang 		return res;
1211186623f4SDavid Yang 	if ((data1 & YT921X_FDB_IO1_STATUS_M) ==
1212186623f4SDavid Yang 	    YT921X_FDB_IO1_STATUS_INVALID) {
1213186623f4SDavid Yang 		*ports_maskp = 0;
1214186623f4SDavid Yang 		return 0;
1215186623f4SDavid Yang 	}
1216186623f4SDavid Yang 
1217186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0);
1218186623f4SDavid Yang 	if (res)
1219186623f4SDavid Yang 		return res;
1220186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2);
1221186623f4SDavid Yang 	if (res)
1222186623f4SDavid Yang 		return res;
1223186623f4SDavid Yang 
1224186623f4SDavid Yang 	addr[0] = data0 >> 24;
1225186623f4SDavid Yang 	addr[1] = data0 >> 16;
1226186623f4SDavid Yang 	addr[2] = data0 >> 8;
1227186623f4SDavid Yang 	addr[3] = data0;
1228186623f4SDavid Yang 	addr[4] = data1 >> 8;
1229186623f4SDavid Yang 	addr[5] = data1;
1230186623f4SDavid Yang 	*vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1);
1231186623f4SDavid Yang 	*indexp = index;
1232186623f4SDavid Yang 	*ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2);
1233186623f4SDavid Yang 	*statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1);
1234186623f4SDavid Yang 
1235186623f4SDavid Yang 	dev_dbg(dev,
1236186623f4SDavid Yang 		"%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n",
1237186623f4SDavid Yang 		__func__, *indexp, addr[0], addr[1], addr[2], addr[3],
1238186623f4SDavid Yang 		addr[4], addr[5], *vidp, *ports_maskp, *statusp);
1239186623f4SDavid Yang 	return 0;
1240186623f4SDavid Yang }
1241186623f4SDavid Yang 
1242186623f4SDavid Yang static int
1243186623f4SDavid Yang yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
1244186623f4SDavid Yang 		dsa_fdb_dump_cb_t *cb, void *data)
1245186623f4SDavid Yang {
1246186623f4SDavid Yang 	unsigned char addr[ETH_ALEN];
1247186623f4SDavid Yang 	u8 status;
1248186623f4SDavid Yang 	u16 pmask;
1249186623f4SDavid Yang 	u16 index;
1250186623f4SDavid Yang 	u32 ctrl;
1251186623f4SDavid Yang 	u16 vid;
1252186623f4SDavid Yang 	int res;
1253186623f4SDavid Yang 
1254186623f4SDavid Yang 	ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
1255186623f4SDavid Yang 	       YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
1256186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1257186623f4SDavid Yang 	if (res)
1258186623f4SDavid Yang 		return res;
1259186623f4SDavid Yang 	res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status);
1260186623f4SDavid Yang 	if (res)
1261186623f4SDavid Yang 		return res;
1262186623f4SDavid Yang 	if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
1263186623f4SDavid Yang 		res = cb(addr, vid,
1264186623f4SDavid Yang 			 status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
1265186623f4SDavid Yang 		if (res)
1266186623f4SDavid Yang 			return res;
1267186623f4SDavid Yang 	}
1268186623f4SDavid Yang 
1269186623f4SDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
1270186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
1271186623f4SDavid Yang 	if (res)
1272186623f4SDavid Yang 		return res;
1273186623f4SDavid Yang 
1274186623f4SDavid Yang 	index = 0;
1275186623f4SDavid Yang 	do {
1276186623f4SDavid Yang 		ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
1277186623f4SDavid Yang 		       YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT |
1278186623f4SDavid Yang 		       YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
1279186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1280186623f4SDavid Yang 		if (res)
1281186623f4SDavid Yang 			return res;
1282186623f4SDavid Yang 
1283186623f4SDavid Yang 		res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index,
1284186623f4SDavid Yang 				      &status);
1285186623f4SDavid Yang 		if (res)
1286186623f4SDavid Yang 			return res;
1287186623f4SDavid Yang 		if (!pmask)
1288186623f4SDavid Yang 			break;
1289186623f4SDavid Yang 
1290186623f4SDavid Yang 		if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
1291186623f4SDavid Yang 			res = cb(addr, vid,
1292186623f4SDavid Yang 				 status == YT921X_FDB_ENTRY_STATUS_STATIC,
1293186623f4SDavid Yang 				 data);
1294186623f4SDavid Yang 			if (res)
1295186623f4SDavid Yang 				return res;
1296186623f4SDavid Yang 		}
1297186623f4SDavid Yang 
1298186623f4SDavid Yang 		/* Never call GET_NEXT with 4095, otherwise it will hang
1299186623f4SDavid Yang 		 * forever until a reset!
1300186623f4SDavid Yang 		 */
1301186623f4SDavid Yang 	} while (index < YT921X_FDB_NUM - 1);
1302186623f4SDavid Yang 
1303186623f4SDavid Yang 	return 0;
1304186623f4SDavid Yang }
1305186623f4SDavid Yang 
1306186623f4SDavid Yang static int
1307186623f4SDavid Yang yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid,
1308186623f4SDavid Yang 		     bool flush_static)
1309186623f4SDavid Yang {
1310186623f4SDavid Yang 	u32 ctrl;
1311186623f4SDavid Yang 	u32 val;
1312186623f4SDavid Yang 	int res;
1313186623f4SDavid Yang 
1314186623f4SDavid Yang 	if (vid < 4096) {
1315186623f4SDavid Yang 		ctrl = YT921X_FDB_IO1_FID(vid);
1316186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
1317186623f4SDavid Yang 		if (res)
1318186623f4SDavid Yang 			return res;
1319186623f4SDavid Yang 	}
1320186623f4SDavid Yang 
1321186623f4SDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
1322186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
1323186623f4SDavid Yang 	if (res)
1324186623f4SDavid Yang 		return res;
1325186623f4SDavid Yang 
1326186623f4SDavid Yang 	ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START;
1327186623f4SDavid Yang 	if (vid >= 4096)
1328186623f4SDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_PORT;
1329186623f4SDavid Yang 	else
1330186623f4SDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID;
1331186623f4SDavid Yang 	if (flush_static)
1332186623f4SDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_STATIC;
1333186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1334186623f4SDavid Yang 	if (res)
1335186623f4SDavid Yang 		return res;
1336186623f4SDavid Yang 
1337186623f4SDavid Yang 	res = yt921x_fdb_wait(priv, &val);
1338186623f4SDavid Yang 	if (res)
1339186623f4SDavid Yang 		return res;
1340186623f4SDavid Yang 
1341186623f4SDavid Yang 	return 0;
1342186623f4SDavid Yang }
1343186623f4SDavid Yang 
1344186623f4SDavid Yang static int
1345186623f4SDavid Yang yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static)
1346186623f4SDavid Yang {
1347186623f4SDavid Yang 	return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static);
1348186623f4SDavid Yang }
1349186623f4SDavid Yang 
1350186623f4SDavid Yang static int
1351186623f4SDavid Yang yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1,
1352186623f4SDavid Yang 			  u16 ctrl2)
1353186623f4SDavid Yang {
1354186623f4SDavid Yang 	u32 ctrl;
1355186623f4SDavid Yang 	u32 val;
1356186623f4SDavid Yang 	int res;
1357186623f4SDavid Yang 
1358186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1);
1359186623f4SDavid Yang 	if (res)
1360186623f4SDavid Yang 		return res;
1361186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2);
1362186623f4SDavid Yang 	if (res)
1363186623f4SDavid Yang 		return res;
1364186623f4SDavid Yang 
1365186623f4SDavid Yang 	ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
1366186623f4SDavid Yang 	       YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
1367186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1368186623f4SDavid Yang 	if (res)
1369186623f4SDavid Yang 		return res;
1370186623f4SDavid Yang 
1371186623f4SDavid Yang 	return yt921x_fdb_wait(priv, &val);
1372186623f4SDavid Yang }
1373186623f4SDavid Yang 
1374186623f4SDavid Yang static int
1375186623f4SDavid Yang yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
1376186623f4SDavid Yang 	       u16 ports_mask)
1377186623f4SDavid Yang {
1378186623f4SDavid Yang 	u32 ctrl;
1379186623f4SDavid Yang 	u32 val;
1380186623f4SDavid Yang 	int res;
1381186623f4SDavid Yang 
1382186623f4SDavid Yang 	ctrl = YT921X_FDB_IO1_STATUS_STATIC;
1383186623f4SDavid Yang 	res = yt921x_fdb_in01(priv, addr, vid, ctrl);
1384186623f4SDavid Yang 	if (res)
1385186623f4SDavid Yang 		return res;
1386186623f4SDavid Yang 
1387186623f4SDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
1388186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
1389186623f4SDavid Yang 	if (res)
1390186623f4SDavid Yang 		return res;
1391186623f4SDavid Yang 
1392186623f4SDavid Yang 	ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
1393186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1394186623f4SDavid Yang 	if (res)
1395186623f4SDavid Yang 		return res;
1396186623f4SDavid Yang 
1397186623f4SDavid Yang 	return yt921x_fdb_wait(priv, &val);
1398186623f4SDavid Yang }
1399186623f4SDavid Yang 
1400186623f4SDavid Yang static int
1401186623f4SDavid Yang yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
1402186623f4SDavid Yang 		 u16 vid, u16 ports_mask)
1403186623f4SDavid Yang {
1404186623f4SDavid Yang 	u16 index;
1405186623f4SDavid Yang 	u32 ctrl1;
1406186623f4SDavid Yang 	u32 ctrl2;
1407186623f4SDavid Yang 	u32 ctrl;
1408186623f4SDavid Yang 	u32 val2;
1409186623f4SDavid Yang 	u32 val;
1410186623f4SDavid Yang 	int res;
1411186623f4SDavid Yang 
1412186623f4SDavid Yang 	/* Check for presence */
1413186623f4SDavid Yang 	res = yt921x_fdb_has(priv, addr, vid, &index);
1414186623f4SDavid Yang 	if (res)
1415186623f4SDavid Yang 		return res;
1416186623f4SDavid Yang 	if (index >= YT921X_FDB_NUM)
1417186623f4SDavid Yang 		return 0;
1418186623f4SDavid Yang 
1419186623f4SDavid Yang 	/* Check if action required */
1420186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
1421186623f4SDavid Yang 	if (res)
1422186623f4SDavid Yang 		return res;
1423186623f4SDavid Yang 
1424186623f4SDavid Yang 	ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
1425186623f4SDavid Yang 	if (ctrl2 == val2)
1426186623f4SDavid Yang 		return 0;
1427186623f4SDavid Yang 	if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
1428186623f4SDavid Yang 		ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
1429186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
1430186623f4SDavid Yang 		if (res)
1431186623f4SDavid Yang 			return res;
1432186623f4SDavid Yang 
1433186623f4SDavid Yang 		return yt921x_fdb_wait(priv, &val);
1434186623f4SDavid Yang 	}
1435186623f4SDavid Yang 
1436186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
1437186623f4SDavid Yang 	if (res)
1438186623f4SDavid Yang 		return res;
1439186623f4SDavid Yang 
1440186623f4SDavid Yang 	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
1441186623f4SDavid Yang }
1442186623f4SDavid Yang 
1443186623f4SDavid Yang static int
1444186623f4SDavid Yang yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
1445186623f4SDavid Yang 		u16 ports_mask)
1446186623f4SDavid Yang {
1447186623f4SDavid Yang 	u16 index;
1448186623f4SDavid Yang 	u32 ctrl1;
1449186623f4SDavid Yang 	u32 ctrl2;
1450186623f4SDavid Yang 	u32 val1;
1451186623f4SDavid Yang 	u32 val2;
1452186623f4SDavid Yang 	int res;
1453186623f4SDavid Yang 
1454186623f4SDavid Yang 	/* Check for presence */
1455186623f4SDavid Yang 	res = yt921x_fdb_has(priv, addr, vid, &index);
1456186623f4SDavid Yang 	if (res)
1457186623f4SDavid Yang 		return res;
1458186623f4SDavid Yang 	if (index >= YT921X_FDB_NUM)
1459186623f4SDavid Yang 		return yt921x_fdb_add(priv, addr, vid, ports_mask);
1460186623f4SDavid Yang 
1461186623f4SDavid Yang 	/* Check if action required */
1462186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
1463186623f4SDavid Yang 	if (res)
1464186623f4SDavid Yang 		return res;
1465186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
1466186623f4SDavid Yang 	if (res)
1467186623f4SDavid Yang 		return res;
1468186623f4SDavid Yang 
1469186623f4SDavid Yang 	ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
1470186623f4SDavid Yang 	ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
1471186623f4SDavid Yang 	ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask);
1472186623f4SDavid Yang 	if (ctrl1 == val1 && ctrl2 == val2)
1473186623f4SDavid Yang 		return 0;
1474186623f4SDavid Yang 
1475186623f4SDavid Yang 	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
1476186623f4SDavid Yang }
1477186623f4SDavid Yang 
1478186623f4SDavid Yang static int
1479186623f4SDavid Yang yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
1480186623f4SDavid Yang 			 dsa_fdb_dump_cb_t *cb, void *data)
1481186623f4SDavid Yang {
1482186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1483186623f4SDavid Yang 	int res;
1484186623f4SDavid Yang 
1485186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1486186623f4SDavid Yang 	/* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
1487186623f4SDavid Yang 	 * only wants to see unicast
1488186623f4SDavid Yang 	 */
1489186623f4SDavid Yang 	res = yt921x_fdb_dump(priv, BIT(port), cb, data);
1490186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1491186623f4SDavid Yang 
1492186623f4SDavid Yang 	return res;
1493186623f4SDavid Yang }
1494186623f4SDavid Yang 
1495186623f4SDavid Yang static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port)
1496186623f4SDavid Yang {
1497186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1498186623f4SDavid Yang 	struct device *dev = to_device(priv);
1499186623f4SDavid Yang 	int res;
1500186623f4SDavid Yang 
1501186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1502186623f4SDavid Yang 	res = yt921x_fdb_flush_port(priv, port, false);
1503186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1504186623f4SDavid Yang 
1505186623f4SDavid Yang 	if (res)
1506186623f4SDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
1507186623f4SDavid Yang 			port, res);
1508186623f4SDavid Yang }
1509186623f4SDavid Yang 
1510186623f4SDavid Yang static int
1511186623f4SDavid Yang yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
1512186623f4SDavid Yang {
1513186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1514186623f4SDavid Yang 	u32 ctrl;
1515186623f4SDavid Yang 	int res;
1516186623f4SDavid Yang 
1517186623f4SDavid Yang 	/* AGEING reg is set in 5s step */
1518186623f4SDavid Yang 	ctrl = clamp(msecs / 5000, 1, U16_MAX);
1519186623f4SDavid Yang 
1520186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1521186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_AGEING, ctrl);
1522186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1523186623f4SDavid Yang 
1524186623f4SDavid Yang 	return res;
1525186623f4SDavid Yang }
1526186623f4SDavid Yang 
1527186623f4SDavid Yang static int
1528186623f4SDavid Yang yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port,
1529186623f4SDavid Yang 			const unsigned char *addr, u16 vid, struct dsa_db db)
1530186623f4SDavid Yang {
1531186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1532186623f4SDavid Yang 	int res;
1533186623f4SDavid Yang 
1534186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1535186623f4SDavid Yang 	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
1536186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1537186623f4SDavid Yang 
1538186623f4SDavid Yang 	return res;
1539186623f4SDavid Yang }
1540186623f4SDavid Yang 
1541186623f4SDavid Yang static int
1542186623f4SDavid Yang yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
1543186623f4SDavid Yang 			const unsigned char *addr, u16 vid, struct dsa_db db)
1544186623f4SDavid Yang {
1545186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1546186623f4SDavid Yang 	int res;
1547186623f4SDavid Yang 
1548186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1549186623f4SDavid Yang 	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
1550186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1551186623f4SDavid Yang 
1552186623f4SDavid Yang 	return res;
1553186623f4SDavid Yang }
1554186623f4SDavid Yang 
1555186623f4SDavid Yang static int
1556186623f4SDavid Yang yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port,
1557186623f4SDavid Yang 			const struct switchdev_obj_port_mdb *mdb,
1558186623f4SDavid Yang 			struct dsa_db db)
1559186623f4SDavid Yang {
1560186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1561186623f4SDavid Yang 	const unsigned char *addr = mdb->addr;
1562186623f4SDavid Yang 	u16 vid = mdb->vid;
1563186623f4SDavid Yang 	int res;
1564186623f4SDavid Yang 
1565186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1566186623f4SDavid Yang 	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
1567186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1568186623f4SDavid Yang 
1569186623f4SDavid Yang 	return res;
1570186623f4SDavid Yang }
1571186623f4SDavid Yang 
1572186623f4SDavid Yang static int
1573186623f4SDavid Yang yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port,
1574186623f4SDavid Yang 			const struct switchdev_obj_port_mdb *mdb,
1575186623f4SDavid Yang 			struct dsa_db db)
1576186623f4SDavid Yang {
1577186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1578186623f4SDavid Yang 	const unsigned char *addr = mdb->addr;
1579186623f4SDavid Yang 	u16 vid = mdb->vid;
1580186623f4SDavid Yang 	int res;
1581186623f4SDavid Yang 
1582186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1583186623f4SDavid Yang 	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
1584186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1585186623f4SDavid Yang 
1586186623f4SDavid Yang 	return res;
1587186623f4SDavid Yang }
1588186623f4SDavid Yang 
1589186623f4SDavid Yang static int
1590186623f4SDavid Yang yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid)
1591186623f4SDavid Yang {
1592186623f4SDavid Yang 	u32 mask;
1593186623f4SDavid Yang 	u32 ctrl;
1594186623f4SDavid Yang 
1595186623f4SDavid Yang 	mask = YT921X_PORT_VLAN_CTRL_CVID_M;
1596186623f4SDavid Yang 	ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid);
1597186623f4SDavid Yang 	return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port),
1598186623f4SDavid Yang 				      mask, ctrl);
1599186623f4SDavid Yang }
1600186623f4SDavid Yang 
1601186623f4SDavid Yang static int
1602186623f4SDavid Yang yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
1603186623f4SDavid Yang {
1604186623f4SDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
1605186623f4SDavid Yang 	struct net_device *bdev;
1606186623f4SDavid Yang 	u16 pvid;
1607186623f4SDavid Yang 	u32 mask;
1608186623f4SDavid Yang 	u32 ctrl;
1609186623f4SDavid Yang 	int res;
1610186623f4SDavid Yang 
1611186623f4SDavid Yang 	bdev = dsa_port_bridge_dev_get(dp);
1612186623f4SDavid Yang 
1613186623f4SDavid Yang 	if (!bdev || !vlan_filtering)
1614186623f4SDavid Yang 		pvid = YT921X_VID_UNWARE;
1615186623f4SDavid Yang 	else
1616186623f4SDavid Yang 		br_vlan_get_pvid(bdev, &pvid);
1617186623f4SDavid Yang 	res = yt921x_port_set_pvid(priv, port, pvid);
1618186623f4SDavid Yang 	if (res)
1619186623f4SDavid Yang 		return res;
1620186623f4SDavid Yang 
1621186623f4SDavid Yang 	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED |
1622186623f4SDavid Yang 	       YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
1623186623f4SDavid Yang 	ctrl = 0;
1624186623f4SDavid Yang 	/* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
1625186623f4SDavid Yang 	if (vlan_filtering && !pvid)
1626186623f4SDavid Yang 		ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
1627186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
1628186623f4SDavid Yang 				     mask, ctrl);
1629186623f4SDavid Yang 	if (res)
1630186623f4SDavid Yang 		return res;
1631186623f4SDavid Yang 
1632186623f4SDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER,
1633186623f4SDavid Yang 				     YT921X_VLAN_IGR_FILTER_PORTn(port),
1634186623f4SDavid Yang 				     vlan_filtering);
1635186623f4SDavid Yang 	if (res)
1636186623f4SDavid Yang 		return res;
1637186623f4SDavid Yang 
1638186623f4SDavid Yang 	/* Turn on / off VLAN awareness */
1639186623f4SDavid Yang 	mask = YT921X_PORT_IGR_TPIDn_CTAG_M;
1640186623f4SDavid Yang 	if (!vlan_filtering)
1641186623f4SDavid Yang 		ctrl = 0;
1642186623f4SDavid Yang 	else
1643186623f4SDavid Yang 		ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0);
1644186623f4SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_IGR_TPID(port),
1645186623f4SDavid Yang 				     mask, ctrl);
1646186623f4SDavid Yang 	if (res)
1647186623f4SDavid Yang 		return res;
1648186623f4SDavid Yang 
1649186623f4SDavid Yang 	return 0;
1650186623f4SDavid Yang }
1651186623f4SDavid Yang 
1652186623f4SDavid Yang static int
1653186623f4SDavid Yang yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
1654186623f4SDavid Yang {
1655186623f4SDavid Yang 	u64 mask64;
1656186623f4SDavid Yang 
1657186623f4SDavid Yang 	mask64 = YT921X_VLAN_CTRL_PORTS(port) |
1658186623f4SDavid Yang 		 YT921X_VLAN_CTRL_UNTAG_PORTn(port);
1659186623f4SDavid Yang 
1660186623f4SDavid Yang 	return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64);
1661186623f4SDavid Yang }
1662186623f4SDavid Yang 
1663186623f4SDavid Yang static int
1664186623f4SDavid Yang yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
1665186623f4SDavid Yang {
1666186623f4SDavid Yang 	u64 mask64;
1667186623f4SDavid Yang 	u64 ctrl64;
1668186623f4SDavid Yang 
1669186623f4SDavid Yang 	mask64 = YT921X_VLAN_CTRL_PORTn(port) |
1670186623f4SDavid Yang 		 YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask);
1671186623f4SDavid Yang 	ctrl64 = mask64;
1672186623f4SDavid Yang 
1673186623f4SDavid Yang 	mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
1674186623f4SDavid Yang 	if (untagged)
1675186623f4SDavid Yang 		ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
1676186623f4SDavid Yang 
1677186623f4SDavid Yang 	return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
1678186623f4SDavid Yang 					mask64, ctrl64);
1679186623f4SDavid Yang }
1680186623f4SDavid Yang 
1681186623f4SDavid Yang static int
1682186623f4SDavid Yang yt921x_pvid_clear(struct yt921x_priv *priv, int port)
1683186623f4SDavid Yang {
1684186623f4SDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
1685186623f4SDavid Yang 	bool vlan_filtering;
1686186623f4SDavid Yang 	u32 mask;
1687186623f4SDavid Yang 	int res;
1688186623f4SDavid Yang 
1689186623f4SDavid Yang 	vlan_filtering = dsa_port_is_vlan_filtering(dp);
1690186623f4SDavid Yang 
1691186623f4SDavid Yang 	res = yt921x_port_set_pvid(priv, port,
1692186623f4SDavid Yang 				   vlan_filtering ? 0 : YT921X_VID_UNWARE);
1693186623f4SDavid Yang 	if (res)
1694186623f4SDavid Yang 		return res;
1695186623f4SDavid Yang 
1696186623f4SDavid Yang 	if (vlan_filtering) {
1697186623f4SDavid Yang 		mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
1698186623f4SDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
1699186623f4SDavid Yang 					  mask);
1700186623f4SDavid Yang 		if (res)
1701186623f4SDavid Yang 			return res;
1702186623f4SDavid Yang 	}
1703186623f4SDavid Yang 
1704186623f4SDavid Yang 	return 0;
1705186623f4SDavid Yang }
1706186623f4SDavid Yang 
1707186623f4SDavid Yang static int
1708186623f4SDavid Yang yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid)
1709186623f4SDavid Yang {
1710186623f4SDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
1711186623f4SDavid Yang 	bool vlan_filtering;
1712186623f4SDavid Yang 	u32 mask;
1713186623f4SDavid Yang 	int res;
1714186623f4SDavid Yang 
1715186623f4SDavid Yang 	vlan_filtering = dsa_port_is_vlan_filtering(dp);
1716186623f4SDavid Yang 
1717186623f4SDavid Yang 	if (vlan_filtering) {
1718186623f4SDavid Yang 		res = yt921x_port_set_pvid(priv, port, vid);
1719186623f4SDavid Yang 		if (res)
1720186623f4SDavid Yang 			return res;
1721186623f4SDavid Yang 	}
1722186623f4SDavid Yang 
1723186623f4SDavid Yang 	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
1724186623f4SDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask);
1725186623f4SDavid Yang 	if (res)
1726186623f4SDavid Yang 		return res;
1727186623f4SDavid Yang 
1728186623f4SDavid Yang 	return 0;
1729186623f4SDavid Yang }
1730186623f4SDavid Yang 
1731186623f4SDavid Yang static int
1732186623f4SDavid Yang yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
1733186623f4SDavid Yang 			       bool vlan_filtering,
1734186623f4SDavid Yang 			       struct netlink_ext_ack *extack)
1735186623f4SDavid Yang {
1736186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1737186623f4SDavid Yang 	int res;
1738186623f4SDavid Yang 
1739186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
1740186623f4SDavid Yang 		return 0;
1741186623f4SDavid Yang 
1742186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1743186623f4SDavid Yang 	res = yt921x_vlan_filtering(priv, port, vlan_filtering);
1744186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1745186623f4SDavid Yang 
1746186623f4SDavid Yang 	return res;
1747186623f4SDavid Yang }
1748186623f4SDavid Yang 
1749186623f4SDavid Yang static int
1750186623f4SDavid Yang yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port,
1751186623f4SDavid Yang 			 const struct switchdev_obj_port_vlan *vlan)
1752186623f4SDavid Yang {
1753186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1754186623f4SDavid Yang 	u16 vid = vlan->vid;
1755186623f4SDavid Yang 	u16 pvid;
1756186623f4SDavid Yang 	int res;
1757186623f4SDavid Yang 
1758186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
1759186623f4SDavid Yang 		return 0;
1760186623f4SDavid Yang 
1761186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1762186623f4SDavid Yang 	do {
1763186623f4SDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
1764186623f4SDavid Yang 		struct net_device *bdev;
1765186623f4SDavid Yang 
1766186623f4SDavid Yang 		res = yt921x_vlan_del(priv, port, vid);
1767186623f4SDavid Yang 		if (res)
1768186623f4SDavid Yang 			break;
1769186623f4SDavid Yang 
1770186623f4SDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
1771186623f4SDavid Yang 		if (bdev) {
1772186623f4SDavid Yang 			br_vlan_get_pvid(bdev, &pvid);
1773186623f4SDavid Yang 			if (pvid == vid)
1774186623f4SDavid Yang 				res = yt921x_pvid_clear(priv, port);
1775186623f4SDavid Yang 		}
1776186623f4SDavid Yang 	} while (0);
1777186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1778186623f4SDavid Yang 
1779186623f4SDavid Yang 	return res;
1780186623f4SDavid Yang }
1781186623f4SDavid Yang 
1782186623f4SDavid Yang static int
1783186623f4SDavid Yang yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
1784186623f4SDavid Yang 			 const struct switchdev_obj_port_vlan *vlan,
1785186623f4SDavid Yang 			 struct netlink_ext_ack *extack)
1786186623f4SDavid Yang {
1787186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
1788186623f4SDavid Yang 	u16 vid = vlan->vid;
1789186623f4SDavid Yang 	u16 pvid;
1790186623f4SDavid Yang 	int res;
1791186623f4SDavid Yang 
1792186623f4SDavid Yang 	/* CPU port is supposed to be a member of every VLAN; see
1793186623f4SDavid Yang 	 * yt921x_vlan_add() and yt921x_port_setup()
1794186623f4SDavid Yang 	 */
1795186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
1796186623f4SDavid Yang 		return 0;
1797186623f4SDavid Yang 
1798186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
1799186623f4SDavid Yang 	do {
1800186623f4SDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
1801186623f4SDavid Yang 		struct net_device *bdev;
1802186623f4SDavid Yang 
1803186623f4SDavid Yang 		res = yt921x_vlan_add(priv, port, vid,
1804186623f4SDavid Yang 				      vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
1805186623f4SDavid Yang 		if (res)
1806186623f4SDavid Yang 			break;
1807186623f4SDavid Yang 
1808186623f4SDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
1809186623f4SDavid Yang 		if (bdev) {
1810186623f4SDavid Yang 			if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
1811186623f4SDavid Yang 				res = yt921x_pvid_set(priv, port, vid);
1812186623f4SDavid Yang 			} else {
1813186623f4SDavid Yang 				br_vlan_get_pvid(bdev, &pvid);
1814186623f4SDavid Yang 				if (pvid == vid)
1815186623f4SDavid Yang 					res = yt921x_pvid_clear(priv, port);
1816186623f4SDavid Yang 			}
1817186623f4SDavid Yang 		}
1818186623f4SDavid Yang 	} while (0);
1819186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
1820186623f4SDavid Yang 
1821186623f4SDavid Yang 	return res;
1822186623f4SDavid Yang }
1823186623f4SDavid Yang 
1824186623f4SDavid Yang static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
1825186623f4SDavid Yang {
1826186623f4SDavid Yang 	u32 mask;
1827186623f4SDavid Yang 	u32 ctrl;
1828186623f4SDavid Yang 	int res;
1829186623f4SDavid Yang 
1830186623f4SDavid Yang 	ctrl = ~priv->cpu_ports_mask;
1831186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
1832186623f4SDavid Yang 	if (res)
1833186623f4SDavid Yang 		return res;
1834186623f4SDavid Yang 
1835186623f4SDavid Yang 	/* Turn off FDB learning to prevent FDB pollution */
1836186623f4SDavid Yang 	mask = YT921X_PORT_LEARN_DIS;
1837186623f4SDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask);
1838186623f4SDavid Yang 	if (res)
1839186623f4SDavid Yang 		return res;
1840186623f4SDavid Yang 
1841186623f4SDavid Yang 	/* Turn off VLAN awareness */
1842186623f4SDavid Yang 	mask = YT921X_PORT_IGR_TPIDn_CTAG_M;
1843186623f4SDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_IGR_TPID(port), mask);
1844186623f4SDavid Yang 	if (res)
1845186623f4SDavid Yang 		return res;
1846186623f4SDavid Yang 
1847186623f4SDavid Yang 	/* Unrelated since learning is off and all packets are trapped;
1848186623f4SDavid Yang 	 * set it anyway
1849186623f4SDavid Yang 	 */
1850186623f4SDavid Yang 	res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
1851186623f4SDavid Yang 	if (res)
1852186623f4SDavid Yang 		return res;
1853186623f4SDavid Yang 
1854186623f4SDavid Yang 	return 0;
1855186623f4SDavid Yang }
1856186623f4SDavid Yang 
1857186623f4SDavid Yang static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
1858186623f4SDavid Yang {
1859186623f4SDavid Yang 	u32 mask;
1860186623f4SDavid Yang 	int res;
1861186623f4SDavid Yang 
1862186623f4SDavid Yang 	mask = YT921X_PORT_LEARN_DIS;
1863186623f4SDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask);
1864186623f4SDavid Yang 	if (res)
1865186623f4SDavid Yang 		return res;
1866186623f4SDavid Yang 
1867186623f4SDavid Yang 	return 0;
1868186623f4SDavid Yang }
1869186623f4SDavid Yang 
1870186623f4SDavid Yang static int yt921x_isolate(struct yt921x_priv *priv, int port)
1871186623f4SDavid Yang {
1872186623f4SDavid Yang 	u32 mask;
1873186623f4SDavid Yang 	int res;
1874186623f4SDavid Yang 
1875186623f4SDavid Yang 	mask = BIT(port);
1876186623f4SDavid Yang 	for (int i = 0; i < YT921X_PORT_NUM; i++) {
1877186623f4SDavid Yang 		if ((BIT(i) & priv->cpu_ports_mask) || i == port)
1878186623f4SDavid Yang 			continue;
1879186623f4SDavid Yang 
1880186623f4SDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
1881186623f4SDavid Yang 					  mask);
1882186623f4SDavid Yang 		if (res)
1883186623f4SDavid Yang 			return res;
1884186623f4SDavid Yang 	}
1885186623f4SDavid Yang 
1886186623f4SDavid Yang 	return 0;
1887186623f4SDavid Yang }
1888186623f4SDavid Yang 
1889186623f4SDavid Yang /* Make sure to include the CPU port in ports_mask, or your bridge will
1890186623f4SDavid Yang  * not have it.
1891186623f4SDavid Yang  */
1892186623f4SDavid Yang static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
1893186623f4SDavid Yang {
1894186623f4SDavid Yang 	unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
1895186623f4SDavid Yang 	u32 isolated_mask;
1896186623f4SDavid Yang 	u32 ctrl;
1897186623f4SDavid Yang 	int port;
1898186623f4SDavid Yang 	int res;
1899186623f4SDavid Yang 
1900186623f4SDavid Yang 	isolated_mask = 0;
1901186623f4SDavid Yang 	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
1902186623f4SDavid Yang 		struct yt921x_port *pp = &priv->ports[port];
1903186623f4SDavid Yang 
1904186623f4SDavid Yang 		if (pp->isolated)
1905186623f4SDavid Yang 			isolated_mask |= BIT(port);
1906186623f4SDavid Yang 	}
1907186623f4SDavid Yang 
1908186623f4SDavid Yang 	/* Block from non-cpu bridge ports ... */
1909186623f4SDavid Yang 	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
1910186623f4SDavid Yang 		struct yt921x_port *pp = &priv->ports[port];
1911186623f4SDavid Yang 
1912186623f4SDavid Yang 		/* to non-bridge ports */
1913186623f4SDavid Yang 		ctrl = ~ports_mask;
1914186623f4SDavid Yang 		/* to isolated ports when isolated */
1915186623f4SDavid Yang 		if (pp->isolated)
1916186623f4SDavid Yang 			ctrl |= isolated_mask;
1917186623f4SDavid Yang 		/* to itself when non-hairpin */
1918186623f4SDavid Yang 		if (!pp->hairpin)
1919186623f4SDavid Yang 			ctrl |= BIT(port);
1920186623f4SDavid Yang 		else
1921186623f4SDavid Yang 			ctrl &= ~BIT(port);
1922186623f4SDavid Yang 
1923186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
1924186623f4SDavid Yang 				       ctrl);
1925186623f4SDavid Yang 		if (res)
1926186623f4SDavid Yang 			return res;
1927186623f4SDavid Yang 	}
1928186623f4SDavid Yang 
1929186623f4SDavid Yang 	return 0;
1930186623f4SDavid Yang }
1931186623f4SDavid Yang 
1932186623f4SDavid Yang static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
1933186623f4SDavid Yang {
1934186623f4SDavid Yang 	int res;
1935186623f4SDavid Yang 
1936186623f4SDavid Yang 	res = yt921x_userport_standalone(priv, port);
1937186623f4SDavid Yang 	if (res)
1938186623f4SDavid Yang 		return res;
1939186623f4SDavid Yang 
1940186623f4SDavid Yang 	res = yt921x_isolate(priv, port);
1941186623f4SDavid Yang 	if (res)
1942186623f4SDavid Yang 		return res;
1943186623f4SDavid Yang 
1944186623f4SDavid Yang 	return 0;
1945186623f4SDavid Yang }
1946186623f4SDavid Yang 
1947186623f4SDavid Yang static int
1948186623f4SDavid Yang yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
1949186623f4SDavid Yang {
1950186623f4SDavid Yang 	int res;
1951186623f4SDavid Yang 
1952186623f4SDavid Yang 	res = yt921x_userport_bridge(priv, port);
1953186623f4SDavid Yang 	if (res)
1954186623f4SDavid Yang 		return res;
1955186623f4SDavid Yang 
1956186623f4SDavid Yang 	res = yt921x_bridge(priv, ports_mask);
1957186623f4SDavid Yang 	if (res)
1958186623f4SDavid Yang 		return res;
1959186623f4SDavid Yang 
1960186623f4SDavid Yang 	return 0;
1961186623f4SDavid Yang }
1962186623f4SDavid Yang 
1963186623f4SDavid Yang static u32
1964186623f4SDavid Yang dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev)
1965186623f4SDavid Yang {
1966186623f4SDavid Yang 	struct dsa_port *dp;
1967186623f4SDavid Yang 	u32 mask = 0;
1968186623f4SDavid Yang 
1969186623f4SDavid Yang 	dsa_switch_for_each_user_port(dp, ds)
1970186623f4SDavid Yang 		if (dsa_port_offloads_bridge_dev(dp, bdev))
1971186623f4SDavid Yang 			mask |= BIT(dp->index);
1972186623f4SDavid Yang 
1973186623f4SDavid Yang 	return mask;
1974186623f4SDavid Yang }
1975186623f4SDavid Yang 
1976186623f4SDavid Yang static int
1977186623f4SDavid Yang yt921x_bridge_flags(struct yt921x_priv *priv, int port,
1978186623f4SDavid Yang 		    struct switchdev_brport_flags flags)
1979186623f4SDavid Yang {
1980186623f4SDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
1981186623f4SDavid Yang 	bool do_flush;
1982186623f4SDavid Yang 	u32 mask;
1983186623f4SDavid Yang 	int res;
1984186623f4SDavid Yang 
1985186623f4SDavid Yang 	if (flags.mask & BR_LEARNING) {
1986186623f4SDavid Yang 		bool learning = flags.val & BR_LEARNING;
1987186623f4SDavid Yang 
1988186623f4SDavid Yang 		mask = YT921X_PORT_LEARN_DIS;
1989186623f4SDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
1990186623f4SDavid Yang 					     mask, !learning);
1991186623f4SDavid Yang 		if (res)
1992186623f4SDavid Yang 			return res;
1993186623f4SDavid Yang 	}
1994186623f4SDavid Yang 
1995186623f4SDavid Yang 	/* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
1996186623f4SDavid Yang 	 * is set
1997186623f4SDavid Yang 	 */
1998186623f4SDavid Yang 
1999186623f4SDavid Yang 	/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
2000186623f4SDavid Yang 
2001186623f4SDavid Yang 	do_flush = false;
2002186623f4SDavid Yang 	if (flags.mask & BR_HAIRPIN_MODE) {
2003186623f4SDavid Yang 		pp->hairpin = flags.val & BR_HAIRPIN_MODE;
2004186623f4SDavid Yang 		do_flush = true;
2005186623f4SDavid Yang 	}
2006186623f4SDavid Yang 	if (flags.mask & BR_ISOLATED) {
2007186623f4SDavid Yang 		pp->isolated = flags.val & BR_ISOLATED;
2008186623f4SDavid Yang 		do_flush = true;
2009186623f4SDavid Yang 	}
2010186623f4SDavid Yang 	if (do_flush) {
2011186623f4SDavid Yang 		struct dsa_switch *ds = &priv->ds;
2012186623f4SDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
2013186623f4SDavid Yang 		struct net_device *bdev;
2014186623f4SDavid Yang 
2015186623f4SDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
2016186623f4SDavid Yang 		if (bdev) {
2017186623f4SDavid Yang 			u32 ports_mask;
2018186623f4SDavid Yang 
2019186623f4SDavid Yang 			ports_mask = dsa_bridge_ports(ds, bdev);
2020186623f4SDavid Yang 			ports_mask |= priv->cpu_ports_mask;
2021186623f4SDavid Yang 			res = yt921x_bridge(priv, ports_mask);
2022186623f4SDavid Yang 			if (res)
2023186623f4SDavid Yang 				return res;
2024186623f4SDavid Yang 		}
2025186623f4SDavid Yang 	}
2026186623f4SDavid Yang 
2027186623f4SDavid Yang 	return 0;
2028186623f4SDavid Yang }
2029186623f4SDavid Yang 
2030186623f4SDavid Yang static int
2031186623f4SDavid Yang yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
2032186623f4SDavid Yang 				 struct switchdev_brport_flags flags,
2033186623f4SDavid Yang 				 struct netlink_ext_ack *extack)
2034186623f4SDavid Yang {
2035186623f4SDavid Yang 	if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
2036186623f4SDavid Yang 			   BR_MCAST_FLOOD | BR_ISOLATED))
2037186623f4SDavid Yang 		return -EINVAL;
2038186623f4SDavid Yang 	return 0;
2039186623f4SDavid Yang }
2040186623f4SDavid Yang 
2041186623f4SDavid Yang static int
2042186623f4SDavid Yang yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
2043186623f4SDavid Yang 			     struct switchdev_brport_flags flags,
2044186623f4SDavid Yang 			     struct netlink_ext_ack *extack)
2045186623f4SDavid Yang {
2046186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2047186623f4SDavid Yang 	int res;
2048186623f4SDavid Yang 
2049186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
2050186623f4SDavid Yang 		return 0;
2051186623f4SDavid Yang 
2052186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2053186623f4SDavid Yang 	res = yt921x_bridge_flags(priv, port, flags);
2054186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2055186623f4SDavid Yang 
2056186623f4SDavid Yang 	return res;
2057186623f4SDavid Yang }
2058186623f4SDavid Yang 
2059186623f4SDavid Yang static void
2060186623f4SDavid Yang yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
2061186623f4SDavid Yang 			     struct dsa_bridge bridge)
2062186623f4SDavid Yang {
2063186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2064186623f4SDavid Yang 	struct device *dev = to_device(priv);
2065186623f4SDavid Yang 	int res;
2066186623f4SDavid Yang 
2067186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
2068186623f4SDavid Yang 		return;
2069186623f4SDavid Yang 
2070186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2071186623f4SDavid Yang 	res = yt921x_bridge_leave(priv, port);
2072186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2073186623f4SDavid Yang 
2074186623f4SDavid Yang 	if (res)
2075186623f4SDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "unbridge",
2076186623f4SDavid Yang 			port, res);
2077186623f4SDavid Yang }
2078186623f4SDavid Yang 
2079186623f4SDavid Yang static int
2080186623f4SDavid Yang yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
2081186623f4SDavid Yang 			    struct dsa_bridge bridge, bool *tx_fwd_offload,
2082186623f4SDavid Yang 			    struct netlink_ext_ack *extack)
2083186623f4SDavid Yang {
2084186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2085186623f4SDavid Yang 	u16 ports_mask;
2086186623f4SDavid Yang 	int res;
2087186623f4SDavid Yang 
2088186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port))
2089186623f4SDavid Yang 		return 0;
2090186623f4SDavid Yang 
2091186623f4SDavid Yang 	ports_mask = dsa_bridge_ports(ds, bridge.dev);
2092186623f4SDavid Yang 	ports_mask |= priv->cpu_ports_mask;
2093186623f4SDavid Yang 
2094186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2095186623f4SDavid Yang 	res = yt921x_bridge_join(priv, port, ports_mask);
2096186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2097186623f4SDavid Yang 
2098186623f4SDavid Yang 	return res;
2099186623f4SDavid Yang }
2100186623f4SDavid Yang 
2101633b1d01SDavid Yang static int
2102633b1d01SDavid Yang yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
2103633b1d01SDavid Yang 			      const struct switchdev_mst_state *st)
2104633b1d01SDavid Yang {
2105633b1d01SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2106633b1d01SDavid Yang 	u32 mask;
2107633b1d01SDavid Yang 	u32 ctrl;
2108633b1d01SDavid Yang 	int res;
2109633b1d01SDavid Yang 
2110633b1d01SDavid Yang 	mask = YT921X_STP_PORTn_M(port);
2111633b1d01SDavid Yang 	switch (st->state) {
2112633b1d01SDavid Yang 	case BR_STATE_DISABLED:
2113633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_DISABLED(port);
2114633b1d01SDavid Yang 		break;
2115633b1d01SDavid Yang 	case BR_STATE_LISTENING:
2116633b1d01SDavid Yang 	case BR_STATE_LEARNING:
2117633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
2118633b1d01SDavid Yang 		break;
2119633b1d01SDavid Yang 	case BR_STATE_FORWARDING:
2120633b1d01SDavid Yang 	default:
2121633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_FORWARD(port);
2122633b1d01SDavid Yang 		break;
2123633b1d01SDavid Yang 	case BR_STATE_BLOCKING:
2124633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_BLOCKING(port);
2125633b1d01SDavid Yang 		break;
2126633b1d01SDavid Yang 	}
2127633b1d01SDavid Yang 
2128633b1d01SDavid Yang 	mutex_lock(&priv->reg_lock);
2129633b1d01SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl);
2130633b1d01SDavid Yang 	mutex_unlock(&priv->reg_lock);
2131633b1d01SDavid Yang 
2132633b1d01SDavid Yang 	return res;
2133633b1d01SDavid Yang }
2134633b1d01SDavid Yang 
2135633b1d01SDavid Yang static int
2136633b1d01SDavid Yang yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
2137633b1d01SDavid Yang 			 const struct switchdev_vlan_msti *msti)
2138633b1d01SDavid Yang {
2139633b1d01SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2140633b1d01SDavid Yang 	u64 mask64;
2141633b1d01SDavid Yang 	u64 ctrl64;
2142633b1d01SDavid Yang 	int res;
2143633b1d01SDavid Yang 
2144633b1d01SDavid Yang 	if (!msti->vid)
2145633b1d01SDavid Yang 		return -EINVAL;
2146633b1d01SDavid Yang 	if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
2147633b1d01SDavid Yang 		return -EINVAL;
2148633b1d01SDavid Yang 
2149633b1d01SDavid Yang 	mask64 = YT921X_VLAN_CTRL_STP_ID_M;
2150633b1d01SDavid Yang 	ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti);
2151633b1d01SDavid Yang 
2152633b1d01SDavid Yang 	mutex_lock(&priv->reg_lock);
2153633b1d01SDavid Yang 	res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
2154633b1d01SDavid Yang 				       mask64, ctrl64);
2155633b1d01SDavid Yang 	mutex_unlock(&priv->reg_lock);
2156633b1d01SDavid Yang 
2157633b1d01SDavid Yang 	return res;
2158633b1d01SDavid Yang }
2159633b1d01SDavid Yang 
2160633b1d01SDavid Yang static void
2161633b1d01SDavid Yang yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
2162633b1d01SDavid Yang {
2163633b1d01SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2164633b1d01SDavid Yang 	struct dsa_port *dp = dsa_to_port(ds, port);
2165633b1d01SDavid Yang 	struct device *dev = to_device(priv);
2166633b1d01SDavid Yang 	bool learning;
2167633b1d01SDavid Yang 	u32 mask;
2168633b1d01SDavid Yang 	u32 ctrl;
2169633b1d01SDavid Yang 	int res;
2170633b1d01SDavid Yang 
2171633b1d01SDavid Yang 	mask = YT921X_STP_PORTn_M(port);
2172633b1d01SDavid Yang 	learning = false;
2173633b1d01SDavid Yang 	switch (state) {
2174633b1d01SDavid Yang 	case BR_STATE_DISABLED:
2175633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_DISABLED(port);
2176633b1d01SDavid Yang 		break;
2177633b1d01SDavid Yang 	case BR_STATE_LISTENING:
2178633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
2179633b1d01SDavid Yang 		break;
2180633b1d01SDavid Yang 	case BR_STATE_LEARNING:
2181633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
2182633b1d01SDavid Yang 		learning = dp->learning;
2183633b1d01SDavid Yang 		break;
2184633b1d01SDavid Yang 	case BR_STATE_FORWARDING:
2185633b1d01SDavid Yang 	default:
2186633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_FORWARD(port);
2187633b1d01SDavid Yang 		learning = dp->learning;
2188633b1d01SDavid Yang 		break;
2189633b1d01SDavid Yang 	case BR_STATE_BLOCKING:
2190633b1d01SDavid Yang 		ctrl = YT921X_STP_PORTn_BLOCKING(port);
2191633b1d01SDavid Yang 		break;
2192633b1d01SDavid Yang 	}
2193633b1d01SDavid Yang 
2194633b1d01SDavid Yang 	mutex_lock(&priv->reg_lock);
2195633b1d01SDavid Yang 	do {
2196633b1d01SDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl);
2197633b1d01SDavid Yang 		if (res)
2198633b1d01SDavid Yang 			break;
2199633b1d01SDavid Yang 
2200633b1d01SDavid Yang 		mask = YT921X_PORT_LEARN_DIS;
2201633b1d01SDavid Yang 		ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
2202633b1d01SDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
2203633b1d01SDavid Yang 					     mask, ctrl);
2204633b1d01SDavid Yang 	} while (0);
2205633b1d01SDavid Yang 	mutex_unlock(&priv->reg_lock);
2206633b1d01SDavid Yang 
2207633b1d01SDavid Yang 	if (res)
2208633b1d01SDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
2209633b1d01SDavid Yang 			port, res);
2210633b1d01SDavid Yang }
2211633b1d01SDavid Yang 
2212186623f4SDavid Yang static int yt921x_port_down(struct yt921x_priv *priv, int port)
2213186623f4SDavid Yang {
2214186623f4SDavid Yang 	u32 mask;
2215186623f4SDavid Yang 	int res;
2216186623f4SDavid Yang 
2217186623f4SDavid Yang 	mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
2218186623f4SDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
2219186623f4SDavid Yang 	if (res)
2220186623f4SDavid Yang 		return res;
2221186623f4SDavid Yang 
2222186623f4SDavid Yang 	if (yt921x_port_is_external(port)) {
2223186623f4SDavid Yang 		mask = YT921X_SERDES_LINK;
2224186623f4SDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
2225186623f4SDavid Yang 		if (res)
2226186623f4SDavid Yang 			return res;
2227186623f4SDavid Yang 
2228186623f4SDavid Yang 		mask = YT921X_XMII_LINK;
2229186623f4SDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
2230186623f4SDavid Yang 		if (res)
2231186623f4SDavid Yang 			return res;
2232186623f4SDavid Yang 	}
2233186623f4SDavid Yang 
2234186623f4SDavid Yang 	return 0;
2235186623f4SDavid Yang }
2236186623f4SDavid Yang 
2237186623f4SDavid Yang static int
2238186623f4SDavid Yang yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
2239186623f4SDavid Yang 	       phy_interface_t interface, int speed, int duplex,
2240186623f4SDavid Yang 	       bool tx_pause, bool rx_pause)
2241186623f4SDavid Yang {
2242186623f4SDavid Yang 	u32 mask;
2243186623f4SDavid Yang 	u32 ctrl;
2244186623f4SDavid Yang 	int res;
2245186623f4SDavid Yang 
2246186623f4SDavid Yang 	switch (speed) {
2247186623f4SDavid Yang 	case SPEED_10:
2248186623f4SDavid Yang 		ctrl = YT921X_PORT_SPEED_10;
2249186623f4SDavid Yang 		break;
2250186623f4SDavid Yang 	case SPEED_100:
2251186623f4SDavid Yang 		ctrl = YT921X_PORT_SPEED_100;
2252186623f4SDavid Yang 		break;
2253186623f4SDavid Yang 	case SPEED_1000:
2254186623f4SDavid Yang 		ctrl = YT921X_PORT_SPEED_1000;
2255186623f4SDavid Yang 		break;
2256186623f4SDavid Yang 	case SPEED_2500:
2257186623f4SDavid Yang 		ctrl = YT921X_PORT_SPEED_2500;
2258186623f4SDavid Yang 		break;
2259186623f4SDavid Yang 	case SPEED_10000:
2260186623f4SDavid Yang 		ctrl = YT921X_PORT_SPEED_10000;
2261186623f4SDavid Yang 		break;
2262186623f4SDavid Yang 	default:
2263186623f4SDavid Yang 		return -EINVAL;
2264186623f4SDavid Yang 	}
2265186623f4SDavid Yang 	if (duplex == DUPLEX_FULL)
2266186623f4SDavid Yang 		ctrl |= YT921X_PORT_DUPLEX_FULL;
2267186623f4SDavid Yang 	if (tx_pause)
2268186623f4SDavid Yang 		ctrl |= YT921X_PORT_TX_PAUSE;
2269186623f4SDavid Yang 	if (rx_pause)
2270186623f4SDavid Yang 		ctrl |= YT921X_PORT_RX_PAUSE;
2271186623f4SDavid Yang 	ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
2272186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl);
2273186623f4SDavid Yang 	if (res)
2274186623f4SDavid Yang 		return res;
2275186623f4SDavid Yang 
2276186623f4SDavid Yang 	if (yt921x_port_is_external(port)) {
2277186623f4SDavid Yang 		mask = YT921X_SERDES_SPEED_M;
2278186623f4SDavid Yang 		switch (speed) {
2279186623f4SDavid Yang 		case SPEED_10:
2280186623f4SDavid Yang 			ctrl = YT921X_SERDES_SPEED_10;
2281186623f4SDavid Yang 			break;
2282186623f4SDavid Yang 		case SPEED_100:
2283186623f4SDavid Yang 			ctrl = YT921X_SERDES_SPEED_100;
2284186623f4SDavid Yang 			break;
2285186623f4SDavid Yang 		case SPEED_1000:
2286186623f4SDavid Yang 			ctrl = YT921X_SERDES_SPEED_1000;
2287186623f4SDavid Yang 			break;
2288186623f4SDavid Yang 		case SPEED_2500:
2289186623f4SDavid Yang 			ctrl = YT921X_SERDES_SPEED_2500;
2290186623f4SDavid Yang 			break;
2291186623f4SDavid Yang 		case SPEED_10000:
2292186623f4SDavid Yang 			ctrl = YT921X_SERDES_SPEED_10000;
2293186623f4SDavid Yang 			break;
2294186623f4SDavid Yang 		default:
2295186623f4SDavid Yang 			return -EINVAL;
2296186623f4SDavid Yang 		}
2297186623f4SDavid Yang 		mask |= YT921X_SERDES_DUPLEX_FULL;
2298186623f4SDavid Yang 		if (duplex == DUPLEX_FULL)
2299186623f4SDavid Yang 			ctrl |= YT921X_SERDES_DUPLEX_FULL;
2300186623f4SDavid Yang 		mask |= YT921X_SERDES_TX_PAUSE;
2301186623f4SDavid Yang 		if (tx_pause)
2302186623f4SDavid Yang 			ctrl |= YT921X_SERDES_TX_PAUSE;
2303186623f4SDavid Yang 		mask |= YT921X_SERDES_RX_PAUSE;
2304186623f4SDavid Yang 		if (rx_pause)
2305186623f4SDavid Yang 			ctrl |= YT921X_SERDES_RX_PAUSE;
2306186623f4SDavid Yang 		mask |= YT921X_SERDES_LINK;
2307186623f4SDavid Yang 		ctrl |= YT921X_SERDES_LINK;
2308186623f4SDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
2309186623f4SDavid Yang 					     mask, ctrl);
2310186623f4SDavid Yang 		if (res)
2311186623f4SDavid Yang 			return res;
2312186623f4SDavid Yang 
2313186623f4SDavid Yang 		mask = YT921X_XMII_LINK;
2314186623f4SDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
2315186623f4SDavid Yang 		if (res)
2316186623f4SDavid Yang 			return res;
2317186623f4SDavid Yang 
2318186623f4SDavid Yang 		switch (speed) {
2319186623f4SDavid Yang 		case SPEED_10:
2320186623f4SDavid Yang 			ctrl = YT921X_MDIO_POLLING_SPEED_10;
2321186623f4SDavid Yang 			break;
2322186623f4SDavid Yang 		case SPEED_100:
2323186623f4SDavid Yang 			ctrl = YT921X_MDIO_POLLING_SPEED_100;
2324186623f4SDavid Yang 			break;
2325186623f4SDavid Yang 		case SPEED_1000:
2326186623f4SDavid Yang 			ctrl = YT921X_MDIO_POLLING_SPEED_1000;
2327186623f4SDavid Yang 			break;
2328186623f4SDavid Yang 		case SPEED_2500:
2329186623f4SDavid Yang 			ctrl = YT921X_MDIO_POLLING_SPEED_2500;
2330186623f4SDavid Yang 			break;
2331186623f4SDavid Yang 		case SPEED_10000:
2332186623f4SDavid Yang 			ctrl = YT921X_MDIO_POLLING_SPEED_10000;
2333186623f4SDavid Yang 			break;
2334186623f4SDavid Yang 		default:
2335186623f4SDavid Yang 			return -EINVAL;
2336186623f4SDavid Yang 		}
2337186623f4SDavid Yang 		if (duplex == DUPLEX_FULL)
2338186623f4SDavid Yang 			ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
2339186623f4SDavid Yang 		ctrl |= YT921X_MDIO_POLLING_LINK;
2340186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl);
2341186623f4SDavid Yang 		if (res)
2342186623f4SDavid Yang 			return res;
2343186623f4SDavid Yang 	}
2344186623f4SDavid Yang 
2345186623f4SDavid Yang 	return 0;
2346186623f4SDavid Yang }
2347186623f4SDavid Yang 
2348186623f4SDavid Yang static int
2349186623f4SDavid Yang yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
2350186623f4SDavid Yang 		   phy_interface_t interface)
2351186623f4SDavid Yang {
2352186623f4SDavid Yang 	struct device *dev = to_device(priv);
2353186623f4SDavid Yang 	u32 mask;
2354186623f4SDavid Yang 	u32 ctrl;
2355186623f4SDavid Yang 	int res;
2356186623f4SDavid Yang 
2357186623f4SDavid Yang 	if (!yt921x_port_is_external(port)) {
2358186623f4SDavid Yang 		if (interface != PHY_INTERFACE_MODE_INTERNAL) {
2359186623f4SDavid Yang 			dev_err(dev, "Wrong mode %d on port %d\n",
2360186623f4SDavid Yang 				interface, port);
2361186623f4SDavid Yang 			return -EINVAL;
2362186623f4SDavid Yang 		}
2363186623f4SDavid Yang 		return 0;
2364186623f4SDavid Yang 	}
2365186623f4SDavid Yang 
2366186623f4SDavid Yang 	switch (interface) {
2367186623f4SDavid Yang 	/* SERDES */
2368186623f4SDavid Yang 	case PHY_INTERFACE_MODE_SGMII:
2369186623f4SDavid Yang 	case PHY_INTERFACE_MODE_100BASEX:
2370186623f4SDavid Yang 	case PHY_INTERFACE_MODE_1000BASEX:
2371186623f4SDavid Yang 	case PHY_INTERFACE_MODE_2500BASEX:
2372186623f4SDavid Yang 		mask = YT921X_SERDES_CTRL_PORTn(port);
2373186623f4SDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
2374186623f4SDavid Yang 		if (res)
2375186623f4SDavid Yang 			return res;
2376186623f4SDavid Yang 
2377186623f4SDavid Yang 		mask = YT921X_XMII_CTRL_PORTn(port);
2378186623f4SDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask);
2379186623f4SDavid Yang 		if (res)
2380186623f4SDavid Yang 			return res;
2381186623f4SDavid Yang 
2382186623f4SDavid Yang 		mask = YT921X_SERDES_MODE_M;
2383186623f4SDavid Yang 		switch (interface) {
2384186623f4SDavid Yang 		case PHY_INTERFACE_MODE_SGMII:
2385186623f4SDavid Yang 			ctrl = YT921X_SERDES_MODE_SGMII;
2386186623f4SDavid Yang 			break;
2387186623f4SDavid Yang 		case PHY_INTERFACE_MODE_100BASEX:
2388186623f4SDavid Yang 			ctrl = YT921X_SERDES_MODE_100BASEX;
2389186623f4SDavid Yang 			break;
2390186623f4SDavid Yang 		case PHY_INTERFACE_MODE_1000BASEX:
2391186623f4SDavid Yang 			ctrl = YT921X_SERDES_MODE_1000BASEX;
2392186623f4SDavid Yang 			break;
2393186623f4SDavid Yang 		case PHY_INTERFACE_MODE_2500BASEX:
2394186623f4SDavid Yang 			ctrl = YT921X_SERDES_MODE_2500BASEX;
2395186623f4SDavid Yang 			break;
2396186623f4SDavid Yang 		default:
2397186623f4SDavid Yang 			return -EINVAL;
2398186623f4SDavid Yang 		}
2399186623f4SDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
2400186623f4SDavid Yang 					     mask, ctrl);
2401186623f4SDavid Yang 		if (res)
2402186623f4SDavid Yang 			return res;
2403186623f4SDavid Yang 
2404186623f4SDavid Yang 		break;
2405186623f4SDavid Yang 	/* add XMII support here */
2406186623f4SDavid Yang 	default:
2407186623f4SDavid Yang 		return -EINVAL;
2408186623f4SDavid Yang 	}
2409186623f4SDavid Yang 
2410186623f4SDavid Yang 	return 0;
2411186623f4SDavid Yang }
2412186623f4SDavid Yang 
2413186623f4SDavid Yang static void
2414186623f4SDavid Yang yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
2415186623f4SDavid Yang 			     phy_interface_t interface)
2416186623f4SDavid Yang {
2417186623f4SDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
2418186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
2419186623f4SDavid Yang 	int port = dp->index;
2420186623f4SDavid Yang 	int res;
2421186623f4SDavid Yang 
2422186623f4SDavid Yang 	/* No need to sync; port control block is hold until device remove */
2423186623f4SDavid Yang 	cancel_delayed_work(&priv->ports[port].mib_read);
2424186623f4SDavid Yang 
2425186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2426186623f4SDavid Yang 	res = yt921x_port_down(priv, port);
2427186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2428186623f4SDavid Yang 
2429186623f4SDavid Yang 	if (res)
2430186623f4SDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
2431186623f4SDavid Yang 			port, res);
2432186623f4SDavid Yang }
2433186623f4SDavid Yang 
2434186623f4SDavid Yang static void
2435186623f4SDavid Yang yt921x_phylink_mac_link_up(struct phylink_config *config,
2436186623f4SDavid Yang 			   struct phy_device *phydev, unsigned int mode,
2437186623f4SDavid Yang 			   phy_interface_t interface, int speed, int duplex,
2438186623f4SDavid Yang 			   bool tx_pause, bool rx_pause)
2439186623f4SDavid Yang {
2440186623f4SDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
2441186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
2442186623f4SDavid Yang 	int port = dp->index;
2443186623f4SDavid Yang 	int res;
2444186623f4SDavid Yang 
2445186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2446186623f4SDavid Yang 	res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
2447186623f4SDavid Yang 			     tx_pause, rx_pause);
2448186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2449186623f4SDavid Yang 
2450186623f4SDavid Yang 	if (res)
2451186623f4SDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
2452186623f4SDavid Yang 			port, res);
2453186623f4SDavid Yang 
2454186623f4SDavid Yang 	schedule_delayed_work(&priv->ports[port].mib_read, 0);
2455186623f4SDavid Yang }
2456186623f4SDavid Yang 
2457186623f4SDavid Yang static void
2458186623f4SDavid Yang yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
2459186623f4SDavid Yang 			  const struct phylink_link_state *state)
2460186623f4SDavid Yang {
2461186623f4SDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
2462186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
2463186623f4SDavid Yang 	int port = dp->index;
2464186623f4SDavid Yang 	int res;
2465186623f4SDavid Yang 
2466186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2467186623f4SDavid Yang 	res = yt921x_port_config(priv, port, mode, state->interface);
2468186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2469186623f4SDavid Yang 
2470186623f4SDavid Yang 	if (res)
2471186623f4SDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
2472186623f4SDavid Yang 			port, res);
2473186623f4SDavid Yang }
2474186623f4SDavid Yang 
2475186623f4SDavid Yang static void
2476186623f4SDavid Yang yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
2477186623f4SDavid Yang 			    struct phylink_config *config)
2478186623f4SDavid Yang {
2479186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2480186623f4SDavid Yang 	const struct yt921x_info *info = priv->info;
2481186623f4SDavid Yang 
2482186623f4SDavid Yang 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
2483186623f4SDavid Yang 				   MAC_10 | MAC_100 | MAC_1000;
2484186623f4SDavid Yang 
2485186623f4SDavid Yang 	if (info->internal_mask & BIT(port)) {
2486186623f4SDavid Yang 		/* Port 10 for MCU should probably go here too. But since that
2487186623f4SDavid Yang 		 * is untested yet, turn it down for the moment by letting it
2488186623f4SDavid Yang 		 * fall to the default branch.
2489186623f4SDavid Yang 		 */
2490186623f4SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
2491186623f4SDavid Yang 			  config->supported_interfaces);
2492186623f4SDavid Yang 	} else if (info->external_mask & BIT(port)) {
2493186623f4SDavid Yang 		/* TODO: external ports may support SERDES only, XMII only, or
2494186623f4SDavid Yang 		 * SERDES + XMII depending on the chip. However, we can't get
2495186623f4SDavid Yang 		 * the accurate config table due to lack of document, thus
2496186623f4SDavid Yang 		 * we simply declare SERDES + XMII and rely on the correctness
2497186623f4SDavid Yang 		 * of devicetree for now.
2498186623f4SDavid Yang 		 */
2499186623f4SDavid Yang 
2500186623f4SDavid Yang 		/* SERDES */
2501186623f4SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_SGMII,
2502186623f4SDavid Yang 			  config->supported_interfaces);
2503186623f4SDavid Yang 		/* REVSGMII (SGMII in PHY role) should go here, once
2504186623f4SDavid Yang 		 * PHY_INTERFACE_MODE_REVSGMII is introduced.
2505186623f4SDavid Yang 		 */
2506186623f4SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_100BASEX,
2507186623f4SDavid Yang 			  config->supported_interfaces);
2508186623f4SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
2509186623f4SDavid Yang 			  config->supported_interfaces);
2510186623f4SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
2511186623f4SDavid Yang 			  config->supported_interfaces);
2512186623f4SDavid Yang 		config->mac_capabilities |= MAC_2500FD;
2513186623f4SDavid Yang 
2514186623f4SDavid Yang 		/* XMII */
2515186623f4SDavid Yang 
2516186623f4SDavid Yang 		/* Not tested. To add support for XMII:
2517186623f4SDavid Yang 		 *   - Add proper interface modes below
2518186623f4SDavid Yang 		 *   - Handle them in yt921x_port_config()
2519186623f4SDavid Yang 		 */
2520186623f4SDavid Yang 	}
2521186623f4SDavid Yang 	/* no such port: empty supported_interfaces causes phylink to turn it
2522186623f4SDavid Yang 	 * down
2523186623f4SDavid Yang 	 */
2524186623f4SDavid Yang }
2525186623f4SDavid Yang 
2526186623f4SDavid Yang static int yt921x_port_setup(struct yt921x_priv *priv, int port)
2527186623f4SDavid Yang {
2528186623f4SDavid Yang 	struct dsa_switch *ds = &priv->ds;
2529186623f4SDavid Yang 	u32 ctrl;
2530186623f4SDavid Yang 	int res;
2531186623f4SDavid Yang 
2532186623f4SDavid Yang 	res = yt921x_userport_standalone(priv, port);
2533186623f4SDavid Yang 	if (res)
2534186623f4SDavid Yang 		return res;
2535186623f4SDavid Yang 
2536186623f4SDavid Yang 	if (dsa_is_cpu_port(ds, port)) {
2537186623f4SDavid Yang 		/* Egress of CPU port is supposed to be completely controlled
2538186623f4SDavid Yang 		 * via tagging, so set to oneway isolated (drop all packets
2539186623f4SDavid Yang 		 * without tag).
2540186623f4SDavid Yang 		 */
2541186623f4SDavid Yang 		ctrl = ~(u32)0;
2542186623f4SDavid Yang 		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
2543186623f4SDavid Yang 				       ctrl);
2544186623f4SDavid Yang 		if (res)
2545186623f4SDavid Yang 			return res;
2546186623f4SDavid Yang 
2547186623f4SDavid Yang 		/* To simplify FDB "isolation" simulation, we also disable
2548186623f4SDavid Yang 		 * learning on the CPU port, and let software identify packets
2549186623f4SDavid Yang 		 * towarding CPU (either trapped or a static FDB entry is
2550186623f4SDavid Yang 		 * matched, no matter which bridge that entry is for), which is
2551186623f4SDavid Yang 		 * already done by yt921x_userport_standalone(). As a result,
2552186623f4SDavid Yang 		 * VLAN-awareness becomes unrelated on the CPU port (set to
2553186623f4SDavid Yang 		 * VLAN-unaware by the way).
2554186623f4SDavid Yang 		 */
2555186623f4SDavid Yang 	}
2556186623f4SDavid Yang 
2557186623f4SDavid Yang 	return 0;
2558186623f4SDavid Yang }
2559186623f4SDavid Yang 
2560186623f4SDavid Yang static enum dsa_tag_protocol
2561186623f4SDavid Yang yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
2562186623f4SDavid Yang 			    enum dsa_tag_protocol m)
2563186623f4SDavid Yang {
2564186623f4SDavid Yang 	return DSA_TAG_PROTO_YT921X;
2565186623f4SDavid Yang }
2566186623f4SDavid Yang 
2567186623f4SDavid Yang static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
2568186623f4SDavid Yang {
2569186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2570186623f4SDavid Yang 	int res;
2571186623f4SDavid Yang 
2572186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2573186623f4SDavid Yang 	res = yt921x_port_setup(priv, port);
2574186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2575186623f4SDavid Yang 
2576186623f4SDavid Yang 	return res;
2577186623f4SDavid Yang }
2578186623f4SDavid Yang 
2579186623f4SDavid Yang static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp)
2580186623f4SDavid Yang {
2581186623f4SDavid Yang 	u32 val = YT921X_EDATA_DATA_IDLE;
2582186623f4SDavid Yang 	int res;
2583186623f4SDavid Yang 
2584186623f4SDavid Yang 	res = yt921x_reg_wait(priv, YT921X_EDATA_DATA,
2585186623f4SDavid Yang 			      YT921X_EDATA_DATA_STATUS_M, &val);
2586186623f4SDavid Yang 	if (res)
2587186623f4SDavid Yang 		return res;
2588186623f4SDavid Yang 
2589186623f4SDavid Yang 	*valp = val;
2590186623f4SDavid Yang 	return 0;
2591186623f4SDavid Yang }
2592186623f4SDavid Yang 
2593186623f4SDavid Yang static int
2594186623f4SDavid Yang yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp)
2595186623f4SDavid Yang {
2596186623f4SDavid Yang 	u32 ctrl;
2597186623f4SDavid Yang 	u32 val;
2598186623f4SDavid Yang 	int res;
2599186623f4SDavid Yang 
2600186623f4SDavid Yang 	ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
2601186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
2602186623f4SDavid Yang 	if (res)
2603186623f4SDavid Yang 		return res;
2604186623f4SDavid Yang 	res = yt921x_edata_wait(priv, &val);
2605186623f4SDavid Yang 	if (res)
2606186623f4SDavid Yang 		return res;
2607186623f4SDavid Yang 
2608186623f4SDavid Yang 	*valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
2609186623f4SDavid Yang 	return 0;
2610186623f4SDavid Yang }
2611186623f4SDavid Yang 
2612186623f4SDavid Yang static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
2613186623f4SDavid Yang {
2614186623f4SDavid Yang 	u32 val;
2615186623f4SDavid Yang 	int res;
2616186623f4SDavid Yang 
2617186623f4SDavid Yang 	res = yt921x_edata_wait(priv, &val);
2618186623f4SDavid Yang 	if (res)
2619186623f4SDavid Yang 		return res;
2620186623f4SDavid Yang 	return yt921x_edata_read_cont(priv, addr, valp);
2621186623f4SDavid Yang }
2622186623f4SDavid Yang 
2623186623f4SDavid Yang static int yt921x_chip_detect(struct yt921x_priv *priv)
2624186623f4SDavid Yang {
2625186623f4SDavid Yang 	struct device *dev = to_device(priv);
2626186623f4SDavid Yang 	const struct yt921x_info *info;
2627186623f4SDavid Yang 	u8 extmode;
2628186623f4SDavid Yang 	u32 chipid;
2629186623f4SDavid Yang 	u32 major;
2630186623f4SDavid Yang 	u32 mode;
2631186623f4SDavid Yang 	int res;
2632186623f4SDavid Yang 
2633186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
2634186623f4SDavid Yang 	if (res)
2635186623f4SDavid Yang 		return res;
2636186623f4SDavid Yang 
2637186623f4SDavid Yang 	major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
2638186623f4SDavid Yang 
2639186623f4SDavid Yang 	for (info = yt921x_infos; info->name; info++)
2640186623f4SDavid Yang 		if (info->major == major)
2641186623f4SDavid Yang 			break;
2642186623f4SDavid Yang 	if (!info->name) {
2643186623f4SDavid Yang 		dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
2644186623f4SDavid Yang 		return -ENODEV;
2645186623f4SDavid Yang 	}
2646186623f4SDavid Yang 
2647186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode);
2648186623f4SDavid Yang 	if (res)
2649186623f4SDavid Yang 		return res;
2650186623f4SDavid Yang 	res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
2651186623f4SDavid Yang 	if (res)
2652186623f4SDavid Yang 		return res;
2653186623f4SDavid Yang 
2654186623f4SDavid Yang 	for (; info->name; info++)
2655186623f4SDavid Yang 		if (info->major == major && info->mode == mode &&
2656186623f4SDavid Yang 		    info->extmode == extmode)
2657186623f4SDavid Yang 			break;
2658186623f4SDavid Yang 	if (!info->name) {
2659186623f4SDavid Yang 		dev_err(dev,
2660186623f4SDavid Yang 			"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
2661186623f4SDavid Yang 			chipid, mode, extmode);
2662186623f4SDavid Yang 		return -ENODEV;
2663186623f4SDavid Yang 	}
2664186623f4SDavid Yang 
2665186623f4SDavid Yang 	/* Print chipid here since we are interested in lower 16 bits */
2666186623f4SDavid Yang 	dev_info(dev,
2667186623f4SDavid Yang 		 "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
2668186623f4SDavid Yang 		 info->name, chipid, mode, extmode);
2669186623f4SDavid Yang 
2670186623f4SDavid Yang 	priv->info = info;
2671186623f4SDavid Yang 	return 0;
2672186623f4SDavid Yang }
2673186623f4SDavid Yang 
2674186623f4SDavid Yang static int yt921x_chip_reset(struct yt921x_priv *priv)
2675186623f4SDavid Yang {
2676186623f4SDavid Yang 	struct device *dev = to_device(priv);
2677186623f4SDavid Yang 	u16 eth_p_tag;
2678186623f4SDavid Yang 	u32 val;
2679186623f4SDavid Yang 	int res;
2680186623f4SDavid Yang 
2681186623f4SDavid Yang 	res = yt921x_chip_detect(priv);
2682186623f4SDavid Yang 	if (res)
2683186623f4SDavid Yang 		return res;
2684186623f4SDavid Yang 
2685186623f4SDavid Yang 	/* Reset */
2686186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
2687186623f4SDavid Yang 	if (res)
2688186623f4SDavid Yang 		return res;
2689186623f4SDavid Yang 
2690186623f4SDavid Yang 	/* RST_HW is almost same as GPIO hard reset, so we need this delay. */
2691186623f4SDavid Yang 	fsleep(YT921X_RST_DELAY_US);
2692186623f4SDavid Yang 
2693186623f4SDavid Yang 	val = 0;
2694186623f4SDavid Yang 	res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
2695186623f4SDavid Yang 	if (res)
2696186623f4SDavid Yang 		return res;
2697186623f4SDavid Yang 
2698186623f4SDavid Yang 	/* Check for tag EtherType; do it after reset in case you messed it up
2699186623f4SDavid Yang 	 * before.
2700186623f4SDavid Yang 	 */
2701186623f4SDavid Yang 	res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
2702186623f4SDavid Yang 	if (res)
2703186623f4SDavid Yang 		return res;
2704186623f4SDavid Yang 	eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
2705186623f4SDavid Yang 	if (eth_p_tag != ETH_P_YT921X) {
2706186623f4SDavid Yang 		dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
2707186623f4SDavid Yang 			ETH_P_YT921X);
2708186623f4SDavid Yang 		/* Despite being possible, we choose not to set CPU_TAG_TPID,
2709186623f4SDavid Yang 		 * since there is no way it can be different unless you have the
2710186623f4SDavid Yang 		 * wrong chip.
2711186623f4SDavid Yang 		 */
2712186623f4SDavid Yang 		return -EINVAL;
2713186623f4SDavid Yang 	}
2714186623f4SDavid Yang 
2715186623f4SDavid Yang 	return 0;
2716186623f4SDavid Yang }
2717186623f4SDavid Yang 
2718186623f4SDavid Yang static int yt921x_chip_setup(struct yt921x_priv *priv)
2719186623f4SDavid Yang {
2720186623f4SDavid Yang 	struct dsa_switch *ds = &priv->ds;
2721186623f4SDavid Yang 	unsigned long cpu_ports_mask;
2722186623f4SDavid Yang 	u64 ctrl64;
2723186623f4SDavid Yang 	u32 ctrl;
2724186623f4SDavid Yang 	int port;
2725186623f4SDavid Yang 	int res;
2726186623f4SDavid Yang 
2727186623f4SDavid Yang 	/* Enable DSA */
2728186623f4SDavid Yang 	priv->cpu_ports_mask = dsa_cpu_ports(ds);
2729186623f4SDavid Yang 
2730186623f4SDavid Yang 	ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
2731186623f4SDavid Yang 	       YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
2732186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
2733186623f4SDavid Yang 	if (res)
2734186623f4SDavid Yang 		return res;
2735186623f4SDavid Yang 
2736186623f4SDavid Yang 	/* Enable and clear MIB */
2737186623f4SDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_FUNC, YT921X_FUNC_MIB);
2738186623f4SDavid Yang 	if (res)
2739186623f4SDavid Yang 		return res;
2740186623f4SDavid Yang 
2741186623f4SDavid Yang 	ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
2742186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);
2743186623f4SDavid Yang 	if (res)
2744186623f4SDavid Yang 		return res;
2745186623f4SDavid Yang 
2746186623f4SDavid Yang 	/* Setup software switch */
2747186623f4SDavid Yang 	ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
2748186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl);
2749186623f4SDavid Yang 	if (res)
2750186623f4SDavid Yang 		return res;
2751186623f4SDavid Yang 
2752186623f4SDavid Yang 	ctrl = GENMASK(10, 0);
2753186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
2754186623f4SDavid Yang 	if (res)
2755186623f4SDavid Yang 		return res;
2756186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
2757186623f4SDavid Yang 	if (res)
2758186623f4SDavid Yang 		return res;
2759186623f4SDavid Yang 
2760186623f4SDavid Yang 	/* YT921x does not support native DSA port bridging, so we use port
2761186623f4SDavid Yang 	 * isolation to emulate it. However, be especially careful that port
2762186623f4SDavid Yang 	 * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
2763186623f4SDavid Yang 	 * another bridge) is matched and the destination port (in another
2764186623f4SDavid Yang 	 * bridge) is blocked, the packet will be dropped instead of flooding to
2765186623f4SDavid Yang 	 * the "bridged" ports, thus we need to trap and handle those packets by
2766186623f4SDavid Yang 	 * software.
2767186623f4SDavid Yang 	 *
2768186623f4SDavid Yang 	 * If there is no more than one bridge, we might be able to drop them
2769186623f4SDavid Yang 	 * directly given some conditions are met, but we trap them in all cases
2770186623f4SDavid Yang 	 * for now.
2771186623f4SDavid Yang 	 */
2772186623f4SDavid Yang 	ctrl = 0;
2773186623f4SDavid Yang 	for (int i = 0; i < YT921X_PORT_NUM; i++)
2774186623f4SDavid Yang 		ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
2775186623f4SDavid Yang 	/* Except for CPU ports, if any packets are sent via CPU ports without
2776186623f4SDavid Yang 	 * tag, they should be dropped.
2777186623f4SDavid Yang 	 */
2778186623f4SDavid Yang 	cpu_ports_mask = priv->cpu_ports_mask;
2779186623f4SDavid Yang 	for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
2780186623f4SDavid Yang 		ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
2781186623f4SDavid Yang 		ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
2782186623f4SDavid Yang 	}
2783186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
2784186623f4SDavid Yang 	if (res)
2785186623f4SDavid Yang 		return res;
2786186623f4SDavid Yang 	res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
2787186623f4SDavid Yang 	if (res)
2788186623f4SDavid Yang 		return res;
2789186623f4SDavid Yang 
2790186623f4SDavid Yang 	/* Tagged VID 0 should be treated as untagged, which confuses the
2791186623f4SDavid Yang 	 * hardware a lot
2792186623f4SDavid Yang 	 */
2793186623f4SDavid Yang 	ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M;
2794186623f4SDavid Yang 	res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64);
2795186623f4SDavid Yang 	if (res)
2796186623f4SDavid Yang 		return res;
2797186623f4SDavid Yang 
2798186623f4SDavid Yang 	/* Miscellaneous */
2799186623f4SDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
2800186623f4SDavid Yang 	if (res)
2801186623f4SDavid Yang 		return res;
2802186623f4SDavid Yang 
2803186623f4SDavid Yang 	return 0;
2804186623f4SDavid Yang }
2805186623f4SDavid Yang 
2806186623f4SDavid Yang static int yt921x_dsa_setup(struct dsa_switch *ds)
2807186623f4SDavid Yang {
2808186623f4SDavid Yang 	struct yt921x_priv *priv = to_yt921x_priv(ds);
2809186623f4SDavid Yang 	struct device *dev = to_device(priv);
2810186623f4SDavid Yang 	struct device_node *np = dev->of_node;
2811186623f4SDavid Yang 	struct device_node *child;
2812186623f4SDavid Yang 	int res;
2813186623f4SDavid Yang 
2814186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2815186623f4SDavid Yang 	res = yt921x_chip_reset(priv);
2816186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2817186623f4SDavid Yang 
2818186623f4SDavid Yang 	if (res)
2819186623f4SDavid Yang 		return res;
2820186623f4SDavid Yang 
2821186623f4SDavid Yang 	/* Register the internal mdio bus. Nodes for internal ports should have
2822186623f4SDavid Yang 	 * proper phy-handle pointing to their PHYs. Not enabling the internal
2823186623f4SDavid Yang 	 * bus is possible, though pretty wired, if internal ports are not used.
2824186623f4SDavid Yang 	 */
2825186623f4SDavid Yang 	child = of_get_child_by_name(np, "mdio");
2826186623f4SDavid Yang 	if (child) {
2827186623f4SDavid Yang 		res = yt921x_mbus_int_init(priv, child);
2828186623f4SDavid Yang 		of_node_put(child);
2829186623f4SDavid Yang 		if (res)
2830186623f4SDavid Yang 			return res;
2831186623f4SDavid Yang 	}
2832186623f4SDavid Yang 
2833186623f4SDavid Yang 	/* External mdio bus is optional */
2834186623f4SDavid Yang 	child = of_get_child_by_name(np, "mdio-external");
2835186623f4SDavid Yang 	if (child) {
2836186623f4SDavid Yang 		res = yt921x_mbus_ext_init(priv, child);
2837186623f4SDavid Yang 		of_node_put(child);
2838186623f4SDavid Yang 		if (res)
2839186623f4SDavid Yang 			return res;
2840186623f4SDavid Yang 
2841186623f4SDavid Yang 		dev_err(dev, "Untested external mdio bus\n");
2842186623f4SDavid Yang 		return -ENODEV;
2843186623f4SDavid Yang 	}
2844186623f4SDavid Yang 
2845186623f4SDavid Yang 	mutex_lock(&priv->reg_lock);
2846186623f4SDavid Yang 	res = yt921x_chip_setup(priv);
2847186623f4SDavid Yang 	mutex_unlock(&priv->reg_lock);
2848186623f4SDavid Yang 
2849186623f4SDavid Yang 	if (res)
2850186623f4SDavid Yang 		return res;
2851186623f4SDavid Yang 
2852186623f4SDavid Yang 	return 0;
2853186623f4SDavid Yang }
2854186623f4SDavid Yang 
2855186623f4SDavid Yang static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
2856186623f4SDavid Yang 	.mac_link_down	= yt921x_phylink_mac_link_down,
2857186623f4SDavid Yang 	.mac_link_up	= yt921x_phylink_mac_link_up,
2858186623f4SDavid Yang 	.mac_config	= yt921x_phylink_mac_config,
2859186623f4SDavid Yang };
2860186623f4SDavid Yang 
2861186623f4SDavid Yang static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
2862186623f4SDavid Yang 	/* mib */
2863186623f4SDavid Yang 	.get_strings		= yt921x_dsa_get_strings,
2864186623f4SDavid Yang 	.get_ethtool_stats	= yt921x_dsa_get_ethtool_stats,
2865186623f4SDavid Yang 	.get_sset_count		= yt921x_dsa_get_sset_count,
2866186623f4SDavid Yang 	.get_eth_mac_stats	= yt921x_dsa_get_eth_mac_stats,
2867186623f4SDavid Yang 	.get_eth_ctrl_stats	= yt921x_dsa_get_eth_ctrl_stats,
2868186623f4SDavid Yang 	.get_rmon_stats		= yt921x_dsa_get_rmon_stats,
2869186623f4SDavid Yang 	.get_stats64		= yt921x_dsa_get_stats64,
2870186623f4SDavid Yang 	.get_pause_stats	= yt921x_dsa_get_pause_stats,
2871186623f4SDavid Yang 	/* eee */
2872186623f4SDavid Yang 	.support_eee		= dsa_supports_eee,
2873186623f4SDavid Yang 	.set_mac_eee		= yt921x_dsa_set_mac_eee,
2874186623f4SDavid Yang 	/* mtu */
2875186623f4SDavid Yang 	.port_change_mtu	= yt921x_dsa_port_change_mtu,
2876186623f4SDavid Yang 	.port_max_mtu		= yt921x_dsa_port_max_mtu,
2877*42e63b13SVladimir Oltean 	/* hsr */
2878*42e63b13SVladimir Oltean 	.port_hsr_leave		= dsa_port_simple_hsr_leave,
2879*42e63b13SVladimir Oltean 	.port_hsr_join		= dsa_port_simple_hsr_join,
2880186623f4SDavid Yang 	/* mirror */
2881186623f4SDavid Yang 	.port_mirror_del	= yt921x_dsa_port_mirror_del,
2882186623f4SDavid Yang 	.port_mirror_add	= yt921x_dsa_port_mirror_add,
2883186623f4SDavid Yang 	/* fdb */
2884186623f4SDavid Yang 	.port_fdb_dump		= yt921x_dsa_port_fdb_dump,
2885186623f4SDavid Yang 	.port_fast_age		= yt921x_dsa_port_fast_age,
2886186623f4SDavid Yang 	.set_ageing_time	= yt921x_dsa_set_ageing_time,
2887186623f4SDavid Yang 	.port_fdb_del		= yt921x_dsa_port_fdb_del,
2888186623f4SDavid Yang 	.port_fdb_add		= yt921x_dsa_port_fdb_add,
2889186623f4SDavid Yang 	.port_mdb_del		= yt921x_dsa_port_mdb_del,
2890186623f4SDavid Yang 	.port_mdb_add		= yt921x_dsa_port_mdb_add,
2891186623f4SDavid Yang 	/* vlan */
2892186623f4SDavid Yang 	.port_vlan_filtering	= yt921x_dsa_port_vlan_filtering,
2893186623f4SDavid Yang 	.port_vlan_del		= yt921x_dsa_port_vlan_del,
2894186623f4SDavid Yang 	.port_vlan_add		= yt921x_dsa_port_vlan_add,
2895186623f4SDavid Yang 	/* bridge */
2896186623f4SDavid Yang 	.port_pre_bridge_flags	= yt921x_dsa_port_pre_bridge_flags,
2897186623f4SDavid Yang 	.port_bridge_flags	= yt921x_dsa_port_bridge_flags,
2898186623f4SDavid Yang 	.port_bridge_leave	= yt921x_dsa_port_bridge_leave,
2899186623f4SDavid Yang 	.port_bridge_join	= yt921x_dsa_port_bridge_join,
2900633b1d01SDavid Yang 	/* mst */
2901633b1d01SDavid Yang 	.port_mst_state_set	= yt921x_dsa_port_mst_state_set,
2902633b1d01SDavid Yang 	.vlan_msti_set		= yt921x_dsa_vlan_msti_set,
2903633b1d01SDavid Yang 	.port_stp_state_set	= yt921x_dsa_port_stp_state_set,
2904186623f4SDavid Yang 	/* port */
2905186623f4SDavid Yang 	.get_tag_protocol	= yt921x_dsa_get_tag_protocol,
2906186623f4SDavid Yang 	.phylink_get_caps	= yt921x_dsa_phylink_get_caps,
2907186623f4SDavid Yang 	.port_setup		= yt921x_dsa_port_setup,
2908186623f4SDavid Yang 	/* chip */
2909186623f4SDavid Yang 	.setup			= yt921x_dsa_setup,
2910186623f4SDavid Yang };
2911186623f4SDavid Yang 
2912186623f4SDavid Yang static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
2913186623f4SDavid Yang {
2914186623f4SDavid Yang 	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
2915186623f4SDavid Yang 
2916186623f4SDavid Yang 	if (!priv)
2917186623f4SDavid Yang 		return;
2918186623f4SDavid Yang 
2919186623f4SDavid Yang 	dsa_switch_shutdown(&priv->ds);
2920186623f4SDavid Yang }
2921186623f4SDavid Yang 
2922186623f4SDavid Yang static void yt921x_mdio_remove(struct mdio_device *mdiodev)
2923186623f4SDavid Yang {
2924186623f4SDavid Yang 	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
2925186623f4SDavid Yang 
2926186623f4SDavid Yang 	if (!priv)
2927186623f4SDavid Yang 		return;
2928186623f4SDavid Yang 
2929186623f4SDavid Yang 	for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
2930186623f4SDavid Yang 		struct yt921x_port *pp = &priv->ports[i];
2931186623f4SDavid Yang 
2932186623f4SDavid Yang 		disable_delayed_work_sync(&pp->mib_read);
2933186623f4SDavid Yang 	}
2934186623f4SDavid Yang 
2935186623f4SDavid Yang 	dsa_unregister_switch(&priv->ds);
2936186623f4SDavid Yang 
2937186623f4SDavid Yang 	mutex_destroy(&priv->reg_lock);
2938186623f4SDavid Yang }
2939186623f4SDavid Yang 
2940186623f4SDavid Yang static int yt921x_mdio_probe(struct mdio_device *mdiodev)
2941186623f4SDavid Yang {
2942186623f4SDavid Yang 	struct device *dev = &mdiodev->dev;
2943186623f4SDavid Yang 	struct yt921x_reg_mdio *mdio;
2944186623f4SDavid Yang 	struct yt921x_priv *priv;
2945186623f4SDavid Yang 	struct dsa_switch *ds;
2946186623f4SDavid Yang 
2947186623f4SDavid Yang 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2948186623f4SDavid Yang 	if (!priv)
2949186623f4SDavid Yang 		return -ENOMEM;
2950186623f4SDavid Yang 
2951186623f4SDavid Yang 	mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
2952186623f4SDavid Yang 	if (!mdio)
2953186623f4SDavid Yang 		return -ENOMEM;
2954186623f4SDavid Yang 
2955186623f4SDavid Yang 	mdio->bus = mdiodev->bus;
2956186623f4SDavid Yang 	mdio->addr = mdiodev->addr;
2957186623f4SDavid Yang 	mdio->switchid = 0;
2958186623f4SDavid Yang 
2959186623f4SDavid Yang 	mutex_init(&priv->reg_lock);
2960186623f4SDavid Yang 
2961186623f4SDavid Yang 	priv->reg_ops = &yt921x_reg_ops_mdio;
2962186623f4SDavid Yang 	priv->reg_ctx = mdio;
2963186623f4SDavid Yang 
2964186623f4SDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
2965186623f4SDavid Yang 		struct yt921x_port *pp = &priv->ports[i];
2966186623f4SDavid Yang 
2967186623f4SDavid Yang 		pp->index = i;
2968186623f4SDavid Yang 		INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
2969186623f4SDavid Yang 	}
2970186623f4SDavid Yang 
2971186623f4SDavid Yang 	ds = &priv->ds;
2972186623f4SDavid Yang 	ds->dev = dev;
2973186623f4SDavid Yang 	ds->assisted_learning_on_cpu_port = true;
2974186623f4SDavid Yang 	ds->priv = priv;
2975186623f4SDavid Yang 	ds->ops = &yt921x_dsa_switch_ops;
2976ea2d3befSDavid Yang 	ds->ageing_time_min = 1 * 5000;
2977ea2d3befSDavid Yang 	ds->ageing_time_max = U16_MAX * 5000;
2978186623f4SDavid Yang 	ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
2979186623f4SDavid Yang 	ds->num_ports = YT921X_PORT_NUM;
2980186623f4SDavid Yang 
2981186623f4SDavid Yang 	mdiodev_set_drvdata(mdiodev, priv);
2982186623f4SDavid Yang 
2983186623f4SDavid Yang 	return dsa_register_switch(ds);
2984186623f4SDavid Yang }
2985186623f4SDavid Yang 
2986186623f4SDavid Yang static const struct of_device_id yt921x_of_match[] = {
2987186623f4SDavid Yang 	{ .compatible = "motorcomm,yt9215" },
2988186623f4SDavid Yang 	{}
2989186623f4SDavid Yang };
2990186623f4SDavid Yang MODULE_DEVICE_TABLE(of, yt921x_of_match);
2991186623f4SDavid Yang 
2992186623f4SDavid Yang static struct mdio_driver yt921x_mdio_driver = {
2993186623f4SDavid Yang 	.probe = yt921x_mdio_probe,
2994186623f4SDavid Yang 	.remove = yt921x_mdio_remove,
2995186623f4SDavid Yang 	.shutdown = yt921x_mdio_shutdown,
2996186623f4SDavid Yang 	.mdiodrv.driver = {
2997186623f4SDavid Yang 		.name = YT921X_NAME,
2998186623f4SDavid Yang 		.of_match_table = yt921x_of_match,
2999186623f4SDavid Yang 	},
3000186623f4SDavid Yang };
3001186623f4SDavid Yang 
3002186623f4SDavid Yang mdio_module_driver(yt921x_mdio_driver);
3003186623f4SDavid Yang 
3004186623f4SDavid Yang MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
3005186623f4SDavid Yang MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch");
3006186623f4SDavid Yang MODULE_LICENSE("GPL");
3007