diff options
Diffstat (limited to 'drivers/net/dsa')
| -rw-r--r-- | drivers/net/dsa/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/b53/b53_common.c | 10 | ||||
| -rw-r--r-- | drivers/net/dsa/b53/b53_priv.h | 7 | ||||
| -rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 11 | ||||
| -rw-r--r-- | drivers/net/dsa/dsa_loop.c | 9 | ||||
| -rw-r--r-- | drivers/net/dsa/lan9303-core.c | 138 | ||||
| -rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 12 | ||||
| -rw-r--r-- | drivers/net/dsa/mt7530.c | 288 | ||||
| -rw-r--r-- | drivers/net/dsa/mt7530.h | 83 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 94 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 2 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 16 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_atu.c | 87 | ||||
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_vtu.c | 74 | 
14 files changed, 695 insertions, 138 deletions
| diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 83a9bc892a3b..2b81b97e994f 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -33,7 +33,7 @@ config NET_DSA_MT7530  config NET_DSA_MV88E6060  	tristate "Marvell 88E6060 ethernet switch chip support" -	depends on NET_DSA +	depends on NET_DSA && NET_DSA_LEGACY  	select NET_DSA_TAG_TRAILER  	---help---  	  This enables support for the Marvell 88E6060 ethernet switch diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 4498ab897d94..db830a1141d9 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1029,8 +1029,7 @@ int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)  EXPORT_SYMBOL(b53_vlan_filtering);  int b53_vlan_prepare(struct dsa_switch *ds, int port, -		     const struct switchdev_obj_port_vlan *vlan, -		     struct switchdev_trans *trans) +		     const struct switchdev_obj_port_vlan *vlan)  {  	struct b53_device *dev = ds->priv; @@ -1047,8 +1046,7 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port,  EXPORT_SYMBOL(b53_vlan_prepare);  void b53_vlan_add(struct dsa_switch *ds, int port, -		  const struct switchdev_obj_port_vlan *vlan, -		  struct switchdev_trans *trans) +		  const struct switchdev_obj_port_vlan *vlan)  {  	struct b53_device *dev = ds->priv;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; @@ -1495,8 +1493,7 @@ static bool b53_can_enable_brcm_tags(struct dsa_switch *ds, int port)  	return false;  } -static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, -						  int port) +enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port)  {  	struct b53_device *dev = ds->priv; @@ -1517,6 +1514,7 @@ static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds,  	return DSA_TAG_PROTO_BRCM;  } +EXPORT_SYMBOL(b53_get_tag_protocol);  int b53_mirror_add(struct dsa_switch *ds, int port,  		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress) diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index daaaa1ecb996..d954cf36ecd8 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -295,11 +295,9 @@ void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);  void b53_br_fast_age(struct dsa_switch *ds, int port);  int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);  int b53_vlan_prepare(struct dsa_switch *ds, int port, -		     const struct switchdev_obj_port_vlan *vlan, -		     struct switchdev_trans *trans); +		     const struct switchdev_obj_port_vlan *vlan);  void b53_vlan_add(struct dsa_switch *ds, int port, -		  const struct switchdev_obj_port_vlan *vlan, -		  struct switchdev_trans *trans); +		  const struct switchdev_obj_port_vlan *vlan);  int b53_vlan_del(struct dsa_switch *ds, int port,  		 const struct switchdev_obj_port_vlan *vlan);  int b53_fdb_add(struct dsa_switch *ds, int port, @@ -310,6 +308,7 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,  		 dsa_fdb_dump_cb_t *cb, void *data);  int b53_mirror_add(struct dsa_switch *ds, int port,  		   struct dsa_mall_mirror_tc_entry *mirror, bool ingress); +enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds, int port);  void b53_mirror_del(struct dsa_switch *ds, int port,  		    struct dsa_mall_mirror_tc_entry *mirror);  int b53_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index b62d47210db8..0378eded31f2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -34,12 +34,6 @@  #include "b53/b53_priv.h"  #include "b53/b53_regs.h" -static enum dsa_tag_protocol bcm_sf2_sw_get_tag_protocol(struct dsa_switch *ds, -							 int port) -{ -	return DSA_TAG_PROTO_BRCM; -} -  static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)  {  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -860,7 +854,7 @@ static const struct b53_io_ops bcm_sf2_io_ops = {  };  static const struct dsa_switch_ops bcm_sf2_ops = { -	.get_tag_protocol	= bcm_sf2_sw_get_tag_protocol, +	.get_tag_protocol	= b53_get_tag_protocol,  	.setup			= bcm_sf2_sw_setup,  	.get_strings		= b53_get_strings,  	.get_ethtool_stats	= b53_get_ethtool_stats, @@ -954,6 +948,9 @@ static const struct of_device_id bcm_sf2_of_match[] = {  	{ .compatible = "brcm,bcm7278-switch-v4.0",  	  .data = &bcm_sf2_7278_data  	}, +	{ .compatible = "brcm,bcm7278-switch-v4.8", +	  .data = &bcm_sf2_7278_data +	},  	{ /* sentinel */ },  };  MODULE_DEVICE_TABLE(of, bcm_sf2_of_match); diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index bb71d3d6f65b..7aa84ee4e771 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -174,9 +174,9 @@ static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,  	return 0;  } -static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, -				      const struct switchdev_obj_port_vlan *vlan, -				      struct switchdev_trans *trans) +static int +dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, +			   const struct switchdev_obj_port_vlan *vlan)  {  	struct dsa_loop_priv *ps = ds->priv;  	struct mii_bus *bus = ps->bus; @@ -193,8 +193,7 @@ static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,  }  static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, -				   const struct switchdev_obj_port_vlan *vlan, -				   struct switchdev_trans *trans) +				   const struct switchdev_obj_port_vlan *vlan)  {  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index b24566bb74d2..6171c0853ff1 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -249,6 +249,28 @@ static int lan9303_read(struct regmap *regmap, unsigned int offset, u32 *reg)  	return -EIO;  } +static int lan9303_read_wait(struct lan9303 *chip, int offset, u32 mask) +{ +	int i; + +	for (i = 0; i < 25; i++) { +		u32 reg; +		int ret; + +		ret = lan9303_read(chip->regmap, offset, ®); +		if (ret) { +			dev_err(chip->dev, "%s failed to read offset %d: %d\n", +				__func__, offset, ret); +			return ret; +		} +		if (!(reg & mask)) +			return 0; +		usleep_range(1000, 2000); +	} + +	return -ETIMEDOUT; +} +  static int lan9303_virt_phy_reg_read(struct lan9303 *chip, int regnum)  {  	int ret; @@ -274,22 +296,8 @@ static int lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val)  static int lan9303_indirect_phy_wait_for_completion(struct lan9303 *chip)  { -	int ret, i; -	u32 reg; - -	for (i = 0; i < 25; i++) { -		ret = lan9303_read(chip->regmap, LAN9303_PMI_ACCESS, ®); -		if (ret) { -			dev_err(chip->dev, -				"Failed to read pmi access status: %d\n", ret); -			return ret; -		} -		if (!(reg & LAN9303_PMI_ACCESS_MII_BUSY)) -			return 0; -		usleep_range(1000, 2000); -	} - -	return -EIO; +	return lan9303_read_wait(chip, LAN9303_PMI_ACCESS, +				 LAN9303_PMI_ACCESS_MII_BUSY);  }  static int lan9303_indirect_phy_read(struct lan9303 *chip, int addr, int regnum) @@ -366,22 +374,8 @@ EXPORT_SYMBOL_GPL(lan9303_indirect_phy_ops);  static int lan9303_switch_wait_for_completion(struct lan9303 *chip)  { -	int ret, i; -	u32 reg; - -	for (i = 0; i < 25; i++) { -		ret = lan9303_read(chip->regmap, LAN9303_SWITCH_CSR_CMD, ®); -		if (ret) { -			dev_err(chip->dev, -				"Failed to read csr command status: %d\n", ret); -			return ret; -		} -		if (!(reg & LAN9303_SWITCH_CSR_CMD_BUSY)) -			return 0; -		usleep_range(1000, 2000); -	} - -	return -EIO; +	return lan9303_read_wait(chip, LAN9303_SWITCH_CSR_CMD, +				 LAN9303_SWITCH_CSR_CMD_BUSY);  }  static int lan9303_write_switch_reg(struct lan9303 *chip, u16 regnum, u32 val) @@ -485,7 +479,8 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip)  {  	int reg; -	/* depending on the 'phy_addr_sel_strap' setting, the three phys are +	/* Calculate chip->phy_addr_base: +	 * Depending on the 'phy_addr_sel_strap' setting, the three phys are  	 * using IDs 0-1-2 or IDs 1-2-3. We cannot read back the  	 * 'phy_addr_sel_strap' setting directly, so we need a test, which  	 * configuration is active: @@ -500,13 +495,10 @@ static int lan9303_detect_phy_setup(struct lan9303 *chip)  		return reg;  	} -	if ((reg != 0) && (reg != 0xffff)) -		chip->phy_addr_sel_strap = 1; -	else -		chip->phy_addr_sel_strap = 0; +	chip->phy_addr_base = reg != 0 && reg != 0xffff;  	dev_dbg(chip->dev, "Phy setup '%s' detected\n", -		chip->phy_addr_sel_strap ? "1-2-3" : "0-1-2"); +		chip->phy_addr_base ? "1-2-3" : "0-1-2");  	return 0;  } @@ -546,20 +538,19 @@ lan9303_alr_cache_find_mac(struct lan9303 *chip, const u8 *mac_addr)  	return NULL;  } -/* Wait a while until mask & reg == value. Otherwise return timeout. */ -static int lan9303_csr_reg_wait(struct lan9303 *chip, int regno, -				int mask, char value) +static int lan9303_csr_reg_wait(struct lan9303 *chip, int regno, u32 mask)  {  	int i; -	for (i = 0; i < 0x1000; i++) { +	for (i = 0; i < 25; i++) {  		u32 reg;  		lan9303_read_switch_reg(chip, regno, ®); -		if ((reg & mask) == value) +		if (!(reg & mask))  			return 0;  		usleep_range(1000, 2000);  	} +  	return -ETIMEDOUT;  } @@ -569,8 +560,7 @@ static int lan9303_alr_make_entry_raw(struct lan9303 *chip, u32 dat0, u32 dat1)  	lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_WR_DAT_1, dat1);  	lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD,  				 LAN9303_ALR_CMD_MAKE_ENTRY); -	lan9303_csr_reg_wait(chip, LAN9303_SWE_ALR_CMD_STS, ALR_STS_MAKE_PEND, -			     0); +	lan9303_csr_reg_wait(chip, LAN9303_SWE_ALR_CMD_STS, ALR_STS_MAKE_PEND);  	lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0);  	return 0; @@ -583,6 +573,7 @@ static void lan9303_alr_loop(struct lan9303 *chip, alr_loop_cb_t *cb, void *ctx)  {  	int i; +	mutex_lock(&chip->alr_mutex);  	lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD,  				 LAN9303_ALR_CMD_GET_FIRST);  	lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0); @@ -606,6 +597,7 @@ static void lan9303_alr_loop(struct lan9303 *chip, alr_loop_cb_t *cb, void *ctx)  					 LAN9303_ALR_CMD_GET_NEXT);  		lan9303_write_switch_reg(chip, LAN9303_SWE_ALR_CMD, 0);  	} +	mutex_unlock(&chip->alr_mutex);  }  static void alr_reg_to_mac(u32 dat0, u32 dat1, u8 mac[6]) @@ -694,16 +686,20 @@ static int lan9303_alr_add_port(struct lan9303 *chip, const u8 *mac, int port,  {  	struct lan9303_alr_cache_entry *entr; +	mutex_lock(&chip->alr_mutex);  	entr = lan9303_alr_cache_find_mac(chip, mac);  	if (!entr) { /*New entry */  		entr = lan9303_alr_cache_find_free(chip); -		if (!entr) +		if (!entr) { +			mutex_unlock(&chip->alr_mutex);  			return -ENOSPC; +		}  		ether_addr_copy(entr->mac_addr, mac);  	}  	entr->port_map |= BIT(port);  	entr->stp_override = stp_override;  	lan9303_alr_set_entry(chip, mac, entr->port_map, stp_override); +	mutex_unlock(&chip->alr_mutex);  	return 0;  } @@ -713,15 +709,18 @@ static int lan9303_alr_del_port(struct lan9303 *chip, const u8 *mac, int port)  {  	struct lan9303_alr_cache_entry *entr; +	mutex_lock(&chip->alr_mutex);  	entr = lan9303_alr_cache_find_mac(chip, mac);  	if (!entr) -		return 0;  /* no static entry found */ +		goto out;  /* no static entry found */  	entr->port_map &= ~BIT(port);  	if (entr->port_map == 0) /* zero means its free again */  		eth_zero_addr(entr->mac_addr);  	lan9303_alr_set_entry(chip, mac, entr->port_map, entr->stp_override); +out: +	mutex_unlock(&chip->alr_mutex);  	return 0;  } @@ -818,18 +817,16 @@ static void lan9303_bridge_ports(struct lan9303 *chip)  	lan9303_alr_add_port(chip, eth_stp_addr, 0, true);  } -static int lan9303_handle_reset(struct lan9303 *chip) +static void lan9303_handle_reset(struct lan9303 *chip)  {  	if (!chip->reset_gpio) -		return 0; +		return;  	if (chip->reset_duration != 0)  		msleep(chip->reset_duration);  	/* release (deassert) reset and activate the device */  	gpiod_set_value_cansleep(chip->reset_gpio, 0); - -	return 0;  }  /* stop processing packets for all ports */ @@ -866,7 +863,7 @@ static int lan9303_check_device(struct lan9303 *chip)  	if ((reg >> 16) != LAN9303_CHIP_ID) {  		dev_err(chip->dev, "expecting LAN9303 chip, but found: %X\n",  			reg >> 16); -		return ret; +		return -ENODEV;  	}  	/* The default state of the LAN9303 device is to forward packets between @@ -1018,7 +1015,7 @@ static int lan9303_get_sset_count(struct dsa_switch *ds)  static int lan9303_phy_read(struct dsa_switch *ds, int phy, int regnum)  {  	struct lan9303 *chip = ds->priv; -	int phy_base = chip->phy_addr_sel_strap; +	int phy_base = chip->phy_addr_base;  	if (phy == phy_base)  		return lan9303_virt_phy_reg_read(chip, regnum); @@ -1032,7 +1029,7 @@ static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum,  			     u16 val)  {  	struct lan9303 *chip = ds->priv; -	int phy_base = chip->phy_addr_sel_strap; +	int phy_base = chip->phy_addr_base;  	if (phy == phy_base)  		return lan9303_virt_phy_reg_write(chip, regnum, val); @@ -1069,7 +1066,7 @@ static void lan9303_adjust_link(struct dsa_switch *ds, int port,  	res =  lan9303_phy_write(ds, port, MII_BMCR, ctl); -	if (port == chip->phy_addr_sel_strap) { +	if (port == chip->phy_addr_base) {  		/* Virtual Phy: Remove Turbo 200Mbit mode */  		lan9303_read(chip->regmap, LAN9303_VIRT_SPECIAL_CTRL, &ctl); @@ -1093,8 +1090,7 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port,  	struct lan9303 *chip = ds->priv;  	lan9303_disable_processing_port(chip, port); -	lan9303_phy_write(ds, chip->phy_addr_sel_strap + port, -			  MII_BMCR, BMCR_PDOWN); +	lan9303_phy_write(ds, chip->phy_addr_base + port, MII_BMCR, BMCR_PDOWN);  }  static int lan9303_port_bridge_join(struct dsa_switch *ds, int port, @@ -1217,8 +1213,7 @@ static int lan9303_port_fdb_dump(struct dsa_switch *ds, int port,  }  static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port, -				    const struct switchdev_obj_port_mdb *mdb, -				    struct switchdev_trans *trans) +				    const struct switchdev_obj_port_mdb *mdb)  {  	struct lan9303 *chip = ds->priv; @@ -1235,8 +1230,7 @@ static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port,  }  static void lan9303_port_mdb_add(struct dsa_switch *ds, int port, -				 const struct switchdev_obj_port_mdb *mdb, -				 struct switchdev_trans *trans) +				 const struct switchdev_obj_port_mdb *mdb)  {  	struct lan9303 *chip = ds->priv; @@ -1284,26 +1278,31 @@ static const struct dsa_switch_ops lan9303_switch_ops = {  static int lan9303_register_switch(struct lan9303 *chip)  { +	int base; +  	chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS);  	if (!chip->ds)  		return -ENOMEM;  	chip->ds->priv = chip;  	chip->ds->ops = &lan9303_switch_ops; -	chip->ds->phys_mii_mask = chip->phy_addr_sel_strap ? 0xe : 0x7; +	base = chip->phy_addr_base; +	chip->ds->phys_mii_mask = GENMASK(LAN9303_NUM_PORTS - 1 + base, base);  	return dsa_register_switch(chip->ds);  } -static void lan9303_probe_reset_gpio(struct lan9303 *chip, +static int lan9303_probe_reset_gpio(struct lan9303 *chip,  				     struct device_node *np)  {  	chip->reset_gpio = devm_gpiod_get_optional(chip->dev, "reset",  						   GPIOD_OUT_LOW); +	if (IS_ERR(chip->reset_gpio)) +		return PTR_ERR(chip->reset_gpio); -	if (IS_ERR(chip->reset_gpio)) { +	if (!chip->reset_gpio) {  		dev_dbg(chip->dev, "No reset GPIO defined\n"); -		return; +		return 0;  	}  	chip->reset_duration = 200; @@ -1318,6 +1317,8 @@ static void lan9303_probe_reset_gpio(struct lan9303 *chip,  	/* A sane reset duration should not be longer than 1s */  	if (chip->reset_duration > 1000)  		chip->reset_duration = 1000; + +	return 0;  }  int lan9303_probe(struct lan9303 *chip, struct device_node *np) @@ -1325,13 +1326,14 @@ int lan9303_probe(struct lan9303 *chip, struct device_node *np)  	int ret;  	mutex_init(&chip->indirect_mutex); +	mutex_init(&chip->alr_mutex); -	lan9303_probe_reset_gpio(chip, np); - -	ret = lan9303_handle_reset(chip); +	ret = lan9303_probe_reset_gpio(chip, np);  	if (ret)  		return ret; +	lan9303_handle_reset(chip); +  	ret = lan9303_check_device(chip);  	if (ret)  		return ret; diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b5be93a1e0df..663b0d5b982b 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -559,8 +559,7 @@ static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag)  }  static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port, -				 const struct switchdev_obj_port_vlan *vlan, -				 struct switchdev_trans *trans) +				 const struct switchdev_obj_port_vlan *vlan)  {  	/* nothing needed */ @@ -568,8 +567,7 @@ static int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,  }  static void ksz_port_vlan_add(struct dsa_switch *ds, int port, -			      const struct switchdev_obj_port_vlan *vlan, -			      struct switchdev_trans *trans) +			      const struct switchdev_obj_port_vlan *vlan)  {  	struct ksz_device *dev = ds->priv;  	u32 vlan_table[3]; @@ -858,16 +856,14 @@ exit:  }  static int ksz_port_mdb_prepare(struct dsa_switch *ds, int port, -				const struct switchdev_obj_port_mdb *mdb, -				struct switchdev_trans *trans) +				const struct switchdev_obj_port_mdb *mdb)  {  	/* nothing to do */  	return 0;  }  static void ksz_port_mdb_add(struct dsa_switch *ds, int port, -			     const struct switchdev_obj_port_mdb *mdb, -			     struct switchdev_trans *trans) +			     const struct switchdev_obj_port_mdb *mdb)  {  	struct ksz_device *dev = ds->priv;  	u32 static_table[4]; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 2820d69810b3..8a0bb000d056 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -805,6 +805,69 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,  }  static void +mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port) +{ +	struct mt7530_priv *priv = ds->priv; +	bool all_user_ports_removed = true; +	int i; + +	/* 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 +	 * port. +	 */ +	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, +		   MT7530_PORT_MATRIX_MODE); +	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK, +		   VLAN_ATTR(MT7530_VLAN_TRANSPARENT)); + +	priv->ports[port].vlan_filtering = false; + +	for (i = 0; i < MT7530_NUM_PORTS; i++) { +		if (dsa_is_user_port(ds, i) && +		    priv->ports[i].vlan_filtering) { +			all_user_ports_removed = false; +			break; +		} +	} + +	/* CPU port also does the same thing until all user ports belonging to +	 * the CPU port get out of VLAN filtering mode. +	 */ +	if (all_user_ports_removed) { +		mt7530_write(priv, MT7530_PCR_P(MT7530_CPU_PORT), +			     PCR_MATRIX(dsa_user_ports(priv->ds))); +		mt7530_write(priv, MT7530_PVC_P(MT7530_CPU_PORT), +			     PORT_SPEC_TAG); +	} +} + +static void +mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port) +{ +	struct mt7530_priv *priv = ds->priv; + +	/* The real fabric path would be decided on the membership in the +	 * entry of VLAN table. PCR_MATRIX set up here with ALL_MEMBERS +	 * means potential VLAN can be consisting of certain subset of all +	 * ports. +	 */ +	mt7530_rmw(priv, MT7530_PCR_P(port), +		   PCR_MATRIX_MASK, PCR_MATRIX(MT7530_ALL_MEMBERS)); + +	/* Trapped into security mode allows packet forwarding through VLAN +	 * table lookup. +	 */ +	mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, +		   MT7530_PORT_SECURITY_MODE); + +	/* Set the port as a user port which is to be able to recognize VID +	 * from incoming packets before fetching entry within the VLAN table. +	 */ +	mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK, +		   VLAN_ATTR(MT7530_VLAN_USER)); +} + +static void  mt7530_port_bridge_leave(struct dsa_switch *ds, int port,  			 struct net_device *bridge)  { @@ -817,8 +880,11 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,  		/* 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. +		 * And the other port's port matrix cannot be broken when the +		 * other port is still a VLAN-aware port.  		 */ -		if (dsa_is_user_port(ds, i) && i != port) { +		if (!priv->ports[i].vlan_filtering && +		    dsa_is_user_port(ds, i) && i != port) {  			if (dsa_to_port(ds, i)->bridge_dev != bridge)  				continue;  			if (priv->ports[i].enable) @@ -836,6 +902,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,  			   PCR_MATRIX(BIT(MT7530_CPU_PORT)));  	priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT)); +	mt7530_port_set_vlan_unaware(ds, port); +  	mutex_unlock(&priv->reg_mutex);  } @@ -906,6 +974,220 @@ err:  	return 0;  } +static int +mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) +{ +	struct mt7530_dummy_poll p; +	u32 val; +	int ret; + +	val = VTCR_BUSY | VTCR_FUNC(cmd) | vid; +	mt7530_write(priv, MT7530_VTCR, val); + +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR); +	ret = readx_poll_timeout(_mt7530_read, &p, val, +				 !(val & VTCR_BUSY), 20, 20000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		return ret; +	} + +	val = mt7530_read(priv, MT7530_VTCR); +	if (val & VTCR_INVALID) { +		dev_err(priv->dev, "read VTCR invalid\n"); +		return -EINVAL; +	} + +	return 0; +} + +static int +mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, +			   bool vlan_filtering) +{ +	struct mt7530_priv *priv = ds->priv; + +	priv->ports[port].vlan_filtering = vlan_filtering; + +	if (vlan_filtering) { +		/* The port is being kept as VLAN-unaware port when bridge is +		 * set up with vlan_filtering not being set, Otherwise, the +		 * port and the corresponding CPU port is required the setup +		 * for becoming a VLAN-aware port. +		 */ +		mt7530_port_set_vlan_aware(ds, port); +		mt7530_port_set_vlan_aware(ds, MT7530_CPU_PORT); +	} + +	return 0; +} + +static int +mt7530_port_vlan_prepare(struct dsa_switch *ds, int port, +			 const struct switchdev_obj_port_vlan *vlan) +{ +	/* nothing needed */ + +	return 0; +} + +static void +mt7530_hw_vlan_add(struct mt7530_priv *priv, +		   struct mt7530_hw_vlan_entry *entry) +{ +	u8 new_members; +	u32 val; + +	new_members = entry->old_members | BIT(entry->port) | +		      BIT(MT7530_CPU_PORT); + +	/* Validate the entry with independent learning, create egress tag per +	 * VLAN and joining the port as one of the port members. +	 */ +	val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID; +	mt7530_write(priv, MT7530_VAWD1, val); + +	/* Decide whether adding tag or not for those outgoing packets from the +	 * port inside the VLAN. +	 */ +	val = entry->untagged ? MT7530_VLAN_EGRESS_UNTAG : +				MT7530_VLAN_EGRESS_TAG; +	mt7530_rmw(priv, MT7530_VAWD2, +		   ETAG_CTRL_P_MASK(entry->port), +		   ETAG_CTRL_P(entry->port, val)); + +	/* CPU port is always taken as a tagged port for serving more than one +	 * VLANs across and also being applied with egress type stack mode for +	 * that VLAN tags would be appended after hardware special tag used as +	 * DSA tag. +	 */ +	mt7530_rmw(priv, MT7530_VAWD2, +		   ETAG_CTRL_P_MASK(MT7530_CPU_PORT), +		   ETAG_CTRL_P(MT7530_CPU_PORT, +			       MT7530_VLAN_EGRESS_STACK)); +} + +static void +mt7530_hw_vlan_del(struct mt7530_priv *priv, +		   struct mt7530_hw_vlan_entry *entry) +{ +	u8 new_members; +	u32 val; + +	new_members = entry->old_members & ~BIT(entry->port); + +	val = mt7530_read(priv, MT7530_VAWD1); +	if (!(val & VLAN_VALID)) { +		dev_err(priv->dev, +			"Cannot be deleted due to invalid entry\n"); +		return; +	} + +	/* If certain member apart from CPU port is still alive in the VLAN, +	 * the entry would be kept valid. Otherwise, the entry is got to be +	 * disabled. +	 */ +	if (new_members && new_members != BIT(MT7530_CPU_PORT)) { +		val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | +		      VLAN_VALID; +		mt7530_write(priv, MT7530_VAWD1, val); +	} else { +		mt7530_write(priv, MT7530_VAWD1, 0); +		mt7530_write(priv, MT7530_VAWD2, 0); +	} +} + +static void +mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid, +		      struct mt7530_hw_vlan_entry *entry, +		      mt7530_vlan_op vlan_op) +{ +	u32 val; + +	/* Fetch entry */ +	mt7530_vlan_cmd(priv, MT7530_VTCR_RD_VID, vid); + +	val = mt7530_read(priv, MT7530_VAWD1); + +	entry->old_members = (val >> PORT_MEM_SHFT) & PORT_MEM_MASK; + +	/* Manipulate entry */ +	vlan_op(priv, entry); + +	/* Flush result to hardware */ +	mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid); +} + +static void +mt7530_port_vlan_add(struct dsa_switch *ds, int port, +		     const struct switchdev_obj_port_vlan *vlan) +{ +	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; +	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +	struct mt7530_hw_vlan_entry new_entry; +	struct mt7530_priv *priv = ds->priv; +	u16 vid; + +	/* The port is kept as VLAN-unaware if bridge with vlan_filtering not +	 * being set. +	 */ +	if (!priv->ports[port].vlan_filtering) +		return; + +	mutex_lock(&priv->reg_mutex); + +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { +		mt7530_hw_vlan_entry_init(&new_entry, port, untagged); +		mt7530_hw_vlan_update(priv, vid, &new_entry, +				      mt7530_hw_vlan_add); +	} + +	if (pvid) { +		mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, +			   G0_PORT_VID(vlan->vid_end)); +		priv->ports[port].pvid = vlan->vid_end; +	} + +	mutex_unlock(&priv->reg_mutex); +} + +static int +mt7530_port_vlan_del(struct dsa_switch *ds, int port, +		     const struct switchdev_obj_port_vlan *vlan) +{ +	struct mt7530_hw_vlan_entry target_entry; +	struct mt7530_priv *priv = ds->priv; +	u16 vid, pvid; + +	/* The port is kept as VLAN-unaware if bridge with vlan_filtering not +	 * being set. +	 */ +	if (!priv->ports[port].vlan_filtering) +		return 0; + +	mutex_lock(&priv->reg_mutex); + +	pvid = priv->ports[port].pvid; +	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { +		mt7530_hw_vlan_entry_init(&target_entry, port, 0); +		mt7530_hw_vlan_update(priv, vid, &target_entry, +				      mt7530_hw_vlan_del); + +		/* PVID is being restored to the default whenever the PVID port +		 * is being removed from the VLAN. +		 */ +		if (pvid == vid) +			pvid = G0_PORT_VID_DEF; +	} + +	mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid); +	priv->ports[port].pvid = pvid; + +	mutex_unlock(&priv->reg_mutex); + +	return 0; +} +  static enum dsa_tag_protocol  mtk_get_tag_protocol(struct dsa_switch *ds, int port)  { @@ -1035,6 +1317,10 @@ static const struct dsa_switch_ops mt7530_switch_ops = {  	.port_fdb_add		= mt7530_port_fdb_add,  	.port_fdb_del		= mt7530_port_fdb_del,  	.port_fdb_dump		= mt7530_port_fdb_dump, +	.port_vlan_filtering	= mt7530_port_vlan_filtering, +	.port_vlan_prepare	= mt7530_port_vlan_prepare, +	.port_vlan_add		= mt7530_port_vlan_add, +	.port_vlan_del		= mt7530_port_vlan_del,  };  static int diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 74db9822eb40..d9b407a22a58 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -17,6 +17,7 @@  #define MT7530_NUM_PORTS		7  #define MT7530_CPU_PORT			6  #define MT7530_NUM_FDB_RECORDS		2048 +#define MT7530_ALL_MEMBERS		0xff  #define	NUM_TRGMII_CTRL			5 @@ -88,21 +89,42 @@ enum mt7530_fdb_cmd {  /* Register for vlan table control */  #define MT7530_VTCR			0x90  #define  VTCR_BUSY			BIT(31) -#define  VTCR_FUNC			(((x) & 0xf) << 12) -#define  VTCR_FUNC_RD_VID		0x1 -#define  VTCR_FUNC_WR_VID		0x2 -#define  VTCR_FUNC_INV_VID		0x3 -#define  VTCR_FUNC_VAL_VID		0x4 +#define  VTCR_INVALID			BIT(16) +#define  VTCR_FUNC(x)			(((x) & 0xf) << 12)  #define  VTCR_VID			((x) & 0xfff) +enum mt7530_vlan_cmd { +	/* Read/Write the specified VID entry from VAWD register based +	 * on VID. +	 */ +	MT7530_VTCR_RD_VID = 0, +	MT7530_VTCR_WR_VID = 1, +}; +  /* Register for setup vlan and acl write data */  #define MT7530_VAWD1			0x94  #define  PORT_STAG			BIT(31) +/* Independent VLAN Learning */  #define  IVL_MAC			BIT(30) +/* Per VLAN Egress Tag Control */ +#define  VTAG_EN			BIT(28) +/* VLAN Member Control */  #define  PORT_MEM(x)			(((x) & 0xff) << 16) -#define  VALID				BIT(1) +/* VLAN Entry Valid */ +#define  VLAN_VALID			BIT(0) +#define  PORT_MEM_SHFT			16 +#define  PORT_MEM_MASK			0xff  #define MT7530_VAWD2			0x98 +/* Egress Tag Control */ +#define  ETAG_CTRL_P(p, x)		(((x) & 0x3) << ((p) << 1)) +#define  ETAG_CTRL_P_MASK(p)		ETAG_CTRL_P(p, 3) + +enum mt7530_vlan_egress_attr { +	MT7530_VLAN_EGRESS_UNTAG = 0, +	MT7530_VLAN_EGRESS_TAG = 2, +	MT7530_VLAN_EGRESS_STACK = 3, +};  /* Register for port STP state control */  #define MT7530_SSP_P(x)			(0x2000 + ((x) * 0x100)) @@ -120,11 +142,23 @@ enum mt7530_stp_state {  /* Register for port control */  #define MT7530_PCR_P(x)			(0x2004 + ((x) * 0x100))  #define  PORT_VLAN(x)			((x) & 0x3) + +enum mt7530_port_mode { +	/* Port Matrix Mode: Frames are forwarded by the PCR_MATRIX members. */ +	MT7530_PORT_MATRIX_MODE = PORT_VLAN(0), + +	/* Security Mode: Discard any frame due to ingress membership +	 * violation or VID missed on the VLAN table. +	 */ +	MT7530_PORT_SECURITY_MODE = PORT_VLAN(3), +}; +  #define  PCR_MATRIX(x)			(((x) & 0xff) << 16)  #define  PORT_PRI(x)			(((x) & 0x7) << 24)  #define  EG_TAG(x)			(((x) & 0x3) << 28)  #define  PCR_MATRIX_MASK		PCR_MATRIX(0xff)  #define  PCR_MATRIX_CLR			PCR_MATRIX(0) +#define  PCR_PORT_VLAN_MASK		PORT_VLAN(3)  /* Register for port security control */  #define MT7530_PSC_P(x)			(0x200c + ((x) * 0x100)) @@ -134,10 +168,20 @@ enum mt7530_stp_state {  #define MT7530_PVC_P(x)			(0x2010 + ((x) * 0x100))  #define  PORT_SPEC_TAG			BIT(5)  #define  VLAN_ATTR(x)			(((x) & 0x3) << 6) +#define  VLAN_ATTR_MASK			VLAN_ATTR(3) + +enum mt7530_vlan_port_attr { +	MT7530_VLAN_USER = 0, +	MT7530_VLAN_TRANSPARENT = 3, +}; +  #define  STAG_VPID			(((x) & 0xffff) << 16)  /* Register for port port-and-protocol based vlan 1 control */  #define MT7530_PPBV1_P(x)		(0x2014 + ((x) * 0x100)) +#define  G0_PORT_VID(x)			(((x) & 0xfff) << 0) +#define  G0_PORT_VID_MASK		G0_PORT_VID(0xfff) +#define  G0_PORT_VID_DEF		G0_PORT_VID(1)  /* Register for port MAC control register */  #define MT7530_PMCR_P(x)		(0x3000 + ((x) * 0x100)) @@ -345,9 +389,20 @@ struct mt7530_fdb {  	bool noarp;  }; +/* struct mt7530_port -	This is the main data structure for holding the state + *			of the port. + * @enable:	The status used for show port is enabled or not. + * @pm:		The matrix used to show all connections with the port. + * @pvid:	The VLAN specified is to be considered a PVID at ingress.  Any + *		untagged frames will be assigned to the related VLAN. + * @vlan_filtering: The flags indicating whether the port that can recognize + *		    VLAN-tagged frames. + */  struct mt7530_port {  	bool enable;  	u32 pm; +	u16 pvid; +	bool vlan_filtering;  };  /* struct mt7530_priv -	This is the main data structure for holding the state @@ -382,6 +437,22 @@ struct mt7530_priv {  	struct mutex reg_mutex;  }; +struct mt7530_hw_vlan_entry { +	int port; +	u8  old_members; +	bool untagged; +}; + +static inline void mt7530_hw_vlan_entry_init(struct mt7530_hw_vlan_entry *e, +					     int port, bool untagged) +{ +	e->port = port; +	e->untagged = untagged; +} + +typedef void (*mt7530_vlan_op)(struct mt7530_priv *, +			       struct mt7530_hw_vlan_entry *); +  struct mt7530_hw_stats {  	const char	*string;  	u16		reg; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 66d33e97cbc5..eb328bade225 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1185,8 +1185,7 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,  static int  mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, -			    const struct switchdev_obj_port_vlan *vlan, -			    struct switchdev_trans *trans) +			    const struct switchdev_obj_port_vlan *vlan)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	int err; @@ -1295,8 +1294,7 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,  }  static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, -				    const struct switchdev_obj_port_vlan *vlan, -				    struct switchdev_trans *trans) +				    const struct switchdev_obj_port_vlan *vlan)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; @@ -1725,9 +1723,11 @@ static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port)  static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)  { -	bool flood = port == dsa_upstream_port(chip->ds); +	struct dsa_switch *ds = chip->ds; +	bool flood;  	/* Upstream ports flood frames with unknown unicast or multicast DA */ +	flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);  	if (chip->info->ops->port_set_egress_floods)  		return chip->info->ops->port_set_egress_floods(chip, port,  							       flood, flood); @@ -1744,6 +1744,39 @@ static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,  	return 0;  } +static int mv88e6xxx_setup_upstream_port(struct mv88e6xxx_chip *chip, int port) +{ +	struct dsa_switch *ds = chip->ds; +	int upstream_port; +	int err; + +	upstream_port = dsa_upstream_port(ds, port); +	if (chip->info->ops->port_set_upstream_port) { +		err = chip->info->ops->port_set_upstream_port(chip, port, +							      upstream_port); +		if (err) +			return err; +	} + +	if (port == upstream_port) { +		if (chip->info->ops->set_cpu_port) { +			err = chip->info->ops->set_cpu_port(chip, +							    upstream_port); +			if (err) +				return err; +		} + +		if (chip->info->ops->set_egress_port) { +			err = chip->info->ops->set_egress_port(chip, +							       upstream_port); +			if (err) +				return err; +		} +	} + +	return 0; +} +  static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  {  	struct dsa_switch *ds = chip->ds; @@ -1814,13 +1847,9 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	if (err)  		return err; -	reg = 0; -	if (chip->info->ops->port_set_upstream_port) { -		err = chip->info->ops->port_set_upstream_port( -			chip, port, dsa_upstream_port(ds)); -		if (err) -			return err; -	} +	err = mv88e6xxx_setup_upstream_port(chip, port); +	if (err) +		return err;  	err = mv88e6xxx_port_set_8021q_mode(chip, port,  				MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED); @@ -1946,21 +1975,8 @@ static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,  static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)  {  	struct dsa_switch *ds = chip->ds; -	u32 upstream_port = dsa_upstream_port(ds);  	int err; -	if (chip->info->ops->set_cpu_port) { -		err = chip->info->ops->set_cpu_port(chip, upstream_port); -		if (err) -			return err; -	} - -	if (chip->info->ops->set_egress_port) { -		err = chip->info->ops->set_egress_port(chip, upstream_port); -		if (err) -			return err; -	} -  	/* Disable remote management, and set the switch's DSA device number. */  	err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL2,  				 MV88E6XXX_G1_CTL2_MULTIPLE_CASCADE | @@ -3741,6 +3757,7 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,  	return chip->info->tag_protocol;  } +#if IS_ENABLED(CONFIG_NET_DSA_LEGACY)  static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,  				       struct device *host_dev, int sw_addr,  				       void **priv) @@ -3788,10 +3805,10 @@ free:  	return NULL;  } +#endif  static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port, -				      const struct switchdev_obj_port_mdb *mdb, -				      struct switchdev_trans *trans) +				      const struct switchdev_obj_port_mdb *mdb)  {  	/* We don't need any dynamic resource from the kernel (yet),  	 * so skip the prepare phase. @@ -3801,8 +3818,7 @@ static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,  }  static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, -				   const struct switchdev_obj_port_mdb *mdb, -				   struct switchdev_trans *trans) +				   const struct switchdev_obj_port_mdb *mdb)  {  	struct mv88e6xxx_chip *chip = ds->priv; @@ -3829,7 +3845,9 @@ static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,  }  static const struct dsa_switch_ops mv88e6xxx_switch_ops = { +#if IS_ENABLED(CONFIG_NET_DSA_LEGACY)  	.probe			= mv88e6xxx_drv_probe, +#endif  	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,  	.setup			= mv88e6xxx_setup,  	.adjust_link		= mv88e6xxx_adjust_link, @@ -3958,11 +3976,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)  			if (err)  				goto out_g1_irq;  		} + +		err = mv88e6xxx_g1_atu_prob_irq_setup(chip); +		if (err) +			goto out_g2_irq; + +		err = mv88e6xxx_g1_vtu_prob_irq_setup(chip); +		if (err) +			goto out_g1_atu_prob_irq;  	}  	err = mv88e6xxx_mdios_register(chip, np);  	if (err) -		goto out_g2_irq; +		goto out_g1_vtu_prob_irq;  	err = mv88e6xxx_register_switch(chip);  	if (err) @@ -3972,6 +3998,12 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)  out_mdio:  	mv88e6xxx_mdios_unregister(chip); +out_g1_vtu_prob_irq: +	if (chip->irq > 0) +		mv88e6xxx_g1_vtu_prob_irq_free(chip); +out_g1_atu_prob_irq: +	if (chip->irq > 0) +		mv88e6xxx_g1_atu_prob_irq_free(chip);  out_g2_irq:  	if (chip->info->g2_irqs > 0 && chip->irq > 0)  		mv88e6xxx_g2_irq_free(chip); @@ -3995,6 +4027,8 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)  	mv88e6xxx_mdios_unregister(chip);  	if (chip->irq > 0) { +		mv88e6xxx_g1_vtu_prob_irq_free(chip); +		mv88e6xxx_g1_atu_prob_irq_free(chip);  		if (chip->info->g2_irqs > 0)  			mv88e6xxx_g2_irq_free(chip);  		mutex_lock(&chip->reg_lock); diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 334f6f7544ba..3dba6e90adcf 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -207,6 +207,8 @@ struct mv88e6xxx_chip {  	int irq;  	int device_irq;  	int watchdog_irq; +	int atu_prob_irq; +	int vtu_prob_irq;  };  struct mv88e6xxx_bus_ops { diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index b0dc7518b47f..6aee7316fea6 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -29,9 +29,9 @@  #define MV88E6XXX_G1_STS_IRQ_AVB			8  #define MV88E6XXX_G1_STS_IRQ_DEVICE			7  #define MV88E6XXX_G1_STS_IRQ_STATS			6 -#define MV88E6XXX_G1_STS_IRQ_VTU_PROBLEM		5 +#define MV88E6XXX_G1_STS_IRQ_VTU_PROB			5  #define MV88E6XXX_G1_STS_IRQ_VTU_DONE			4 -#define MV88E6XXX_G1_STS_IRQ_ATU_PROBLEM		3 +#define MV88E6XXX_G1_STS_IRQ_ATU_PROB			3  #define MV88E6XXX_G1_STS_IRQ_ATU_DONE			2  #define MV88E6XXX_G1_STS_IRQ_TCAM_DONE			1  #define MV88E6XXX_G1_STS_IRQ_EEPROM_DONE		0 @@ -82,6 +82,10 @@  #define MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT	0x4000  #define MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE	0x5000  #define MV88E6XXX_G1_VTU_OP_STU_GET_NEXT	0x6000 +#define MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION	0x7000 +#define MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION	BIT(6) +#define MV88E6XXX_G1_VTU_OP_MISS_VIOLATION	BIT(5) +#define MV88E6XXX_G1_VTU_OP_SPID_MASK		0xf  /* Offset 0x06: VTU VID Register */  #define MV88E6XXX_G1_VTU_VID		0x06 @@ -122,6 +126,10 @@  #define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB		0x5000  #define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB	0x6000  #define MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION		0x7000 +#define MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION		BIT(7) +#define MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION		BIT(6) +#define MV88E6XXX_G1_ATU_OP_MISS_VIOLTATION		BIT(5) +#define MV88E6XXX_G1_ATU_OP_FULL_VIOLATION		BIT(4)  /* Offset 0x0C: ATU Data Register */  #define MV88E6XXX_G1_ATU_DATA				0x0c @@ -255,6 +263,8 @@ int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,  int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all);  int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,  			    bool all); +int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g1_atu_prob_irq_free(struct mv88e6xxx_chip *chip);  int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,  			     struct mv88e6xxx_vtu_entry *entry); @@ -269,5 +279,7 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,  int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,  			       struct mv88e6xxx_vtu_entry *entry);  int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip);  #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index efeef4b01442..20d941f4273b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -9,6 +9,8 @@   * the Free Software Foundation; either version 2 of the License, or   * (at your option) any later version.   */ +#include <linux/interrupt.h> +#include <linux/irqdomain.h>  #include "chip.h"  #include "global1.h" @@ -307,3 +309,88 @@ int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,  	return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);  } + +static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id) +{ +	struct mv88e6xxx_chip *chip = dev_id; +	struct mv88e6xxx_atu_entry entry; +	int err; +	u16 val; + +	mutex_lock(&chip->reg_lock); + +	err = mv88e6xxx_g1_atu_op(chip, 0, +				  MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION); +	if (err) +		goto out; + +	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &val); +	if (err) +		goto out; + +	err = mv88e6xxx_g1_atu_data_read(chip, &entry); +	if (err) +		goto out; + +	err = mv88e6xxx_g1_atu_mac_read(chip, &entry); +	if (err) +		goto out; + +	mutex_unlock(&chip->reg_lock); + +	if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) { +		dev_err_ratelimited(chip->dev, +				    "ATU age out violation for %pM\n", +				    entry.mac); +	} + +	if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) { +		dev_err_ratelimited(chip->dev, +				    "ATU member violation for %pM portvec %x\n", +				    entry.mac, entry.portvec); +	} + +	if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) +		dev_err_ratelimited(chip->dev, +				    "ATU miss violation for %pM portvec %x\n", +				    entry.mac, entry.portvec); + +	if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION) +		dev_err_ratelimited(chip->dev, +				    "ATU full violation for %pM portvec %x\n", +				    entry.mac, entry.portvec); + +	return IRQ_HANDLED; + +out: +	mutex_unlock(&chip->reg_lock); + +	dev_err(chip->dev, "ATU problem: error %d while handling interrupt\n", +		err); +	return IRQ_HANDLED; +} + +int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip *chip) +{ +	int err; + +	chip->atu_prob_irq = irq_find_mapping(chip->g1_irq.domain, +					      MV88E6XXX_G1_STS_IRQ_ATU_PROB); +	if (chip->atu_prob_irq < 0) +		return chip->atu_prob_irq; + +	err = request_threaded_irq(chip->atu_prob_irq, NULL, +				   mv88e6xxx_g1_atu_prob_irq_thread_fn, +				   IRQF_ONESHOT, "mv88e6xxx-g1-atu-prob", +				   chip); +	if (err) +		irq_dispose_mapping(chip->atu_prob_irq); + +	return err; +} + +void mv88e6xxx_g1_atu_prob_irq_free(struct mv88e6xxx_chip *chip) +{ +	free_irq(chip->atu_prob_irq, chip); +	irq_dispose_mapping(chip->atu_prob_irq); +} diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 8c8a0ec3d6e9..7997961647de 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -11,6 +11,9 @@   * (at your option) any later version.   */ +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +  #include "chip.h"  #include "global1.h" @@ -513,3 +516,74 @@ int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)  	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);  } + +static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) +{ +	struct mv88e6xxx_chip *chip = dev_id; +	struct mv88e6xxx_vtu_entry entry; +	int spid; +	int err; +	u16 val; + +	mutex_lock(&chip->reg_lock); + +	err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION); +	if (err) +		goto out; + +	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); +	if (err) +		goto out; + +	err = mv88e6xxx_g1_vtu_vid_read(chip, &entry); +	if (err) +		goto out; + +	mutex_unlock(&chip->reg_lock); + +	spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; + +	if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { +		dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", +				    entry.vid, spid); +	} + +	if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) +		dev_err_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", +				    entry.vid, spid); + +	return IRQ_HANDLED; + +out: +	mutex_unlock(&chip->reg_lock); + +	dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n", +		err); + +	return IRQ_HANDLED; +} + +int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip) +{ +	int err; + +	chip->vtu_prob_irq = irq_find_mapping(chip->g1_irq.domain, +					      MV88E6XXX_G1_STS_IRQ_VTU_PROB); +	if (chip->vtu_prob_irq < 0) +		return chip->vtu_prob_irq; + +	err = request_threaded_irq(chip->vtu_prob_irq, NULL, +				   mv88e6xxx_g1_vtu_prob_irq_thread_fn, +				   IRQF_ONESHOT, "mv88e6xxx-g1-vtu-prob", +				   chip); +	if (err) +		irq_dispose_mapping(chip->vtu_prob_irq); + +	return err; +} + +void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip) +{ +	free_irq(chip->vtu_prob_irq, chip); +	irq_dispose_mapping(chip->vtu_prob_irq); +} |