xref: /linux/drivers/net/dsa/motorcomm/chip.c (revision 5d7f3375cd574fc83f0cd30839b21455c0e79ae2)
19c472a0dSDavid Yang // SPDX-License-Identifier: GPL-2.0-or-later
29c472a0dSDavid Yang /*
39c472a0dSDavid Yang  * Driver for Motorcomm YT921x Switch
49c472a0dSDavid Yang  *
59c472a0dSDavid Yang  * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII,
69c472a0dSDavid Yang  * be sure to do your own checks before porting to another chip.
79c472a0dSDavid Yang  *
89c472a0dSDavid Yang  * Copyright (c) 2025 David Yang
99c472a0dSDavid Yang  */
109c472a0dSDavid Yang 
119c472a0dSDavid Yang #include <linux/dcbnl.h>
129c472a0dSDavid Yang #include <linux/etherdevice.h>
139c472a0dSDavid Yang #include <linux/if_bridge.h>
149c472a0dSDavid Yang #include <linux/if_hsr.h>
159c472a0dSDavid Yang #include <linux/if_vlan.h>
169c472a0dSDavid Yang #include <linux/mdio.h>
179c472a0dSDavid Yang #include <linux/module.h>
189c472a0dSDavid Yang #include <linux/of.h>
199c472a0dSDavid Yang #include <linux/of_mdio.h>
209c472a0dSDavid Yang #include <linux/of_net.h>
219c472a0dSDavid Yang #include <linux/sort.h>
22ed445358SDavid Yang #include <linux/u64_stats_sync.h>
239c472a0dSDavid Yang 
249c472a0dSDavid Yang #include <net/dsa.h>
259c472a0dSDavid Yang #include <net/dscp.h>
269c472a0dSDavid Yang #include <net/ieee8021q.h>
279c472a0dSDavid Yang 
289c472a0dSDavid Yang #include "chip.h"
299af9a816SDavid Yang #include "leds.h"
30b9eca1c2SDavid Yang #include "mdio_bus.h"
3125265e7cSDavid Yang #include "mib.h"
324b8fbb87SDavid Yang #include "pcs.h"
33e9423a64SDavid Yang #include "smi.h"
34ba13bd27SDavid Yang #include "tc.h"
359c472a0dSDavid Yang 
369c472a0dSDavid Yang static const struct yt921x_info yt921x_infos[] = {
379c472a0dSDavid Yang 	{
389c472a0dSDavid Yang 		"YT9215SC", YT9215_MAJOR, 1, 0,
39b6d27629SDavid Yang 		GENMASK(4, 0),
40605597afSDavid Yang 		BIT(9),
41b6d27629SDavid Yang 		BIT(8) | BIT(9),
429c472a0dSDavid Yang 	},
439c472a0dSDavid Yang 	{
449c472a0dSDavid Yang 		"YT9215S", YT9215_MAJOR, 2, 0,
45b6d27629SDavid Yang 		GENMASK(4, 0),
46605597afSDavid Yang 		BIT(9),
47605597afSDavid Yang 		BIT(8),
489c472a0dSDavid Yang 	},
499c472a0dSDavid Yang 	{
509c472a0dSDavid Yang 		"YT9215RB", YT9215_MAJOR, 3, 0,
51b6d27629SDavid Yang 		GENMASK(4, 0),
52b6d27629SDavid Yang 		BIT(8) | BIT(9),
53605597afSDavid Yang 		0,
549c472a0dSDavid Yang 	},
559c472a0dSDavid Yang 	{
569c472a0dSDavid Yang 		"YT9214NB", YT9215_MAJOR, 3, 2,
57b6d27629SDavid Yang 		BIT(1) | BIT(3),
58605597afSDavid Yang 		BIT(9),
59605597afSDavid Yang 		BIT(8),
609c472a0dSDavid Yang 	},
619c472a0dSDavid Yang 	{
629c472a0dSDavid Yang 		"YT9213NB", YT9215_MAJOR, 3, 3,
63b6d27629SDavid Yang 		BIT(1) | BIT(3),
64b6d27629SDavid Yang 		BIT(9),
65605597afSDavid Yang 		BIT(9),
669c472a0dSDavid Yang 	},
679c472a0dSDavid Yang 	{
689c472a0dSDavid Yang 		"YT9218N", YT9218_MAJOR, 0, 0,
69b6d27629SDavid Yang 		GENMASK(7, 0),
709c472a0dSDavid Yang 		0,
71605597afSDavid Yang 		0,
729c472a0dSDavid Yang 	},
739c472a0dSDavid Yang 	{
749c472a0dSDavid Yang 		"YT9218MB", YT9218_MAJOR, 1, 0,
75b6d27629SDavid Yang 		GENMASK(7, 0),
76b6d27629SDavid Yang 		BIT(8) | BIT(9),
77605597afSDavid Yang 		BIT(8) | BIT(9),
789c472a0dSDavid Yang 	},
799c472a0dSDavid Yang 	{}
809c472a0dSDavid Yang };
819c472a0dSDavid Yang 
829c472a0dSDavid Yang #define YT921X_VID_UNWARE	4095
839c472a0dSDavid Yang 
849c472a0dSDavid Yang struct yt921x_reg_mdio {
859c472a0dSDavid Yang 	struct mii_bus *bus;
869c472a0dSDavid Yang 	int addr;
879c472a0dSDavid Yang 	/* SWITCH_ID_1 / SWITCH_ID_0 of the device
889c472a0dSDavid Yang 	 *
899c472a0dSDavid Yang 	 * This is a way to multiplex multiple devices on the same MII phyaddr
909c472a0dSDavid Yang 	 * and should be configurable in DT. However, MDIO core simply doesn't
919c472a0dSDavid Yang 	 * allow multiple devices over one reg addr, so this is a fixed value
929c472a0dSDavid Yang 	 * for now until a solution is found.
939c472a0dSDavid Yang 	 *
949c472a0dSDavid Yang 	 * Keep this because we need switchid to form MII regaddrs anyway.
959c472a0dSDavid Yang 	 */
969c472a0dSDavid Yang 	unsigned char switchid;
979c472a0dSDavid Yang };
989c472a0dSDavid Yang 
999c472a0dSDavid Yang /* TODO: SPI/I2C */
1009c472a0dSDavid Yang 
1019c472a0dSDavid Yang static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
1029c472a0dSDavid Yang {
1039c472a0dSDavid Yang 	struct yt921x_reg_mdio *mdio = context;
1049c472a0dSDavid Yang 	struct mii_bus *bus = mdio->bus;
1059c472a0dSDavid Yang 	int addr = mdio->addr;
1069c472a0dSDavid Yang 	u32 reg_addr;
1079c472a0dSDavid Yang 	u32 reg_data;
1089c472a0dSDavid Yang 	u32 val;
1099c472a0dSDavid Yang 	int res;
1109c472a0dSDavid Yang 
1119c472a0dSDavid Yang 	/* Hold the mdio bus lock to avoid (un)locking for 4 times */
1129c472a0dSDavid Yang 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
1139c472a0dSDavid Yang 
1149c472a0dSDavid Yang 	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
1159c472a0dSDavid Yang 		   YT921X_SMI_READ;
1169c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
1179c472a0dSDavid Yang 	if (res)
1189c472a0dSDavid Yang 		goto end;
1199c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
1209c472a0dSDavid Yang 	if (res)
1219c472a0dSDavid Yang 		goto end;
1229c472a0dSDavid Yang 
1239c472a0dSDavid Yang 	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
1249c472a0dSDavid Yang 		   YT921X_SMI_READ;
1259c472a0dSDavid Yang 	res = __mdiobus_read(bus, addr, reg_data);
1269c472a0dSDavid Yang 	if (res < 0)
1279c472a0dSDavid Yang 		goto end;
1289c472a0dSDavid Yang 	val = (u16)res;
1299c472a0dSDavid Yang 	res = __mdiobus_read(bus, addr, reg_data);
1309c472a0dSDavid Yang 	if (res < 0)
1319c472a0dSDavid Yang 		goto end;
1329c472a0dSDavid Yang 	val = (val << 16) | (u16)res;
1339c472a0dSDavid Yang 
1349c472a0dSDavid Yang 	*valp = val;
1359c472a0dSDavid Yang 	res = 0;
1369c472a0dSDavid Yang 
1379c472a0dSDavid Yang end:
1389c472a0dSDavid Yang 	mutex_unlock(&bus->mdio_lock);
1399c472a0dSDavid Yang 	return res;
1409c472a0dSDavid Yang }
1419c472a0dSDavid Yang 
1429c472a0dSDavid Yang static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
1439c472a0dSDavid Yang {
1449c472a0dSDavid Yang 	struct yt921x_reg_mdio *mdio = context;
1459c472a0dSDavid Yang 	struct mii_bus *bus = mdio->bus;
1469c472a0dSDavid Yang 	int addr = mdio->addr;
1479c472a0dSDavid Yang 	u32 reg_addr;
1489c472a0dSDavid Yang 	u32 reg_data;
1499c472a0dSDavid Yang 	int res;
1509c472a0dSDavid Yang 
1519c472a0dSDavid Yang 	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
1529c472a0dSDavid Yang 
1539c472a0dSDavid Yang 	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
1549c472a0dSDavid Yang 		   YT921X_SMI_WRITE;
1559c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
1569c472a0dSDavid Yang 	if (res)
1579c472a0dSDavid Yang 		goto end;
1589c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
1599c472a0dSDavid Yang 	if (res)
1609c472a0dSDavid Yang 		goto end;
1619c472a0dSDavid Yang 
1629c472a0dSDavid Yang 	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
1639c472a0dSDavid Yang 		   YT921X_SMI_WRITE;
1649c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
1659c472a0dSDavid Yang 	if (res)
1669c472a0dSDavid Yang 		goto end;
1679c472a0dSDavid Yang 	res = __mdiobus_write(bus, addr, reg_data, (u16)val);
1689c472a0dSDavid Yang 	if (res)
1699c472a0dSDavid Yang 		goto end;
1709c472a0dSDavid Yang 
1719c472a0dSDavid Yang 	res = 0;
1729c472a0dSDavid Yang 
1739c472a0dSDavid Yang end:
1749c472a0dSDavid Yang 	mutex_unlock(&bus->mdio_lock);
1759c472a0dSDavid Yang 	return res;
1769c472a0dSDavid Yang }
1779c472a0dSDavid Yang 
1789c472a0dSDavid Yang static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
1799c472a0dSDavid Yang 	.read = yt921x_reg_mdio_read,
1809c472a0dSDavid Yang 	.write = yt921x_reg_mdio_write,
1819c472a0dSDavid Yang };
1829c472a0dSDavid Yang 
1839c472a0dSDavid Yang /* TODO: SPI/I2C */
1849c472a0dSDavid Yang 
1859c472a0dSDavid Yang static int
1869c472a0dSDavid Yang yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
1879c472a0dSDavid Yang {
1889c472a0dSDavid Yang 	/* Poor datasheet for EEE operations; don't ask if you are confused */
1899c472a0dSDavid Yang 
1909c472a0dSDavid Yang 	bool enable = e->eee_enabled;
1919c472a0dSDavid Yang 	u16 new_mask;
1929c472a0dSDavid Yang 	int res;
1939c472a0dSDavid Yang 
1949c472a0dSDavid Yang 	/* Enable / disable global EEE */
1959c472a0dSDavid Yang 	new_mask = priv->eee_ports_mask;
1969c472a0dSDavid Yang 	new_mask &= ~BIT(port);
1979c472a0dSDavid Yang 	new_mask |= !enable ? 0 : BIT(port);
1989c472a0dSDavid Yang 
1999c472a0dSDavid Yang 	if (!!new_mask != !!priv->eee_ports_mask) {
2009c472a0dSDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
2019c472a0dSDavid Yang 					     YT921X_PON_STRAP_EEE, !!new_mask);
2029c472a0dSDavid Yang 		if (res)
2039c472a0dSDavid Yang 			return res;
2049c472a0dSDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
2059c472a0dSDavid Yang 					     YT921X_PON_STRAP_EEE, !!new_mask);
2069c472a0dSDavid Yang 		if (res)
2079c472a0dSDavid Yang 			return res;
2089c472a0dSDavid Yang 	}
2099c472a0dSDavid Yang 
2109c472a0dSDavid Yang 	priv->eee_ports_mask = new_mask;
2119c472a0dSDavid Yang 
2129c472a0dSDavid Yang 	/* Enable / disable port EEE */
2139c472a0dSDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
2149c472a0dSDavid Yang 				     YT921X_EEE_CTRL_ENn(port), enable);
2159c472a0dSDavid Yang 	if (res)
2169c472a0dSDavid Yang 		return res;
2179c472a0dSDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
2189c472a0dSDavid Yang 				     YT921X_EEE_VAL_DATA, enable);
2199c472a0dSDavid Yang 	if (res)
2209c472a0dSDavid Yang 		return res;
2219c472a0dSDavid Yang 
2229c472a0dSDavid Yang 	return 0;
2239c472a0dSDavid Yang }
2249c472a0dSDavid Yang 
2259c472a0dSDavid Yang static int
2269c472a0dSDavid Yang yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
2279c472a0dSDavid Yang {
228cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
2299c472a0dSDavid Yang 	int res;
2309c472a0dSDavid Yang 
2319c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
2329c472a0dSDavid Yang 	res = yt921x_set_eee(priv, port, e);
2339c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
2349c472a0dSDavid Yang 
2359c472a0dSDavid Yang 	return res;
2369c472a0dSDavid Yang }
2379c472a0dSDavid Yang 
2389c472a0dSDavid Yang static int
2399c472a0dSDavid Yang yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
2409c472a0dSDavid Yang {
2419c472a0dSDavid Yang 	/* Only serves as packet filter, since the frame size is always set to
2429c472a0dSDavid Yang 	 * maximum after reset
2439c472a0dSDavid Yang 	 */
2449c472a0dSDavid Yang 
245cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
2469c472a0dSDavid Yang 	struct dsa_port *dp = dsa_to_port(ds, port);
2479c472a0dSDavid Yang 	int frame_size;
2489c472a0dSDavid Yang 	int res;
2499c472a0dSDavid Yang 
2509c472a0dSDavid Yang 	frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
2519c472a0dSDavid Yang 	if (dsa_port_is_cpu(dp))
2529c472a0dSDavid Yang 		frame_size += YT921X_TAG_LEN;
2539c472a0dSDavid Yang 
2549c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
2559c472a0dSDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
2569c472a0dSDavid Yang 				     YT921X_MAC_FRAME_SIZE_M,
2579c472a0dSDavid Yang 				     YT921X_MAC_FRAME_SIZE(frame_size));
2589c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
2599c472a0dSDavid Yang 
2609c472a0dSDavid Yang 	return res;
2619c472a0dSDavid Yang }
2629c472a0dSDavid Yang 
2639c472a0dSDavid Yang static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
2649c472a0dSDavid Yang {
2659c472a0dSDavid Yang 	/* Only called for user ports, exclude tag len here */
2669c472a0dSDavid Yang 	return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
2679c472a0dSDavid Yang }
2689c472a0dSDavid Yang 
2699c472a0dSDavid Yang static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag,
2709c472a0dSDavid Yang 			   struct netlink_ext_ack *extack)
2719c472a0dSDavid Yang {
2729c472a0dSDavid Yang 	u32 val;
2739c472a0dSDavid Yang 	int res;
2749c472a0dSDavid Yang 
2759c472a0dSDavid Yang 	/* Hash Mode is global. Make sure the same Hash Mode is set to all the
2769c472a0dSDavid Yang 	 * 2 possible lags.
2779c472a0dSDavid Yang 	 * If we are the unique LAG we can set whatever hash mode we want.
2789c472a0dSDavid Yang 	 * To change hash mode it's needed to remove all LAG and change the mode
2799c472a0dSDavid Yang 	 * with the latest.
2809c472a0dSDavid Yang 	 */
2819c472a0dSDavid Yang 	if (unique_lag) {
2829c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl);
2839c472a0dSDavid Yang 		if (res)
2849c472a0dSDavid Yang 			return res;
2859c472a0dSDavid Yang 	} else {
2869c472a0dSDavid Yang 		res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val);
2879c472a0dSDavid Yang 		if (res)
2889c472a0dSDavid Yang 			return res;
2899c472a0dSDavid Yang 
2909c472a0dSDavid Yang 		if (val != ctrl) {
2919c472a0dSDavid Yang 			NL_SET_ERR_MSG_MOD(extack,
2929c472a0dSDavid Yang 					   "Mismatched Hash Mode across different lags is not supported");
2939c472a0dSDavid Yang 			return -EOPNOTSUPP;
2949c472a0dSDavid Yang 		}
2959c472a0dSDavid Yang 	}
2969c472a0dSDavid Yang 
2979c472a0dSDavid Yang 	return 0;
2989c472a0dSDavid Yang }
2999c472a0dSDavid Yang 
3009c472a0dSDavid Yang static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask)
3019c472a0dSDavid Yang {
3029c472a0dSDavid Yang 	unsigned long targets_mask = ports_mask;
3039c472a0dSDavid Yang 	unsigned int cnt;
3049c472a0dSDavid Yang 	u32 ctrl;
3059c472a0dSDavid Yang 	int port;
3069c472a0dSDavid Yang 	int res;
3079c472a0dSDavid Yang 
3089c472a0dSDavid Yang 	cnt = 0;
3099c472a0dSDavid Yang 	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
3109c472a0dSDavid Yang 		ctrl = YT921X_LAG_MEMBER_PORT(port);
3119c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt),
3129c472a0dSDavid Yang 				       ctrl);
3139c472a0dSDavid Yang 		if (res)
3149c472a0dSDavid Yang 			return res;
3159c472a0dSDavid Yang 
3169c472a0dSDavid Yang 		cnt++;
3179c472a0dSDavid Yang 	}
3189c472a0dSDavid Yang 
3199c472a0dSDavid Yang 	ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) |
3209c472a0dSDavid Yang 	       YT921X_LAG_GROUP_MEMBER_NUM(cnt);
3219c472a0dSDavid Yang 	return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl);
3229c472a0dSDavid Yang }
3239c472a0dSDavid Yang 
3249c472a0dSDavid Yang static int
3259c472a0dSDavid Yang yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag)
3269c472a0dSDavid Yang {
327cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
3289c472a0dSDavid Yang 	struct dsa_port *dp;
3299c472a0dSDavid Yang 	u32 ctrl;
3309c472a0dSDavid Yang 	int res;
3319c472a0dSDavid Yang 
3329c472a0dSDavid Yang 	if (!lag.id)
3339c472a0dSDavid Yang 		return -EINVAL;
3349c472a0dSDavid Yang 
3359c472a0dSDavid Yang 	ctrl = 0;
3369c472a0dSDavid Yang 	dsa_lag_foreach_port(dp, ds->dst, &lag)
3379c472a0dSDavid Yang 		ctrl |= BIT(dp->index);
3389c472a0dSDavid Yang 
3399c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
3409c472a0dSDavid Yang 	res = yt921x_lag_set(priv, lag.id - 1, ctrl);
3419c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
3429c472a0dSDavid Yang 
3439c472a0dSDavid Yang 	return res;
3449c472a0dSDavid Yang }
3459c472a0dSDavid Yang 
3469c472a0dSDavid Yang static int
3479c472a0dSDavid Yang yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag,
3489c472a0dSDavid Yang 			  struct netdev_lag_upper_info *info,
3499c472a0dSDavid Yang 			  struct netlink_ext_ack *extack)
3509c472a0dSDavid Yang {
3519c472a0dSDavid Yang 	unsigned int members;
3529c472a0dSDavid Yang 	struct dsa_port *dp;
3539c472a0dSDavid Yang 
3549c472a0dSDavid Yang 	if (!lag.id)
3559c472a0dSDavid Yang 		return -EINVAL;
3569c472a0dSDavid Yang 
3579c472a0dSDavid Yang 	members = 0;
3589c472a0dSDavid Yang 	dsa_lag_foreach_port(dp, ds->dst, &lag)
3599c472a0dSDavid Yang 		/* Includes the port joining the LAG */
3609c472a0dSDavid Yang 		members++;
3619c472a0dSDavid Yang 
3629c472a0dSDavid Yang 	if (members > YT921X_LAG_PORT_NUM) {
3639c472a0dSDavid Yang 		NL_SET_ERR_MSG_MOD(extack,
3649c472a0dSDavid Yang 				   "Cannot offload more than 4 LAG ports");
3659c472a0dSDavid Yang 		return -EOPNOTSUPP;
3669c472a0dSDavid Yang 	}
3679c472a0dSDavid Yang 
3689c472a0dSDavid Yang 	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
3699c472a0dSDavid Yang 		NL_SET_ERR_MSG_MOD(extack,
3709c472a0dSDavid Yang 				   "Can only offload LAG using hash TX type");
3719c472a0dSDavid Yang 		return -EOPNOTSUPP;
3729c472a0dSDavid Yang 	}
3739c472a0dSDavid Yang 
3749c472a0dSDavid Yang 	if (info->hash_type != NETDEV_LAG_HASH_L2 &&
3759c472a0dSDavid Yang 	    info->hash_type != NETDEV_LAG_HASH_L23 &&
3769c472a0dSDavid Yang 	    info->hash_type != NETDEV_LAG_HASH_L34) {
3779c472a0dSDavid Yang 		NL_SET_ERR_MSG_MOD(extack,
3789c472a0dSDavid Yang 				   "Can only offload L2 or L2+L3 or L3+L4 TX hash");
3799c472a0dSDavid Yang 		return -EOPNOTSUPP;
3809c472a0dSDavid Yang 	}
3819c472a0dSDavid Yang 
3829c472a0dSDavid Yang 	return 0;
3839c472a0dSDavid Yang }
3849c472a0dSDavid Yang 
3859c472a0dSDavid Yang static int
3869c472a0dSDavid Yang yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
3879c472a0dSDavid Yang 			 struct netdev_lag_upper_info *info,
3889c472a0dSDavid Yang 			 struct netlink_ext_ack *extack)
3899c472a0dSDavid Yang {
390cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
3919c472a0dSDavid Yang 	struct dsa_port *dp;
3929c472a0dSDavid Yang 	bool unique_lag;
3939c472a0dSDavid Yang 	unsigned int i;
3949c472a0dSDavid Yang 	u32 ctrl;
3959c472a0dSDavid Yang 	int res;
3969c472a0dSDavid Yang 
3979c472a0dSDavid Yang 	res = yt921x_dsa_port_lag_check(ds, lag, info, extack);
3989c472a0dSDavid Yang 	if (res)
3999c472a0dSDavid Yang 		return res;
4009c472a0dSDavid Yang 
4019c472a0dSDavid Yang 	ctrl = 0;
4029c472a0dSDavid Yang 	switch (info->hash_type) {
4039c472a0dSDavid Yang 	case NETDEV_LAG_HASH_L34:
4049c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_DST;
4059c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_SRC;
4069c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_PROTO;
4079c472a0dSDavid Yang 
4089c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_L4_DPORT;
4099c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_L4_SPORT;
4109c472a0dSDavid Yang 		break;
4119c472a0dSDavid Yang 	case NETDEV_LAG_HASH_L23:
4129c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_MAC_DA;
4139c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_MAC_SA;
4149c472a0dSDavid Yang 
4159c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_DST;
4169c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_SRC;
4179c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_IP_PROTO;
4189c472a0dSDavid Yang 		break;
4199c472a0dSDavid Yang 	case NETDEV_LAG_HASH_L2:
4209c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_MAC_DA;
4219c472a0dSDavid Yang 		ctrl |= YT921X_LAG_HASH_MAC_SA;
4229c472a0dSDavid Yang 		break;
4239c472a0dSDavid Yang 	default:
4249c472a0dSDavid Yang 		return -EOPNOTSUPP;
4259c472a0dSDavid Yang 	}
4269c472a0dSDavid Yang 
4279c472a0dSDavid Yang 	/* Check if we are the unique configured LAG */
4289c472a0dSDavid Yang 	unique_lag = true;
4299c472a0dSDavid Yang 	dsa_lags_foreach_id(i, ds->dst)
4309c472a0dSDavid Yang 		if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
4319c472a0dSDavid Yang 			unique_lag = false;
4329c472a0dSDavid Yang 			break;
4339c472a0dSDavid Yang 		}
4349c472a0dSDavid Yang 
4359c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
4369c472a0dSDavid Yang 	do {
4379c472a0dSDavid Yang 		res = yt921x_lag_hash(priv, ctrl, unique_lag, extack);
4389c472a0dSDavid Yang 		if (res)
4399c472a0dSDavid Yang 			break;
4409c472a0dSDavid Yang 
4419c472a0dSDavid Yang 		ctrl = 0;
4429c472a0dSDavid Yang 		dsa_lag_foreach_port(dp, ds->dst, &lag)
4439c472a0dSDavid Yang 			ctrl |= BIT(dp->index);
4449c472a0dSDavid Yang 		res = yt921x_lag_set(priv, lag.id - 1, ctrl);
4459c472a0dSDavid Yang 	} while (0);
4469c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
4479c472a0dSDavid Yang 
4489c472a0dSDavid Yang 	return res;
4499c472a0dSDavid Yang }
4509c472a0dSDavid Yang 
4519c472a0dSDavid Yang static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
4529c472a0dSDavid Yang {
453cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
4549c472a0dSDavid Yang 	u32 val = YT921X_FDB_RESULT_DONE;
4559c472a0dSDavid Yang 	int res;
4569c472a0dSDavid Yang 
4579c472a0dSDavid Yang 	res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE,
4589c472a0dSDavid Yang 			      &val);
4599c472a0dSDavid Yang 	if (res) {
4609c472a0dSDavid Yang 		dev_err(dev, "FDB probably stuck\n");
4619c472a0dSDavid Yang 		return res;
4629c472a0dSDavid Yang 	}
4639c472a0dSDavid Yang 
4649c472a0dSDavid Yang 	*valp = val;
4659c472a0dSDavid Yang 	return 0;
4669c472a0dSDavid Yang }
4679c472a0dSDavid Yang 
4689c472a0dSDavid Yang static int
4699c472a0dSDavid Yang yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr,
4709c472a0dSDavid Yang 		u16 vid, u32 ctrl1)
4719c472a0dSDavid Yang {
4729c472a0dSDavid Yang 	u32 ctrl;
4739c472a0dSDavid Yang 	int res;
4749c472a0dSDavid Yang 
4759c472a0dSDavid Yang 	ctrl = ethaddr_hi4_to_u32(addr);
4769c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl);
4779c472a0dSDavid Yang 	if (res)
4789c472a0dSDavid Yang 		return res;
4799c472a0dSDavid Yang 
4809c472a0dSDavid Yang 	ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr);
4819c472a0dSDavid Yang 	return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
4829c472a0dSDavid Yang }
4839c472a0dSDavid Yang 
4849c472a0dSDavid Yang static int
4859c472a0dSDavid Yang yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
4869c472a0dSDavid Yang 	       u16 *indexp)
4879c472a0dSDavid Yang {
4889c472a0dSDavid Yang 	u32 ctrl;
4899c472a0dSDavid Yang 	u32 val;
4909c472a0dSDavid Yang 	int res;
4919c472a0dSDavid Yang 
4929c472a0dSDavid Yang 	res = yt921x_fdb_in01(priv, addr, vid, 0);
4939c472a0dSDavid Yang 	if (res)
4949c472a0dSDavid Yang 		return res;
4959c472a0dSDavid Yang 
4969c472a0dSDavid Yang 	ctrl = 0;
4979c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
4989c472a0dSDavid Yang 	if (res)
4999c472a0dSDavid Yang 		return res;
5009c472a0dSDavid Yang 
5019c472a0dSDavid Yang 	ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
5029c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
5039c472a0dSDavid Yang 	if (res)
5049c472a0dSDavid Yang 		return res;
5059c472a0dSDavid Yang 
5069c472a0dSDavid Yang 	res = yt921x_fdb_wait(priv, &val);
5079c472a0dSDavid Yang 	if (res)
5089c472a0dSDavid Yang 		return res;
5099c472a0dSDavid Yang 	if (val & YT921X_FDB_RESULT_NOTFOUND) {
5109c472a0dSDavid Yang 		*indexp = YT921X_FDB_NUM;
5119c472a0dSDavid Yang 		return 0;
5129c472a0dSDavid Yang 	}
5139c472a0dSDavid Yang 
5149c472a0dSDavid Yang 	*indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
5159c472a0dSDavid Yang 	return 0;
5169c472a0dSDavid Yang }
5179c472a0dSDavid Yang 
5189c472a0dSDavid Yang static int
5199c472a0dSDavid Yang yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp,
5209c472a0dSDavid Yang 		u16 *ports_maskp, u16 *indexp, u8 *statusp)
5219c472a0dSDavid Yang {
522cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
5239c472a0dSDavid Yang 	u16 index;
5249c472a0dSDavid Yang 	u32 data0;
5259c472a0dSDavid Yang 	u32 data1;
5269c472a0dSDavid Yang 	u32 data2;
5279c472a0dSDavid Yang 	u32 val;
5289c472a0dSDavid Yang 	int res;
5299c472a0dSDavid Yang 
5309c472a0dSDavid Yang 	res = yt921x_fdb_wait(priv, &val);
5319c472a0dSDavid Yang 	if (res)
5329c472a0dSDavid Yang 		return res;
5339c472a0dSDavid Yang 	if (val & YT921X_FDB_RESULT_NOTFOUND) {
5349c472a0dSDavid Yang 		*ports_maskp = 0;
5359c472a0dSDavid Yang 		return 0;
5369c472a0dSDavid Yang 	}
5379c472a0dSDavid Yang 	index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
5389c472a0dSDavid Yang 
5399c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1);
5409c472a0dSDavid Yang 	if (res)
5419c472a0dSDavid Yang 		return res;
5429c472a0dSDavid Yang 	if ((data1 & YT921X_FDB_IO1_STATUS_M) ==
5439c472a0dSDavid Yang 	    YT921X_FDB_IO1_STATUS_INVALID) {
5449c472a0dSDavid Yang 		*ports_maskp = 0;
5459c472a0dSDavid Yang 		return 0;
5469c472a0dSDavid Yang 	}
5479c472a0dSDavid Yang 
5489c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0);
5499c472a0dSDavid Yang 	if (res)
5509c472a0dSDavid Yang 		return res;
5519c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2);
5529c472a0dSDavid Yang 	if (res)
5539c472a0dSDavid Yang 		return res;
5549c472a0dSDavid Yang 
5559c472a0dSDavid Yang 	addr[0] = data0 >> 24;
5569c472a0dSDavid Yang 	addr[1] = data0 >> 16;
5579c472a0dSDavid Yang 	addr[2] = data0 >> 8;
5589c472a0dSDavid Yang 	addr[3] = data0;
5599c472a0dSDavid Yang 	addr[4] = data1 >> 8;
5609c472a0dSDavid Yang 	addr[5] = data1;
5619c472a0dSDavid Yang 	*vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1);
5629c472a0dSDavid Yang 	*indexp = index;
5639c472a0dSDavid Yang 	*ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2);
5649c472a0dSDavid Yang 	*statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1);
5659c472a0dSDavid Yang 
5669c472a0dSDavid Yang 	dev_dbg(dev,
5679c472a0dSDavid Yang 		"%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n",
5689c472a0dSDavid Yang 		__func__, *indexp, addr[0], addr[1], addr[2], addr[3],
5699c472a0dSDavid Yang 		addr[4], addr[5], *vidp, *ports_maskp, *statusp);
5709c472a0dSDavid Yang 	return 0;
5719c472a0dSDavid Yang }
5729c472a0dSDavid Yang 
5739c472a0dSDavid Yang static int
5749c472a0dSDavid Yang yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
5759c472a0dSDavid Yang 		dsa_fdb_dump_cb_t *cb, void *data)
5769c472a0dSDavid Yang {
5779c472a0dSDavid Yang 	unsigned char addr[ETH_ALEN];
5789c472a0dSDavid Yang 	u8 status;
5799c472a0dSDavid Yang 	u16 pmask;
5809c472a0dSDavid Yang 	u16 index;
5819c472a0dSDavid Yang 	u32 ctrl;
5829c472a0dSDavid Yang 	u16 vid;
5839c472a0dSDavid Yang 	int res;
5849c472a0dSDavid Yang 
5859c472a0dSDavid Yang 	ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
5869c472a0dSDavid Yang 	       YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
5879c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
5889c472a0dSDavid Yang 	if (res)
5899c472a0dSDavid Yang 		return res;
5909c472a0dSDavid Yang 	res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status);
5919c472a0dSDavid Yang 	if (res)
5929c472a0dSDavid Yang 		return res;
5939c472a0dSDavid Yang 	if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
5949c472a0dSDavid Yang 		res = cb(addr, vid,
5959c472a0dSDavid Yang 			 status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
5969c472a0dSDavid Yang 		if (res)
5979c472a0dSDavid Yang 			return res;
5989c472a0dSDavid Yang 	}
5999c472a0dSDavid Yang 
6009c472a0dSDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
6019c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
6029c472a0dSDavid Yang 	if (res)
6039c472a0dSDavid Yang 		return res;
6049c472a0dSDavid Yang 
6059c472a0dSDavid Yang 	index = 0;
6069c472a0dSDavid Yang 	do {
6079c472a0dSDavid Yang 		ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
6089c472a0dSDavid Yang 		       YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT |
6099c472a0dSDavid Yang 		       YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
6109c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
6119c472a0dSDavid Yang 		if (res)
6129c472a0dSDavid Yang 			return res;
6139c472a0dSDavid Yang 
6149c472a0dSDavid Yang 		res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index,
6159c472a0dSDavid Yang 				      &status);
6169c472a0dSDavid Yang 		if (res)
6179c472a0dSDavid Yang 			return res;
6189c472a0dSDavid Yang 		if (!pmask)
6199c472a0dSDavid Yang 			break;
6209c472a0dSDavid Yang 
6219c472a0dSDavid Yang 		if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
6229c472a0dSDavid Yang 			res = cb(addr, vid,
6239c472a0dSDavid Yang 				 status == YT921X_FDB_ENTRY_STATUS_STATIC,
6249c472a0dSDavid Yang 				 data);
6259c472a0dSDavid Yang 			if (res)
6269c472a0dSDavid Yang 				return res;
6279c472a0dSDavid Yang 		}
6289c472a0dSDavid Yang 
6299c472a0dSDavid Yang 		/* Never call GET_NEXT with 4095, otherwise it will hang
6309c472a0dSDavid Yang 		 * forever until a reset!
6319c472a0dSDavid Yang 		 */
6329c472a0dSDavid Yang 	} while (index < YT921X_FDB_NUM - 1);
6339c472a0dSDavid Yang 
6349c472a0dSDavid Yang 	return 0;
6359c472a0dSDavid Yang }
6369c472a0dSDavid Yang 
6379c472a0dSDavid Yang static int
6389c472a0dSDavid Yang yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid,
6399c472a0dSDavid Yang 		     bool flush_static)
6409c472a0dSDavid Yang {
6419c472a0dSDavid Yang 	u32 ctrl;
6429c472a0dSDavid Yang 	u32 val;
6439c472a0dSDavid Yang 	int res;
6449c472a0dSDavid Yang 
6459c472a0dSDavid Yang 	if (vid < 4096) {
6469c472a0dSDavid Yang 		ctrl = YT921X_FDB_IO1_FID(vid);
6479c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
6489c472a0dSDavid Yang 		if (res)
6499c472a0dSDavid Yang 			return res;
6509c472a0dSDavid Yang 	}
6519c472a0dSDavid Yang 
6529c472a0dSDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
6539c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
6549c472a0dSDavid Yang 	if (res)
6559c472a0dSDavid Yang 		return res;
6569c472a0dSDavid Yang 
6579c472a0dSDavid Yang 	ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START;
6589c472a0dSDavid Yang 	if (vid >= 4096)
6599c472a0dSDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_PORT;
6609c472a0dSDavid Yang 	else
6619c472a0dSDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID;
6629c472a0dSDavid Yang 	if (flush_static)
6639c472a0dSDavid Yang 		ctrl |= YT921X_FDB_OP_FLUSH_STATIC;
6649c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
6659c472a0dSDavid Yang 	if (res)
6669c472a0dSDavid Yang 		return res;
6679c472a0dSDavid Yang 
6689c472a0dSDavid Yang 	res = yt921x_fdb_wait(priv, &val);
6699c472a0dSDavid Yang 	if (res)
6709c472a0dSDavid Yang 		return res;
6719c472a0dSDavid Yang 
6729c472a0dSDavid Yang 	return 0;
6739c472a0dSDavid Yang }
6749c472a0dSDavid Yang 
6759c472a0dSDavid Yang static int
6769c472a0dSDavid Yang yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static)
6779c472a0dSDavid Yang {
6789c472a0dSDavid Yang 	return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static);
6799c472a0dSDavid Yang }
6809c472a0dSDavid Yang 
6819c472a0dSDavid Yang static int
682*5d7f3375SDavid Yang yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u32 ctrl1,
683*5d7f3375SDavid Yang 			  u32 ctrl2)
6849c472a0dSDavid Yang {
6859c472a0dSDavid Yang 	u32 ctrl;
6869c472a0dSDavid Yang 	u32 val;
6879c472a0dSDavid Yang 	int res;
6889c472a0dSDavid Yang 
6899c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1);
6909c472a0dSDavid Yang 	if (res)
6919c472a0dSDavid Yang 		return res;
6929c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2);
6939c472a0dSDavid Yang 	if (res)
6949c472a0dSDavid Yang 		return res;
6959c472a0dSDavid Yang 
6969c472a0dSDavid Yang 	ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
6979c472a0dSDavid Yang 	       YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
6989c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
6999c472a0dSDavid Yang 	if (res)
7009c472a0dSDavid Yang 		return res;
7019c472a0dSDavid Yang 
7029c472a0dSDavid Yang 	return yt921x_fdb_wait(priv, &val);
7039c472a0dSDavid Yang }
7049c472a0dSDavid Yang 
7059c472a0dSDavid Yang static int
7069c472a0dSDavid Yang yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
7079c472a0dSDavid Yang 	       u16 ports_mask)
7089c472a0dSDavid Yang {
7099c472a0dSDavid Yang 	u32 ctrl;
7109c472a0dSDavid Yang 	u32 val;
7119c472a0dSDavid Yang 	int res;
7129c472a0dSDavid Yang 
7139c472a0dSDavid Yang 	ctrl = YT921X_FDB_IO1_STATUS_STATIC;
7149c472a0dSDavid Yang 	res = yt921x_fdb_in01(priv, addr, vid, ctrl);
7159c472a0dSDavid Yang 	if (res)
7169c472a0dSDavid Yang 		return res;
7179c472a0dSDavid Yang 
7189c472a0dSDavid Yang 	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
7199c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
7209c472a0dSDavid Yang 	if (res)
7219c472a0dSDavid Yang 		return res;
7229c472a0dSDavid Yang 
7239c472a0dSDavid Yang 	ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
7249c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
7259c472a0dSDavid Yang 	if (res)
7269c472a0dSDavid Yang 		return res;
7279c472a0dSDavid Yang 
7289c472a0dSDavid Yang 	return yt921x_fdb_wait(priv, &val);
7299c472a0dSDavid Yang }
7309c472a0dSDavid Yang 
7319c472a0dSDavid Yang static int
7329c472a0dSDavid Yang yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
7339c472a0dSDavid Yang 		 u16 vid, u16 ports_mask)
7349c472a0dSDavid Yang {
7359c472a0dSDavid Yang 	u16 index;
7369c472a0dSDavid Yang 	u32 ctrl1;
7379c472a0dSDavid Yang 	u32 ctrl2;
7389c472a0dSDavid Yang 	u32 ctrl;
7399c472a0dSDavid Yang 	u32 val2;
7409c472a0dSDavid Yang 	u32 val;
7419c472a0dSDavid Yang 	int res;
7429c472a0dSDavid Yang 
7439c472a0dSDavid Yang 	/* Check for presence */
7449c472a0dSDavid Yang 	res = yt921x_fdb_has(priv, addr, vid, &index);
7459c472a0dSDavid Yang 	if (res)
7469c472a0dSDavid Yang 		return res;
7479c472a0dSDavid Yang 	if (index >= YT921X_FDB_NUM)
7489c472a0dSDavid Yang 		return 0;
7499c472a0dSDavid Yang 
7509c472a0dSDavid Yang 	/* Check if action required */
7519c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
7529c472a0dSDavid Yang 	if (res)
7539c472a0dSDavid Yang 		return res;
7549c472a0dSDavid Yang 
7559c472a0dSDavid Yang 	ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
7569c472a0dSDavid Yang 	if (ctrl2 == val2)
7579c472a0dSDavid Yang 		return 0;
7589c472a0dSDavid Yang 	if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
7599c472a0dSDavid Yang 		ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
7609c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
7619c472a0dSDavid Yang 		if (res)
7629c472a0dSDavid Yang 			return res;
7639c472a0dSDavid Yang 
7649c472a0dSDavid Yang 		return yt921x_fdb_wait(priv, &val);
7659c472a0dSDavid Yang 	}
7669c472a0dSDavid Yang 
7679c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
7689c472a0dSDavid Yang 	if (res)
7699c472a0dSDavid Yang 		return res;
7709c472a0dSDavid Yang 
7719c472a0dSDavid Yang 	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
7729c472a0dSDavid Yang }
7739c472a0dSDavid Yang 
7749c472a0dSDavid Yang static int
7759c472a0dSDavid Yang yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
7769c472a0dSDavid Yang 		u16 ports_mask)
7779c472a0dSDavid Yang {
7789c472a0dSDavid Yang 	u16 index;
7799c472a0dSDavid Yang 	u32 ctrl1;
7809c472a0dSDavid Yang 	u32 ctrl2;
7819c472a0dSDavid Yang 	u32 val1;
7829c472a0dSDavid Yang 	u32 val2;
7839c472a0dSDavid Yang 	int res;
7849c472a0dSDavid Yang 
7859c472a0dSDavid Yang 	/* Check for presence */
7869c472a0dSDavid Yang 	res = yt921x_fdb_has(priv, addr, vid, &index);
7879c472a0dSDavid Yang 	if (res)
7889c472a0dSDavid Yang 		return res;
7899c472a0dSDavid Yang 	if (index >= YT921X_FDB_NUM)
7909c472a0dSDavid Yang 		return yt921x_fdb_add(priv, addr, vid, ports_mask);
7919c472a0dSDavid Yang 
7929c472a0dSDavid Yang 	/* Check if action required */
7939c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
7949c472a0dSDavid Yang 	if (res)
7959c472a0dSDavid Yang 		return res;
7969c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
7979c472a0dSDavid Yang 	if (res)
7989c472a0dSDavid Yang 		return res;
7999c472a0dSDavid Yang 
8009c472a0dSDavid Yang 	ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
8019c472a0dSDavid Yang 	ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
8029c472a0dSDavid Yang 	ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask);
8039c472a0dSDavid Yang 	if (ctrl1 == val1 && ctrl2 == val2)
8049c472a0dSDavid Yang 		return 0;
8059c472a0dSDavid Yang 
8069c472a0dSDavid Yang 	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
8079c472a0dSDavid Yang }
8089c472a0dSDavid Yang 
8099c472a0dSDavid Yang static int
8109c472a0dSDavid Yang yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
8119c472a0dSDavid Yang 			 dsa_fdb_dump_cb_t *cb, void *data)
8129c472a0dSDavid Yang {
813cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
8149c472a0dSDavid Yang 	int res;
8159c472a0dSDavid Yang 
8169c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8179c472a0dSDavid Yang 	/* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
8189c472a0dSDavid Yang 	 * only wants to see unicast
8199c472a0dSDavid Yang 	 */
8209c472a0dSDavid Yang 	res = yt921x_fdb_dump(priv, BIT(port), cb, data);
8219c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8229c472a0dSDavid Yang 
8239c472a0dSDavid Yang 	return res;
8249c472a0dSDavid Yang }
8259c472a0dSDavid Yang 
8269c472a0dSDavid Yang static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port)
8279c472a0dSDavid Yang {
828cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
829cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
8309c472a0dSDavid Yang 	int res;
8319c472a0dSDavid Yang 
8329c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8339c472a0dSDavid Yang 	res = yt921x_fdb_flush_port(priv, port, false);
8349c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8359c472a0dSDavid Yang 
8369c472a0dSDavid Yang 	if (res)
8379c472a0dSDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
8389c472a0dSDavid Yang 			port, res);
8399c472a0dSDavid Yang }
8409c472a0dSDavid Yang 
8419c472a0dSDavid Yang static int
8429c472a0dSDavid Yang yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
8439c472a0dSDavid Yang {
844cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
8459c472a0dSDavid Yang 	u32 ctrl;
8469c472a0dSDavid Yang 	int res;
8479c472a0dSDavid Yang 
8489c472a0dSDavid Yang 	/* AGEING reg is set in 5s step */
8499c472a0dSDavid Yang 	ctrl = clamp(msecs / 5000, 1, U16_MAX);
8509c472a0dSDavid Yang 
8519c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8529c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_AGEING, ctrl);
8539c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8549c472a0dSDavid Yang 
8559c472a0dSDavid Yang 	return res;
8569c472a0dSDavid Yang }
8579c472a0dSDavid Yang 
8589c472a0dSDavid Yang static int
8599c472a0dSDavid Yang yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port,
8609c472a0dSDavid Yang 			const unsigned char *addr, u16 vid, struct dsa_db db)
8619c472a0dSDavid Yang {
862cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
8639c472a0dSDavid Yang 	int res;
8649c472a0dSDavid Yang 
8659c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8669c472a0dSDavid Yang 	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
8679c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8689c472a0dSDavid Yang 
8699c472a0dSDavid Yang 	return res;
8709c472a0dSDavid Yang }
8719c472a0dSDavid Yang 
8729c472a0dSDavid Yang static int
8739c472a0dSDavid Yang yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
8749c472a0dSDavid Yang 			const unsigned char *addr, u16 vid, struct dsa_db db)
8759c472a0dSDavid Yang {
876cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
8779c472a0dSDavid Yang 	int res;
8789c472a0dSDavid Yang 
8799c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8809c472a0dSDavid Yang 	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
8819c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8829c472a0dSDavid Yang 
8839c472a0dSDavid Yang 	return res;
8849c472a0dSDavid Yang }
8859c472a0dSDavid Yang 
8869c472a0dSDavid Yang static int
8879c472a0dSDavid Yang yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port,
8889c472a0dSDavid Yang 			const struct switchdev_obj_port_mdb *mdb,
8899c472a0dSDavid Yang 			struct dsa_db db)
8909c472a0dSDavid Yang {
891cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
8929c472a0dSDavid Yang 	const unsigned char *addr = mdb->addr;
8939c472a0dSDavid Yang 	u16 vid = mdb->vid;
8949c472a0dSDavid Yang 	int res;
8959c472a0dSDavid Yang 
8969c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
8979c472a0dSDavid Yang 	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
8989c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
8999c472a0dSDavid Yang 
9009c472a0dSDavid Yang 	return res;
9019c472a0dSDavid Yang }
9029c472a0dSDavid Yang 
9039c472a0dSDavid Yang static int
9049c472a0dSDavid Yang yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port,
9059c472a0dSDavid Yang 			const struct switchdev_obj_port_mdb *mdb,
9069c472a0dSDavid Yang 			struct dsa_db db)
9079c472a0dSDavid Yang {
908cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
9099c472a0dSDavid Yang 	const unsigned char *addr = mdb->addr;
9109c472a0dSDavid Yang 	u16 vid = mdb->vid;
9119c472a0dSDavid Yang 	int res;
9129c472a0dSDavid Yang 
9139c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
9149c472a0dSDavid Yang 	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
9159c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
9169c472a0dSDavid Yang 
9179c472a0dSDavid Yang 	return res;
9189c472a0dSDavid Yang }
9199c472a0dSDavid Yang 
9209c472a0dSDavid Yang static int
9219c472a0dSDavid Yang yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware)
9229c472a0dSDavid Yang {
9239c472a0dSDavid Yang 	u32 ctrl;
9249c472a0dSDavid Yang 
9259c472a0dSDavid Yang 	/* Abuse SVLAN for PCP parsing without polluting the FDB - it just works
9269c472a0dSDavid Yang 	 * despite YT921X_VLAN_CTRL_SVLAN_EN never being set
9279c472a0dSDavid Yang 	 */
9289c472a0dSDavid Yang 	if (!vlan_aware)
9299c472a0dSDavid Yang 		ctrl = YT921X_PORT_IGR_TPIDn_STAG(0);
9309c472a0dSDavid Yang 	else
9319c472a0dSDavid Yang 		ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0);
9329c472a0dSDavid Yang 	return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl);
9339c472a0dSDavid Yang }
9349c472a0dSDavid Yang 
9359c472a0dSDavid Yang static int
9369c472a0dSDavid Yang yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid)
9379c472a0dSDavid Yang {
9389c472a0dSDavid Yang 	u32 mask;
9399c472a0dSDavid Yang 	u32 ctrl;
9409c472a0dSDavid Yang 
9419c472a0dSDavid Yang 	mask = YT921X_PORT_VLAN_CTRL_CVID_M;
9429c472a0dSDavid Yang 	ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid);
9439c472a0dSDavid Yang 	return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port),
9449c472a0dSDavid Yang 				      mask, ctrl);
9459c472a0dSDavid Yang }
9469c472a0dSDavid Yang 
9479c472a0dSDavid Yang static int
9489c472a0dSDavid Yang yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
9499c472a0dSDavid Yang {
9509c472a0dSDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
9519c472a0dSDavid Yang 	struct net_device *bdev;
9529c472a0dSDavid Yang 	u16 pvid;
9539c472a0dSDavid Yang 	u32 mask;
9549c472a0dSDavid Yang 	u32 ctrl;
9559c472a0dSDavid Yang 	int res;
9569c472a0dSDavid Yang 
9579c472a0dSDavid Yang 	bdev = dsa_port_bridge_dev_get(dp);
9589c472a0dSDavid Yang 
9599c472a0dSDavid Yang 	if (!bdev || !vlan_filtering)
9609c472a0dSDavid Yang 		pvid = YT921X_VID_UNWARE;
9619c472a0dSDavid Yang 	else
9629c472a0dSDavid Yang 		br_vlan_get_pvid(bdev, &pvid);
9639c472a0dSDavid Yang 	res = yt921x_port_set_pvid(priv, port, pvid);
9649c472a0dSDavid Yang 	if (res)
9659c472a0dSDavid Yang 		return res;
9669c472a0dSDavid Yang 
9679c472a0dSDavid Yang 	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED |
9689c472a0dSDavid Yang 	       YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
9699c472a0dSDavid Yang 	ctrl = 0;
9709c472a0dSDavid Yang 	/* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
9719c472a0dSDavid Yang 	if (vlan_filtering && !pvid)
9729c472a0dSDavid Yang 		ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
9739c472a0dSDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
9749c472a0dSDavid Yang 				     mask, ctrl);
9759c472a0dSDavid Yang 	if (res)
9769c472a0dSDavid Yang 		return res;
9779c472a0dSDavid Yang 
9789c472a0dSDavid Yang 	res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER,
9799c472a0dSDavid Yang 				     YT921X_VLAN_IGR_FILTER_PORTn(port),
9809c472a0dSDavid Yang 				     vlan_filtering);
9819c472a0dSDavid Yang 	if (res)
9829c472a0dSDavid Yang 		return res;
9839c472a0dSDavid Yang 
9849c472a0dSDavid Yang 	res = yt921x_vlan_aware_set(priv, port, vlan_filtering);
9859c472a0dSDavid Yang 	if (res)
9869c472a0dSDavid Yang 		return res;
9879c472a0dSDavid Yang 
9889c472a0dSDavid Yang 	return 0;
9899c472a0dSDavid Yang }
9909c472a0dSDavid Yang 
9919c472a0dSDavid Yang static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
9929c472a0dSDavid Yang {
9939c472a0dSDavid Yang 	u32 masks[2];
9949c472a0dSDavid Yang 
9959c472a0dSDavid Yang 	masks[0] = YT921X_VLAN_CTRLa_PORTn(port);
9969c472a0dSDavid Yang 	masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
9979c472a0dSDavid Yang 
9989c472a0dSDavid Yang 	return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks);
9999c472a0dSDavid Yang }
10009c472a0dSDavid Yang 
10019c472a0dSDavid Yang static int
10029c472a0dSDavid Yang yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
10039c472a0dSDavid Yang {
10049c472a0dSDavid Yang 	u32 masks[2];
10059c472a0dSDavid Yang 	u32 ctrls[2];
10069c472a0dSDavid Yang 
10079c472a0dSDavid Yang 	masks[0] = YT921X_VLAN_CTRLa_PORTn(port) |
10089c472a0dSDavid Yang 		   YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask);
10099c472a0dSDavid Yang 	ctrls[0] = masks[0];
10109c472a0dSDavid Yang 
10119c472a0dSDavid Yang 	masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
10129c472a0dSDavid Yang 	ctrls[1] = untagged ? masks[1] : 0;
10139c472a0dSDavid Yang 
10149c472a0dSDavid Yang 	return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
10159c472a0dSDavid Yang 					masks, ctrls);
10169c472a0dSDavid Yang }
10179c472a0dSDavid Yang 
10189c472a0dSDavid Yang static int
10199c472a0dSDavid Yang yt921x_pvid_clear(struct yt921x_priv *priv, int port)
10209c472a0dSDavid Yang {
10219c472a0dSDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
10229c472a0dSDavid Yang 	bool vlan_filtering;
10239c472a0dSDavid Yang 	u32 mask;
10249c472a0dSDavid Yang 	int res;
10259c472a0dSDavid Yang 
10269c472a0dSDavid Yang 	vlan_filtering = dsa_port_is_vlan_filtering(dp);
10279c472a0dSDavid Yang 
10289c472a0dSDavid Yang 	res = yt921x_port_set_pvid(priv, port,
10299c472a0dSDavid Yang 				   vlan_filtering ? 0 : YT921X_VID_UNWARE);
10309c472a0dSDavid Yang 	if (res)
10319c472a0dSDavid Yang 		return res;
10329c472a0dSDavid Yang 
10339c472a0dSDavid Yang 	if (vlan_filtering) {
10349c472a0dSDavid Yang 		mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
10359c472a0dSDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
10369c472a0dSDavid Yang 					  mask);
10379c472a0dSDavid Yang 		if (res)
10389c472a0dSDavid Yang 			return res;
10399c472a0dSDavid Yang 	}
10409c472a0dSDavid Yang 
10419c472a0dSDavid Yang 	return 0;
10429c472a0dSDavid Yang }
10439c472a0dSDavid Yang 
10449c472a0dSDavid Yang static int
10459c472a0dSDavid Yang yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid)
10469c472a0dSDavid Yang {
10479c472a0dSDavid Yang 	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
10489c472a0dSDavid Yang 	bool vlan_filtering;
10499c472a0dSDavid Yang 	u32 mask;
10509c472a0dSDavid Yang 	int res;
10519c472a0dSDavid Yang 
10529c472a0dSDavid Yang 	vlan_filtering = dsa_port_is_vlan_filtering(dp);
10539c472a0dSDavid Yang 
10549c472a0dSDavid Yang 	if (vlan_filtering) {
10559c472a0dSDavid Yang 		res = yt921x_port_set_pvid(priv, port, vid);
10569c472a0dSDavid Yang 		if (res)
10579c472a0dSDavid Yang 			return res;
10589c472a0dSDavid Yang 	}
10599c472a0dSDavid Yang 
10609c472a0dSDavid Yang 	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
10619c472a0dSDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask);
10629c472a0dSDavid Yang 	if (res)
10639c472a0dSDavid Yang 		return res;
10649c472a0dSDavid Yang 
10659c472a0dSDavid Yang 	return 0;
10669c472a0dSDavid Yang }
10679c472a0dSDavid Yang 
10689c472a0dSDavid Yang static int
10699c472a0dSDavid Yang yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
10709c472a0dSDavid Yang 			       bool vlan_filtering,
10719c472a0dSDavid Yang 			       struct netlink_ext_ack *extack)
10729c472a0dSDavid Yang {
1073cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
10749c472a0dSDavid Yang 	int res;
10759c472a0dSDavid Yang 
10769c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
10779c472a0dSDavid Yang 		return 0;
10789c472a0dSDavid Yang 
10799c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
10809c472a0dSDavid Yang 	res = yt921x_vlan_filtering(priv, port, vlan_filtering);
10819c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
10829c472a0dSDavid Yang 
10839c472a0dSDavid Yang 	return res;
10849c472a0dSDavid Yang }
10859c472a0dSDavid Yang 
10869c472a0dSDavid Yang static int
10879c472a0dSDavid Yang yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port,
10889c472a0dSDavid Yang 			 const struct switchdev_obj_port_vlan *vlan)
10899c472a0dSDavid Yang {
1090cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
10919c472a0dSDavid Yang 	u16 vid = vlan->vid;
10929c472a0dSDavid Yang 	u16 pvid;
10939c472a0dSDavid Yang 	int res;
10949c472a0dSDavid Yang 
10959c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
10969c472a0dSDavid Yang 		return 0;
10979c472a0dSDavid Yang 
10989c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
10999c472a0dSDavid Yang 	do {
11009c472a0dSDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
11019c472a0dSDavid Yang 		struct net_device *bdev;
11029c472a0dSDavid Yang 
11039c472a0dSDavid Yang 		res = yt921x_vlan_del(priv, port, vid);
11049c472a0dSDavid Yang 		if (res)
11059c472a0dSDavid Yang 			break;
11069c472a0dSDavid Yang 
11079c472a0dSDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
11089c472a0dSDavid Yang 		if (bdev) {
11099c472a0dSDavid Yang 			br_vlan_get_pvid(bdev, &pvid);
11109c472a0dSDavid Yang 			if (pvid == vid)
11119c472a0dSDavid Yang 				res = yt921x_pvid_clear(priv, port);
11129c472a0dSDavid Yang 		}
11139c472a0dSDavid Yang 	} while (0);
11149c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
11159c472a0dSDavid Yang 
11169c472a0dSDavid Yang 	return res;
11179c472a0dSDavid Yang }
11189c472a0dSDavid Yang 
11199c472a0dSDavid Yang static int
11209c472a0dSDavid Yang yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
11219c472a0dSDavid Yang 			 const struct switchdev_obj_port_vlan *vlan,
11229c472a0dSDavid Yang 			 struct netlink_ext_ack *extack)
11239c472a0dSDavid Yang {
1124cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
11259c472a0dSDavid Yang 	u16 vid = vlan->vid;
11269c472a0dSDavid Yang 	u16 pvid;
11279c472a0dSDavid Yang 	int res;
11289c472a0dSDavid Yang 
11299c472a0dSDavid Yang 	/* CPU port is supposed to be a member of every VLAN; see
11309c472a0dSDavid Yang 	 * yt921x_vlan_add() and yt921x_port_setup()
11319c472a0dSDavid Yang 	 */
11329c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
11339c472a0dSDavid Yang 		return 0;
11349c472a0dSDavid Yang 
11359c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
11369c472a0dSDavid Yang 	do {
11379c472a0dSDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
11389c472a0dSDavid Yang 		struct net_device *bdev;
11399c472a0dSDavid Yang 
11409c472a0dSDavid Yang 		res = yt921x_vlan_add(priv, port, vid,
11419c472a0dSDavid Yang 				      vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
11429c472a0dSDavid Yang 		if (res)
11439c472a0dSDavid Yang 			break;
11449c472a0dSDavid Yang 
11459c472a0dSDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
11469c472a0dSDavid Yang 		if (bdev) {
11479c472a0dSDavid Yang 			if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
11489c472a0dSDavid Yang 				res = yt921x_pvid_set(priv, port, vid);
11499c472a0dSDavid Yang 			} else {
11509c472a0dSDavid Yang 				br_vlan_get_pvid(bdev, &pvid);
11519c472a0dSDavid Yang 				if (pvid == vid)
11529c472a0dSDavid Yang 					res = yt921x_pvid_clear(priv, port);
11539c472a0dSDavid Yang 			}
11549c472a0dSDavid Yang 		}
11559c472a0dSDavid Yang 	} while (0);
11569c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
11579c472a0dSDavid Yang 
11589c472a0dSDavid Yang 	return res;
11599c472a0dSDavid Yang }
11609c472a0dSDavid Yang 
11619c472a0dSDavid Yang static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
11629c472a0dSDavid Yang {
11639c472a0dSDavid Yang 	u32 mask;
11649c472a0dSDavid Yang 	u32 ctrl;
11659c472a0dSDavid Yang 	int res;
11669c472a0dSDavid Yang 
11679c472a0dSDavid Yang 	ctrl = ~priv->cpu_ports_mask;
11689c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
11699c472a0dSDavid Yang 	if (res)
11709c472a0dSDavid Yang 		return res;
11719c472a0dSDavid Yang 
11729c472a0dSDavid Yang 	/* Turn off FDB learning to prevent FDB pollution */
11739c472a0dSDavid Yang 	mask = YT921X_PORT_LEARN_DIS;
11749c472a0dSDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask);
11759c472a0dSDavid Yang 	if (res)
11769c472a0dSDavid Yang 		return res;
11779c472a0dSDavid Yang 
11789c472a0dSDavid Yang 	/* Turn off VLAN awareness */
11799c472a0dSDavid Yang 	res = yt921x_vlan_aware_set(priv, port, false);
11809c472a0dSDavid Yang 	if (res)
11819c472a0dSDavid Yang 		return res;
11829c472a0dSDavid Yang 
11839c472a0dSDavid Yang 	/* Unrelated since learning is off and all packets are trapped;
11849c472a0dSDavid Yang 	 * set it anyway
11859c472a0dSDavid Yang 	 */
11869c472a0dSDavid Yang 	res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
11879c472a0dSDavid Yang 	if (res)
11889c472a0dSDavid Yang 		return res;
11899c472a0dSDavid Yang 
11909c472a0dSDavid Yang 	return 0;
11919c472a0dSDavid Yang }
11929c472a0dSDavid Yang 
11939c472a0dSDavid Yang static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
11949c472a0dSDavid Yang {
11959c472a0dSDavid Yang 	u32 mask;
11969c472a0dSDavid Yang 	int res;
11979c472a0dSDavid Yang 
11989c472a0dSDavid Yang 	mask = YT921X_PORT_LEARN_DIS;
11999c472a0dSDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask);
12009c472a0dSDavid Yang 	if (res)
12019c472a0dSDavid Yang 		return res;
12029c472a0dSDavid Yang 
12039c472a0dSDavid Yang 	return 0;
12049c472a0dSDavid Yang }
12059c472a0dSDavid Yang 
12069c472a0dSDavid Yang static int yt921x_isolate(struct yt921x_priv *priv, int port)
12079c472a0dSDavid Yang {
12089c472a0dSDavid Yang 	u32 mask;
12099c472a0dSDavid Yang 	int res;
12109c472a0dSDavid Yang 
12119c472a0dSDavid Yang 	mask = BIT(port);
12129c472a0dSDavid Yang 	for (int i = 0; i < YT921X_PORT_NUM; i++) {
12139c472a0dSDavid Yang 		if ((BIT(i) & priv->cpu_ports_mask) || i == port)
12149c472a0dSDavid Yang 			continue;
12159c472a0dSDavid Yang 
12169c472a0dSDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
12179c472a0dSDavid Yang 					  mask);
12189c472a0dSDavid Yang 		if (res)
12199c472a0dSDavid Yang 			return res;
12209c472a0dSDavid Yang 	}
12219c472a0dSDavid Yang 
12229c472a0dSDavid Yang 	return 0;
12239c472a0dSDavid Yang }
12249c472a0dSDavid Yang 
12259c472a0dSDavid Yang /* Make sure to include the CPU port in ports_mask, or your bridge will
12269c472a0dSDavid Yang  * not have it.
12279c472a0dSDavid Yang  */
12289c472a0dSDavid Yang static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
12299c472a0dSDavid Yang {
12309c472a0dSDavid Yang 	unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
12319c472a0dSDavid Yang 	u32 isolated_mask;
12329c472a0dSDavid Yang 	u32 ctrl;
12339c472a0dSDavid Yang 	int port;
12349c472a0dSDavid Yang 	int res;
12359c472a0dSDavid Yang 
12369c472a0dSDavid Yang 	isolated_mask = 0;
12379c472a0dSDavid Yang 	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
12389c472a0dSDavid Yang 		struct yt921x_port *pp = &priv->ports[port];
12399c472a0dSDavid Yang 
12409c472a0dSDavid Yang 		if (pp->isolated)
12419c472a0dSDavid Yang 			isolated_mask |= BIT(port);
12429c472a0dSDavid Yang 	}
12439c472a0dSDavid Yang 
12449c472a0dSDavid Yang 	/* Block from non-cpu bridge ports ... */
12459c472a0dSDavid Yang 	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
12469c472a0dSDavid Yang 		struct yt921x_port *pp = &priv->ports[port];
12479c472a0dSDavid Yang 
12489c472a0dSDavid Yang 		/* to non-bridge ports */
12499c472a0dSDavid Yang 		ctrl = ~ports_mask;
12509c472a0dSDavid Yang 		/* to isolated ports when isolated */
12519c472a0dSDavid Yang 		if (pp->isolated)
12529c472a0dSDavid Yang 			ctrl |= isolated_mask;
12539c472a0dSDavid Yang 		/* to itself when non-hairpin */
12549c472a0dSDavid Yang 		if (!pp->hairpin)
12559c472a0dSDavid Yang 			ctrl |= BIT(port);
12569c472a0dSDavid Yang 		else
12579c472a0dSDavid Yang 			ctrl &= ~BIT(port);
12589c472a0dSDavid Yang 
12599c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
12609c472a0dSDavid Yang 				       ctrl);
12619c472a0dSDavid Yang 		if (res)
12629c472a0dSDavid Yang 			return res;
12639c472a0dSDavid Yang 	}
12649c472a0dSDavid Yang 
12659c472a0dSDavid Yang 	return 0;
12669c472a0dSDavid Yang }
12679c472a0dSDavid Yang 
12689c472a0dSDavid Yang static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
12699c472a0dSDavid Yang {
12709c472a0dSDavid Yang 	int res;
12719c472a0dSDavid Yang 
12729c472a0dSDavid Yang 	res = yt921x_userport_standalone(priv, port);
12739c472a0dSDavid Yang 	if (res)
12749c472a0dSDavid Yang 		return res;
12759c472a0dSDavid Yang 
12769c472a0dSDavid Yang 	res = yt921x_isolate(priv, port);
12779c472a0dSDavid Yang 	if (res)
12789c472a0dSDavid Yang 		return res;
12799c472a0dSDavid Yang 
12809c472a0dSDavid Yang 	return 0;
12819c472a0dSDavid Yang }
12829c472a0dSDavid Yang 
12839c472a0dSDavid Yang static int
12849c472a0dSDavid Yang yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
12859c472a0dSDavid Yang {
12869c472a0dSDavid Yang 	int res;
12879c472a0dSDavid Yang 
12889c472a0dSDavid Yang 	res = yt921x_userport_bridge(priv, port);
12899c472a0dSDavid Yang 	if (res)
12909c472a0dSDavid Yang 		return res;
12919c472a0dSDavid Yang 
12929c472a0dSDavid Yang 	res = yt921x_bridge(priv, ports_mask);
12939c472a0dSDavid Yang 	if (res)
12949c472a0dSDavid Yang 		return res;
12959c472a0dSDavid Yang 
12969c472a0dSDavid Yang 	return 0;
12979c472a0dSDavid Yang }
12989c472a0dSDavid Yang 
12999c472a0dSDavid Yang static int
13009c472a0dSDavid Yang yt921x_bridge_flags(struct yt921x_priv *priv, int port,
13019c472a0dSDavid Yang 		    struct switchdev_brport_flags flags)
13029c472a0dSDavid Yang {
13039c472a0dSDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
13049c472a0dSDavid Yang 	bool do_flush;
13059c472a0dSDavid Yang 	u32 mask;
13069c472a0dSDavid Yang 	int res;
13079c472a0dSDavid Yang 
13089c472a0dSDavid Yang 	if (flags.mask & BR_LEARNING) {
13099c472a0dSDavid Yang 		bool learning = flags.val & BR_LEARNING;
13109c472a0dSDavid Yang 
13119c472a0dSDavid Yang 		mask = YT921X_PORT_LEARN_DIS;
13129c472a0dSDavid Yang 		res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
13139c472a0dSDavid Yang 					     mask, !learning);
13149c472a0dSDavid Yang 		if (res)
13159c472a0dSDavid Yang 			return res;
13169c472a0dSDavid Yang 	}
13179c472a0dSDavid Yang 
13189c472a0dSDavid Yang 	/* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
13199c472a0dSDavid Yang 	 * is set
13209c472a0dSDavid Yang 	 */
13219c472a0dSDavid Yang 
13229c472a0dSDavid Yang 	/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
13239c472a0dSDavid Yang 
13249c472a0dSDavid Yang 	do_flush = false;
13259c472a0dSDavid Yang 	if (flags.mask & BR_HAIRPIN_MODE) {
13269c472a0dSDavid Yang 		pp->hairpin = flags.val & BR_HAIRPIN_MODE;
13279c472a0dSDavid Yang 		do_flush = true;
13289c472a0dSDavid Yang 	}
13299c472a0dSDavid Yang 	if (flags.mask & BR_ISOLATED) {
13309c472a0dSDavid Yang 		pp->isolated = flags.val & BR_ISOLATED;
13319c472a0dSDavid Yang 		do_flush = true;
13329c472a0dSDavid Yang 	}
13339c472a0dSDavid Yang 	if (do_flush) {
13349c472a0dSDavid Yang 		struct dsa_switch *ds = &priv->ds;
13359c472a0dSDavid Yang 		struct dsa_port *dp = dsa_to_port(ds, port);
13369c472a0dSDavid Yang 		struct net_device *bdev;
13379c472a0dSDavid Yang 
13389c472a0dSDavid Yang 		bdev = dsa_port_bridge_dev_get(dp);
13399c472a0dSDavid Yang 		if (bdev) {
13409c472a0dSDavid Yang 			u32 ports_mask;
13419c472a0dSDavid Yang 
13429c472a0dSDavid Yang 			ports_mask = dsa_bridge_ports(ds, bdev);
13439c472a0dSDavid Yang 			ports_mask |= priv->cpu_ports_mask;
13449c472a0dSDavid Yang 			res = yt921x_bridge(priv, ports_mask);
13459c472a0dSDavid Yang 			if (res)
13469c472a0dSDavid Yang 				return res;
13479c472a0dSDavid Yang 		}
13489c472a0dSDavid Yang 	}
13499c472a0dSDavid Yang 
13509c472a0dSDavid Yang 	return 0;
13519c472a0dSDavid Yang }
13529c472a0dSDavid Yang 
13539c472a0dSDavid Yang static int
13549c472a0dSDavid Yang yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
13559c472a0dSDavid Yang 				 struct switchdev_brport_flags flags,
13569c472a0dSDavid Yang 				 struct netlink_ext_ack *extack)
13579c472a0dSDavid Yang {
13589c472a0dSDavid Yang 	if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
13599c472a0dSDavid Yang 			   BR_MCAST_FLOOD | BR_ISOLATED))
13609c472a0dSDavid Yang 		return -EINVAL;
13619c472a0dSDavid Yang 	return 0;
13629c472a0dSDavid Yang }
13639c472a0dSDavid Yang 
13649c472a0dSDavid Yang static int
13659c472a0dSDavid Yang yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
13669c472a0dSDavid Yang 			     struct switchdev_brport_flags flags,
13679c472a0dSDavid Yang 			     struct netlink_ext_ack *extack)
13689c472a0dSDavid Yang {
1369cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
13709c472a0dSDavid Yang 	int res;
13719c472a0dSDavid Yang 
13729c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
13739c472a0dSDavid Yang 		return 0;
13749c472a0dSDavid Yang 
13759c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
13769c472a0dSDavid Yang 	res = yt921x_bridge_flags(priv, port, flags);
13779c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
13789c472a0dSDavid Yang 
13799c472a0dSDavid Yang 	return res;
13809c472a0dSDavid Yang }
13819c472a0dSDavid Yang 
13829c472a0dSDavid Yang static void
13839c472a0dSDavid Yang yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
13849c472a0dSDavid Yang 			     struct dsa_bridge bridge)
13859c472a0dSDavid Yang {
1386cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
1387cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
13889c472a0dSDavid Yang 	int res;
13899c472a0dSDavid Yang 
13909c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
13919c472a0dSDavid Yang 		return;
13929c472a0dSDavid Yang 
13939c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
13949c472a0dSDavid Yang 	res = yt921x_bridge_leave(priv, port);
13959c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
13969c472a0dSDavid Yang 
13979c472a0dSDavid Yang 	if (res)
13989c472a0dSDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "unbridge",
13999c472a0dSDavid Yang 			port, res);
14009c472a0dSDavid Yang }
14019c472a0dSDavid Yang 
14029c472a0dSDavid Yang static int
14039c472a0dSDavid Yang yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
14049c472a0dSDavid Yang 			    struct dsa_bridge bridge, bool *tx_fwd_offload,
14059c472a0dSDavid Yang 			    struct netlink_ext_ack *extack)
14069c472a0dSDavid Yang {
1407cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
14089c472a0dSDavid Yang 	u16 ports_mask;
14099c472a0dSDavid Yang 	int res;
14109c472a0dSDavid Yang 
14119c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port))
14129c472a0dSDavid Yang 		return 0;
14139c472a0dSDavid Yang 
14149c472a0dSDavid Yang 	ports_mask = dsa_bridge_ports(ds, bridge.dev);
14159c472a0dSDavid Yang 	ports_mask |= priv->cpu_ports_mask;
14169c472a0dSDavid Yang 
14179c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
14189c472a0dSDavid Yang 	res = yt921x_bridge_join(priv, port, ports_mask);
14199c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
14209c472a0dSDavid Yang 
14219c472a0dSDavid Yang 	return res;
14229c472a0dSDavid Yang }
14239c472a0dSDavid Yang 
14249c472a0dSDavid Yang static int
14259c472a0dSDavid Yang yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
14269c472a0dSDavid Yang 			      const struct switchdev_mst_state *st)
14279c472a0dSDavid Yang {
1428cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
14299c472a0dSDavid Yang 	u32 mask;
14309c472a0dSDavid Yang 	u32 ctrl;
14319c472a0dSDavid Yang 	int res;
14329c472a0dSDavid Yang 
14339c472a0dSDavid Yang 	mask = YT921X_STP_PORTn_M(port);
14349c472a0dSDavid Yang 	switch (st->state) {
14359c472a0dSDavid Yang 	case BR_STATE_DISABLED:
14369c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_DISABLED(port);
14379c472a0dSDavid Yang 		break;
14389c472a0dSDavid Yang 	case BR_STATE_LISTENING:
14399c472a0dSDavid Yang 	case BR_STATE_LEARNING:
14409c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
14419c472a0dSDavid Yang 		break;
14429c472a0dSDavid Yang 	case BR_STATE_FORWARDING:
14439c472a0dSDavid Yang 	default:
14449c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_FORWARD(port);
14459c472a0dSDavid Yang 		break;
14469c472a0dSDavid Yang 	case BR_STATE_BLOCKING:
14479c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_BLOCKING(port);
14489c472a0dSDavid Yang 		break;
14499c472a0dSDavid Yang 	}
14509c472a0dSDavid Yang 
14519c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
14529c472a0dSDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl);
14539c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
14549c472a0dSDavid Yang 
14559c472a0dSDavid Yang 	return res;
14569c472a0dSDavid Yang }
14579c472a0dSDavid Yang 
14589c472a0dSDavid Yang static int
14599c472a0dSDavid Yang yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
14609c472a0dSDavid Yang 			 const struct switchdev_vlan_msti *msti)
14619c472a0dSDavid Yang {
1462cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
14639c472a0dSDavid Yang 	u32 masks[2];
14649c472a0dSDavid Yang 	u32 ctrls[2];
14659c472a0dSDavid Yang 	int res;
14669c472a0dSDavid Yang 
14679c472a0dSDavid Yang 	if (!msti->vid)
14689c472a0dSDavid Yang 		return -EINVAL;
14699c472a0dSDavid Yang 	if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
14709c472a0dSDavid Yang 		return -EINVAL;
14719c472a0dSDavid Yang 
14729c472a0dSDavid Yang 	masks[0] = 0;
14739c472a0dSDavid Yang 	ctrls[0] = 0;
14749c472a0dSDavid Yang 	masks[1] = YT921X_VLAN_CTRLb_STP_ID_M;
14759c472a0dSDavid Yang 	ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti);
14769c472a0dSDavid Yang 
14779c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
14789c472a0dSDavid Yang 	res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
14799c472a0dSDavid Yang 				       masks, ctrls);
14809c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
14819c472a0dSDavid Yang 
14829c472a0dSDavid Yang 	return res;
14839c472a0dSDavid Yang }
14849c472a0dSDavid Yang 
14859c472a0dSDavid Yang static void
14869c472a0dSDavid Yang yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
14879c472a0dSDavid Yang {
1488cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
1489cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
14909c472a0dSDavid Yang 	struct dsa_port *dp = dsa_to_port(ds, port);
14919c472a0dSDavid Yang 	bool learning;
14929c472a0dSDavid Yang 	u32 mask;
14939c472a0dSDavid Yang 	u32 ctrl;
14949c472a0dSDavid Yang 	int res;
14959c472a0dSDavid Yang 
14969c472a0dSDavid Yang 	mask = YT921X_STP_PORTn_M(port);
14979c472a0dSDavid Yang 	learning = false;
14989c472a0dSDavid Yang 	switch (state) {
14999c472a0dSDavid Yang 	case BR_STATE_DISABLED:
15009c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_DISABLED(port);
15019c472a0dSDavid Yang 		break;
15029c472a0dSDavid Yang 	case BR_STATE_LISTENING:
15039c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
15049c472a0dSDavid Yang 		break;
15059c472a0dSDavid Yang 	case BR_STATE_LEARNING:
15069c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_LEARNING(port);
15079c472a0dSDavid Yang 		learning = dp->learning;
15089c472a0dSDavid Yang 		break;
15099c472a0dSDavid Yang 	case BR_STATE_FORWARDING:
15109c472a0dSDavid Yang 	default:
15119c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_FORWARD(port);
15129c472a0dSDavid Yang 		learning = dp->learning;
15139c472a0dSDavid Yang 		break;
15149c472a0dSDavid Yang 	case BR_STATE_BLOCKING:
15159c472a0dSDavid Yang 		ctrl = YT921X_STP_PORTn_BLOCKING(port);
15169c472a0dSDavid Yang 		break;
15179c472a0dSDavid Yang 	}
15189c472a0dSDavid Yang 
15199c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
15209c472a0dSDavid Yang 	do {
15219c472a0dSDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl);
15229c472a0dSDavid Yang 		if (res)
15239c472a0dSDavid Yang 			break;
15249c472a0dSDavid Yang 
15259c472a0dSDavid Yang 		mask = YT921X_PORT_LEARN_DIS;
15269c472a0dSDavid Yang 		ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
15279c472a0dSDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
15289c472a0dSDavid Yang 					     mask, ctrl);
15299c472a0dSDavid Yang 	} while (0);
15309c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
15319c472a0dSDavid Yang 
15329c472a0dSDavid Yang 	if (res)
15339c472a0dSDavid Yang 		dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
15349c472a0dSDavid Yang 			port, res);
15359c472a0dSDavid Yang }
15369c472a0dSDavid Yang 
15379c472a0dSDavid Yang static int __maybe_unused
15389c472a0dSDavid Yang yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port)
15399c472a0dSDavid Yang {
1540cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
15419c472a0dSDavid Yang 	u32 val;
15429c472a0dSDavid Yang 	int res;
15439c472a0dSDavid Yang 
15449c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
15459c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val);
15469c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
15479c472a0dSDavid Yang 
15489c472a0dSDavid Yang 	if (res)
15499c472a0dSDavid Yang 		return res;
15509c472a0dSDavid Yang 
15519c472a0dSDavid Yang 	return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val);
15529c472a0dSDavid Yang }
15539c472a0dSDavid Yang 
15549c472a0dSDavid Yang static int __maybe_unused
15559c472a0dSDavid Yang yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
15569c472a0dSDavid Yang {
1557cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
15589c472a0dSDavid Yang 	u32 mask;
15599c472a0dSDavid Yang 	u32 ctrl;
15609c472a0dSDavid Yang 	int res;
15619c472a0dSDavid Yang 
15629c472a0dSDavid Yang 	if (prio >= YT921X_PRIO_NUM)
15639c472a0dSDavid Yang 		return -EINVAL;
15649c472a0dSDavid Yang 
15659c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
15669c472a0dSDavid Yang 	mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN;
15679c472a0dSDavid Yang 	ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN;
15689c472a0dSDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl);
15699c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
15709c472a0dSDavid Yang 
15719c472a0dSDavid Yang 	return res;
15729c472a0dSDavid Yang }
15739c472a0dSDavid Yang 
15749c472a0dSDavid Yang static int __maybe_unused appprios_cmp(const void *a, const void *b)
15759c472a0dSDavid Yang {
15769c472a0dSDavid Yang 	return ((const u8 *)b)[1] - ((const u8 *)a)[1];
15779c472a0dSDavid Yang }
15789c472a0dSDavid Yang 
15799c472a0dSDavid Yang static int __maybe_unused
15809c472a0dSDavid Yang yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
15819c472a0dSDavid Yang 			     int *nselp)
15829c472a0dSDavid Yang {
1583cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
15849c472a0dSDavid Yang 	u8 appprios[2][2] = {};
15859c472a0dSDavid Yang 	int nsel;
15869c472a0dSDavid Yang 	u32 val;
15879c472a0dSDavid Yang 	int res;
15889c472a0dSDavid Yang 
15899c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
15909c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val);
15919c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
15929c472a0dSDavid Yang 
15939c472a0dSDavid Yang 	if (res)
15949c472a0dSDavid Yang 		return res;
15959c472a0dSDavid Yang 
15969c472a0dSDavid Yang 	appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP;
15979c472a0dSDavid Yang 	appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7;
15989c472a0dSDavid Yang 	appprios[1][0] = DCB_APP_SEL_PCP;
15999c472a0dSDavid Yang 	appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7;
16009c472a0dSDavid Yang 	sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp,
16019c472a0dSDavid Yang 	     NULL);
16029c472a0dSDavid Yang 
16039c472a0dSDavid Yang 	nsel = 0;
16049c472a0dSDavid Yang 	for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) {
16059c472a0dSDavid Yang 		sel[nsel] = appprios[i][0];
16069c472a0dSDavid Yang 		nsel++;
16079c472a0dSDavid Yang 	}
16089c472a0dSDavid Yang 	*nselp = nsel;
16099c472a0dSDavid Yang 
16109c472a0dSDavid Yang 	return 0;
16119c472a0dSDavid Yang }
16129c472a0dSDavid Yang 
16139c472a0dSDavid Yang static int __maybe_unused
16149c472a0dSDavid Yang yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
16159c472a0dSDavid Yang 			     int nsel)
16169c472a0dSDavid Yang {
1617cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
1618cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
16199c472a0dSDavid Yang 	u32 ctrl;
16209c472a0dSDavid Yang 	int res;
16219c472a0dSDavid Yang 
16229c472a0dSDavid Yang 	if (nsel > YT921X_APP_SEL_NUM)
16239c472a0dSDavid Yang 		return -EINVAL;
16249c472a0dSDavid Yang 
16259c472a0dSDavid Yang 	ctrl = 0;
16269c472a0dSDavid Yang 	for (int i = 0; i < nsel; i++) {
16279c472a0dSDavid Yang 		switch (sel[i]) {
16289c472a0dSDavid Yang 		case IEEE_8021QAZ_APP_SEL_DSCP:
16299c472a0dSDavid Yang 			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP,
16309c472a0dSDavid Yang 							  7 - i);
16319c472a0dSDavid Yang 			break;
16329c472a0dSDavid Yang 		case DCB_APP_SEL_PCP:
16339c472a0dSDavid Yang 			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP,
16349c472a0dSDavid Yang 							  7 - i);
16359c472a0dSDavid Yang 			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP,
16369c472a0dSDavid Yang 							  7 - i);
16379c472a0dSDavid Yang 			break;
16389c472a0dSDavid Yang 		default:
16399c472a0dSDavid Yang 			dev_err(dev,
16409c472a0dSDavid Yang 				"Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n",
16419c472a0dSDavid Yang 				i + 1);
16429c472a0dSDavid Yang 			return -EOPNOTSUPP;
16439c472a0dSDavid Yang 		}
16449c472a0dSDavid Yang 	}
16459c472a0dSDavid Yang 
16469c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
16479c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl);
16489c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
16499c472a0dSDavid Yang 
16509c472a0dSDavid Yang 	return res;
16519c472a0dSDavid Yang }
16529c472a0dSDavid Yang 
16539c472a0dSDavid Yang static int yt921x_port_down(struct yt921x_priv *priv, int port)
16549c472a0dSDavid Yang {
165507b60e1bSDavid Yang 	const struct yt921x_info *info = priv->info;
16569c472a0dSDavid Yang 	u32 mask;
16579c472a0dSDavid Yang 	int res;
16589c472a0dSDavid Yang 
1659be5cdff0SDavid Yang 	mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_RX_MAC_EN |
1660be5cdff0SDavid Yang 	       YT921X_PORT_TX_MAC_EN;
16619c472a0dSDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
16629c472a0dSDavid Yang 	if (res)
16639c472a0dSDavid Yang 		return res;
16649c472a0dSDavid Yang 
166507b60e1bSDavid Yang 	if (BIT(port) & info->serdes_mask) {
16669c472a0dSDavid Yang 		mask = YT921X_SERDES_LINK;
16679c472a0dSDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
16689c472a0dSDavid Yang 		if (res)
16699c472a0dSDavid Yang 			return res;
167007b60e1bSDavid Yang 	}
167107b60e1bSDavid Yang 	if (BIT(port) & info->xmii_mask) {
16729c472a0dSDavid Yang 		mask = YT921X_XMII_LINK;
16739c472a0dSDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
16749c472a0dSDavid Yang 		if (res)
16759c472a0dSDavid Yang 			return res;
16769c472a0dSDavid Yang 	}
16779c472a0dSDavid Yang 
16789c472a0dSDavid Yang 	return 0;
16799c472a0dSDavid Yang }
16809c472a0dSDavid Yang 
16819c472a0dSDavid Yang static int
16829c472a0dSDavid Yang yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
16839c472a0dSDavid Yang 	       phy_interface_t interface, int speed, int duplex,
16849c472a0dSDavid Yang 	       bool tx_pause, bool rx_pause)
16859c472a0dSDavid Yang {
168607b60e1bSDavid Yang 	const struct yt921x_info *info = priv->info;
168707b60e1bSDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
16885423f892SDavid Yang 	int ps = ethtool_speed_to_yt921x(speed);
16899c472a0dSDavid Yang 	u32 mask;
16909c472a0dSDavid Yang 	u32 ctrl;
16919c472a0dSDavid Yang 	int res;
16929c472a0dSDavid Yang 
16935423f892SDavid Yang 	if (ps == YT921X_SPEED_INVALID)
16949c472a0dSDavid Yang 		return -EINVAL;
16955423f892SDavid Yang 
16964b8fbb87SDavid Yang 	mask = YT921X_PORT_SPEED_M | YT921X_PORT_TX_MAC_EN |
16974b8fbb87SDavid Yang 	       YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_PAUSE |
16984b8fbb87SDavid Yang 	       YT921X_PORT_RX_PAUSE | YT921X_PORT_DUPLEX_FULL |
16994b8fbb87SDavid Yang 	       YT921X_PORT_CTRL_LINK_AN;
17005423f892SDavid Yang 	ctrl = YT921X_PORT_SPEED(ps);
17019c472a0dSDavid Yang 	if (duplex == DUPLEX_FULL)
17029c472a0dSDavid Yang 		ctrl |= YT921X_PORT_DUPLEX_FULL;
17039c472a0dSDavid Yang 	if (tx_pause)
17049c472a0dSDavid Yang 		ctrl |= YT921X_PORT_TX_PAUSE;
17059c472a0dSDavid Yang 	if (rx_pause)
17069c472a0dSDavid Yang 		ctrl |= YT921X_PORT_RX_PAUSE;
17079c472a0dSDavid Yang 	ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
17084b8fbb87SDavid Yang 	if (pp->serdes && pp->inband)
17094b8fbb87SDavid Yang 		ctrl |= YT921X_PORT_CTRL_LINK_AN;
17104b8fbb87SDavid Yang 	res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl);
17119c472a0dSDavid Yang 	if (res)
17129c472a0dSDavid Yang 		return res;
17139c472a0dSDavid Yang 
171407b60e1bSDavid Yang 	if (!(BIT(port) & (info->serdes_mask | info->xmii_mask)))
171507b60e1bSDavid Yang 		return 0;
171607b60e1bSDavid Yang 
171707b60e1bSDavid Yang 	if (pp->serdes) {
17189c472a0dSDavid Yang 		mask = YT921X_SERDES_SPEED_M;
17195423f892SDavid Yang 		ctrl = YT921X_SERDES_SPEED(ps);
17209c472a0dSDavid Yang 		mask |= YT921X_SERDES_DUPLEX_FULL;
17219c472a0dSDavid Yang 		if (duplex == DUPLEX_FULL)
17229c472a0dSDavid Yang 			ctrl |= YT921X_SERDES_DUPLEX_FULL;
17239c472a0dSDavid Yang 		mask |= YT921X_SERDES_TX_PAUSE;
17249c472a0dSDavid Yang 		if (tx_pause)
17259c472a0dSDavid Yang 			ctrl |= YT921X_SERDES_TX_PAUSE;
17269c472a0dSDavid Yang 		mask |= YT921X_SERDES_RX_PAUSE;
17279c472a0dSDavid Yang 		if (rx_pause)
17289c472a0dSDavid Yang 			ctrl |= YT921X_SERDES_RX_PAUSE;
17299c472a0dSDavid Yang 		mask |= YT921X_SERDES_LINK;
17304b8fbb87SDavid Yang 		if (!pp->inband)
17319c472a0dSDavid Yang 			ctrl |= YT921X_SERDES_LINK;
17329c472a0dSDavid Yang 		res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
17339c472a0dSDavid Yang 					     mask, ctrl);
17349c472a0dSDavid Yang 		if (res)
17359c472a0dSDavid Yang 			return res;
173607b60e1bSDavid Yang 	} else {
17379c472a0dSDavid Yang 		mask = YT921X_XMII_LINK;
17389c472a0dSDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
17399c472a0dSDavid Yang 		if (res)
17409c472a0dSDavid Yang 			return res;
17419c472a0dSDavid Yang 
17425423f892SDavid Yang 		ctrl = YT921X_MDIO_POLLING_SPEED(ps);
17439c472a0dSDavid Yang 		if (duplex == DUPLEX_FULL)
17449c472a0dSDavid Yang 			ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
17459c472a0dSDavid Yang 		ctrl |= YT921X_MDIO_POLLING_LINK;
17469c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl);
17479c472a0dSDavid Yang 		if (res)
17489c472a0dSDavid Yang 			return res;
17499c472a0dSDavid Yang 	}
17509c472a0dSDavid Yang 
17519c472a0dSDavid Yang 	return 0;
17529c472a0dSDavid Yang }
17539c472a0dSDavid Yang 
17549c472a0dSDavid Yang static int
17559c472a0dSDavid Yang yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
17569c472a0dSDavid Yang 		   phy_interface_t interface)
17579c472a0dSDavid Yang {
1758cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
175907b60e1bSDavid Yang 	const struct yt921x_info *info = priv->info;
176007b60e1bSDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
17619c472a0dSDavid Yang 	u32 mask;
17629c472a0dSDavid Yang 	int res;
17639c472a0dSDavid Yang 
176407b60e1bSDavid Yang 	if (BIT(port) & info->internal_mask) {
176507b60e1bSDavid Yang 		if (interface != PHY_INTERFACE_MODE_INTERNAL)
176607b60e1bSDavid Yang 			goto err;
17679c472a0dSDavid Yang 		return 0;
17689c472a0dSDavid Yang 	}
17699c472a0dSDavid Yang 
17709c472a0dSDavid Yang 	switch (interface) {
17719c472a0dSDavid Yang 	/* SERDES */
17729c472a0dSDavid Yang 	case PHY_INTERFACE_MODE_SGMII:
17739c472a0dSDavid Yang 	case PHY_INTERFACE_MODE_100BASEX:
17749c472a0dSDavid Yang 	case PHY_INTERFACE_MODE_1000BASEX:
17759c472a0dSDavid Yang 	case PHY_INTERFACE_MODE_2500BASEX:
177607b60e1bSDavid Yang 		if (!(BIT(port) & info->serdes_mask))
177707b60e1bSDavid Yang 			goto err;
177807b60e1bSDavid Yang 
17799c472a0dSDavid Yang 		mask = YT921X_SERDES_CTRL_PORTn(port);
17809c472a0dSDavid Yang 		res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
17819c472a0dSDavid Yang 		if (res)
17829c472a0dSDavid Yang 			return res;
17839c472a0dSDavid Yang 
17849c472a0dSDavid Yang 		mask = YT921X_XMII_CTRL_PORTn(port);
17859c472a0dSDavid Yang 		res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask);
17869c472a0dSDavid Yang 		if (res)
17879c472a0dSDavid Yang 			return res;
17889c472a0dSDavid Yang 
178907b60e1bSDavid Yang 		/* The order is quite arbitrary - we can't return to a safe
179007b60e1bSDavid Yang 		 * state on IO errors.
179107b60e1bSDavid Yang 		 */
179207b60e1bSDavid Yang 		pp->serdes = true;
179307b60e1bSDavid Yang 
17949c472a0dSDavid Yang 		break;
17959c472a0dSDavid Yang 	/* add XMII support here */
17969c472a0dSDavid Yang 	default:
179707b60e1bSDavid Yang 		goto err;
17989c472a0dSDavid Yang 	}
17999c472a0dSDavid Yang 
18009c472a0dSDavid Yang 	return 0;
180107b60e1bSDavid Yang 
180207b60e1bSDavid Yang err:
180307b60e1bSDavid Yang 	dev_err(dev, "Wrong mode %d on port %d\n", interface, port);
180407b60e1bSDavid Yang 	return -EINVAL;
18059c472a0dSDavid Yang }
18069c472a0dSDavid Yang 
18074b8fbb87SDavid Yang static struct phylink_pcs *
18084b8fbb87SDavid Yang yt921x_phylink_mac_select_pcs(struct phylink_config *config,
18094b8fbb87SDavid Yang 			      phy_interface_t interface)
18104b8fbb87SDavid Yang {
18114b8fbb87SDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
1812cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds);
18134b8fbb87SDavid Yang 	const struct yt921x_info *info = priv->info;
18144b8fbb87SDavid Yang 	int port = dp->index;
18154b8fbb87SDavid Yang 
18164b8fbb87SDavid Yang 	if (!(BIT(port) & info->serdes_mask))
18174b8fbb87SDavid Yang 		return NULL;
18184b8fbb87SDavid Yang 
18194b8fbb87SDavid Yang 	switch (interface) {
18204b8fbb87SDavid Yang 	case PHY_INTERFACE_MODE_SGMII:
18214b8fbb87SDavid Yang 	case PHY_INTERFACE_MODE_100BASEX:
18224b8fbb87SDavid Yang 	case PHY_INTERFACE_MODE_1000BASEX:
18234b8fbb87SDavid Yang 	case PHY_INTERFACE_MODE_2500BASEX:
18244b8fbb87SDavid Yang 		return &priv->ports[port].pcs;
18254b8fbb87SDavid Yang 	default:
18264b8fbb87SDavid Yang 		return NULL;
18274b8fbb87SDavid Yang 	}
18284b8fbb87SDavid Yang }
18294b8fbb87SDavid Yang 
18309c472a0dSDavid Yang static void
18319c472a0dSDavid Yang yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
18329c472a0dSDavid Yang 			     phy_interface_t interface)
18339c472a0dSDavid Yang {
18349c472a0dSDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
1835cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds);
1836a219b5eaSDavid Yang 	struct yt921x_mib *pm;
18379c472a0dSDavid Yang 	int port = dp->index;
18389c472a0dSDavid Yang 	int res;
18399c472a0dSDavid Yang 
18409c472a0dSDavid Yang 	/* No need to sync; port control block is hold until device remove */
1841a219b5eaSDavid Yang 	pm = priv->ports[port].mib;
1842a219b5eaSDavid Yang 	if (pm)
1843a219b5eaSDavid Yang 		cancel_delayed_work(&pm->work);
18449c472a0dSDavid Yang 
18459c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
18469c472a0dSDavid Yang 	res = yt921x_port_down(priv, port);
18479c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
18489c472a0dSDavid Yang 
18499c472a0dSDavid Yang 	if (res)
18509c472a0dSDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
18519c472a0dSDavid Yang 			port, res);
18529c472a0dSDavid Yang }
18539c472a0dSDavid Yang 
18549c472a0dSDavid Yang static void
18559c472a0dSDavid Yang yt921x_phylink_mac_link_up(struct phylink_config *config,
18569c472a0dSDavid Yang 			   struct phy_device *phydev, unsigned int mode,
18579c472a0dSDavid Yang 			   phy_interface_t interface, int speed, int duplex,
18589c472a0dSDavid Yang 			   bool tx_pause, bool rx_pause)
18599c472a0dSDavid Yang {
18609c472a0dSDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
1861cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds);
1862a219b5eaSDavid Yang 	struct yt921x_mib *pm;
18639c472a0dSDavid Yang 	int port = dp->index;
18649c472a0dSDavid Yang 	int res;
18659c472a0dSDavid Yang 
18669c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
18679c472a0dSDavid Yang 	res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
18689c472a0dSDavid Yang 			     tx_pause, rx_pause);
18699c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
18709c472a0dSDavid Yang 
18719c472a0dSDavid Yang 	if (res)
18729c472a0dSDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
18739c472a0dSDavid Yang 			port, res);
18749c472a0dSDavid Yang 
1875a219b5eaSDavid Yang 	pm = priv->ports[port].mib;
1876a219b5eaSDavid Yang 	if (pm)
1877a219b5eaSDavid Yang 		schedule_delayed_work(&pm->work, 0);
18789c472a0dSDavid Yang }
18799c472a0dSDavid Yang 
18809c472a0dSDavid Yang static void
18819c472a0dSDavid Yang yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
18829c472a0dSDavid Yang 			  const struct phylink_link_state *state)
18839c472a0dSDavid Yang {
18849c472a0dSDavid Yang 	struct dsa_port *dp = dsa_phylink_to_port(config);
1885cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds);
18869c472a0dSDavid Yang 	int port = dp->index;
18879c472a0dSDavid Yang 	int res;
18889c472a0dSDavid Yang 
18899c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
18909c472a0dSDavid Yang 	res = yt921x_port_config(priv, port, mode, state->interface);
18919c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
18929c472a0dSDavid Yang 
18939c472a0dSDavid Yang 	if (res)
18949c472a0dSDavid Yang 		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
18959c472a0dSDavid Yang 			port, res);
18969c472a0dSDavid Yang }
18979c472a0dSDavid Yang 
18989c472a0dSDavid Yang static void
18999c472a0dSDavid Yang yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
19009c472a0dSDavid Yang 			    struct phylink_config *config)
19019c472a0dSDavid Yang {
1902cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
19039c472a0dSDavid Yang 	const struct yt921x_info *info = priv->info;
19049c472a0dSDavid Yang 
19059c472a0dSDavid Yang 	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
19069c472a0dSDavid Yang 				   MAC_10 | MAC_100 | MAC_1000;
19079c472a0dSDavid Yang 
19089c472a0dSDavid Yang 	if (info->internal_mask & BIT(port)) {
19099c472a0dSDavid Yang 		/* Port 10 for MCU should probably go here too. But since that
19109c472a0dSDavid Yang 		 * is untested yet, turn it down for the moment by letting it
19119c472a0dSDavid Yang 		 * fall to the default branch.
19129c472a0dSDavid Yang 		 */
19139c472a0dSDavid Yang 		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
19149c472a0dSDavid Yang 			  config->supported_interfaces);
1915605597afSDavid Yang 		return;
1916605597afSDavid Yang 	}
19179c472a0dSDavid Yang 
1918605597afSDavid Yang 	if (BIT(port) & info->serdes_mask) {
19199c472a0dSDavid Yang 		__set_bit(PHY_INTERFACE_MODE_SGMII,
19209c472a0dSDavid Yang 			  config->supported_interfaces);
19219c472a0dSDavid Yang 		/* REVSGMII (SGMII in PHY role) should go here, once
19229c472a0dSDavid Yang 		 * PHY_INTERFACE_MODE_REVSGMII is introduced.
19239c472a0dSDavid Yang 		 */
19249c472a0dSDavid Yang 		__set_bit(PHY_INTERFACE_MODE_100BASEX,
19259c472a0dSDavid Yang 			  config->supported_interfaces);
19269c472a0dSDavid Yang 		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
19279c472a0dSDavid Yang 			  config->supported_interfaces);
19289c472a0dSDavid Yang 		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
19299c472a0dSDavid Yang 			  config->supported_interfaces);
19309c472a0dSDavid Yang 		config->mac_capabilities |= MAC_2500FD;
1931605597afSDavid Yang 	}
1932605597afSDavid Yang 	if (BIT(port) & info->xmii_mask) {
19339c472a0dSDavid Yang 		/* Not tested. To add support for XMII:
19349c472a0dSDavid Yang 		 *   - Add proper interface modes below
19359c472a0dSDavid Yang 		 *   - Handle them in yt921x_port_config()
19369c472a0dSDavid Yang 		 */
19379c472a0dSDavid Yang 	}
19389c472a0dSDavid Yang 	/* no such port: empty supported_interfaces causes phylink to turn it
19399c472a0dSDavid Yang 	 * down
19409c472a0dSDavid Yang 	 */
19419c472a0dSDavid Yang }
19429c472a0dSDavid Yang 
19439c472a0dSDavid Yang static int yt921x_port_setup(struct yt921x_priv *priv, int port)
19449c472a0dSDavid Yang {
19459c472a0dSDavid Yang 	struct dsa_switch *ds = &priv->ds;
19469c472a0dSDavid Yang 	u32 ctrl;
19479c472a0dSDavid Yang 	int res;
19489c472a0dSDavid Yang 
19499c472a0dSDavid Yang 	res = yt921x_userport_standalone(priv, port);
19509c472a0dSDavid Yang 	if (res)
19519c472a0dSDavid Yang 		return res;
19529c472a0dSDavid Yang 
19539c472a0dSDavid Yang 	/* Clear prio order (even if DCB is not enabled) to avoid unsolicited
19549c472a0dSDavid Yang 	 * priorities
19559c472a0dSDavid Yang 	 */
19569c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0);
19579c472a0dSDavid Yang 	if (res)
19589c472a0dSDavid Yang 		return res;
19599c472a0dSDavid Yang 
19609c472a0dSDavid Yang 	if (dsa_is_cpu_port(ds, port)) {
19619c472a0dSDavid Yang 		/* Egress of CPU port is supposed to be completely controlled
19629c472a0dSDavid Yang 		 * via tagging, so set to oneway isolated (drop all packets
19639c472a0dSDavid Yang 		 * without tag).
19649c472a0dSDavid Yang 		 */
19659c472a0dSDavid Yang 		ctrl = ~(u32)0;
19669c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
19679c472a0dSDavid Yang 				       ctrl);
19689c472a0dSDavid Yang 		if (res)
19699c472a0dSDavid Yang 			return res;
19709c472a0dSDavid Yang 
19719c472a0dSDavid Yang 		/* To simplify FDB "isolation" simulation, we also disable
19729c472a0dSDavid Yang 		 * learning on the CPU port, and let software identify packets
19739c472a0dSDavid Yang 		 * towarding CPU (either trapped or a static FDB entry is
19749c472a0dSDavid Yang 		 * matched, no matter which bridge that entry is for), which is
19759c472a0dSDavid Yang 		 * already done by yt921x_userport_standalone(). As a result,
19769c472a0dSDavid Yang 		 * VLAN-awareness becomes unrelated on the CPU port (set to
19779c472a0dSDavid Yang 		 * VLAN-unaware by the way).
19789c472a0dSDavid Yang 		 */
19799c472a0dSDavid Yang 	}
19809c472a0dSDavid Yang 
19819c472a0dSDavid Yang 	return 0;
19829c472a0dSDavid Yang }
19839c472a0dSDavid Yang 
19849c472a0dSDavid Yang static enum dsa_tag_protocol
19859c472a0dSDavid Yang yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
19869c472a0dSDavid Yang 			    enum dsa_tag_protocol m)
19879c472a0dSDavid Yang {
19889c472a0dSDavid Yang 	return DSA_TAG_PROTO_YT921X;
19899c472a0dSDavid Yang }
19909c472a0dSDavid Yang 
1991a219b5eaSDavid Yang static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port)
1992a219b5eaSDavid Yang {
1993a219b5eaSDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
1994a219b5eaSDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
1995a219b5eaSDavid Yang 	struct yt921x_mib *pm = pp->mib;
1996a219b5eaSDavid Yang 
1997a219b5eaSDavid Yang 	/* get_stats64() might not be synchronized with port_teardown(), hold
1998a219b5eaSDavid Yang 	 * the MIB block till the device teardown
1999a219b5eaSDavid Yang 	 */
2000a219b5eaSDavid Yang 	if (pm)
2001a219b5eaSDavid Yang 		disable_delayed_work_sync(&pm->work);
2002a219b5eaSDavid Yang }
2003a219b5eaSDavid Yang 
20049c472a0dSDavid Yang static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
20059c472a0dSDavid Yang {
2006cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
2007a219b5eaSDavid Yang 	struct yt921x_port *pp = &priv->ports[port];
2008a219b5eaSDavid Yang 	const struct yt921x_info *info = priv->info;
2009a219b5eaSDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
2010a219b5eaSDavid Yang 	struct yt921x_mib *pm = pp->mib;
20119c472a0dSDavid Yang 	int res;
20129c472a0dSDavid Yang 
2013a219b5eaSDavid Yang 	if (!pm && (BIT(port) & yt921x_info_ports_mask(info))) {
2014a219b5eaSDavid Yang 		pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
2015a219b5eaSDavid Yang 		if (!pm)
2016a219b5eaSDavid Yang 			return -ENOMEM;
2017a219b5eaSDavid Yang 		pp->mib = pm;
2018a219b5eaSDavid Yang 
2019a219b5eaSDavid Yang 		pm->port = pp;
2020ed445358SDavid Yang 		u64_stats_init(&pm->syncp);
2021a219b5eaSDavid Yang 		INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll);
2022a219b5eaSDavid Yang 		/* Start disabled, so that the enable below always pairs with
2023a219b5eaSDavid Yang 		 * either this disable or the one from a previous teardown
2024a219b5eaSDavid Yang 		 */
2025a219b5eaSDavid Yang 		disable_delayed_work_sync(&pm->work);
2026a219b5eaSDavid Yang 	}
2027a219b5eaSDavid Yang 
20289c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
20299c472a0dSDavid Yang 	res = yt921x_port_setup(priv, port);
20309c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
2031a219b5eaSDavid Yang 	if (res)
20329c472a0dSDavid Yang 		return res;
2033a219b5eaSDavid Yang 
2034a219b5eaSDavid Yang 	if (pm)
2035a219b5eaSDavid Yang 		enable_delayed_work(&pm->work);
2036a219b5eaSDavid Yang 
2037a219b5eaSDavid Yang 	return 0;
20389c472a0dSDavid Yang }
20399c472a0dSDavid Yang 
20409c472a0dSDavid Yang /* Not "port" - DSCP mapping is global */
20419c472a0dSDavid Yang static int __maybe_unused
20429c472a0dSDavid Yang yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
20439c472a0dSDavid Yang {
2044cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
20459c472a0dSDavid Yang 	u32 val;
20469c472a0dSDavid Yang 	int res;
20479c472a0dSDavid Yang 
20489c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
20499c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
20509c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
20519c472a0dSDavid Yang 
20529c472a0dSDavid Yang 	if (res)
20539c472a0dSDavid Yang 		return res;
20549c472a0dSDavid Yang 
20559c472a0dSDavid Yang 	return FIELD_GET(YT921X_IPM_PRIO_M, val);
20569c472a0dSDavid Yang }
20579c472a0dSDavid Yang 
20589c472a0dSDavid Yang static int __maybe_unused
20599c472a0dSDavid Yang yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
20609c472a0dSDavid Yang {
2061cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
20629c472a0dSDavid Yang 	u32 val;
20639c472a0dSDavid Yang 	int res;
20649c472a0dSDavid Yang 
20659c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
20669c472a0dSDavid Yang 	/* During a "dcb app replace" command, the new app table entry will be
20679c472a0dSDavid Yang 	 * added first, then the old one will be deleted. But the hardware only
20689c472a0dSDavid Yang 	 * supports one QoS class per DSCP value (duh), so if we blindly delete
20699c472a0dSDavid Yang 	 * the app table entry for this DSCP value, we end up deleting the
20709c472a0dSDavid Yang 	 * entry with the new priority. Avoid that by checking whether user
20719c472a0dSDavid Yang 	 * space wants to delete the priority which is currently configured, or
20729c472a0dSDavid Yang 	 * something else which is no longer current.
20739c472a0dSDavid Yang 	 */
20749c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
20759c472a0dSDavid Yang 	if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio)
20769c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
20779c472a0dSDavid Yang 				       YT921X_IPM_PRIO(IEEE8021Q_TT_BK));
20789c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
20799c472a0dSDavid Yang 
20809c472a0dSDavid Yang 	return res;
20819c472a0dSDavid Yang }
20829c472a0dSDavid Yang 
20839c472a0dSDavid Yang static int __maybe_unused
20849c472a0dSDavid Yang yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
20859c472a0dSDavid Yang {
2086cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
20879c472a0dSDavid Yang 	int res;
20889c472a0dSDavid Yang 
20899c472a0dSDavid Yang 	if (prio >= YT921X_PRIO_NUM)
20909c472a0dSDavid Yang 		return -EINVAL;
20919c472a0dSDavid Yang 
20929c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
20939c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
20949c472a0dSDavid Yang 			       YT921X_IPM_PRIO(prio));
20959c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
20969c472a0dSDavid Yang 
20979c472a0dSDavid Yang 	return res;
20989c472a0dSDavid Yang }
20999c472a0dSDavid Yang 
21009c472a0dSDavid Yang static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp)
21019c472a0dSDavid Yang {
21029c472a0dSDavid Yang 	u32 val = YT921X_EDATA_DATA_IDLE;
21039c472a0dSDavid Yang 	int res;
21049c472a0dSDavid Yang 
21059c472a0dSDavid Yang 	res = yt921x_reg_wait(priv, YT921X_EDATA_DATA,
21069c472a0dSDavid Yang 			      YT921X_EDATA_DATA_STATUS_M, &val);
21079c472a0dSDavid Yang 	if (res)
21089c472a0dSDavid Yang 		return res;
21099c472a0dSDavid Yang 
21109c472a0dSDavid Yang 	*valp = val;
21119c472a0dSDavid Yang 	return 0;
21129c472a0dSDavid Yang }
21139c472a0dSDavid Yang 
21149c472a0dSDavid Yang static int
21159c472a0dSDavid Yang yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp)
21169c472a0dSDavid Yang {
21179c472a0dSDavid Yang 	u32 ctrl;
21189c472a0dSDavid Yang 	u32 val;
21199c472a0dSDavid Yang 	int res;
21209c472a0dSDavid Yang 
21219c472a0dSDavid Yang 	ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
21229c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
21239c472a0dSDavid Yang 	if (res)
21249c472a0dSDavid Yang 		return res;
21259c472a0dSDavid Yang 	res = yt921x_edata_wait(priv, &val);
21269c472a0dSDavid Yang 	if (res)
21279c472a0dSDavid Yang 		return res;
21289c472a0dSDavid Yang 
21299c472a0dSDavid Yang 	*valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
21309c472a0dSDavid Yang 	return 0;
21319c472a0dSDavid Yang }
21329c472a0dSDavid Yang 
21339c472a0dSDavid Yang static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
21349c472a0dSDavid Yang {
21359c472a0dSDavid Yang 	u32 val;
21369c472a0dSDavid Yang 	int res;
21379c472a0dSDavid Yang 
21389c472a0dSDavid Yang 	res = yt921x_edata_wait(priv, &val);
21399c472a0dSDavid Yang 	if (res)
21409c472a0dSDavid Yang 		return res;
21419c472a0dSDavid Yang 	return yt921x_edata_read_cont(priv, addr, valp);
21429c472a0dSDavid Yang }
21439c472a0dSDavid Yang 
21449af9a816SDavid Yang static void yt921x_dsa_teardown(struct dsa_switch *ds)
21459af9a816SDavid Yang {
2146e3bfd256SArnd Bergmann #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
2147cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
21489af9a816SDavid Yang 
21499af9a816SDavid Yang 	yt921x_leds_remove(priv);
21509af9a816SDavid Yang #endif
21519af9a816SDavid Yang }
21529af9a816SDavid Yang 
21539c472a0dSDavid Yang static int yt921x_chip_detect(struct yt921x_priv *priv)
21549c472a0dSDavid Yang {
2155cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
21569c472a0dSDavid Yang 	const struct yt921x_info *info;
21579c472a0dSDavid Yang 	u8 extmode;
21589c472a0dSDavid Yang 	u32 chipid;
21599c472a0dSDavid Yang 	u32 major;
21609c472a0dSDavid Yang 	u32 mode;
21619c472a0dSDavid Yang 	u32 val;
21629c472a0dSDavid Yang 	int res;
21639c472a0dSDavid Yang 
21649c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
21659c472a0dSDavid Yang 	if (res)
21669c472a0dSDavid Yang 		return res;
21679c472a0dSDavid Yang 
21689c472a0dSDavid Yang 	major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
21699c472a0dSDavid Yang 
21709c472a0dSDavid Yang 	for (info = yt921x_infos; info->name; info++)
21719c472a0dSDavid Yang 		if (info->major == major)
21729c472a0dSDavid Yang 			break;
21739c472a0dSDavid Yang 	if (!info->name) {
21749c472a0dSDavid Yang 		dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
21759c472a0dSDavid Yang 		return -ENODEV;
21769c472a0dSDavid Yang 	}
21779c472a0dSDavid Yang 
21789c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode);
21799c472a0dSDavid Yang 	if (res)
21809c472a0dSDavid Yang 		return res;
21819c472a0dSDavid Yang 	res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
21829c472a0dSDavid Yang 	if (res)
21839c472a0dSDavid Yang 		return res;
21849c472a0dSDavid Yang 
21859c472a0dSDavid Yang 	for (; info->name; info++)
21869c472a0dSDavid Yang 		if (info->major == major && info->mode == mode &&
21879c472a0dSDavid Yang 		    info->extmode == extmode)
21889c472a0dSDavid Yang 			break;
21899c472a0dSDavid Yang 	if (!info->name) {
21909c472a0dSDavid Yang 		dev_err(dev,
21919c472a0dSDavid Yang 			"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
21929c472a0dSDavid Yang 			chipid, mode, extmode);
21939c472a0dSDavid Yang 		return -ENODEV;
21949c472a0dSDavid Yang 	}
21959c472a0dSDavid Yang 
21969c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val);
21979c472a0dSDavid Yang 	if (res)
21989c472a0dSDavid Yang 		return res;
21999c472a0dSDavid Yang 	switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) {
22009c472a0dSDavid Yang 	case 0:
22019c472a0dSDavid Yang 		priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6;
22029c472a0dSDavid Yang 		break;
22039c472a0dSDavid Yang 	case YT921X_SYS_CLK_143M:
22049c472a0dSDavid Yang 		priv->cycle_ns = 7;
22059c472a0dSDavid Yang 		break;
22069c472a0dSDavid Yang 	default:
22079c472a0dSDavid Yang 		priv->cycle_ns = 8;
22089c472a0dSDavid Yang 	}
22099c472a0dSDavid Yang 
22109c472a0dSDavid Yang 	/* Print chipid here since we are interested in lower 16 bits */
22119c472a0dSDavid Yang 	dev_info(dev,
22129c472a0dSDavid Yang 		 "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
22139c472a0dSDavid Yang 		 info->name, chipid, mode, extmode);
22149c472a0dSDavid Yang 
22159c472a0dSDavid Yang 	priv->info = info;
22169c472a0dSDavid Yang 
22179c472a0dSDavid Yang 	return 0;
22189c472a0dSDavid Yang }
22199c472a0dSDavid Yang 
22209c472a0dSDavid Yang static int yt921x_chip_reset(struct yt921x_priv *priv)
22219c472a0dSDavid Yang {
2222cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
22239c472a0dSDavid Yang 	u16 eth_p_tag;
22249c472a0dSDavid Yang 	u32 val;
22259c472a0dSDavid Yang 	int res;
22269c472a0dSDavid Yang 
22279c472a0dSDavid Yang 	res = yt921x_chip_detect(priv);
22289c472a0dSDavid Yang 	if (res)
22299c472a0dSDavid Yang 		return res;
22309c472a0dSDavid Yang 
22319c472a0dSDavid Yang 	/* Reset */
22329c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
22339c472a0dSDavid Yang 	if (res)
22349c472a0dSDavid Yang 		return res;
22359c472a0dSDavid Yang 
22369c472a0dSDavid Yang 	/* RST_HW is almost same as GPIO hard reset, so we need this delay. */
22379c472a0dSDavid Yang 	fsleep(YT921X_RST_DELAY_US);
22389c472a0dSDavid Yang 
22399c472a0dSDavid Yang 	val = 0;
22409c472a0dSDavid Yang 	res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
22419c472a0dSDavid Yang 	if (res)
22429c472a0dSDavid Yang 		return res;
22439c472a0dSDavid Yang 
22449c472a0dSDavid Yang 	/* Check for tag EtherType; do it after reset in case you messed it up
22459c472a0dSDavid Yang 	 * before.
22469c472a0dSDavid Yang 	 */
22479c472a0dSDavid Yang 	res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
22489c472a0dSDavid Yang 	if (res)
22499c472a0dSDavid Yang 		return res;
22509c472a0dSDavid Yang 	eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
22519c472a0dSDavid Yang 	if (eth_p_tag != ETH_P_YT921X) {
22529c472a0dSDavid Yang 		dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
22539c472a0dSDavid Yang 			ETH_P_YT921X);
22549c472a0dSDavid Yang 		/* Despite being possible, we choose not to set CPU_TAG_TPID,
22559c472a0dSDavid Yang 		 * since there is no way it can be different unless you have the
22569c472a0dSDavid Yang 		 * wrong chip.
22579c472a0dSDavid Yang 		 */
22589c472a0dSDavid Yang 		return -EINVAL;
22599c472a0dSDavid Yang 	}
22609c472a0dSDavid Yang 
22619c472a0dSDavid Yang 	return 0;
22629c472a0dSDavid Yang }
22639c472a0dSDavid Yang 
22649c472a0dSDavid Yang static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
22659c472a0dSDavid Yang {
22669c472a0dSDavid Yang 	struct dsa_switch *ds = &priv->ds;
22679c472a0dSDavid Yang 	unsigned long cpu_ports_mask;
22689c472a0dSDavid Yang 	u32 ctrls[2];
22699c472a0dSDavid Yang 	u32 ctrl;
22709c472a0dSDavid Yang 	int port;
22719c472a0dSDavid Yang 	int res;
22729c472a0dSDavid Yang 
22739c472a0dSDavid Yang 	/* Enable DSA */
22749c472a0dSDavid Yang 	priv->cpu_ports_mask = dsa_cpu_ports(ds);
22759c472a0dSDavid Yang 
22769c472a0dSDavid Yang 	ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
22779c472a0dSDavid Yang 	       YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
22789c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
22799c472a0dSDavid Yang 	if (res)
22809c472a0dSDavid Yang 		return res;
22819c472a0dSDavid Yang 
22829c472a0dSDavid Yang 	/* Setup software switch */
22839c472a0dSDavid Yang 	ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
22849c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl);
22859c472a0dSDavid Yang 	if (res)
22869c472a0dSDavid Yang 		return res;
22879c472a0dSDavid Yang 
22889c472a0dSDavid Yang 	ctrl = GENMASK(10, 0);
22899c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
22909c472a0dSDavid Yang 	if (res)
22919c472a0dSDavid Yang 		return res;
22929c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
22939c472a0dSDavid Yang 	if (res)
22949c472a0dSDavid Yang 		return res;
22959c472a0dSDavid Yang 
22969c472a0dSDavid Yang 	/* YT921x does not support native DSA port bridging, so we use port
22979c472a0dSDavid Yang 	 * isolation to emulate it. However, be especially careful that port
22989c472a0dSDavid Yang 	 * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
22999c472a0dSDavid Yang 	 * another bridge) is matched and the destination port (in another
23009c472a0dSDavid Yang 	 * bridge) is blocked, the packet will be dropped instead of flooding to
23019c472a0dSDavid Yang 	 * the "bridged" ports, thus we need to trap and handle those packets by
23029c472a0dSDavid Yang 	 * software.
23039c472a0dSDavid Yang 	 *
23049c472a0dSDavid Yang 	 * If there is no more than one bridge, we might be able to drop them
23059c472a0dSDavid Yang 	 * directly given some conditions are met, but we trap them in all cases
23069c472a0dSDavid Yang 	 * for now.
23079c472a0dSDavid Yang 	 */
23089c472a0dSDavid Yang 	ctrl = 0;
23099c472a0dSDavid Yang 	for (int i = 0; i < YT921X_PORT_NUM; i++)
23109c472a0dSDavid Yang 		ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
23119c472a0dSDavid Yang 	/* Except for CPU ports, if any packets are sent via CPU ports without
23129c472a0dSDavid Yang 	 * tag, they should be dropped.
23139c472a0dSDavid Yang 	 */
23149c472a0dSDavid Yang 	cpu_ports_mask = priv->cpu_ports_mask;
23159c472a0dSDavid Yang 	for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
23169c472a0dSDavid Yang 		ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
23179c472a0dSDavid Yang 		ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
23189c472a0dSDavid Yang 	}
23199c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
23209c472a0dSDavid Yang 	if (res)
23219c472a0dSDavid Yang 		return res;
23229c472a0dSDavid Yang 	res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
23239c472a0dSDavid Yang 	if (res)
23249c472a0dSDavid Yang 		return res;
23259c472a0dSDavid Yang 
23269c472a0dSDavid Yang 	/* Tagged VID 0 should be treated as untagged, which confuses the
23279c472a0dSDavid Yang 	 * hardware a lot
23289c472a0dSDavid Yang 	 */
23299c472a0dSDavid Yang 	ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M;
23309c472a0dSDavid Yang 	ctrls[1] = 0;
23319c472a0dSDavid Yang 	res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls);
23329c472a0dSDavid Yang 	if (res)
23339c472a0dSDavid Yang 		return res;
23349c472a0dSDavid Yang 
23359c472a0dSDavid Yang 	return 0;
23369c472a0dSDavid Yang }
23379c472a0dSDavid Yang 
23389c472a0dSDavid Yang static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv)
23399c472a0dSDavid Yang {
23409c472a0dSDavid Yang 	u32 ctrl;
23419c472a0dSDavid Yang 	int res;
23429c472a0dSDavid Yang 
23439c472a0dSDavid Yang 	/* DSCP to internal priorities */
23449c472a0dSDavid Yang 	for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) {
23459c472a0dSDavid Yang 		int prio = ietf_dscp_to_ieee8021q_tt(dscp);
23469c472a0dSDavid Yang 
23479c472a0dSDavid Yang 		if (prio < 0)
23489c472a0dSDavid Yang 			return prio;
23499c472a0dSDavid Yang 
23509c472a0dSDavid Yang 		res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
23519c472a0dSDavid Yang 				       YT921X_IPM_PRIO(prio));
23529c472a0dSDavid Yang 		if (res)
23539c472a0dSDavid Yang 			return res;
23549c472a0dSDavid Yang 	}
23559c472a0dSDavid Yang 
23569c472a0dSDavid Yang 	/* 802.1Q QoS to internal priorities */
23579c472a0dSDavid Yang 	for (u8 pcp = 0; pcp < 8; pcp++)
23589c472a0dSDavid Yang 		for (u8 dei = 0; dei < 2; dei++) {
23599c472a0dSDavid Yang 			ctrl = YT921X_IPM_PRIO(pcp);
23609c472a0dSDavid Yang 			if (dei)
23619c472a0dSDavid Yang 				/* "Red" almost means drop, so it's not that
23629c472a0dSDavid Yang 				 * useful. Note that tc police does not support
23639c472a0dSDavid Yang 				 * Three-Color very well
23649c472a0dSDavid Yang 				 */
23659c472a0dSDavid Yang 				ctrl |= YT921X_IPM_COLOR_YELLOW;
23669c472a0dSDavid Yang 
23679c472a0dSDavid Yang 			for (u8 svlan = 0; svlan < 2; svlan++) {
23689c472a0dSDavid Yang 				u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp);
23699c472a0dSDavid Yang 
23709c472a0dSDavid Yang 				res = yt921x_reg_write(priv, reg, ctrl);
23719c472a0dSDavid Yang 				if (res)
23729c472a0dSDavid Yang 					return res;
23739c472a0dSDavid Yang 			}
23749c472a0dSDavid Yang 		}
23759c472a0dSDavid Yang 
23769c472a0dSDavid Yang 	return 0;
23779c472a0dSDavid Yang }
23789c472a0dSDavid Yang 
23799c472a0dSDavid Yang static int yt921x_chip_setup(struct yt921x_priv *priv)
23809c472a0dSDavid Yang {
23819c472a0dSDavid Yang 	u32 ctrl;
23829c472a0dSDavid Yang 	int res;
23839c472a0dSDavid Yang 
23849c472a0dSDavid Yang 	ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER;
23859c472a0dSDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl);
23869c472a0dSDavid Yang 	if (res)
23879c472a0dSDavid Yang 		return res;
23889c472a0dSDavid Yang 
23899c472a0dSDavid Yang 	res = yt921x_chip_setup_dsa(priv);
23909c472a0dSDavid Yang 	if (res)
23919c472a0dSDavid Yang 		return res;
23929c472a0dSDavid Yang 
23939c472a0dSDavid Yang 	res = yt921x_chip_setup_tc(priv);
23949c472a0dSDavid Yang 	if (res)
23959c472a0dSDavid Yang 		return res;
23969c472a0dSDavid Yang 
23979c472a0dSDavid Yang 	res = yt921x_chip_setup_acl(priv);
23989c472a0dSDavid Yang 	if (res)
23999c472a0dSDavid Yang 		return res;
24009c472a0dSDavid Yang 
24019c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB)
24029c472a0dSDavid Yang 	res = yt921x_chip_setup_qos(priv);
24039c472a0dSDavid Yang 	if (res)
24049c472a0dSDavid Yang 		return res;
24059c472a0dSDavid Yang #endif
24069c472a0dSDavid Yang 
24079c472a0dSDavid Yang 	/* Miscellaneous */
24089c472a0dSDavid Yang 	res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
24099c472a0dSDavid Yang 	if (res)
24109c472a0dSDavid Yang 		return res;
24119c472a0dSDavid Yang 
24124b8fbb87SDavid Yang 	res = yt921x_reg_clear_bits(priv, YT921X_SERDES_CTRL,
24134b8fbb87SDavid Yang 				    YT921X_SERDES_CTRL_TEST);
24144b8fbb87SDavid Yang 	if (res)
24154b8fbb87SDavid Yang 		return res;
24164b8fbb87SDavid Yang 
24179c472a0dSDavid Yang 	return 0;
24189c472a0dSDavid Yang }
24199c472a0dSDavid Yang 
24209c472a0dSDavid Yang static int yt921x_dsa_setup(struct dsa_switch *ds)
24219c472a0dSDavid Yang {
2422cece0b57SDavid Yang 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
2423cece0b57SDavid Yang 	struct device *dev = yt921x_priv_to_device(priv);
24249c472a0dSDavid Yang 	struct device_node *np = dev->of_node;
24259c472a0dSDavid Yang 	struct device_node *child;
24264b8fbb87SDavid Yang 	unsigned long mask;
24274b8fbb87SDavid Yang 	int port;
24289c472a0dSDavid Yang 	int res;
24299c472a0dSDavid Yang 
24309c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
24319c472a0dSDavid Yang 	res = yt921x_chip_reset(priv);
24329c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
24339c472a0dSDavid Yang 
24349c472a0dSDavid Yang 	if (res)
24359c472a0dSDavid Yang 		return res;
24369c472a0dSDavid Yang 
24379c472a0dSDavid Yang 	/* Register the internal mdio bus. Nodes for internal ports should have
24389c472a0dSDavid Yang 	 * proper phy-handle pointing to their PHYs. Not enabling the internal
24399c472a0dSDavid Yang 	 * bus is possible, though pretty wired, if internal ports are not used.
24409c472a0dSDavid Yang 	 */
24419c472a0dSDavid Yang 	child = of_get_child_by_name(np, "mdio");
24429c472a0dSDavid Yang 	if (child) {
24439c472a0dSDavid Yang 		res = yt921x_mbus_int_init(priv, child);
24449c472a0dSDavid Yang 		of_node_put(child);
24459c472a0dSDavid Yang 		if (res)
24469c472a0dSDavid Yang 			return res;
24479c472a0dSDavid Yang 	}
24489c472a0dSDavid Yang 
24499c472a0dSDavid Yang 	/* External mdio bus is optional */
24509c472a0dSDavid Yang 	child = of_get_child_by_name(np, "mdio-external");
24519c472a0dSDavid Yang 	if (child) {
24529c472a0dSDavid Yang 		res = yt921x_mbus_ext_init(priv, child);
24539c472a0dSDavid Yang 		of_node_put(child);
24549c472a0dSDavid Yang 		if (res)
24559c472a0dSDavid Yang 			return res;
24569c472a0dSDavid Yang 
24579c472a0dSDavid Yang 		dev_err(dev, "Untested external mdio bus\n");
24589c472a0dSDavid Yang 		return -ENODEV;
24599c472a0dSDavid Yang 	}
24609c472a0dSDavid Yang 
24614b8fbb87SDavid Yang 	mask = priv->info->serdes_mask;
24624b8fbb87SDavid Yang 	for_each_set_bit(port, &mask, YT921X_PORT_NUM) {
24634b8fbb87SDavid Yang 		struct yt921x_port *pp = &priv->ports[port];
24644b8fbb87SDavid Yang 
24654b8fbb87SDavid Yang 		pp->pcs.ops = &yt921x_phylink_pcs_ops;
24664b8fbb87SDavid Yang 		pp->pcs.poll = true;
24674b8fbb87SDavid Yang 
24684b8fbb87SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_SGMII,
24694b8fbb87SDavid Yang 			  pp->pcs.supported_interfaces);
24704b8fbb87SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_100BASEX,
24714b8fbb87SDavid Yang 			  pp->pcs.supported_interfaces);
24724b8fbb87SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
24734b8fbb87SDavid Yang 			  pp->pcs.supported_interfaces);
24744b8fbb87SDavid Yang 		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
24754b8fbb87SDavid Yang 			  pp->pcs.supported_interfaces);
24764b8fbb87SDavid Yang 	}
24774b8fbb87SDavid Yang 
24789c472a0dSDavid Yang 	mutex_lock(&priv->reg_lock);
24799c472a0dSDavid Yang 	res = yt921x_chip_setup(priv);
24809c472a0dSDavid Yang 	mutex_unlock(&priv->reg_lock);
24819c472a0dSDavid Yang 
24829c472a0dSDavid Yang 	if (res)
24839c472a0dSDavid Yang 		return res;
24849c472a0dSDavid Yang 
24859af9a816SDavid Yang #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
24869af9a816SDavid Yang 	res = yt921x_leds_setup(priv);
24879af9a816SDavid Yang 	if (res)
24889af9a816SDavid Yang 		dev_warn(dev, "Failed to setup LEDs: %d\n", res);
24899af9a816SDavid Yang #endif
24909af9a816SDavid Yang 
24919c472a0dSDavid Yang 	return 0;
24929c472a0dSDavid Yang }
24939c472a0dSDavid Yang 
24949c472a0dSDavid Yang static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
24954b8fbb87SDavid Yang 	.mac_select_pcs	= yt921x_phylink_mac_select_pcs,
24969c472a0dSDavid Yang 	.mac_link_down	= yt921x_phylink_mac_link_down,
24979c472a0dSDavid Yang 	.mac_link_up	= yt921x_phylink_mac_link_up,
24989c472a0dSDavid Yang 	.mac_config	= yt921x_phylink_mac_config,
24999c472a0dSDavid Yang };
25009c472a0dSDavid Yang 
25019c472a0dSDavid Yang static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
25029c472a0dSDavid Yang 	/* mib */
25039c472a0dSDavid Yang 	.get_strings		= yt921x_dsa_get_strings,
25049c472a0dSDavid Yang 	.get_ethtool_stats	= yt921x_dsa_get_ethtool_stats,
25059c472a0dSDavid Yang 	.get_sset_count		= yt921x_dsa_get_sset_count,
25069c472a0dSDavid Yang 	.get_eth_mac_stats	= yt921x_dsa_get_eth_mac_stats,
25079c472a0dSDavid Yang 	.get_eth_ctrl_stats	= yt921x_dsa_get_eth_ctrl_stats,
25089c472a0dSDavid Yang 	.get_rmon_stats		= yt921x_dsa_get_rmon_stats,
25099c472a0dSDavid Yang 	.get_stats64		= yt921x_dsa_get_stats64,
25109c472a0dSDavid Yang 	.get_pause_stats	= yt921x_dsa_get_pause_stats,
25119c472a0dSDavid Yang 	/* eee */
25129c472a0dSDavid Yang 	.support_eee		= dsa_supports_eee,
25139c472a0dSDavid Yang 	.set_mac_eee		= yt921x_dsa_set_mac_eee,
25149c472a0dSDavid Yang 	/* mtu */
25159c472a0dSDavid Yang 	.port_change_mtu	= yt921x_dsa_port_change_mtu,
25169c472a0dSDavid Yang 	.port_max_mtu		= yt921x_dsa_port_max_mtu,
25179c472a0dSDavid Yang 	/* rate */
25189c472a0dSDavid Yang 	.port_policer_del	= yt921x_dsa_port_policer_del,
25199c472a0dSDavid Yang 	.port_policer_add	= yt921x_dsa_port_policer_add,
25209c472a0dSDavid Yang 	.port_setup_tc		= yt921x_dsa_port_setup_tc,
25219c472a0dSDavid Yang 	/* acl */
25229c472a0dSDavid Yang 	.cls_flower_del		= yt921x_dsa_cls_flower_del,
25239c472a0dSDavid Yang 	.cls_flower_add		= yt921x_dsa_cls_flower_add,
25249c472a0dSDavid Yang 	/* hsr */
25259c472a0dSDavid Yang 	.port_hsr_leave		= dsa_port_simple_hsr_leave,
25269c472a0dSDavid Yang 	.port_hsr_join		= dsa_port_simple_hsr_join,
25279c472a0dSDavid Yang 	/* mirror */
25289c472a0dSDavid Yang 	.port_mirror_del	= yt921x_dsa_port_mirror_del,
25299c472a0dSDavid Yang 	.port_mirror_add	= yt921x_dsa_port_mirror_add,
25309c472a0dSDavid Yang 	/* lag */
25319c472a0dSDavid Yang 	.port_lag_leave		= yt921x_dsa_port_lag_leave,
25329c472a0dSDavid Yang 	.port_lag_join		= yt921x_dsa_port_lag_join,
25339c472a0dSDavid Yang 	/* fdb */
25349c472a0dSDavid Yang 	.port_fdb_dump		= yt921x_dsa_port_fdb_dump,
25359c472a0dSDavid Yang 	.port_fast_age		= yt921x_dsa_port_fast_age,
25369c472a0dSDavid Yang 	.set_ageing_time	= yt921x_dsa_set_ageing_time,
25379c472a0dSDavid Yang 	.port_fdb_del		= yt921x_dsa_port_fdb_del,
25389c472a0dSDavid Yang 	.port_fdb_add		= yt921x_dsa_port_fdb_add,
25399c472a0dSDavid Yang 	.port_mdb_del		= yt921x_dsa_port_mdb_del,
25409c472a0dSDavid Yang 	.port_mdb_add		= yt921x_dsa_port_mdb_add,
25419c472a0dSDavid Yang 	/* vlan */
25429c472a0dSDavid Yang 	.port_vlan_filtering	= yt921x_dsa_port_vlan_filtering,
25439c472a0dSDavid Yang 	.port_vlan_del		= yt921x_dsa_port_vlan_del,
25449c472a0dSDavid Yang 	.port_vlan_add		= yt921x_dsa_port_vlan_add,
25459c472a0dSDavid Yang 	/* bridge */
25469c472a0dSDavid Yang 	.port_pre_bridge_flags	= yt921x_dsa_port_pre_bridge_flags,
25479c472a0dSDavid Yang 	.port_bridge_flags	= yt921x_dsa_port_bridge_flags,
25489c472a0dSDavid Yang 	.port_bridge_leave	= yt921x_dsa_port_bridge_leave,
25499c472a0dSDavid Yang 	.port_bridge_join	= yt921x_dsa_port_bridge_join,
25509c472a0dSDavid Yang 	/* mst */
25519c472a0dSDavid Yang 	.port_mst_state_set	= yt921x_dsa_port_mst_state_set,
25529c472a0dSDavid Yang 	.vlan_msti_set		= yt921x_dsa_vlan_msti_set,
25539c472a0dSDavid Yang 	.port_stp_state_set	= yt921x_dsa_port_stp_state_set,
25549c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB)
25559c472a0dSDavid Yang 	/* dcb */
25569c472a0dSDavid Yang 	.port_get_default_prio	= yt921x_dsa_port_get_default_prio,
25579c472a0dSDavid Yang 	.port_set_default_prio	= yt921x_dsa_port_set_default_prio,
25589c472a0dSDavid Yang 	.port_get_apptrust	= yt921x_dsa_port_get_apptrust,
25599c472a0dSDavid Yang 	.port_set_apptrust	= yt921x_dsa_port_set_apptrust,
25609c472a0dSDavid Yang #endif
25619c472a0dSDavid Yang 	/* port */
25629c472a0dSDavid Yang 	.get_tag_protocol	= yt921x_dsa_get_tag_protocol,
25639c472a0dSDavid Yang 	.phylink_get_caps	= yt921x_dsa_phylink_get_caps,
2564a219b5eaSDavid Yang 	.port_teardown		= yt921x_dsa_port_teardown,
25659c472a0dSDavid Yang 	.port_setup		= yt921x_dsa_port_setup,
25669c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB)
25679c472a0dSDavid Yang 	/* dscp */
25689c472a0dSDavid Yang 	.port_get_dscp_prio	= yt921x_dsa_port_get_dscp_prio,
25699c472a0dSDavid Yang 	.port_del_dscp_prio	= yt921x_dsa_port_del_dscp_prio,
25709c472a0dSDavid Yang 	.port_add_dscp_prio	= yt921x_dsa_port_add_dscp_prio,
25719c472a0dSDavid Yang #endif
25729c472a0dSDavid Yang 	/* chip */
25739af9a816SDavid Yang 	.teardown		= yt921x_dsa_teardown,
25749c472a0dSDavid Yang 	.setup			= yt921x_dsa_setup,
25759c472a0dSDavid Yang };
25769c472a0dSDavid Yang 
25779c472a0dSDavid Yang static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
25789c472a0dSDavid Yang {
25799c472a0dSDavid Yang 	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
25809c472a0dSDavid Yang 
25819c472a0dSDavid Yang 	if (!priv)
25829c472a0dSDavid Yang 		return;
25839c472a0dSDavid Yang 
25849c472a0dSDavid Yang 	dsa_switch_shutdown(&priv->ds);
25859c472a0dSDavid Yang }
25869c472a0dSDavid Yang 
25879c472a0dSDavid Yang static void yt921x_mdio_remove(struct mdio_device *mdiodev)
25889c472a0dSDavid Yang {
25899c472a0dSDavid Yang 	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
25909c472a0dSDavid Yang 
25919c472a0dSDavid Yang 	if (!priv)
25929c472a0dSDavid Yang 		return;
25939c472a0dSDavid Yang 
25949c472a0dSDavid Yang 	dsa_unregister_switch(&priv->ds);
25959c472a0dSDavid Yang 
25969c472a0dSDavid Yang 	for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
25979c472a0dSDavid Yang 		struct yt921x_acl_blk *aclblk = priv->acl_blks[i];
25989c472a0dSDavid Yang 
25999c472a0dSDavid Yang 		if (!aclblk)
26009c472a0dSDavid Yang 			continue;
26019c472a0dSDavid Yang 		for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) {
26029c472a0dSDavid Yang 			struct yt921x_acl_rule *aclrule = aclblk->rules[j];
26039c472a0dSDavid Yang 
26049c472a0dSDavid Yang 			if (!aclrule)
26059c472a0dSDavid Yang 				continue;
26069c472a0dSDavid Yang 			kvfree(aclrule);
26079c472a0dSDavid Yang 		}
26089c472a0dSDavid Yang 		kvfree(aclblk);
26099c472a0dSDavid Yang 	}
26109c472a0dSDavid Yang 	mutex_destroy(&priv->reg_lock);
26119c472a0dSDavid Yang }
26129c472a0dSDavid Yang 
26139c472a0dSDavid Yang static int yt921x_mdio_probe(struct mdio_device *mdiodev)
26149c472a0dSDavid Yang {
26159c472a0dSDavid Yang 	struct device *dev = &mdiodev->dev;
26169c472a0dSDavid Yang 	struct yt921x_reg_mdio *mdio;
26179c472a0dSDavid Yang 	struct yt921x_priv *priv;
26189c472a0dSDavid Yang 	struct dsa_switch *ds;
26199c472a0dSDavid Yang 
26209c472a0dSDavid Yang 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
26219c472a0dSDavid Yang 	if (!priv)
26229c472a0dSDavid Yang 		return -ENOMEM;
26239c472a0dSDavid Yang 
26249c472a0dSDavid Yang 	mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
26259c472a0dSDavid Yang 	if (!mdio)
26269c472a0dSDavid Yang 		return -ENOMEM;
26279c472a0dSDavid Yang 
26289c472a0dSDavid Yang 	mdio->bus = mdiodev->bus;
26299c472a0dSDavid Yang 	mdio->addr = mdiodev->addr;
26309c472a0dSDavid Yang 	mdio->switchid = 0;
26319c472a0dSDavid Yang 
26329c472a0dSDavid Yang 	mutex_init(&priv->reg_lock);
26339c472a0dSDavid Yang 
26349c472a0dSDavid Yang 	priv->reg_ops = &yt921x_reg_ops_mdio;
26359c472a0dSDavid Yang 	priv->reg_ctx = mdio;
26369c472a0dSDavid Yang 
26379c472a0dSDavid Yang 	for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
26389c472a0dSDavid Yang 		struct yt921x_port *pp = &priv->ports[i];
26399c472a0dSDavid Yang 
26409c472a0dSDavid Yang 		pp->index = i;
26419c472a0dSDavid Yang 	}
26429c472a0dSDavid Yang 
26439c472a0dSDavid Yang 	ds = &priv->ds;
26449c472a0dSDavid Yang 	ds->dev = dev;
26459c472a0dSDavid Yang 	ds->assisted_learning_on_cpu_port = true;
26469c472a0dSDavid Yang 	ds->dscp_prio_mapping_is_global = true;
26479c472a0dSDavid Yang 	ds->priv = priv;
26489c472a0dSDavid Yang 	ds->ops = &yt921x_dsa_switch_ops;
26499c472a0dSDavid Yang 	ds->ageing_time_min = 1 * 5000;
26509c472a0dSDavid Yang 	ds->ageing_time_max = U16_MAX * 5000;
26519c472a0dSDavid Yang 	ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
26529c472a0dSDavid Yang 	ds->num_lag_ids = YT921X_LAG_NUM;
26539c472a0dSDavid Yang 	ds->num_ports = YT921X_PORT_NUM;
26549c472a0dSDavid Yang 
26559c472a0dSDavid Yang 	mdiodev_set_drvdata(mdiodev, priv);
26569c472a0dSDavid Yang 
26579c472a0dSDavid Yang 	return dsa_register_switch(ds);
26589c472a0dSDavid Yang }
26599c472a0dSDavid Yang 
26609c472a0dSDavid Yang static const struct of_device_id yt921x_of_match[] = {
26619c472a0dSDavid Yang 	{ .compatible = "motorcomm,yt9215" },
26629c472a0dSDavid Yang 	{}
26639c472a0dSDavid Yang };
26649c472a0dSDavid Yang MODULE_DEVICE_TABLE(of, yt921x_of_match);
26659c472a0dSDavid Yang 
26669c472a0dSDavid Yang static struct mdio_driver yt921x_mdio_driver = {
26679c472a0dSDavid Yang 	.probe = yt921x_mdio_probe,
26689c472a0dSDavid Yang 	.remove = yt921x_mdio_remove,
26699c472a0dSDavid Yang 	.shutdown = yt921x_mdio_shutdown,
26709c472a0dSDavid Yang 	.mdiodrv.driver = {
26719c472a0dSDavid Yang 		.name = YT921X_NAME,
26729c472a0dSDavid Yang 		.of_match_table = yt921x_of_match,
26739c472a0dSDavid Yang 	},
26749c472a0dSDavid Yang };
26759c472a0dSDavid Yang 
26769c472a0dSDavid Yang mdio_module_driver(yt921x_mdio_driver);
26779c472a0dSDavid Yang 
26789c472a0dSDavid Yang MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
26799c472a0dSDavid Yang MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch");
26809c472a0dSDavid Yang MODULE_LICENSE("GPL");
2681