diff options
Diffstat (limited to 'drivers/net/dsa')
35 files changed, 1165 insertions, 439 deletions
| diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 3092b391031a..2d10b4d6cfbb 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -102,6 +102,7 @@ config NET_DSA_SMSC_LAN9303  	tristate  	select NET_DSA_TAG_LAN9303  	select REGMAP +	imply SMSC_PHY  	help  	  This enables support for the Microchip LAN9303/LAN9354 3 port ethernet  	  switch chips. @@ -126,7 +127,7 @@ config NET_DSA_SMSC_LAN9303_MDIO  config NET_DSA_VITESSE_VSC73XX  	tristate -	select NET_DSA_TAG_NONE +	select NET_DSA_TAG_VSC73XX_8021Q  	select FIXED_PHY  	select VITESSE_PHY  	select GPIOLIB diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 8f50abe739b7..0783fc121bbb 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -2256,6 +2256,9 @@ static int b53_change_mtu(struct dsa_switch *ds, int port, int mtu)  	if (is5325(dev) || is5365(dev))  		return -EOPNOTSUPP; +	if (!dsa_is_cpu_port(ds, port)) +		return 0; +  	enable_jumbo = (mtu >= JMS_MIN_SIZE);  	allow_10_100 = (dev->chip_id == BCM583XX_DEVICE_ID); diff --git a/drivers/net/dsa/hirschmann/hellcreek.h b/drivers/net/dsa/hirschmann/hellcreek.h index 6874cb9dc361..9c2ed2ba79da 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.h +++ b/drivers/net/dsa/hirschmann/hellcreek.h @@ -12,14 +12,16 @@  #include <linux/bitmap.h>  #include <linux/bitops.h> +#include <linux/container_of.h>  #include <linux/device.h> -#include <linux/kernel.h> -#include <linux/mutex.h> -#include <linux/workqueue.h>  #include <linux/leds.h> +#include <linux/mutex.h>  #include <linux/platform_data/hirschmann-hellcreek.h>  #include <linux/ptp_clock_kernel.h>  #include <linux/timecounter.h> +#include <linux/types.h> +#include <linux/workqueue.h> +  #include <net/dsa.h>  #include <net/pkt_sched.h> diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c index bd7aacc71a63..ca2500aba96f 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c @@ -16,7 +16,7 @@  #include "hellcreek_ptp.h"  int hellcreek_get_ts_info(struct dsa_switch *ds, int port, -			  struct ethtool_ts_info *info) +			  struct kernel_ethtool_ts_info *info)  {  	struct hellcreek *hellcreek = ds->priv; diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h index 71af77efb28b..7d88da2134f2 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.h @@ -48,7 +48,7 @@ void hellcreek_port_txtstamp(struct dsa_switch *ds, int port,  			     struct sk_buff *skb);  int hellcreek_get_ts_info(struct dsa_switch *ds, int port, -			  struct ethtool_ts_info *info); +			  struct kernel_ethtool_ts_info *info);  long hellcreek_hwtstamp_work(struct ptp_clock_info *ptp); diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index bbbec322bc4f..c62d27cdc117 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -89,7 +89,7 @@ static void lan9303_i2c_shutdown(struct i2c_client *client)  /*-------------------------------------------------------------------------*/  static const struct i2c_device_id lan9303_i2c_id[] = { -	{ "lan9303", 0 }, +	{ "lan9303" },  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(i2c, lan9303_i2c_id); diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 167a86f39f27..0ac4857e5ee8 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -58,19 +58,19 @@ static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val)  	return 0;  } -static int lan9303_mdio_phy_write(struct lan9303 *chip, int phy, int reg, +static int lan9303_mdio_phy_write(struct lan9303 *chip, int addr, int reg,  				  u16 val)  {  	struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); -	return mdiobus_write_nested(sw_dev->device->bus, phy, reg, val); +	return mdiobus_write_nested(sw_dev->device->bus, addr, reg, val);  } -static int lan9303_mdio_phy_read(struct lan9303 *chip, int phy,  int reg) +static int lan9303_mdio_phy_read(struct lan9303 *chip, int addr, int reg)  {  	struct lan9303_mdio *sw_dev = dev_get_drvdata(chip->dev); -	return mdiobus_read_nested(sw_dev->device->bus, phy, reg); +	return mdiobus_read_nested(sw_dev->device->bus, addr, reg);  }  static const struct lan9303_phy_ops lan9303_mdio_phy_ops = { diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index a557049e34f5..fcd4505f4925 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -236,7 +236,9 @@  #define GSWIP_TABLE_ACTIVE_VLAN		0x01  #define GSWIP_TABLE_VLAN_MAPPING	0x02  #define GSWIP_TABLE_MAC_BRIDGE		0x0b -#define  GSWIP_TABLE_MAC_BRIDGE_STATIC	0x01	/* Static not, aging entry */ +#define  GSWIP_TABLE_MAC_BRIDGE_KEY3_FID	GENMASK(5, 0)	/* Filtering identifier */ +#define  GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT	GENMASK(7, 4)	/* Port on learned entries */ +#define  GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC	BIT(0)		/* Static, non-aging entry */  #define XRX200_GPHY_FW_ALIGN	(16 * 1024) @@ -653,14 +655,8 @@ static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)  	struct gswip_pce_table_entry vlan_active = {0,};  	struct gswip_pce_table_entry vlan_mapping = {0,};  	unsigned int cpu_port = priv->hw_info->cpu_port; -	unsigned int max_ports = priv->hw_info->max_ports;  	int err; -	if (port >= max_ports) { -		dev_err(priv->dev, "single port for %i supported\n", port); -		return -EIO; -	} -  	vlan_active.index = port + 1;  	vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;  	vlan_active.key[0] = 0; /* vid */ @@ -695,13 +691,18 @@ static int gswip_port_enable(struct dsa_switch *ds, int port,  	struct gswip_priv *priv = ds->priv;  	int err; -	if (!dsa_is_user_port(ds, port)) -		return 0; -  	if (!dsa_is_cpu_port(ds, port)) { +		u32 mdio_phy = 0; +  		err = gswip_add_single_port_br(priv, port, true);  		if (err)  			return err; + +		if (phydev) +			mdio_phy = phydev->mdio.addr & GSWIP_MDIO_PHY_ADDR_MASK; + +		gswip_mdio_mask(priv, GSWIP_MDIO_PHY_ADDR_MASK, mdio_phy, +				GSWIP_MDIO_PHYp(port));  	}  	/* RMON Counter Enable for port */ @@ -714,16 +715,6 @@ static int gswip_port_enable(struct dsa_switch *ds, int port,  	gswip_switch_mask(priv, 0, GSWIP_SDMA_PCTRL_EN,  			  GSWIP_SDMA_PCTRLp(port)); -	if (!dsa_is_cpu_port(ds, port)) { -		u32 mdio_phy = 0; - -		if (phydev) -			mdio_phy = phydev->mdio.addr & GSWIP_MDIO_PHY_ADDR_MASK; - -		gswip_mdio_mask(priv, GSWIP_MDIO_PHY_ADDR_MASK, mdio_phy, -				GSWIP_MDIO_PHYp(port)); -	} -  	return 0;  } @@ -731,9 +722,6 @@ static void gswip_port_disable(struct dsa_switch *ds, int port)  {  	struct gswip_priv *priv = ds->priv; -	if (!dsa_is_user_port(ds, port)) -		return; -  	gswip_switch_mask(priv, GSWIP_FDMA_PCTRL_EN, 0,  			  GSWIP_FDMA_PCTRLp(port));  	gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0, @@ -792,7 +780,7 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,  	}  	if (vlan_filtering) { -		/* Use port based VLAN tag */ +		/* Use tag based VLAN */  		gswip_switch_mask(priv,  				  GSWIP_PCE_VCTRL_VSR,  				  GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR | @@ -801,7 +789,7 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,  		gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_TVM, 0,  				  GSWIP_PCE_PCTRL_0p(port));  	} else { -		/* Use port based VLAN tag */ +		/* Use port based VLAN */  		gswip_switch_mask(priv,  				  GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |  				  GSWIP_PCE_VCTRL_VEMR, @@ -836,7 +824,7 @@ static int gswip_setup(struct dsa_switch *ds)  	err = gswip_pce_load_microcode(priv);  	if (err) { -		dev_err(priv->dev, "writing PCE microcode failed, %i", err); +		dev_err(priv->dev, "writing PCE microcode failed, %i\n", err);  		return err;  	} @@ -898,8 +886,6 @@ static int gswip_setup(struct dsa_switch *ds)  	ds->mtu_enforcement_ingress = true; -	gswip_port_enable(ds, cpu_port, NULL); -  	ds->configure_vlan_while_not_filtering = false;  	return 0; @@ -1314,10 +1300,11 @@ static void gswip_port_fast_age(struct dsa_switch *ds, int port)  		if (!mac_bridge.valid)  			continue; -		if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_STATIC) +		if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC)  			continue; -		if (((mac_bridge.val[0] & GENMASK(7, 4)) >> 4) != port) +		if (port != FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT, +				      mac_bridge.val[0]))  			continue;  		mac_bridge.valid = false; @@ -1383,7 +1370,8 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port,  	}  	if (fid == -1) { -		dev_err(priv->dev, "Port not part of a bridge\n"); +		dev_err(priv->dev, "no FID found for bridge %s\n", +			bridge->name);  		return -EINVAL;  	} @@ -1392,9 +1380,9 @@ static int gswip_port_fdb(struct dsa_switch *ds, int port,  	mac_bridge.key[0] = addr[5] | (addr[4] << 8);  	mac_bridge.key[1] = addr[3] | (addr[2] << 8);  	mac_bridge.key[2] = addr[1] | (addr[0] << 8); -	mac_bridge.key[3] = fid; +	mac_bridge.key[3] = FIELD_PREP(GSWIP_TABLE_MAC_BRIDGE_KEY3_FID, fid);  	mac_bridge.val[0] = add ? BIT(port) : 0; /* port map */ -	mac_bridge.val[1] = GSWIP_TABLE_MAC_BRIDGE_STATIC; +	mac_bridge.val[1] = GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC;  	mac_bridge.valid = add;  	err = gswip_pce_table_entry_write(priv, &mac_bridge); @@ -1423,7 +1411,7 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,  {  	struct gswip_priv *priv = ds->priv;  	struct gswip_pce_table_entry mac_bridge = {0,}; -	unsigned char addr[6]; +	unsigned char addr[ETH_ALEN];  	int i;  	int err; @@ -1448,14 +1436,15 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,  		addr[2] = (mac_bridge.key[1] >> 8) & 0xff;  		addr[1] = mac_bridge.key[2] & 0xff;  		addr[0] = (mac_bridge.key[2] >> 8) & 0xff; -		if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_STATIC) { +		if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC) {  			if (mac_bridge.val[0] & BIT(port)) {  				err = cb(addr, 0, true, data);  				if (err)  					return err;  			}  		} else { -			if (((mac_bridge.val[0] & GENMASK(7, 4)) >> 4) == port) { +			if (port == FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT, +					      mac_bridge.val[0])) {  				err = cb(addr, 0, false, data);  				if (err)  					return err; @@ -1474,12 +1463,11 @@ static int gswip_port_max_mtu(struct dsa_switch *ds, int port)  static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  {  	struct gswip_priv *priv = ds->priv; -	int cpu_port = priv->hw_info->cpu_port;  	/* CPU port always has maximum mtu of user ports, so use it to set  	 * switch frame size, including 8 byte special header.  	 */ -	if (port == cpu_port) { +	if (dsa_is_cpu_port(ds, port)) {  		new_mtu += 8;  		gswip_switch_w(priv, VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN,  			       GSWIP_MAC_FLEN); @@ -1516,6 +1504,7 @@ static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,  	case 2:  	case 3:  	case 4: +	case 6:  		__set_bit(PHY_INTERFACE_MODE_INTERNAL,  			  config->supported_interfaces);  		break; @@ -1547,6 +1536,7 @@ static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port,  	case 2:  	case 3:  	case 4: +	case 6:  		__set_bit(PHY_INTERFACE_MODE_INTERNAL,  			  config->supported_interfaces);  		break; @@ -1790,7 +1780,7 @@ static u32 gswip_bcm_ram_entry_read(struct gswip_priv *priv, u32 table,  	err = gswip_switch_r_timeout(priv, GSWIP_BM_RAM_CTRL,  				     GSWIP_BM_RAM_CTRL_BAS);  	if (err) { -		dev_err(priv->dev, "timeout while reading table: %u, index: %u", +		dev_err(priv->dev, "timeout while reading table: %u, index: %u\n",  			table, index);  		return 0;  	} @@ -1929,11 +1919,9 @@ static int gswip_gphy_fw_load(struct gswip_priv *priv, struct gswip_gphy_fw *gph  	msleep(200);  	ret = request_firmware(&fw, gphy_fw->fw_name, dev); -	if (ret) { -		dev_err(dev, "failed to load firmware: %s, error: %i\n", -			gphy_fw->fw_name, ret); -		return ret; -	} +	if (ret) +		return dev_err_probe(dev, ret, "failed to load firmware: %s\n", +				     gphy_fw->fw_name);  	/* GPHY cores need the firmware code in a persistent and contiguous  	 * memory area with a 16 kB boundary aligned start address. @@ -1946,9 +1934,9 @@ static int gswip_gphy_fw_load(struct gswip_priv *priv, struct gswip_gphy_fw *gph  		dev_addr = ALIGN(dma_addr, XRX200_GPHY_FW_ALIGN);  		memcpy(fw_addr, fw->data, fw->size);  	} else { -		dev_err(dev, "failed to alloc firmware memory\n");  		release_firmware(fw); -		return -ENOMEM; +		return dev_err_probe(dev, -ENOMEM, +				     "failed to alloc firmware memory\n");  	}  	release_firmware(fw); @@ -1975,8 +1963,8 @@ static int gswip_gphy_fw_probe(struct gswip_priv *priv,  	gphy_fw->clk_gate = devm_clk_get(dev, gphyname);  	if (IS_ERR(gphy_fw->clk_gate)) { -		dev_err(dev, "Failed to lookup gate clock\n"); -		return PTR_ERR(gphy_fw->clk_gate); +		return dev_err_probe(dev, PTR_ERR(gphy_fw->clk_gate), +				     "Failed to lookup gate clock\n");  	}  	ret = of_property_read_u32(gphy_fw_np, "reg", &gphy_fw->fw_addr_offset); @@ -1996,8 +1984,8 @@ static int gswip_gphy_fw_probe(struct gswip_priv *priv,  		gphy_fw->fw_name = priv->gphy_fw_name_cfg->ge_firmware_name;  		break;  	default: -		dev_err(dev, "Unknown GPHY mode %d\n", gphy_mode); -		return -EINVAL; +		return dev_err_probe(dev, -EINVAL, "Unknown GPHY mode %d\n", +				     gphy_mode);  	}  	gphy_fw->reset = of_reset_control_array_get_exclusive(gphy_fw_np); @@ -2019,7 +2007,7 @@ static void gswip_gphy_fw_remove(struct gswip_priv *priv,  	ret = regmap_write(priv->rcu_regmap, gphy_fw->fw_addr_offset, 0);  	if (ret) -		dev_err(priv->dev, "can not reset GPHY FW pointer"); +		dev_err(priv->dev, "can not reset GPHY FW pointer\n");  	clk_disable_unprepare(gphy_fw->clk_gate); @@ -2048,8 +2036,9 @@ static int gswip_gphy_fw_list(struct gswip_priv *priv,  			priv->gphy_fw_name_cfg = &xrx200a2x_gphy_data;  			break;  		default: -			dev_err(dev, "unknown GSWIP version: 0x%x", version); -			return -ENOENT; +			return dev_err_probe(dev, -ENOENT, +					     "unknown GSWIP version: 0x%x\n", +					     version);  		}  	} @@ -2057,10 +2046,9 @@ static int gswip_gphy_fw_list(struct gswip_priv *priv,  	if (match && match->data)  		priv->gphy_fw_name_cfg = match->data; -	if (!priv->gphy_fw_name_cfg) { -		dev_err(dev, "GPHY compatible type not supported"); -		return -ENOENT; -	} +	if (!priv->gphy_fw_name_cfg) +		return dev_err_probe(dev, -ENOENT, +				     "GPHY compatible type not supported\n");  	priv->num_gphy_fw = of_get_available_child_count(gphy_fw_list_np);  	if (!priv->num_gphy_fw) @@ -2161,8 +2149,8 @@ static int gswip_probe(struct platform_device *pdev)  			return -EINVAL;  		break;  	default: -		dev_err(dev, "unknown GSWIP version: 0x%x", version); -		return -ENOENT; +		return dev_err_probe(dev, -ENOENT, +				     "unknown GSWIP version: 0x%x\n", version);  	}  	/* bring up the mdio bus */ @@ -2170,28 +2158,27 @@ static int gswip_probe(struct platform_device *pdev)  	if (gphy_fw_np) {  		err = gswip_gphy_fw_list(priv, gphy_fw_np, version);  		of_node_put(gphy_fw_np); -		if (err) { -			dev_err(dev, "gphy fw probe failed\n"); -			return err; -		} +		if (err) +			return dev_err_probe(dev, err, +					     "gphy fw probe failed\n");  	}  	/* bring up the mdio bus */  	err = gswip_mdio(priv);  	if (err) { -		dev_err(dev, "mdio probe failed\n"); +		dev_err_probe(dev, err, "mdio probe failed\n");  		goto gphy_fw_remove;  	}  	err = dsa_register_switch(priv->ds);  	if (err) { -		dev_err(dev, "dsa switch register failed: %i\n", err); +		dev_err_probe(dev, err, "dsa switch registration failed\n");  		goto gphy_fw_remove;  	}  	if (!dsa_is_cpu_port(priv->ds, priv->hw_info->cpu_port)) { -		dev_err(dev, "wrong CPU port defined, HW only supports port: %i", -			priv->hw_info->cpu_port); -		err = -EINVAL; +		err = dev_err_probe(dev, -EINVAL, +				    "wrong CPU port defined, HW only supports port: %i\n", +				    priv->hw_info->cpu_port);  		goto disable_switch;  	} diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 82bebee4615c..7d7560f23a73 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -72,8 +72,8 @@ static void ksz9477_i2c_shutdown(struct i2c_client *i2c)  }  static const struct i2c_device_id ksz9477_i2c_id[] = { -	{ "ksz9477-switch", 0 }, -	{}, +	{ "ksz9477-switch" }, +	{}  };  MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 0580b2fee21c..b074b4bb0629 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -3116,7 +3116,8 @@ static void ksz_set_xmii(struct ksz_device *dev, int port,  		/* On KSZ9893, disable RGMII in-band status support */  		if (dev->chip_id == KSZ9893_CHIP_ID ||  		    dev->chip_id == KSZ8563_CHIP_ID || -		    dev->chip_id == KSZ9563_CHIP_ID) +		    dev->chip_id == KSZ9563_CHIP_ID || +		    is_lan937x(dev))  			data8 &= ~P_MII_MAC_MODE;  		break;  	default: @@ -3917,6 +3918,13 @@ static int ksz_hsr_join(struct dsa_switch *ds, int port, struct net_device *hsr,  		return -EOPNOTSUPP;  	} +	/* KSZ9477 can only perform HSR offloading for up to two ports */ +	if (hweight8(dev->hsr_ports) >= 2) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Cannot offload more than two ports - using software HSR"); +		return -EOPNOTSUPP; +	} +  	/* Self MAC address filtering, to avoid frames traversing  	 * the HSR ring more than once.  	 */ diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index ee7db46e469d..5f0a628b9849 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -22,6 +22,7 @@  /* all KSZ switches count ports from 1 */  #define KSZ_PORT_1 0  #define KSZ_PORT_2 1 +#define KSZ_PORT_4 3  struct ksz_device;  struct ksz_port; @@ -637,6 +638,12 @@ static inline int is_lan937x(struct ksz_device *dev)  		dev->chip_id == LAN9374_CHIP_ID;  } +static inline bool is_lan937x_tx_phy(struct ksz_device *dev, int port) +{ +	return (dev->chip_id == LAN9371_CHIP_ID || +		dev->chip_id == LAN9372_CHIP_ID) && port == KSZ_PORT_4; +} +  /* STP State Defines */  #define PORT_TX_ENABLE			BIT(2)  #define PORT_RX_ENABLE			BIT(1) diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c index 1fe105913c75..f0bd46e5d4ec 100644 --- a/drivers/net/dsa/microchip/ksz_ptp.c +++ b/drivers/net/dsa/microchip/ksz_ptp.c @@ -293,7 +293,7 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)  /* The function is return back the capability of timestamping feature when   * requested through ethtool -T <interface> utility   */ -int ksz_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *ts) +int ksz_get_ts_info(struct dsa_switch *ds, int port, struct kernel_ethtool_ts_info *ts)  {  	struct ksz_device *dev = ds->priv;  	struct ksz_ptp_data *ptp_data; diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h index 0ca8ca4f804e..2f1783c0d723 100644 --- a/drivers/net/dsa/microchip/ksz_ptp.h +++ b/drivers/net/dsa/microchip/ksz_ptp.h @@ -38,7 +38,7 @@ int ksz_ptp_clock_register(struct dsa_switch *ds);  void ksz_ptp_clock_unregister(struct dsa_switch *ds);  int ksz_get_ts_info(struct dsa_switch *ds, int port, -		    struct ethtool_ts_info *ts); +		    struct kernel_ethtool_ts_info *ts);  int ksz_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr);  int ksz_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr);  void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb); diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index b479a628b1ae..824d9309a3d3 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -55,6 +55,9 @@ static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg)  	u16 addr_base = REG_PORT_T1_PHY_CTRL_BASE;  	u16 temp; +	if (is_lan937x_tx_phy(dev, addr)) +		addr_base = REG_PORT_TX_PHY_CTRL_BASE; +  	/* get register address based on the logical port */  	temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2))); @@ -320,6 +323,9 @@ void lan937x_phylink_get_caps(struct ksz_device *dev, int port,  		/* MII/RMII/RGMII ports */  		config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE |  					    MAC_100HD | MAC_10 | MAC_1000FD; +	} else if (is_lan937x_tx_phy(dev, port)) { +		config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +					    MAC_100HD | MAC_10;  	}  } @@ -370,23 +376,33 @@ int lan937x_setup(struct dsa_switch *ds)  	ds->vlan_filtering_is_global = true;  	/* Enable aggressive back off for half duplex & UNH mode */ -	lan937x_cfg(dev, REG_SW_MAC_CTRL_0, -		    (SW_PAUSE_UNH_MODE | SW_NEW_BACKOFF | SW_AGGR_BACKOFF), -		    true); +	ret = lan937x_cfg(dev, REG_SW_MAC_CTRL_0, (SW_PAUSE_UNH_MODE | +						   SW_NEW_BACKOFF | +						   SW_AGGR_BACKOFF), true); +	if (ret < 0) +		return ret;  	/* If NO_EXC_COLLISION_DROP bit is set, the switch will not drop  	 * packets when 16 or more collisions occur  	 */ -	lan937x_cfg(dev, REG_SW_MAC_CTRL_1, NO_EXC_COLLISION_DROP, true); +	ret = lan937x_cfg(dev, REG_SW_MAC_CTRL_1, NO_EXC_COLLISION_DROP, true); +	if (ret < 0) +		return ret;  	/* enable global MIB counter freeze function */ -	lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true); +	ret = lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true); +	if (ret < 0) +		return ret;  	/* disable CLK125 & CLK25, 1: disable, 0: enable */ -	lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, -		    (SW_CLK125_ENB | SW_CLK25_ENB), true); +	ret = lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, +			  (SW_CLK125_ENB | SW_CLK25_ENB), true); +	if (ret < 0) +		return ret; -	return 0; +	/* Disable global VPHY support. Related to CPU interface only? */ +	return ksz_rmw32(dev, REG_SW_CFG_STRAP_OVR, SW_VPHY_DISABLE, +			 SW_VPHY_DISABLE);  }  void lan937x_teardown(struct dsa_switch *ds) diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h index 45b606b6429f..2f22a9d01de3 100644 --- a/drivers/net/dsa/microchip/lan937x_reg.h +++ b/drivers/net/dsa/microchip/lan937x_reg.h @@ -37,6 +37,10 @@  #define SW_CLK125_ENB			BIT(1)  #define SW_CLK25_ENB			BIT(0) +/* 2 - PHY Control */ +#define REG_SW_CFG_STRAP_OVR		0x0214 +#define SW_VPHY_DISABLE			BIT(31) +  /* 3 - Operation Control */  #define REG_SW_OPERATION		0x0300 @@ -147,6 +151,7 @@  /* 1 - Phy */  #define REG_PORT_T1_PHY_CTRL_BASE	0x0100 +#define REG_PORT_TX_PHY_CTRL_BASE	0x0280  /* 3 - xMII */  #define PORT_SGMII_SEL			BIT(7) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 598434d8d6e4..ec18e68bf3a8 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1302,13 +1302,62 @@ mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state)  		   FID_PST(FID_BRIDGED, stp_state));  } +static void mt7530_update_port_member(struct mt7530_priv *priv, int port, +				      const struct net_device *bridge_dev, +				      bool join) __must_hold(&priv->reg_mutex) +{ +	struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp; +	struct mt7530_port *p = &priv->ports[port], *other_p; +	struct dsa_port *cpu_dp = dp->cpu_dp; +	u32 port_bitmap = BIT(cpu_dp->index); +	int other_port; +	bool isolated; + +	dsa_switch_for_each_user_port(other_dp, priv->ds) { +		other_port = other_dp->index; +		other_p = &priv->ports[other_port]; + +		if (dp == other_dp) +			continue; + +		/* Add/remove this port to/from the port matrix of the other +		 * ports in the same bridge. If the port is disabled, port +		 * matrix is kept and not being setup until the port becomes +		 * enabled. +		 */ +		if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev)) +			continue; + +		isolated = p->isolated && other_p->isolated; + +		if (join && !isolated) { +			other_p->pm |= PCR_MATRIX(BIT(port)); +			port_bitmap |= BIT(other_port); +		} else { +			other_p->pm &= ~PCR_MATRIX(BIT(port)); +		} + +		if (other_p->enable) +			mt7530_rmw(priv, MT7530_PCR_P(other_port), +				   PCR_MATRIX_MASK, other_p->pm); +	} + +	/* Add/remove the all other ports to this port matrix. For !join +	 * (leaving the bridge), only the CPU port will remain in the port matrix +	 * of this port. +	 */ +	p->pm = PCR_MATRIX(port_bitmap); +	if (priv->ports[port].enable) +		mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, p->pm); +} +  static int  mt7530_port_pre_bridge_flags(struct dsa_switch *ds, int port,  			     struct switchdev_brport_flags flags,  			     struct netlink_ext_ack *extack)  {  	if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | -			   BR_BCAST_FLOOD)) +			   BR_BCAST_FLOOD | BR_ISOLATED))  		return -EINVAL;  	return 0; @@ -1337,6 +1386,17 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port,  		mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)),  			   flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0); +	if (flags.mask & BR_ISOLATED) { +		struct dsa_port *dp = dsa_to_port(ds, port); +		struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp); + +		priv->ports[port].isolated = !!(flags.val & BR_ISOLATED); + +		mutex_lock(&priv->reg_mutex); +		mt7530_update_port_member(priv, port, bridge_dev, true); +		mutex_unlock(&priv->reg_mutex); +	} +  	return 0;  } @@ -1345,39 +1405,11 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,  			struct dsa_bridge bridge, bool *tx_fwd_offload,  			struct netlink_ext_ack *extack)  { -	struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; -	struct dsa_port *cpu_dp = dp->cpu_dp; -	u32 port_bitmap = BIT(cpu_dp->index);  	struct mt7530_priv *priv = ds->priv;  	mutex_lock(&priv->reg_mutex); -	dsa_switch_for_each_user_port(other_dp, ds) { -		int other_port = other_dp->index; - -		if (dp == other_dp) -			continue; - -		/* Add this port to the port matrix of the other ports in the -		 * same bridge. If the port is disabled, port matrix is kept -		 * and not being setup until the port becomes enabled. -		 */ -		if (!dsa_port_offloads_bridge(other_dp, &bridge)) -			continue; - -		if (priv->ports[other_port].enable) -			mt7530_set(priv, MT7530_PCR_P(other_port), -				   PCR_MATRIX(BIT(port))); -		priv->ports[other_port].pm |= PCR_MATRIX(BIT(port)); - -		port_bitmap |= BIT(other_port); -	} - -	/* Add the all other ports to this port matrix. */ -	if (priv->ports[port].enable) -		mt7530_rmw(priv, MT7530_PCR_P(port), -			   PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap)); -	priv->ports[port].pm |= PCR_MATRIX(port_bitmap); +	mt7530_update_port_member(priv, port, bridge.dev, true);  	/* Set to fallback mode for independent VLAN learning */  	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, @@ -1478,38 +1510,11 @@ static void  mt7530_port_bridge_leave(struct dsa_switch *ds, int port,  			 struct dsa_bridge bridge)  { -	struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; -	struct dsa_port *cpu_dp = dp->cpu_dp;  	struct mt7530_priv *priv = ds->priv;  	mutex_lock(&priv->reg_mutex); -	dsa_switch_for_each_user_port(other_dp, ds) { -		int other_port = other_dp->index; - -		if (dp == other_dp) -			continue; - -		/* Remove this port from the port matrix of the other ports -		 * in the same bridge. If the port is disabled, port matrix -		 * is kept and not being setup until the port becomes enabled. -		 */ -		if (!dsa_port_offloads_bridge(other_dp, &bridge)) -			continue; - -		if (priv->ports[other_port].enable) -			mt7530_clear(priv, MT7530_PCR_P(other_port), -				     PCR_MATRIX(BIT(port))); -		priv->ports[other_port].pm &= ~PCR_MATRIX(BIT(port)); -	} - -	/* Set the cpu port to be the only one in the port matrix of -	 * this port. -	 */ -	if (priv->ports[port].enable) -		mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, -			   PCR_MATRIX(BIT(cpu_dp->index))); -	priv->ports[port].pm = PCR_MATRIX(BIT(cpu_dp->index)); +	mt7530_update_port_member(priv, port, bridge.dev, false);  	/* When a port is removed from the bridge, the port would be set up  	 * back to the default as is at initial boot which is a VLAN-unaware diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 2ea4e24628c6..28592123070b 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -721,6 +721,7 @@ struct mt7530_fdb {   */  struct mt7530_port {  	bool enable; +	bool isolated;  	u32 pm;  	u16 pvid;  	struct phylink_pcs *sgmii_pcs; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 07c897b13de1..5b4e2ce5470d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3626,7 +3626,8 @@ static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  	mv88e6xxx_reg_lock(chip);  	if (chip->info->ops->port_set_jumbo_size)  		ret = chip->info->ops->port_set_jumbo_size(chip, port, new_mtu); -	else if (chip->info->ops->set_max_frame_size) +	else if (chip->info->ops->set_max_frame_size && +		 dsa_is_cpu_port(ds, port))  		ret = chip->info->ops->set_max_frame_size(chip, new_mtu);  	mv88e6xxx_reg_unlock(chip); diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index 331b4ca089ff..49e6e1355142 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -64,7 +64,7 @@ static int mv88e6xxx_ptp_read(struct mv88e6xxx_chip *chip, int addr,  #define TX_TSTAMP_TIMEOUT	msecs_to_jiffies(40)  int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, -			  struct ethtool_ts_info *info) +			  struct kernel_ethtool_ts_info *info)  {  	const struct mv88e6xxx_ptp_ops *ptp_ops;  	struct mv88e6xxx_chip *chip; diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h index cf7fb6d660b1..85acc758e3eb 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h @@ -121,7 +121,7 @@ void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,  			     struct sk_buff *skb);  int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, -			  struct ethtool_ts_info *info); +			  struct kernel_ethtool_ts_info *info);  int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip);  void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip); @@ -157,7 +157,7 @@ static inline void mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,  }  static inline int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, -					struct ethtool_ts_info *info) +					struct kernel_ethtool_ts_info *info)  {  	return -EOPNOTSUPP;  } diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 61e95487732d..e554699f06d4 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1050,24 +1050,32 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port,  		  config->supported_interfaces);  } -static void felix_phylink_mac_config(struct dsa_switch *ds, int port, +static void felix_phylink_mac_config(struct phylink_config *config,  				     unsigned int mode,  				     const struct phylink_link_state *state)  { -	struct ocelot *ocelot = ds->priv; -	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ocelot *ocelot = dp->ds->priv; +	int port = dp->index; +	struct felix *felix; + +	felix = ocelot_to_felix(ocelot);  	if (felix->info->phylink_mac_config)  		felix->info->phylink_mac_config(ocelot, port, mode, state);  } -static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds, -							int port, -							phy_interface_t iface) +static struct phylink_pcs * +felix_phylink_mac_select_pcs(struct phylink_config *config, +			     phy_interface_t iface)  { -	struct ocelot *ocelot = ds->priv; -	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ocelot *ocelot = dp->ds->priv;  	struct phylink_pcs *pcs = NULL; +	int port = dp->index; +	struct felix *felix; + +	felix = ocelot_to_felix(ocelot);  	if (felix->pcs && felix->pcs[port])  		pcs = felix->pcs[port]; @@ -1075,11 +1083,13 @@ static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,  	return pcs;  } -static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, +static void felix_phylink_mac_link_down(struct phylink_config *config,  					unsigned int link_an_mode,  					phy_interface_t interface)  { -	struct ocelot *ocelot = ds->priv; +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ocelot *ocelot = dp->ds->priv; +	int port = dp->index;  	struct felix *felix;  	felix = ocelot_to_felix(ocelot); @@ -1088,15 +1098,19 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,  				     felix->info->quirks);  } -static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, +static void felix_phylink_mac_link_up(struct phylink_config *config, +				      struct phy_device *phydev,  				      unsigned int link_an_mode,  				      phy_interface_t interface, -				      struct phy_device *phydev,  				      int speed, int duplex,  				      bool tx_pause, bool rx_pause)  { -	struct ocelot *ocelot = ds->priv; -	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_port *dp = dsa_phylink_to_port(config); +	struct ocelot *ocelot = dp->ds->priv; +	int port = dp->index; +	struct felix *felix; + +	felix = ocelot_to_felix(ocelot);  	ocelot_phylink_mac_link_up(ocelot, port, phydev, link_an_mode,  				   interface, speed, duplex, tx_pause, rx_pause, @@ -1220,7 +1234,7 @@ static int felix_get_sset_count(struct dsa_switch *ds, int port, int sset)  }  static int felix_get_ts_info(struct dsa_switch *ds, int port, -			     struct ethtool_ts_info *info) +			     struct kernel_ethtool_ts_info *info)  {  	struct ocelot *ocelot = ds->priv; @@ -1583,6 +1597,15 @@ static int felix_setup(struct dsa_switch *ds)  		felix_port_qos_map_init(ocelot, dp->index);  	} +	if (felix->info->request_irq) { +		err = felix->info->request_irq(ocelot); +		if (err) { +			dev_err(ocelot->dev, "Failed to request IRQ: %pe\n", +				ERR_PTR(err)); +			goto out_deinit_ports; +		} +	} +  	err = ocelot_devlink_sb_register(ocelot);  	if (err)  		goto out_deinit_ports; @@ -2083,7 +2106,14 @@ static void felix_get_mm_stats(struct dsa_switch *ds, int port,  	ocelot_port_get_mm_stats(ocelot, port, stats);  } -const struct dsa_switch_ops felix_switch_ops = { +static const struct phylink_mac_ops felix_phylink_mac_ops = { +	.mac_select_pcs		= felix_phylink_mac_select_pcs, +	.mac_config		= felix_phylink_mac_config, +	.mac_link_down		= felix_phylink_mac_link_down, +	.mac_link_up		= felix_phylink_mac_link_up, +}; + +static const struct dsa_switch_ops felix_switch_ops = {  	.get_tag_protocol		= felix_get_tag_protocol,  	.change_tag_protocol		= felix_change_tag_protocol,  	.connect_tag_protocol		= felix_connect_tag_protocol, @@ -2104,10 +2134,6 @@ const struct dsa_switch_ops felix_switch_ops = {  	.get_sset_count			= felix_get_sset_count,  	.get_ts_info			= felix_get_ts_info,  	.phylink_get_caps		= felix_phylink_get_caps, -	.phylink_mac_config		= felix_phylink_mac_config, -	.phylink_mac_select_pcs		= felix_phylink_mac_select_pcs, -	.phylink_mac_link_down		= felix_phylink_mac_link_down, -	.phylink_mac_link_up		= felix_phylink_mac_link_up,  	.port_enable			= felix_port_enable,  	.port_fast_age			= felix_port_fast_age,  	.port_fdb_dump			= felix_fdb_dump, @@ -2166,7 +2192,53 @@ const struct dsa_switch_ops felix_switch_ops = {  	.port_set_host_flood		= felix_port_set_host_flood,  	.port_change_conduit		= felix_port_change_conduit,  }; -EXPORT_SYMBOL_GPL(felix_switch_ops); + +int felix_register_switch(struct device *dev, resource_size_t switch_base, +			  int num_flooding_pgids, bool ptp, +			  bool mm_supported, +			  enum dsa_tag_protocol init_tag_proto, +			  const struct felix_info *info) +{ +	struct dsa_switch *ds; +	struct ocelot *ocelot; +	struct felix *felix; +	int err; + +	felix = devm_kzalloc(dev, sizeof(*felix), GFP_KERNEL); +	if (!felix) +		return -ENOMEM; + +	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); +	if (!ds) +		return -ENOMEM; + +	dev_set_drvdata(dev, felix); + +	ocelot = &felix->ocelot; +	ocelot->dev = dev; +	ocelot->num_flooding_pgids = num_flooding_pgids; +	ocelot->ptp = ptp; +	ocelot->mm_supported = mm_supported; + +	felix->info = info; +	felix->switch_base = switch_base; +	felix->ds = ds; +	felix->tag_proto = init_tag_proto; + +	ds->dev = dev; +	ds->num_ports = info->num_ports; +	ds->num_tx_queues = OCELOT_NUM_TC; +	ds->ops = &felix_switch_ops; +	ds->phylink_mac_ops = &felix_phylink_mac_ops; +	ds->priv = ocelot; + +	err = dsa_register_switch(ds); +	if (err) +		dev_err_probe(dev, err, "Failed to register DSA switch\n"); + +	return err; +} +EXPORT_SYMBOL_GPL(felix_register_switch);  struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)  { diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index dbf5872fe367..211991f494e3 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -32,7 +32,6 @@ struct felix_info {  	const u32			*port_modes;  	int				num_mact_rows;  	int				num_ports; -	int				num_tx_queues;  	struct vcap_props		*vcap;  	u16				vcap_pol_base;  	u16				vcap_pol_max; @@ -64,6 +63,7 @@ struct felix_info {  				      const struct phylink_link_state *state);  	int	(*configure_serdes)(struct ocelot *ocelot, int port,  				    struct device_node *portnp); +	int	(*request_irq)(struct ocelot *ocelot);  };  /* Methods for initializing the hardware resources specific to a tagging @@ -82,8 +82,6 @@ struct felix_tag_proto_ops {  			      struct netlink_ext_ack *extack);  }; -extern const struct dsa_switch_ops felix_switch_ops; -  /* DSA glue / front-end for struct ocelot */  struct felix {  	struct dsa_switch		*ds; @@ -99,6 +97,11 @@ struct felix {  	unsigned long			host_flood_mc_mask;  }; +int felix_register_switch(struct device *dev, resource_size_t switch_base, +			  int num_flooding_pgids, bool ptp, +			  bool mm_supported, +			  enum dsa_tag_protocol init_tag_proto, +			  const struct felix_info *info);  struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port);  int felix_netdev_to_port(struct net_device *dev); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 85952d841f28..ba37a566da39 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -2605,6 +2605,28 @@ set:  	}  } +/* The INTB interrupt is shared between for PTP TX timestamp availability + * notification and MAC Merge status change on each port. + */ +static irqreturn_t vsc9959_irq_handler(int irq, void *data) +{ +	struct ocelot *ocelot = data; + +	ocelot_get_txtstamp(ocelot); +	ocelot_mm_irq(ocelot); + +	return IRQ_HANDLED; +} + +static int vsc9959_request_irq(struct ocelot *ocelot) +{ +	struct pci_dev *pdev = to_pci_dev(ocelot->dev); + +	return devm_request_threaded_irq(ocelot->dev, pdev->irq, NULL, +					 &vsc9959_irq_handler, IRQF_ONESHOT, +					 "felix-intb", ocelot); +} +  static const struct ocelot_ops vsc9959_ops = {  	.reset			= vsc9959_reset,  	.wm_enc			= vsc9959_wm_enc, @@ -2636,7 +2658,6 @@ static const struct felix_info felix_info_vsc9959 = {  	.vcap_pol_max2		= 0,  	.num_mact_rows		= 2048,  	.num_ports		= VSC9959_NUM_PORTS, -	.num_tx_queues		= OCELOT_NUM_TC,  	.quirks			= FELIX_MAC_QUIRKS,  	.quirk_no_xtr_irq	= true,  	.ptp_caps		= &vsc9959_ptp_caps, @@ -2645,98 +2666,36 @@ static const struct felix_info felix_info_vsc9959 = {  	.port_modes		= vsc9959_port_modes,  	.port_setup_tc		= vsc9959_port_setup_tc,  	.port_sched_speed_set	= vsc9959_sched_speed_set, +	.request_irq		= vsc9959_request_irq,  }; -/* The INTB interrupt is shared between for PTP TX timestamp availability - * notification and MAC Merge status change on each port. - */ -static irqreturn_t felix_irq_handler(int irq, void *data) -{ -	struct ocelot *ocelot = (struct ocelot *)data; - -	ocelot_get_txtstamp(ocelot); -	ocelot_mm_irq(ocelot); - -	return IRQ_HANDLED; -} -  static int felix_pci_probe(struct pci_dev *pdev,  			   const struct pci_device_id *id)  { -	struct dsa_switch *ds; -	struct ocelot *ocelot; -	struct felix *felix; +	struct device *dev = &pdev->dev; +	resource_size_t switch_base;  	int err; -	if (pdev->dev.of_node && !of_device_is_available(pdev->dev.of_node)) { -		dev_info(&pdev->dev, "device is disabled, skipping\n"); -		return -ENODEV; -	} -  	err = pci_enable_device(pdev);  	if (err) { -		dev_err(&pdev->dev, "device enable failed\n"); -		goto err_pci_enable; -	} - -	felix = kzalloc(sizeof(struct felix), GFP_KERNEL); -	if (!felix) { -		err = -ENOMEM; -		dev_err(&pdev->dev, "Failed to allocate driver memory\n"); -		goto err_alloc_felix; +		dev_err(dev, "device enable failed: %pe\n", ERR_PTR(err)); +		return err;  	} -	pci_set_drvdata(pdev, felix); -	ocelot = &felix->ocelot; -	ocelot->dev = &pdev->dev; -	ocelot->num_flooding_pgids = OCELOT_NUM_TC; -	felix->info = &felix_info_vsc9959; -	felix->switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR); -  	pci_set_master(pdev); -	err = devm_request_threaded_irq(&pdev->dev, pdev->irq, NULL, -					&felix_irq_handler, IRQF_ONESHOT, -					"felix-intb", ocelot); -	if (err) { -		dev_err(&pdev->dev, "Failed to request irq\n"); -		goto err_alloc_irq; -	} - -	ocelot->ptp = 1; -	ocelot->mm_supported = true; - -	ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL); -	if (!ds) { -		err = -ENOMEM; -		dev_err(&pdev->dev, "Failed to allocate DSA switch\n"); -		goto err_alloc_ds; -	} +	switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR); -	ds->dev = &pdev->dev; -	ds->num_ports = felix->info->num_ports; -	ds->num_tx_queues = felix->info->num_tx_queues; -	ds->ops = &felix_switch_ops; -	ds->priv = ocelot; -	felix->ds = ds; -	felix->tag_proto = DSA_TAG_PROTO_OCELOT; - -	err = dsa_register_switch(ds); -	if (err) { -		dev_err_probe(&pdev->dev, err, "Failed to register DSA switch\n"); -		goto err_register_ds; -	} +	err = felix_register_switch(dev, switch_base, OCELOT_NUM_TC, +				    true, true, DSA_TAG_PROTO_OCELOT, +				    &felix_info_vsc9959); +	if (err) +		goto out_disable;  	return 0; -err_register_ds: -	kfree(ds); -err_alloc_ds: -err_alloc_irq: -	kfree(felix); -err_alloc_felix: +out_disable:  	pci_disable_device(pdev); -err_pci_enable:  	return err;  } @@ -2749,9 +2708,6 @@ static void felix_pci_remove(struct pci_dev *pdev)  	dsa_unregister_switch(felix->ds); -	kfree(felix->ds); -	kfree(felix); -  	pci_disable_device(pdev);  } diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c index 22187d831c4b..5632a7248cd4 100644 --- a/drivers/net/dsa/ocelot/ocelot_ext.c +++ b/drivers/net/dsa/ocelot/ocelot_ext.c @@ -57,7 +57,6 @@ static const struct felix_info vsc7512_info = {  	.vcap				= vsc7514_vcap_props,  	.num_mact_rows			= 1024,  	.num_ports			= VSC7514_NUM_PORTS, -	.num_tx_queues			= OCELOT_NUM_TC,  	.port_modes			= vsc7512_port_modes,  	.phylink_mac_config		= ocelot_phylink_mac_config,  	.configure_serdes		= ocelot_port_configure_serdes, @@ -65,54 +64,8 @@ static const struct felix_info vsc7512_info = {  static int ocelot_ext_probe(struct platform_device *pdev)  { -	struct device *dev = &pdev->dev; -	struct dsa_switch *ds; -	struct ocelot *ocelot; -	struct felix *felix; -	int err; - -	felix = kzalloc(sizeof(*felix), GFP_KERNEL); -	if (!felix) -		return -ENOMEM; - -	dev_set_drvdata(dev, felix); - -	ocelot = &felix->ocelot; -	ocelot->dev = dev; - -	ocelot->num_flooding_pgids = 1; - -	felix->info = &vsc7512_info; - -	ds = kzalloc(sizeof(*ds), GFP_KERNEL); -	if (!ds) { -		err = -ENOMEM; -		dev_err_probe(dev, err, "Failed to allocate DSA switch\n"); -		goto err_free_felix; -	} - -	ds->dev = dev; -	ds->num_ports = felix->info->num_ports; -	ds->num_tx_queues = felix->info->num_tx_queues; - -	ds->ops = &felix_switch_ops; -	ds->priv = ocelot; -	felix->ds = ds; -	felix->tag_proto = DSA_TAG_PROTO_OCELOT; - -	err = dsa_register_switch(ds); -	if (err) { -		dev_err_probe(dev, err, "Failed to register DSA switch\n"); -		goto err_free_ds; -	} - -	return 0; - -err_free_ds: -	kfree(ds); -err_free_felix: -	kfree(felix); -	return err; +	return felix_register_switch(&pdev->dev, 0, 1, false, false, +				     DSA_TAG_PROTO_OCELOT, &vsc7512_info);  }  static void ocelot_ext_remove(struct platform_device *pdev) @@ -123,9 +76,6 @@ static void ocelot_ext_remove(struct platform_device *pdev)  		return;  	dsa_unregister_switch(felix->ds); - -	kfree(felix->ds); -	kfree(felix);  }  static void ocelot_ext_shutdown(struct platform_device *pdev) diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 049930da0521..70782649c395 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -963,7 +963,6 @@ static const struct felix_info seville_info_vsc9953 = {  	.quirks			= FELIX_MAC_QUIRKS,  	.num_mact_rows		= 2048,  	.num_ports		= VSC9953_NUM_PORTS, -	.num_tx_queues		= OCELOT_NUM_TC,  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc,  	.mdio_bus_free		= vsc9953_mdio_bus_free,  	.port_modes		= vsc9953_port_modes, @@ -971,62 +970,18 @@ static const struct felix_info seville_info_vsc9953 = {  static int seville_probe(struct platform_device *pdev)  { -	struct dsa_switch *ds; -	struct ocelot *ocelot; +	struct device *dev = &pdev->dev;  	struct resource *res; -	struct felix *felix; -	int err; - -	felix = kzalloc(sizeof(struct felix), GFP_KERNEL); -	if (!felix) { -		err = -ENOMEM; -		dev_err(&pdev->dev, "Failed to allocate driver memory\n"); -		goto err_alloc_felix; -	} - -	platform_set_drvdata(pdev, felix); - -	ocelot = &felix->ocelot; -	ocelot->dev = &pdev->dev; -	ocelot->num_flooding_pgids = 1; -	felix->info = &seville_info_vsc9953;  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) { -		err = -EINVAL; -		dev_err(&pdev->dev, "Invalid resource\n"); -		goto err_alloc_felix; -	} -	felix->switch_base = res->start; - -	ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL); -	if (!ds) { -		err = -ENOMEM; -		dev_err(&pdev->dev, "Failed to allocate DSA switch\n"); -		goto err_alloc_ds; -	} - -	ds->dev = &pdev->dev; -	ds->num_ports = felix->info->num_ports; -	ds->ops = &felix_switch_ops; -	ds->priv = ocelot; -	felix->ds = ds; -	felix->tag_proto = DSA_TAG_PROTO_SEVILLE; - -	err = dsa_register_switch(ds); -	if (err) { -		dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err); -		goto err_register_ds; +		dev_err(dev, "Invalid resource\n"); +		return -EINVAL;  	} -	return 0; - -err_register_ds: -	kfree(ds); -err_alloc_ds: -err_alloc_felix: -	kfree(felix); -	return err; +	return felix_register_switch(dev, res->start, 1, false, false, +				     DSA_TAG_PROTO_SEVILLE, +				     &seville_info_vsc9953);  }  static void seville_remove(struct platform_device *pdev) @@ -1037,9 +992,6 @@ static void seville_remove(struct platform_device *pdev)  		return;  	dsa_unregister_switch(felix->ds); - -	kfree(felix->ds); -	kfree(felix);  }  static void seville_shutdown(struct platform_device *pdev) diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 968cb81088bf..e9f2c67bc15f 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -1021,7 +1021,7 @@ static const struct regmap_config ar9331_mdio_regmap_config = {  	.cache_type = REGCACHE_MAPLE,  }; -static struct regmap_bus ar9331_sw_bus = { +static const struct regmap_bus ar9331_sw_bus = {  	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,  	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,  	.read = ar9331_mdio_read, diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index b3c27cf538e8..f8d8c70642c4 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -565,7 +565,7 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_  	return qca8k_regmap_update_bits_mii(priv, reg, mask, write_val);  } -static struct regmap_config qca8k_regmap_config = { +static const struct regmap_config qca8k_regmap_config = {  	.reg_bits = 16,  	.val_bits = 32,  	.reg_stride = 4, diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 7f80035c5441..560c74c4ac3d 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -614,11 +614,57 @@ void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)  	qca8k_port_configure_learning(ds, port, learning);  } +static int qca8k_update_port_member(struct qca8k_priv *priv, int port, +				    const struct net_device *bridge_dev, +				    bool join) +{ +	bool isolated = !!(priv->port_isolated_map & BIT(port)), other_isolated; +	struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp; +	u32 port_mask = BIT(dp->cpu_dp->index); +	int i, ret; + +	for (i = 0; i < QCA8K_NUM_PORTS; i++) { +		if (i == port) +			continue; +		if (dsa_is_cpu_port(priv->ds, i)) +			continue; + +		other_dp = dsa_to_port(priv->ds, i); +		if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev)) +			continue; + +		other_isolated = !!(priv->port_isolated_map & BIT(i)); + +		/* Add/remove this port to/from the portvlan mask of the other +		 * ports in the bridge +		 */ +		if (join && !(isolated && other_isolated)) { +			port_mask |= BIT(i); +			ret = regmap_set_bits(priv->regmap, +					      QCA8K_PORT_LOOKUP_CTRL(i), +					      BIT(port)); +		} else { +			ret = regmap_clear_bits(priv->regmap, +						QCA8K_PORT_LOOKUP_CTRL(i), +						BIT(port)); +		} + +		if (ret) +			return ret; +	} + +	/* Add/remove all other ports to/from this port's portvlan mask */ +	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +			QCA8K_PORT_LOOKUP_MEMBER, port_mask); + +	return ret; +} +  int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port,  				struct switchdev_brport_flags flags,  				struct netlink_ext_ack *extack)  { -	if (flags.mask & ~BR_LEARNING) +	if (flags.mask & ~(BR_LEARNING | BR_ISOLATED))  		return -EINVAL;  	return 0; @@ -628,6 +674,7 @@ int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,  			    struct switchdev_brport_flags flags,  			    struct netlink_ext_ack *extack)  { +	struct qca8k_priv *priv = ds->priv;  	int ret;  	if (flags.mask & BR_LEARNING) { @@ -637,6 +684,20 @@ int qca8k_port_bridge_flags(struct dsa_switch *ds, int port,  			return ret;  	} +	if (flags.mask & BR_ISOLATED) { +		struct dsa_port *dp = dsa_to_port(ds, port); +		struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp); + +		if (flags.val & BR_ISOLATED) +			priv->port_isolated_map |= BIT(port); +		else +			priv->port_isolated_map &= ~BIT(port); + +		ret = qca8k_update_port_member(priv, port, bridge_dev, true); +		if (ret) +			return ret; +	} +  	return 0;  } @@ -646,62 +707,21 @@ int qca8k_port_bridge_join(struct dsa_switch *ds, int port,  			   struct netlink_ext_ack *extack)  {  	struct qca8k_priv *priv = ds->priv; -	int port_mask, cpu_port; -	int i, ret; - -	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; -	port_mask = BIT(cpu_port); - -	for (i = 0; i < QCA8K_NUM_PORTS; i++) { -		if (dsa_is_cpu_port(ds, i)) -			continue; -		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) -			continue; -		/* Add this port to the portvlan mask of the other ports -		 * in the bridge -		 */ -		ret = regmap_set_bits(priv->regmap, -				      QCA8K_PORT_LOOKUP_CTRL(i), -				      BIT(port)); -		if (ret) -			return ret; -		if (i != port) -			port_mask |= BIT(i); -	} -	/* Add all other ports to this ports portvlan mask */ -	ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -			QCA8K_PORT_LOOKUP_MEMBER, port_mask); - -	return ret; +	return qca8k_update_port_member(priv, port, bridge.dev, true);  }  void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,  			     struct dsa_bridge bridge)  {  	struct qca8k_priv *priv = ds->priv; -	int cpu_port, i; - -	cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - -	for (i = 0; i < QCA8K_NUM_PORTS; i++) { -		if (dsa_is_cpu_port(ds, i)) -			continue; -		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) -			continue; -		/* Remove this port to the portvlan mask of the other ports -		 * in the bridge -		 */ -		regmap_clear_bits(priv->regmap, -				  QCA8K_PORT_LOOKUP_CTRL(i), -				  BIT(port)); -	} +	int err; -	/* Set the cpu port to be the only one in the portvlan mask of -	 * this port -	 */ -	qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); +	err = qca8k_update_port_member(priv, port, bridge.dev, false); +	if (err) +		dev_err(priv->dev, +			"Failed to update switch config for bridge leave: %d\n", +			err);  }  void qca8k_port_fast_age(struct dsa_switch *ds, int port) diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h index 2184d8d2d5a9..3664a2e2f1f6 100644 --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h @@ -451,6 +451,7 @@ struct qca8k_priv {  	 * Bit 1: port enabled. Bit 0: port disabled.  	 */  	u8 port_enabled_map; +	u8 port_isolated_map;  	struct qca8k_ports_config ports_config;  	struct regmap *regmap;  	struct mii_bus *bus; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index ee0fb1c343f1..c7282ce3d11c 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2133,14 +2133,13 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,  	if (rc)  		return rc; -	rc = dsa_tag_8021q_bridge_join(ds, port, bridge); +	rc = dsa_tag_8021q_bridge_join(ds, port, bridge, tx_fwd_offload, +				       extack);  	if (rc) {  		sja1105_bridge_member(ds, port, bridge, false);  		return rc;  	} -	*tx_fwd_offload = true; -  	return 0;  } @@ -3167,8 +3166,7 @@ static int sja1105_setup(struct dsa_switch *ds)  	ds->vlan_filtering_is_global = true;  	ds->untag_bridge_pvid = true;  	ds->fdb_isolation = true; -	/* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ -	ds->max_num_bridges = 7; +	ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES;  	/* Advertise the 8 egress queues */  	ds->num_tx_queues = SJA1105_NUM_TC; diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index a7d41e781398..a1f4ca6ad888 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -111,7 +111,7 @@ int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)  }  int sja1105_get_ts_info(struct dsa_switch *ds, int port, -			struct ethtool_ts_info *info) +			struct kernel_ethtool_ts_info *info)  {  	struct sja1105_private *priv = ds->priv;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data; diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 416461ee95d2..8add2bd5f728 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -101,7 +101,7 @@ void sja1105pqrs_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,  				 enum packing_op op);  int sja1105_get_ts_info(struct dsa_switch *ds, int port, -			struct ethtool_ts_info *ts); +			struct kernel_ethtool_ts_info *ts);  void sja1105_ptp_txtstamp_skb(struct dsa_switch *ds, int slot,  			      struct sk_buff *clone); diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 4b031fefcec6..d9d3e30fd47a 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -22,9 +22,11 @@  #include <linux/of_mdio.h>  #include <linux/bitops.h>  #include <linux/if_bridge.h> +#include <linux/if_vlan.h>  #include <linux/etherdevice.h>  #include <linux/gpio/consumer.h>  #include <linux/gpio/driver.h> +#include <linux/dsa/8021q.h>  #include <linux/random.h>  #include <net/dsa.h> @@ -62,6 +64,8 @@  #define VSC73XX_CAT_DROP	0x6e  #define VSC73XX_CAT_PR_MISC_L2	0x6f  #define VSC73XX_CAT_PR_USR_PRIO	0x75 +#define VSC73XX_CAT_VLAN_MISC	0x79 +#define VSC73XX_CAT_PORT_VLAN	0x7a  #define VSC73XX_Q_MISC_CONF	0xdf  /* MAC_CFG register bits */ @@ -122,6 +126,17 @@  #define VSC73XX_ADVPORTM_IO_LOOPBACK	BIT(1)  #define VSC73XX_ADVPORTM_HOST_LOOPBACK	BIT(0) +/*  TXUPDCFG transmit modify setup bits */ +#define VSC73XX_TXUPDCFG_DSCP_REWR_MODE	GENMASK(20, 19) +#define VSC73XX_TXUPDCFG_DSCP_REWR_ENA	BIT(18) +#define VSC73XX_TXUPDCFG_TX_INT_TO_USRPRIO_ENA	BIT(17) +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID	GENMASK(15, 4) +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA	BIT(3) +#define VSC73XX_TXUPDCFG_TX_UPDATE_CRC_CPU_ENA	BIT(1) +#define VSC73XX_TXUPDCFG_TX_INSERT_TAG	BIT(0) + +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT	4 +  /* CAT_DROP categorizer frame dropping register bits */  #define VSC73XX_CAT_DROP_DROP_MC_SMAC_ENA	BIT(6)  #define VSC73XX_CAT_DROP_FWD_CTRL_ENA		BIT(4) @@ -135,6 +150,15 @@  #define VSC73XX_Q_MISC_CONF_EARLY_TX_512	(1 << 1)  #define VSC73XX_Q_MISC_CONF_MAC_PAUSE_MODE	BIT(0) +/* CAT_VLAN_MISC categorizer VLAN miscellaneous bits */ +#define VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA BIT(8) +#define VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA BIT(7) + +/* CAT_PORT_VLAN categorizer port VLAN */ +#define VSC73XX_CAT_PORT_VLAN_VLAN_CFI BIT(15) +#define VSC73XX_CAT_PORT_VLAN_VLAN_USR_PRIO GENMASK(14, 12) +#define VSC73XX_CAT_PORT_VLAN_VLAN_VID GENMASK(11, 0) +  /* Frame analyzer block 2 registers */  #define VSC73XX_STORMLIMIT	0x02  #define VSC73XX_ADVLEARN	0x03 @@ -164,6 +188,10 @@  #define VSC73XX_AGENCTRL	0xf0  #define VSC73XX_CAPRST		0xff +#define VSC73XX_SRCMASKS_CPU_COPY		BIT(27) +#define VSC73XX_SRCMASKS_MIRROR			BIT(26) +#define VSC73XX_SRCMASKS_PORTS_MASK		GENMASK(7, 0) +  #define VSC73XX_MACACCESS_CPU_COPY		BIT(14)  #define VSC73XX_MACACCESS_FWD_KILL		BIT(13)  #define VSC73XX_MACACCESS_IGNORE_VLAN		BIT(12) @@ -185,7 +213,8 @@  #define VSC73XX_VLANACCESS_VLAN_MIRROR		BIT(29)  #define VSC73XX_VLANACCESS_VLAN_SRC_CHECK	BIT(28)  #define VSC73XX_VLANACCESS_VLAN_PORT_MASK	GENMASK(9, 2) -#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK	GENMASK(2, 0) +#define VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT	2 +#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK	GENMASK(1, 0)  #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE	0  #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY	1  #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY	2 @@ -343,6 +372,17 @@ static const struct vsc73xx_counter vsc73xx_tx_counters[] = {  	{ 29, "TxQoSClass3" }, /* non-standard counter */  }; +struct vsc73xx_vlan_summary { +	size_t num_tagged; +	size_t num_untagged; +}; + +enum vsc73xx_port_vlan_conf { +	VSC73XX_VLAN_FILTER, +	VSC73XX_VLAN_FILTER_UNTAG_ALL, +	VSC73XX_VLAN_IGNORE, +}; +  int vsc73xx_is_addr_valid(u8 block, u8 subblock)  {  	switch (block) { @@ -557,16 +597,103 @@ static enum dsa_tag_protocol vsc73xx_get_tag_protocol(struct dsa_switch *ds,  	 * cannot access the tag. (See "Internal frame header" section  	 * 3.9.1 in the manual.)  	 */ -	return DSA_TAG_PROTO_NONE; +	return DSA_TAG_PROTO_VSC73XX_8021Q; +} + +static int vsc73xx_wait_for_vlan_table_cmd(struct vsc73xx *vsc) +{ +	int ret, err; +	u32 val; + +	ret = read_poll_timeout(vsc73xx_read, err, +				err < 0 || +				((val & VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK) == +				VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE), +				VSC73XX_POLL_SLEEP_US, VSC73XX_POLL_TIMEOUT_US, +				false, vsc, VSC73XX_BLOCK_ANALYZER, +				0, VSC73XX_VLANACCESS, &val); +	if (ret) +		return ret; +	return err; +} + +static int +vsc73xx_read_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 *portmap) +{ +	u32 val; +	int ret; + +	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid); + +	ret = vsc73xx_wait_for_vlan_table_cmd(vsc); +	if (ret) +		return ret; + +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, +			    VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK, +			    VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY); + +	ret = vsc73xx_wait_for_vlan_table_cmd(vsc); +	if (ret) +		return ret; + +	vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, &val); +	*portmap = (val & VSC73XX_VLANACCESS_VLAN_PORT_MASK) >> +		   VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT; + +	return 0; +} + +static int +vsc73xx_write_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 portmap) +{ +	int ret; + +	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid); + +	ret = vsc73xx_wait_for_vlan_table_cmd(vsc); +	if (ret) +		return ret; + +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, +			    VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK | +			    VSC73XX_VLANACCESS_VLAN_SRC_CHECK | +			    VSC73XX_VLANACCESS_VLAN_PORT_MASK, +			    VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY | +			    VSC73XX_VLANACCESS_VLAN_SRC_CHECK | +			    (portmap << VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT)); + +	return vsc73xx_wait_for_vlan_table_cmd(vsc); +} + +static int +vsc73xx_update_vlan_table(struct vsc73xx *vsc, int port, u16 vid, bool set) +{ +	u8 portmap; +	int ret; + +	ret = vsc73xx_read_vlan_table_entry(vsc, vid, &portmap); +	if (ret) +		return ret; + +	if (set) +		portmap |= BIT(port); +	else +		portmap &= ~BIT(port); + +	return vsc73xx_write_vlan_table_entry(vsc, vid, portmap);  }  static int vsc73xx_setup(struct dsa_switch *ds)  {  	struct vsc73xx *vsc = ds->priv; -	int i; +	int i, ret;  	dev_info(vsc->dev, "set up the switch\n"); +	ds->untag_bridge_pvid = true; +	ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES; +  	/* Issue RESET */  	vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GLORESET,  		      VSC73XX_GLORESET_MASTER_RESET); @@ -594,7 +721,7 @@ static int vsc73xx_setup(struct dsa_switch *ds)  		      VSC73XX_MACACCESS,  		      VSC73XX_MACACCESS_CMD_CLEAR_TABLE); -	/* Clear VLAN table */ +	/* Set VLAN table to default values */  	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0,  		      VSC73XX_VLANACCESS,  		      VSC73XX_VLANACCESS_VLAN_TBL_CMD_CLEAR_TABLE); @@ -623,9 +750,9 @@ static int vsc73xx_setup(struct dsa_switch *ds)  	vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GMIIDELAY,  		      VSC73XX_GMIIDELAY_GMII0_GTXDELAY_2_0_NS |  		      VSC73XX_GMIIDELAY_GMII0_RXDELAY_2_0_NS); -	/* Enable reception of frames on all ports */ -	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_RECVMASK, -		      0x5f); +	/* Ingess VLAN reception mask (table 145) */ +	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANMASK, +		      0xff);  	/* IP multicast flood mask (table 144) */  	vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_IFLODMSK,  		      0xff); @@ -638,7 +765,24 @@ static int vsc73xx_setup(struct dsa_switch *ds)  	udelay(4); -	return 0; +	/* Clear VLAN table */ +	for (i = 0; i < VLAN_N_VID; i++) +		vsc73xx_write_vlan_table_entry(vsc, i, 0); + +	INIT_LIST_HEAD(&vsc->vlans); + +	rtnl_lock(); +	ret = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); +	rtnl_unlock(); + +	return ret; +} + +static void vsc73xx_teardown(struct dsa_switch *ds) +{ +	rtnl_lock(); +	dsa_tag_8021q_unregister(ds); +	rtnl_unlock();  }  static void vsc73xx_init_port(struct vsc73xx *vsc, int port) @@ -788,10 +932,6 @@ static void vsc73xx_mac_link_down(struct phylink_config *config,  	/* Allow backward dropping of frames from this port */  	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,  			    VSC73XX_SBACKWDROP, BIT(port), BIT(port)); - -	/* Receive mask (disable forwarding) */ -	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, -			    VSC73XX_RECVMASK, BIT(port), 0);  }  static void vsc73xx_mac_link_up(struct phylink_config *config, @@ -828,6 +968,12 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,  	val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET;  	val |= VSC73XX_MAC_CFG_SEED_LOAD;  	val |= VSC73XX_MAC_CFG_WEXC_DIS; + +	/* Those bits are responsible for MTU only. Kernel takes care about MTU, +	 * let's enable +8 bytes frame length unconditionally. +	 */ +	val |= VSC73XX_MAC_CFG_VLAN_AWR | VSC73XX_MAC_CFG_VLAN_DBLAWR; +  	vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val);  	/* Flow control for the PHY facing ports: @@ -844,10 +990,6 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,  	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,  			    VSC73XX_ARBDISC, BIT(port), 0); -	/* Enable port (forwarding) in the receive mask */ -	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, -			    VSC73XX_RECVMASK, BIT(port), BIT(port)); -  	/* Disallow backward dropping of frames from this port */  	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,  			    VSC73XX_SBACKWDROP, BIT(port), 0); @@ -860,6 +1002,257 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,  			    VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN);  } +static bool vsc73xx_tag_8021q_active(struct dsa_port *dp) +{ +	return !dsa_port_is_vlan_filtering(dp); +} + +static struct vsc73xx_bridge_vlan * +vsc73xx_bridge_vlan_find(struct vsc73xx *vsc, u16 vid) +{ +	struct vsc73xx_bridge_vlan *vlan; + +	list_for_each_entry(vlan, &vsc->vlans, list) +		if (vlan->vid == vid) +			return vlan; + +	return NULL; +} + +static void +vsc73xx_bridge_vlan_remove_port(struct vsc73xx_bridge_vlan *vsc73xx_vlan, +				int port) +{ +	vsc73xx_vlan->portmask &= ~BIT(port); + +	if (vsc73xx_vlan->portmask) +		return; + +	list_del(&vsc73xx_vlan->list); +	kfree(vsc73xx_vlan); +} + +static void vsc73xx_bridge_vlan_summary(struct vsc73xx *vsc, int port, +					struct vsc73xx_vlan_summary *summary, +					u16 ignored_vid) +{ +	size_t num_tagged = 0, num_untagged = 0; +	struct vsc73xx_bridge_vlan *vlan; + +	list_for_each_entry(vlan, &vsc->vlans, list) { +		if (!(vlan->portmask & BIT(port)) || vlan->vid == ignored_vid) +			continue; + +		if (vlan->untagged & BIT(port)) +			num_untagged++; +		else +			num_tagged++; +	} + +	summary->num_untagged = num_untagged; +	summary->num_tagged = num_tagged; +} + +static u16 vsc73xx_find_first_vlan_untagged(struct vsc73xx *vsc, int port) +{ +	struct vsc73xx_bridge_vlan *vlan; + +	list_for_each_entry(vlan, &vsc->vlans, list) +		if ((vlan->portmask & BIT(port)) && +		    (vlan->untagged & BIT(port))) +			return vlan->vid; + +	return VLAN_N_VID; +} + +static int vsc73xx_set_vlan_conf(struct vsc73xx *vsc, int port, +				 enum vsc73xx_port_vlan_conf port_vlan_conf) +{ +	u32 val = 0; +	int ret; + +	if (port_vlan_conf == VSC73XX_VLAN_IGNORE) +		val = VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA | +		      VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA; + +	ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, +				  VSC73XX_CAT_VLAN_MISC, +				  VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA | +				  VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA, val); +	if (ret) +		return ret; + +	val = (port_vlan_conf == VSC73XX_VLAN_FILTER) ? +	      VSC73XX_TXUPDCFG_TX_INSERT_TAG : 0; + +	return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, +				   VSC73XX_TXUPDCFG, +				   VSC73XX_TXUPDCFG_TX_INSERT_TAG, val); +} + +/** + * vsc73xx_vlan_commit_conf - Update VLAN configuration of a port + * @vsc: Switch private data structure + * @port: Port index on which to operate + * + * Update the VLAN behavior of a port to make sure that when it is under + * a VLAN filtering bridge, the port is either filtering with tag + * preservation, or filtering with all VLANs egress-untagged. Otherwise, + * the port ignores VLAN tags from packets and applies the port-based + * VID. + * + * Must be called when changes are made to: + * - the bridge VLAN filtering state of the port + * - the number or attributes of VLANs from the bridge VLAN table, + *   while the port is currently VLAN-aware + * + * Return: 0 on success, or negative errno on error. + */ +static int vsc73xx_vlan_commit_conf(struct vsc73xx *vsc, int port) +{ +	enum vsc73xx_port_vlan_conf port_vlan_conf = VSC73XX_VLAN_IGNORE; +	struct dsa_port *dp = dsa_to_port(vsc->ds, port); + +	if (port == CPU_PORT) { +		port_vlan_conf = VSC73XX_VLAN_FILTER; +	} else if (dsa_port_is_vlan_filtering(dp)) { +		struct vsc73xx_vlan_summary summary; + +		port_vlan_conf = VSC73XX_VLAN_FILTER; + +		vsc73xx_bridge_vlan_summary(vsc, port, &summary, VLAN_N_VID); +		if (summary.num_tagged == 0) +			port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL; +	} + +	return vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); +} + +static int +vsc73xx_vlan_change_untagged(struct vsc73xx *vsc, int port, u16 vid, bool set) +{ +	u32 val = 0; + +	if (set) +		val = VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA | +		      ((vid << VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT) & +		       VSC73XX_TXUPDCFG_TX_UNTAGGED_VID); + +	return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, +				   VSC73XX_TXUPDCFG, +				   VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA | +				   VSC73XX_TXUPDCFG_TX_UNTAGGED_VID, val); +} + +/** + * vsc73xx_vlan_commit_untagged - Update native VLAN of a port + * @vsc: Switch private data structure + * @port: Port index on which to operate + * + * Update the native VLAN of a port (the one VLAN which is transmitted + * as egress-tagged on a trunk port) when port is in VLAN filtering mode and + * only one untagged vid is configured. + * In other cases no need to configure it because switch can untag all vlans on + * the port. + * + * Return: 0 on success, or negative errno on error. + */ +static int vsc73xx_vlan_commit_untagged(struct vsc73xx *vsc, int port) +{ +	struct dsa_port *dp = dsa_to_port(vsc->ds, port); +	struct vsc73xx_vlan_summary summary; +	u16 vid = 0; +	bool valid; + +	if (!dsa_port_is_vlan_filtering(dp)) +		/* Port is configured to untag all vlans in that case. +		 * No need to commit untagged config change. +		 */ +		return 0; + +	vsc73xx_bridge_vlan_summary(vsc, port, &summary, VLAN_N_VID); + +	if (summary.num_untagged > 1) +		/* Port must untag all vlans in that case. +		 * No need to commit untagged config change. +		 */ +		return 0; + +	valid = (summary.num_untagged == 1); +	if (valid) +		vid = vsc73xx_find_first_vlan_untagged(vsc, port); + +	return vsc73xx_vlan_change_untagged(vsc, port, vid, valid); +} + +static int +vsc73xx_vlan_change_pvid(struct vsc73xx *vsc, int port, u16 vid, bool set) +{ +	u32 val = 0; +	int ret; + +	val = set ? 0 : VSC73XX_CAT_DROP_UNTAGGED_ENA; + +	ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, +				  VSC73XX_CAT_DROP, +				  VSC73XX_CAT_DROP_UNTAGGED_ENA, val); +	if (!set || ret) +		return ret; + +	return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, +				   VSC73XX_CAT_PORT_VLAN, +				   VSC73XX_CAT_PORT_VLAN_VLAN_VID, +				   vid & VSC73XX_CAT_PORT_VLAN_VLAN_VID); +} + +/** + * vsc73xx_vlan_commit_pvid - Update port-based default VLAN of a port + * @vsc: Switch private data structure + * @port: Port index on which to operate + * + * Update the PVID of a port so that it follows either the bridge PVID + * configuration, when the bridge is currently VLAN-aware, or the PVID + * from tag_8021q, when the port is standalone or under a VLAN-unaware + * bridge. A port with no PVID drops all untagged and VID 0 tagged + * traffic. + * + * Must be called when changes are made to: + * - the bridge VLAN filtering state of the port + * - the number or attributes of VLANs from the bridge VLAN table, + *   while the port is currently VLAN-aware + * + * Return: 0 on success, or negative errno on error. + */ +static int vsc73xx_vlan_commit_pvid(struct vsc73xx *vsc, int port) +{ +	struct vsc73xx_portinfo *portinfo = &vsc->portinfo[port]; +	bool valid = portinfo->pvid_tag_8021q_configured; +	struct dsa_port *dp = dsa_to_port(vsc->ds, port); +	u16 vid = portinfo->pvid_tag_8021q; + +	if (dsa_port_is_vlan_filtering(dp)) { +		vid = portinfo->pvid_vlan_filtering; +		valid = portinfo->pvid_vlan_filtering_configured; +	} + +	return vsc73xx_vlan_change_pvid(vsc, port, vid, valid); +} + +static int vsc73xx_vlan_commit_settings(struct vsc73xx *vsc, int port) +{ +	int ret; + +	ret = vsc73xx_vlan_commit_untagged(vsc, port); +	if (ret) +		return ret; + +	ret = vsc73xx_vlan_commit_pvid(vsc, port); +	if (ret) +		return ret; + +	return vsc73xx_vlan_commit_conf(vsc, port); +} +  static int vsc73xx_port_enable(struct dsa_switch *ds, int port,  			       struct phy_device *phy)  { @@ -868,7 +1261,7 @@ static int vsc73xx_port_enable(struct dsa_switch *ds, int port,  	dev_info(vsc->dev, "enable port %d\n", port);  	vsc73xx_init_port(vsc, port); -	return 0; +	return vsc73xx_vlan_commit_settings(vsc, port);  }  static void vsc73xx_port_disable(struct dsa_switch *ds, int port) @@ -1039,6 +1432,303 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port,  	config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000;  } +static int +vsc73xx_port_vlan_filtering(struct dsa_switch *ds, int port, +			    bool vlan_filtering, struct netlink_ext_ack *extack) +{ +	struct vsc73xx *vsc = ds->priv; + +	/* The commit to hardware processed below is required because vsc73xx +	 * is using tag_8021q. When vlan_filtering is disabled, tag_8021q uses +	 * pvid/untagged vlans for port recognition. The values configured for +	 * vlans and pvid/untagged states are stored in portinfo structure. +	 * When vlan_filtering is enabled, we need to restore pvid/untagged from +	 * portinfo structure. Analogous routine is processed when +	 * vlan_filtering is disabled, but values used for tag_8021q are +	 * restored. +	 */ + +	return vsc73xx_vlan_commit_settings(vsc, port); +} + +static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port, +				 const struct switchdev_obj_port_vlan *vlan, +				 struct netlink_ext_ack *extack) +{ +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +	struct dsa_port *dp = dsa_to_port(ds, port); +	struct vsc73xx_bridge_vlan *vsc73xx_vlan; +	struct vsc73xx_vlan_summary summary; +	struct vsc73xx_portinfo *portinfo; +	struct vsc73xx *vsc = ds->priv; +	bool commit_to_hardware; +	int ret = 0; + +	/* Be sure to deny alterations to the configuration done by tag_8021q. +	 */ +	if (vid_is_dsa_8021q(vlan->vid)) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Range 3072-4095 reserved for dsa_8021q operation"); +		return -EBUSY; +	} + +	/* The processed vlan->vid is excluded from the search because the VLAN +	 * can be re-added with a different set of flags, so it's easiest to +	 * ignore its old flags from the VLAN database software copy. +	 */ +	vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); + +	/* VSC73XX allows only three untagged states: none, one or all */ +	if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) || +	    (!untagged && summary.num_untagged > 1)) { +		NL_SET_ERR_MSG_MOD(extack, +				   "Port can have only none, one or all untagged vlan"); +		return -EBUSY; +	} + +	vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); + +	if (!vsc73xx_vlan) { +		vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL); +		if (!vsc73xx_vlan) +			return -ENOMEM; + +		vsc73xx_vlan->vid = vlan->vid; + +		list_add_tail(&vsc73xx_vlan->list, &vsc->vlans); +	} + +	vsc73xx_vlan->portmask |= BIT(port); + +	/* CPU port must be always tagged because source port identification is +	 * based on tag_8021q. +	 */ +	if (port == CPU_PORT) +		goto update_vlan_table; + +	if (untagged) +		vsc73xx_vlan->untagged |= BIT(port); +	else +		vsc73xx_vlan->untagged &= ~BIT(port); + +	portinfo = &vsc->portinfo[port]; + +	if (pvid) { +		portinfo->pvid_vlan_filtering_configured = true; +		portinfo->pvid_vlan_filtering = vlan->vid; +	} else if (portinfo->pvid_vlan_filtering_configured && +		   portinfo->pvid_vlan_filtering == vlan->vid) { +		portinfo->pvid_vlan_filtering_configured = false; +	} + +	commit_to_hardware = !vsc73xx_tag_8021q_active(dp); +	if (commit_to_hardware) { +		ret = vsc73xx_vlan_commit_settings(vsc, port); +		if (ret) +			goto err; +	} + +update_vlan_table: +	ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, true); +	if (!ret) +		return 0; +err: +	vsc73xx_bridge_vlan_remove_port(vsc73xx_vlan, port); +	return ret; +} + +static int vsc73xx_port_vlan_del(struct dsa_switch *ds, int port, +				 const struct switchdev_obj_port_vlan *vlan) +{ +	struct vsc73xx_bridge_vlan *vsc73xx_vlan; +	struct vsc73xx_portinfo *portinfo; +	struct vsc73xx *vsc = ds->priv; +	bool commit_to_hardware; +	int ret; + +	ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, false); +	if (ret) +		return ret; + +	portinfo = &vsc->portinfo[port]; + +	if (portinfo->pvid_vlan_filtering_configured && +	    portinfo->pvid_vlan_filtering == vlan->vid) +		portinfo->pvid_vlan_filtering_configured = false; + +	vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); + +	if (vsc73xx_vlan) +		vsc73xx_bridge_vlan_remove_port(vsc73xx_vlan, port); + +	commit_to_hardware = !vsc73xx_tag_8021q_active(dsa_to_port(ds, port)); + +	if (commit_to_hardware) +		return vsc73xx_vlan_commit_settings(vsc, port); + +	return 0; +} + +static int vsc73xx_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, +				      u16 flags) +{ +	bool pvid = flags & BRIDGE_VLAN_INFO_PVID; +	struct vsc73xx_portinfo *portinfo; +	struct vsc73xx *vsc = ds->priv; +	bool commit_to_hardware; +	int ret; + +	portinfo = &vsc->portinfo[port]; + +	if (pvid) { +		portinfo->pvid_tag_8021q_configured = true; +		portinfo->pvid_tag_8021q = vid; +	} + +	commit_to_hardware = vsc73xx_tag_8021q_active(dsa_to_port(ds, port)); +	if (commit_to_hardware) { +		ret = vsc73xx_vlan_commit_settings(vsc, port); +		if (ret) +			return ret; +	} + +	return vsc73xx_update_vlan_table(vsc, port, vid, true); +} + +static int vsc73xx_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) +{ +	struct vsc73xx_portinfo *portinfo; +	struct vsc73xx *vsc = ds->priv; + +	portinfo = &vsc->portinfo[port]; + +	if (portinfo->pvid_tag_8021q_configured && +	    portinfo->pvid_tag_8021q == vid) { +		struct dsa_port *dp = dsa_to_port(ds, port); +		bool commit_to_hardware; +		int err; + +		portinfo->pvid_tag_8021q_configured = false; + +		commit_to_hardware = vsc73xx_tag_8021q_active(dp); +		if (commit_to_hardware) { +			err = vsc73xx_vlan_commit_settings(vsc, port); +			if (err) +				return err; +		} +	} + +	return vsc73xx_update_vlan_table(vsc, port, vid, false); +} + +static int vsc73xx_port_pre_bridge_flags(struct dsa_switch *ds, int port, +					 struct switchdev_brport_flags flags, +					 struct netlink_ext_ack *extack) +{ +	if (flags.mask & ~BR_LEARNING) +		return -EINVAL; + +	return 0; +} + +static int vsc73xx_port_bridge_flags(struct dsa_switch *ds, int port, +				     struct switchdev_brport_flags flags, +				     struct netlink_ext_ack *extack) +{ +	if (flags.mask & BR_LEARNING) { +		u32 val = flags.val & BR_LEARNING ? BIT(port) : 0; +		struct vsc73xx *vsc = ds->priv; + +		return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +					   VSC73XX_LEARNMASK, BIT(port), val); +	} + +	return 0; +} + +static void vsc73xx_refresh_fwd_map(struct dsa_switch *ds, int port, u8 state) +{ +	struct dsa_port *other_dp, *dp = dsa_to_port(ds, port); +	struct vsc73xx *vsc = ds->priv; +	u16 mask; + +	if (state != BR_STATE_FORWARDING) { +		/* Ports that aren't in the forwarding state must not +		 * forward packets anywhere. +		 */ +		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +				    VSC73XX_SRCMASKS + port, +				    VSC73XX_SRCMASKS_PORTS_MASK, 0); + +		dsa_switch_for_each_available_port(other_dp, ds) { +			if (other_dp == dp) +				continue; +			vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +					    VSC73XX_SRCMASKS + other_dp->index, +					    BIT(port), 0); +		} + +		return; +	} + +	/* Forwarding ports must forward to the CPU and to other ports +	 * in the same bridge +	 */ +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +			    VSC73XX_SRCMASKS + CPU_PORT, BIT(port), BIT(port)); + +	mask = BIT(CPU_PORT); + +	dsa_switch_for_each_user_port(other_dp, ds) { +		int other_port = other_dp->index; + +		if (port == other_port || !dsa_port_bridge_same(dp, other_dp) || +		    other_dp->stp_state != BR_STATE_FORWARDING) +			continue; + +		mask |= BIT(other_port); + +		vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +				    VSC73XX_SRCMASKS + other_port, +				    BIT(port), BIT(port)); +	} + +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +			    VSC73XX_SRCMASKS + port, +			    VSC73XX_SRCMASKS_PORTS_MASK, mask); +} + +/* FIXME: STP frames aren't forwarded at this moment. BPDU frames are + * forwarded only from and to PI/SI interface. For more info see chapter + * 2.7.1 (CPU Forwarding) in datasheet. + * This function is required for tag_8021q operations. + */ +static void vsc73xx_port_stp_state_set(struct dsa_switch *ds, int port, +				       u8 state) +{ +	struct dsa_port *dp = dsa_to_port(ds, port); +	struct vsc73xx *vsc = ds->priv; +	u32 val = 0; + +	if (state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) +		val = dp->learning ? BIT(port) : 0; + +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +			    VSC73XX_LEARNMASK, BIT(port), val); + +	val = (state == BR_STATE_BLOCKING || state == BR_STATE_DISABLED) ? +	      0 : BIT(port); +	vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, +			    VSC73XX_RECVMASK, BIT(port), val); + +	/* CPU Port should always forward packets when user ports are forwarding +	 * so let's configure it from other ports only. +	 */ +	if (port != CPU_PORT) +		vsc73xx_refresh_fwd_map(ds, port, state); +} +  static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {  	.mac_config = vsc73xx_mac_config,  	.mac_link_down = vsc73xx_mac_link_down, @@ -1048,6 +1738,7 @@ static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {  static const struct dsa_switch_ops vsc73xx_ds_ops = {  	.get_tag_protocol = vsc73xx_get_tag_protocol,  	.setup = vsc73xx_setup, +	.teardown = vsc73xx_teardown,  	.phy_read = vsc73xx_phy_read,  	.phy_write = vsc73xx_phy_write,  	.get_strings = vsc73xx_get_strings, @@ -1055,9 +1746,19 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {  	.get_sset_count = vsc73xx_get_sset_count,  	.port_enable = vsc73xx_port_enable,  	.port_disable = vsc73xx_port_disable, +	.port_pre_bridge_flags = vsc73xx_port_pre_bridge_flags, +	.port_bridge_flags = vsc73xx_port_bridge_flags, +	.port_bridge_join = dsa_tag_8021q_bridge_join, +	.port_bridge_leave = dsa_tag_8021q_bridge_leave,  	.port_change_mtu = vsc73xx_change_mtu,  	.port_max_mtu = vsc73xx_get_max_mtu, +	.port_stp_state_set = vsc73xx_port_stp_state_set, +	.port_vlan_filtering = vsc73xx_port_vlan_filtering, +	.port_vlan_add = vsc73xx_port_vlan_add, +	.port_vlan_del = vsc73xx_port_vlan_del,  	.phylink_get_caps = vsc73xx_phylink_get_caps, +	.tag_8021q_vlan_add = vsc73xx_tag_8021q_vlan_add, +	.tag_8021q_vlan_del = vsc73xx_tag_8021q_vlan_del,  };  static int vsc73xx_gpio_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h index 2997f7e108b1..3ca579acc798 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.h +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -15,6 +15,22 @@  #define VSC73XX_MAX_NUM_PORTS	8  /** + * struct vsc73xx_portinfo - port data structure: contains storage data + * @pvid_vlan_filtering: pvid vlan number used in vlan filtering mode + * @pvid_tag_8021q: pvid vlan number used in tag_8021q mode + * @pvid_vlan_filtering_configured: informs if port has configured pvid in vlan + *	filtering mode + * @pvid_tag_8021q_configured: imforms if port have configured pvid in tag_8021q + *	mode + */ +struct vsc73xx_portinfo { +	u16		pvid_vlan_filtering; +	u16		pvid_tag_8021q; +	bool		pvid_vlan_filtering_configured; +	bool		pvid_tag_8021q_configured; +}; + +/**   * struct vsc73xx - VSC73xx state container: main data structure   * @dev: The device pointer   * @reset: The descriptor for the GPIO line tied to the reset pin @@ -25,6 +41,10 @@   * @addr: MAC address used in flow control frames   * @ops: Structure with hardware-dependent operations   * @priv: Pointer to the configuration interface structure + * @portinfo: Storage table portinfo structructures + * @vlans: List of configured vlans. Contains port mask and untagged status of + *	every vlan configured in port vlan operation. It doesn't cover tag_8021q + *	vlans.   */  struct vsc73xx {  	struct device			*dev; @@ -35,6 +55,8 @@ struct vsc73xx {  	u8				addr[ETH_ALEN];  	const struct vsc73xx_ops	*ops;  	void				*priv; +	struct vsc73xx_portinfo		portinfo[VSC73XX_MAX_NUM_PORTS]; +	struct list_head		vlans;  };  /** @@ -49,6 +71,21 @@ struct vsc73xx_ops {  		     u32 val);  }; +/** + * struct vsc73xx_bridge_vlan - VSC73xx driver structure which keeps vlan + *	database copy + * @vid: VLAN number + * @portmask: each bit represents one port + * @untagged: each bit represents one port configured with @vid untagged + * @list: list structure + */ +struct vsc73xx_bridge_vlan { +	u16 vid; +	u8 portmask; +	u8 untagged; +	struct list_head list; +}; +  int vsc73xx_is_addr_valid(u8 block, u8 subblock);  int vsc73xx_probe(struct vsc73xx *vsc);  void vsc73xx_remove(struct vsc73xx *vsc); diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c index c1179d7311f7..9b731dea78c1 100644 --- a/drivers/net/dsa/xrs700x/xrs700x_i2c.c +++ b/drivers/net/dsa/xrs700x/xrs700x_i2c.c @@ -127,8 +127,8 @@ static void xrs700x_i2c_shutdown(struct i2c_client *i2c)  }  static const struct i2c_device_id xrs700x_i2c_id[] = { -	{ "xrs700x-switch", 0 }, -	{}, +	{ "xrs700x-switch" }, +	{}  };  MODULE_DEVICE_TABLE(i2c, xrs700x_i2c_id); |