diff options
Diffstat (limited to 'drivers/net/dsa')
34 files changed, 2214 insertions, 818 deletions
| diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index af4761968733..3867f3d4545f 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1860,7 +1860,8 @@ int b53_mdb_del(struct dsa_switch *ds, int port,  }  EXPORT_SYMBOL(b53_mdb_del); -int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) +int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, +		bool *tx_fwd_offload)  {  	struct b53_device *dev = ds->priv;  	s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index; @@ -1887,7 +1888,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)  	b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);  	b53_for_each_port(dev, i) { -		if (dsa_to_port(ds, i)->bridge_dev != br) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		/* Add this local port to the remote port VLAN control @@ -1911,7 +1912,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)  }  EXPORT_SYMBOL(b53_br_join); -void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) +void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge)  {  	struct b53_device *dev = ds->priv;  	struct b53_vlan *vl = &dev->vlans[0]; @@ -1923,7 +1924,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br)  	b53_for_each_port(dev, i) {  		/* Don't touch the remaining ports */ -		if (dsa_to_port(ds, i)->bridge_dev != br) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 579da74ada64..b41dc8ac2ca8 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -324,8 +324,9 @@ void b53_get_strings(struct dsa_switch *ds, int port, u32 stringset,  void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);  int b53_get_sset_count(struct dsa_switch *ds, int port, int sset);  void b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data); -int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge); -void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge); +int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge, +		bool *tx_fwd_offload); +void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge);  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_br_flags_pre(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 13aa43b5cffd..33499fcd8848 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -62,6 +62,38 @@ static u16 bcm_sf2_reg_rgmii_cntrl(struct bcm_sf2_priv *priv, int port)  	return REG_SWITCH_STATUS;  } +static u16 bcm_sf2_reg_led_base(struct bcm_sf2_priv *priv, int port) +{ +	switch (port) { +	case 0: +		return REG_LED_0_CNTRL; +	case 1: +		return REG_LED_1_CNTRL; +	case 2: +		return REG_LED_2_CNTRL; +	} + +	switch (priv->type) { +	case BCM4908_DEVICE_ID: +		switch (port) { +		case 3: +			return REG_LED_3_CNTRL; +		case 7: +			return REG_LED_4_CNTRL; +		default: +			break; +		} +		break; +	default: +		break; +	} + +	WARN_ONCE(1, "Unsupported port %d\n", port); + +	/* RO fallback reg */ +	return REG_SWITCH_STATUS; +} +  /* Return the number of active ports, not counting the IMP (CPU) port */  static unsigned int bcm_sf2_num_active_ports(struct dsa_switch *ds)  { @@ -187,9 +219,14 @@ static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable)  	/* Use PHY-driven LED signaling */  	if (!enable) { -		reg = reg_readl(priv, REG_LED_CNTRL(0)); -		reg |= SPDLNK_SRC_SEL; -		reg_writel(priv, reg, REG_LED_CNTRL(0)); +		u16 led_ctrl = bcm_sf2_reg_led_base(priv, 0); + +		if (priv->type == BCM7278_DEVICE_ID || +		    priv->type == BCM7445_DEVICE_ID) { +			reg = reg_led_readl(priv, led_ctrl, 0); +			reg |= LED_CNTRL_SPDLNK_SRC_SEL; +			reg_led_writel(priv, reg, led_ctrl, 0); +		}  	}  } @@ -1232,9 +1269,14 @@ static const u16 bcm_sf2_4908_reg_offsets[] = {  	[REG_SPHY_CNTRL]	= 0x24,  	[REG_CROSSBAR]		= 0xc8,  	[REG_RGMII_11_CNTRL]	= 0x014c, -	[REG_LED_0_CNTRL]	= 0x40, -	[REG_LED_1_CNTRL]	= 0x4c, -	[REG_LED_2_CNTRL]	= 0x58, +	[REG_LED_0_CNTRL]		= 0x40, +	[REG_LED_1_CNTRL]		= 0x4c, +	[REG_LED_2_CNTRL]		= 0x58, +	[REG_LED_3_CNTRL]		= 0x64, +	[REG_LED_4_CNTRL]		= 0x88, +	[REG_LED_5_CNTRL]		= 0xa0, +	[REG_LED_AGGREGATE_CTRL]	= 0xb8, +  };  static const struct bcm_sf2_of_data bcm_sf2_4908_data = { diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index 0d48402068d3..00afc94ce522 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -210,6 +210,16 @@ SF2_IO_MACRO(acb);  SWITCH_INTR_L2(0);  SWITCH_INTR_L2(1); +static inline u32 reg_led_readl(struct bcm_sf2_priv *priv, u16 off, u16 reg) +{ +	return readl_relaxed(priv->reg + priv->reg_offsets[off] + reg); +} + +static inline void reg_led_writel(struct bcm_sf2_priv *priv, u32 val, u16 off, u16 reg) +{ +	writel_relaxed(val, priv->reg + priv->reg_offsets[off] + reg); +} +  /* RXNFC */  int bcm_sf2_get_rxnfc(struct dsa_switch *ds, int port,  		      struct ethtool_rxnfc *nfc, u32 *rule_locs); diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index 7bffc80f241f..da0dedbd6555 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -25,6 +25,10 @@ enum bcm_sf2_reg_offs {  	REG_LED_0_CNTRL,  	REG_LED_1_CNTRL,  	REG_LED_2_CNTRL, +	REG_LED_3_CNTRL, +	REG_LED_4_CNTRL, +	REG_LED_5_CNTRL, +	REG_LED_AGGREGATE_CTRL,  	REG_SWITCH_REG_MAX,  }; @@ -56,6 +60,63 @@ enum bcm_sf2_reg_offs {  #define CROSSBAR_BCM4908_EXT_GPHY4	1  #define CROSSBAR_BCM4908_EXT_RGMII	2 +/* Relative to REG_LED_*_CNTRL (BCM7278, BCM7445) */ +#define  LED_CNTRL_NO_LINK_ENCODE_SHIFT		0 +#define  LED_CNTRL_M10_ENCODE_SHIFT		2 +#define  LED_CNTRL_M100_ENCODE_SHIFT		4 +#define  LED_CNTRL_M1000_ENCODE_SHIFT		6 +#define  LED_CNTRL_SEL_NO_LINK_ENCODE_SHIFT	8 +#define  LED_CNTRL_SEL_10M_ENCODE_SHIFT		10 +#define  LED_CNTRL_SEL_100M_ENCODE_SHIFT	12 +#define  LED_CNTRL_SEL_1000M_ENCODE_SHIFT	14 +#define  LED_CNTRL_RX_DV_EN			(1 << 16) +#define  LED_CNTRL_TX_EN_EN			(1 << 17) +#define  LED_CNTRL_SPDLNK_LED0_ACT_SEL_SHIFT	18 +#define  LED_CNTRL_SPDLNK_LED1_ACT_SEL_SHIFT	20 +#define  LED_CNTRL_ACT_LED_ACT_SEL_SHIFT	22 +#define  LED_CNTRL_SPDLNK_SRC_SEL		(1 << 24) +#define  LED_CNTRL_SPDLNK_LED0_ACT_POL_SEL	(1 << 25) +#define  LED_CNTRL_SPDLNK_LED1_ACT_POL_SEL	(1 << 26) +#define  LED_CNTRL_ACT_LED_POL_SEL		(1 << 27) +#define  LED_CNTRL_MASK				0x3 + +/* Register relative to REG_LED_*_CNTRL (BCM4908) */ +#define REG_LED_CTRL				0x0 +#define  LED_CTRL_RX_ACT_EN			0x00000001 +#define  LED_CTRL_TX_ACT_EN			0x00000002 +#define  LED_CTRL_SPDLNK_LED0_ACT_SEL		0x00000004 +#define  LED_CTRL_SPDLNK_LED1_ACT_SEL		0x00000008 +#define  LED_CTRL_SPDLNK_LED2_ACT_SEL		0x00000010 +#define  LED_CTRL_ACT_LED_ACT_SEL		0x00000020 +#define  LED_CTRL_SPDLNK_LED0_ACT_POL_SEL	0x00000040 +#define  LED_CTRL_SPDLNK_LED1_ACT_POL_SEL	0x00000080 +#define  LED_CTRL_SPDLNK_LED2_ACT_POL_SEL	0x00000100 +#define  LED_CTRL_ACT_LED_POL_SEL		0x00000200 +#define  LED_CTRL_LED_SPD_OVRD			0x00001c00 +#define  LED_CTRL_LNK_STATUS_OVRD		0x00002000 +#define  LED_CTRL_SPD_OVRD_EN			0x00004000 +#define  LED_CTRL_LNK_OVRD_EN			0x00008000 + +/* Register relative to REG_LED_*_CNTRL (BCM4908) */ +#define REG_LED_LINK_SPEED_ENC_SEL		0x4 +#define  LED_LINK_SPEED_ENC_SEL_NO_LINK_SHIFT	0 +#define  LED_LINK_SPEED_ENC_SEL_10M_SHIFT	3 +#define  LED_LINK_SPEED_ENC_SEL_100M_SHIFT	6 +#define  LED_LINK_SPEED_ENC_SEL_1000M_SHIFT	9 +#define  LED_LINK_SPEED_ENC_SEL_2500M_SHIFT	12 +#define  LED_LINK_SPEED_ENC_SEL_10G_SHIFT	15 +#define  LED_LINK_SPEED_ENC_SEL_MASK		0x7 + +/* Register relative to REG_LED_*_CNTRL (BCM4908) */ +#define REG_LED_LINK_SPEED_ENC			0x8 +#define  LED_LINK_SPEED_ENC_NO_LINK_SHIFT	0 +#define  LED_LINK_SPEED_ENC_M10_SHIFT		3 +#define  LED_LINK_SPEED_ENC_M100_SHIFT		6 +#define  LED_LINK_SPEED_ENC_M1000_SHIFT		9 +#define  LED_LINK_SPEED_ENC_M2500_SHIFT		12 +#define  LED_LINK_SPEED_ENC_M10G_SHIFT		15 +#define  LED_LINK_SPEED_ENC_MASK		0x7 +  /* Relative to REG_RGMII_CNTRL */  #define  RGMII_MODE_EN			(1 << 0)  #define  ID_MODE_DIS			(1 << 1) @@ -73,10 +134,6 @@ enum bcm_sf2_reg_offs {  #define  LPI_COUNT_SHIFT		9  #define  LPI_COUNT_MASK			0x3F -#define REG_LED_CNTRL(x)		(REG_LED_0_CNTRL + (x)) - -#define  SPDLNK_SRC_SEL			(1 << 24) -  /* Register set relative to 'INTRL2_0' and 'INTRL2_1' */  #define INTRL2_CPU_STATUS		0x00  #define INTRL2_CPU_SET			0x04 diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index e638e3eea911..33daaf10c488 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -167,19 +167,20 @@ static int dsa_loop_phy_write(struct dsa_switch *ds, int port,  }  static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, -				     struct net_device *bridge) +				     struct dsa_bridge bridge, +				     bool *tx_fwd_offload)  {  	dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n", -		__func__, port, bridge->name); +		__func__, port, bridge.dev->name);  	return 0;  }  static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port, -				       struct net_device *bridge) +				       struct dsa_bridge bridge)  {  	dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n", -		__func__, port, bridge->name); +		__func__, port, bridge.dev->name);  }  static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 4e0b53d94b52..726f267cb228 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -674,7 +674,8 @@ static int hellcreek_bridge_flags(struct dsa_switch *ds, int port,  }  static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port, -				      struct net_device *br) +				      struct dsa_bridge bridge, +				      bool *tx_fwd_offload)  {  	struct hellcreek *hellcreek = ds->priv; @@ -691,7 +692,7 @@ static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,  }  static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port, -					struct net_device *br) +					struct dsa_bridge bridge)  {  	struct hellcreek *hellcreek = ds->priv; @@ -710,8 +711,9 @@ static int __hellcreek_fdb_add(struct hellcreek *hellcreek,  	u16 meta = 0;  	dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, " -		"OBT=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, entry->portmask, -		entry->is_obt, entry->reprio_en, entry->reprio_tc); +		"OBT=%d, PASS_BLOCKED=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac, +		entry->portmask, entry->is_obt, entry->pass_blocked, +		entry->reprio_en, entry->reprio_tc);  	/* Add mac address */  	hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH); @@ -722,6 +724,8 @@ static int __hellcreek_fdb_add(struct hellcreek *hellcreek,  	meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;  	if (entry->is_obt)  		meta |= HR_FDBWRM0_OBT; +	if (entry->pass_blocked) +		meta |= HR_FDBWRM0_PASS_BLOCKED;  	if (entry->reprio_en) {  		meta |= HR_FDBWRM0_REPRIO_EN;  		meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT; @@ -1049,7 +1053,7 @@ static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)  static int hellcreek_setup_fdb(struct hellcreek *hellcreek)  { -	static struct hellcreek_fdb_entry ptp = { +	static struct hellcreek_fdb_entry l2_ptp = {  		/* MAC: 01-1B-19-00-00-00 */  		.mac	      = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },  		.portmask     = 0x03,	/* Management ports */ @@ -1060,24 +1064,94 @@ static int hellcreek_setup_fdb(struct hellcreek *hellcreek)  		.reprio_tc    = 6,	/* TC: 6 as per IEEE 802.1AS */  		.reprio_en    = 1,  	}; -	static struct hellcreek_fdb_entry p2p = { +	static struct hellcreek_fdb_entry udp4_ptp = { +		/* MAC: 01-00-5E-00-01-81 */ +		.mac	      = { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 }, +		.portmask     = 0x03,	/* Management ports */ +		.age	      = 0, +		.is_obt	      = 0, +		.pass_blocked = 0, +		.is_static    = 1, +		.reprio_tc    = 6, +		.reprio_en    = 1, +	}; +	static struct hellcreek_fdb_entry udp6_ptp = { +		/* MAC: 33-33-00-00-01-81 */ +		.mac	      = { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 }, +		.portmask     = 0x03,	/* Management ports */ +		.age	      = 0, +		.is_obt	      = 0, +		.pass_blocked = 0, +		.is_static    = 1, +		.reprio_tc    = 6, +		.reprio_en    = 1, +	}; +	static struct hellcreek_fdb_entry l2_p2p = {  		/* MAC: 01-80-C2-00-00-0E */  		.mac	      = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },  		.portmask     = 0x03,	/* Management ports */  		.age	      = 0,  		.is_obt	      = 0, -		.pass_blocked = 0, +		.pass_blocked = 1,  		.is_static    = 1,  		.reprio_tc    = 6,	/* TC: 6 as per IEEE 802.1AS */  		.reprio_en    = 1,  	}; +	static struct hellcreek_fdb_entry udp4_p2p = { +		/* MAC: 01-00-5E-00-00-6B */ +		.mac	      = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b }, +		.portmask     = 0x03,	/* Management ports */ +		.age	      = 0, +		.is_obt	      = 0, +		.pass_blocked = 1, +		.is_static    = 1, +		.reprio_tc    = 6, +		.reprio_en    = 1, +	}; +	static struct hellcreek_fdb_entry udp6_p2p = { +		/* MAC: 33-33-00-00-00-6B */ +		.mac	      = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b }, +		.portmask     = 0x03,	/* Management ports */ +		.age	      = 0, +		.is_obt	      = 0, +		.pass_blocked = 1, +		.is_static    = 1, +		.reprio_tc    = 6, +		.reprio_en    = 1, +	}; +	static struct hellcreek_fdb_entry stp = { +		/* MAC: 01-80-C2-00-00-00 */ +		.mac	      = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }, +		.portmask     = 0x03,	/* Management ports */ +		.age	      = 0, +		.is_obt	      = 0, +		.pass_blocked = 1, +		.is_static    = 1, +		.reprio_tc    = 6, +		.reprio_en    = 1, +	};  	int ret;  	mutex_lock(&hellcreek->reg_lock); -	ret = __hellcreek_fdb_add(hellcreek, &ptp); +	ret = __hellcreek_fdb_add(hellcreek, &l2_ptp); +	if (ret) +		goto out; +	ret = __hellcreek_fdb_add(hellcreek, &udp4_ptp);  	if (ret)  		goto out; -	ret = __hellcreek_fdb_add(hellcreek, &p2p); +	ret = __hellcreek_fdb_add(hellcreek, &udp6_ptp); +	if (ret) +		goto out; +	ret = __hellcreek_fdb_add(hellcreek, &l2_p2p); +	if (ret) +		goto out; +	ret = __hellcreek_fdb_add(hellcreek, &udp4_p2p); +	if (ret) +		goto out; +	ret = __hellcreek_fdb_add(hellcreek, &udp6_p2p); +	if (ret) +		goto out; +	ret = __hellcreek_fdb_add(hellcreek, &stp);  out:  	mutex_unlock(&hellcreek->reg_lock); @@ -1384,14 +1458,19 @@ static void hellcreek_teardown(struct dsa_switch *ds)  	dsa_devlink_resources_unregister(ds);  } -static void hellcreek_phylink_validate(struct dsa_switch *ds, int port, -				       unsigned long *supported, -				       struct phylink_link_state *state) +static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port, +				       struct phylink_config *config)  { -	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };  	struct hellcreek *hellcreek = ds->priv; -	dev_dbg(hellcreek->dev, "Phylink validate for port %d\n", port); +	__set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); +	__set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces); + +	/* Include GMII - the hardware does not support this interface +	 * mode, but it's the default interface mode for phylib, so we +	 * need it for compatibility with existing DT. +	 */ +	__set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);  	/* The MAC settings are a hardware configuration option and cannot be  	 * changed at run time or by strapping. Therefore the attached PHYs @@ -1399,12 +1478,9 @@ static void hellcreek_phylink_validate(struct dsa_switch *ds, int port,  	 * by the hardware.  	 */  	if (hellcreek->pdata->is_100_mbits) -		phylink_set(mask, 100baseT_Full); +		config->mac_capabilities = MAC_100FD;  	else -		phylink_set(mask, 1000baseT_Full); - -	linkmode_and(supported, supported, mask); -	linkmode_and(state->advertising, state->advertising, mask); +		config->mac_capabilities = MAC_1000FD;  }  static int @@ -1755,7 +1831,7 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {  	.get_strings	       = hellcreek_get_strings,  	.get_tag_protocol      = hellcreek_get_tag_protocol,  	.get_ts_info	       = hellcreek_get_ts_info, -	.phylink_validate      = hellcreek_phylink_validate, +	.phylink_get_caps      = hellcreek_phylink_get_caps,  	.port_bridge_flags     = hellcreek_bridge_flags,  	.port_bridge_join      = hellcreek_port_bridge_join,  	.port_bridge_leave     = hellcreek_port_bridge_leave, diff --git a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c index 40b41c794dfa..b3bc948d6145 100644 --- a/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c +++ b/drivers/net/dsa/hirschmann/hellcreek_hwtstamp.c @@ -52,10 +52,6 @@ static int hellcreek_set_hwtstamp_config(struct hellcreek *hellcreek, int port,  	 */  	clear_bit_unlock(HELLCREEK_HWTSTAMP_ENABLED, &ps->state); -	/* Reserved for future extensions */ -	if (config->flags) -		return -EINVAL; -  	switch (config->tx_type) {  	case HWTSTAMP_TX_ON:  		tx_tstamp_enable = true; diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 89f920289ae2..d55784d19fa4 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1103,12 +1103,13 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port)  }  static int lan9303_port_bridge_join(struct dsa_switch *ds, int port, -				    struct net_device *br) +				    struct dsa_bridge bridge, +				    bool *tx_fwd_offload)  {  	struct lan9303 *chip = ds->priv;  	dev_dbg(chip->dev, "%s(port %d)\n", __func__, port); -	if (dsa_to_port(ds, 1)->bridge_dev == dsa_to_port(ds, 2)->bridge_dev) { +	if (dsa_port_bridge_same(dsa_to_port(ds, 1), dsa_to_port(ds, 2))) {  		lan9303_bridge_ports(chip);  		chip->is_bridged = true;  /* unleash stp_state_set() */  	} @@ -1117,7 +1118,7 @@ static int lan9303_port_bridge_join(struct dsa_switch *ds, int port,  }  static void lan9303_port_bridge_leave(struct dsa_switch *ds, int port, -				      struct net_device *br) +				      struct dsa_bridge bridge)  {  	struct lan9303 *chip = ds->priv; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 7056d98d8177..46ed953e787e 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -759,7 +759,7 @@ static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,  				     bool vlan_filtering,  				     struct netlink_ext_ack *extack)  { -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; +	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));  	struct gswip_priv *priv = ds->priv;  	/* Do not allow changing the VLAN filtering options while in bridge */ @@ -1146,16 +1146,18 @@ static int gswip_vlan_remove(struct gswip_priv *priv,  }  static int gswip_port_bridge_join(struct dsa_switch *ds, int port, -				  struct net_device *bridge) +				  struct dsa_bridge bridge, +				  bool *tx_fwd_offload)  { +	struct net_device *br = bridge.dev;  	struct gswip_priv *priv = ds->priv;  	int err;  	/* When the bridge uses VLAN filtering we have to configure VLAN  	 * specific bridges. No bridge is configured here.  	 */ -	if (!br_vlan_enabled(bridge)) { -		err = gswip_vlan_add_unaware(priv, bridge, port); +	if (!br_vlan_enabled(br)) { +		err = gswip_vlan_add_unaware(priv, br, port);  		if (err)  			return err;  		priv->port_vlan_filter &= ~BIT(port); @@ -1166,8 +1168,9 @@ static int gswip_port_bridge_join(struct dsa_switch *ds, int port,  }  static void gswip_port_bridge_leave(struct dsa_switch *ds, int port, -				    struct net_device *bridge) +				    struct dsa_bridge bridge)  { +	struct net_device *br = bridge.dev;  	struct gswip_priv *priv = ds->priv;  	gswip_add_single_port_br(priv, port, true); @@ -1175,16 +1178,16 @@ static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,  	/* When the bridge uses VLAN filtering we have to configure VLAN  	 * specific bridges. No bridge is configured here.  	 */ -	if (!br_vlan_enabled(bridge)) -		gswip_vlan_remove(priv, bridge, port, 0, true, false); +	if (!br_vlan_enabled(br)) +		gswip_vlan_remove(priv, br, port, 0, true, false);  }  static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,  				   const struct switchdev_obj_port_vlan *vlan,  				   struct netlink_ext_ack *extack)  { +	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));  	struct gswip_priv *priv = ds->priv; -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;  	unsigned int max_ports = priv->hw_info->max_ports;  	int pos = max_ports;  	int i, idx = -1; @@ -1229,8 +1232,8 @@ static int gswip_port_vlan_add(struct dsa_switch *ds, int port,  			       const struct switchdev_obj_port_vlan *vlan,  			       struct netlink_ext_ack *extack)  { +	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));  	struct gswip_priv *priv = ds->priv; -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;  	int err; @@ -1254,8 +1257,8 @@ static int gswip_port_vlan_add(struct dsa_switch *ds, int port,  static int gswip_port_vlan_del(struct dsa_switch *ds, int port,  			       const struct switchdev_obj_port_vlan *vlan)  { +	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));  	struct gswip_priv *priv = ds->priv; -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;  	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;  	/* We have to receive all packets on the CPU port and should not @@ -1340,8 +1343,8 @@ static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)  static int gswip_port_fdb(struct dsa_switch *ds, int port,  			  const unsigned char *addr, u16 vid, bool add)  { +	struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));  	struct gswip_priv *priv = ds->priv; -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;  	struct gswip_pce_table_entry mac_bridge = {0,};  	unsigned int cpu_port = priv->hw_info->cpu_port;  	int fid = -1; @@ -1438,114 +1441,70 @@ static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,  	return 0;  } -static void gswip_phylink_set_capab(unsigned long *supported, -				    struct phylink_link_state *state) -{ -	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - -	/* Allow all the expected bits */ -	phylink_set(mask, Autoneg); -	phylink_set_port_modes(mask); -	phylink_set(mask, Pause); -	phylink_set(mask, Asym_Pause); - -	/* With the exclusion of MII, Reverse MII and Reduced MII, we -	 * support Gigabit, including Half duplex -	 */ -	if (state->interface != PHY_INTERFACE_MODE_MII && -	    state->interface != PHY_INTERFACE_MODE_REVMII && -	    state->interface != PHY_INTERFACE_MODE_RMII) { -		phylink_set(mask, 1000baseT_Full); -		phylink_set(mask, 1000baseT_Half); -	} - -	phylink_set(mask, 10baseT_Half); -	phylink_set(mask, 10baseT_Full); -	phylink_set(mask, 100baseT_Half); -	phylink_set(mask, 100baseT_Full); - -	linkmode_and(supported, supported, mask); -	linkmode_and(state->advertising, state->advertising, mask); -} - -static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, -					  unsigned long *supported, -					  struct phylink_link_state *state) +static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port, +					  struct phylink_config *config)  {  	switch (port) {  	case 0:  	case 1: -		if (!phy_interface_mode_is_rgmii(state->interface) && -		    state->interface != PHY_INTERFACE_MODE_MII && -		    state->interface != PHY_INTERFACE_MODE_REVMII && -		    state->interface != PHY_INTERFACE_MODE_RMII) -			goto unsupported; +		phy_interface_set_rgmii(config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_MII, +			  config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_REVMII, +			  config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_RMII, +			  config->supported_interfaces);  		break; +  	case 2:  	case 3:  	case 4: -		if (state->interface != PHY_INTERFACE_MODE_INTERNAL) -			goto unsupported; +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces);  		break; +  	case 5: -		if (!phy_interface_mode_is_rgmii(state->interface) && -		    state->interface != PHY_INTERFACE_MODE_INTERNAL) -			goto unsupported; +		phy_interface_set_rgmii(config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces);  		break; -	default: -		linkmode_zero(supported); -		dev_err(ds->dev, "Unsupported port: %i\n", port); -		return;  	} -	gswip_phylink_set_capab(supported, state); - -	return; - -unsupported: -	linkmode_zero(supported); -	dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", -		phy_modes(state->interface), port); +	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +		MAC_10 | MAC_100 | MAC_1000;  } -static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port, -					  unsigned long *supported, -					  struct phylink_link_state *state) +static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port, +					  struct phylink_config *config)  {  	switch (port) {  	case 0: -		if (!phy_interface_mode_is_rgmii(state->interface) && -		    state->interface != PHY_INTERFACE_MODE_GMII && -		    state->interface != PHY_INTERFACE_MODE_RMII) -			goto unsupported; +		phy_interface_set_rgmii(config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_GMII, +			  config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_RMII, +			  config->supported_interfaces);  		break; +  	case 1:  	case 2:  	case 3:  	case 4: -		if (state->interface != PHY_INTERFACE_MODE_INTERNAL) -			goto unsupported; +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces);  		break; +  	case 5: -		if (!phy_interface_mode_is_rgmii(state->interface) && -		    state->interface != PHY_INTERFACE_MODE_INTERNAL && -		    state->interface != PHY_INTERFACE_MODE_RMII) -			goto unsupported; +		phy_interface_set_rgmii(config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_INTERNAL, +			  config->supported_interfaces); +		__set_bit(PHY_INTERFACE_MODE_RMII, +			  config->supported_interfaces);  		break; -	default: -		linkmode_zero(supported); -		dev_err(ds->dev, "Unsupported port: %i\n", port); -		return;  	} -	gswip_phylink_set_capab(supported, state); - -	return; - -unsupported: -	linkmode_zero(supported); -	dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", -		phy_modes(state->interface), port); +	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +		MAC_10 | MAC_100 | MAC_1000;  }  static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link) @@ -1827,7 +1786,7 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = {  	.port_fdb_add		= gswip_port_fdb_add,  	.port_fdb_del		= gswip_port_fdb_del,  	.port_fdb_dump		= gswip_port_fdb_dump, -	.phylink_validate	= gswip_xrx200_phylink_validate, +	.phylink_get_caps	= gswip_xrx200_phylink_get_caps,  	.phylink_mac_config	= gswip_phylink_mac_config,  	.phylink_mac_link_down	= gswip_phylink_mac_link_down,  	.phylink_mac_link_up	= gswip_phylink_mac_link_up, @@ -1851,7 +1810,7 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = {  	.port_fdb_add		= gswip_port_fdb_add,  	.port_fdb_del		= gswip_port_fdb_del,  	.port_fdb_dump		= gswip_port_fdb_dump, -	.phylink_validate	= gswip_xrx300_phylink_validate, +	.phylink_get_caps	= gswip_xrx300_phylink_get_caps,  	.phylink_mac_config	= gswip_phylink_mac_config,  	.phylink_mac_link_down	= gswip_phylink_mac_link_down,  	.phylink_mac_link_up	= gswip_phylink_mac_link_up, diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 013e9c02be71..991b9c6b6ce7 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -10,6 +10,7 @@  #include <linux/delay.h>  #include <linux/export.h>  #include <linux/gpio.h> +#include <linux/if_vlan.h>  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/platform_data/microchip-ksz.h> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 8a04302018dc..55dbda04ea62 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -43,7 +43,7 @@ void ksz_update_port_member(struct ksz_device *dev, int port)  			continue;  		if (port == i)  			continue; -		if (!dp->bridge_dev || dp->bridge_dev != other_dp->bridge_dev) +		if (!dsa_port_bridge_same(dp, other_dp))  			continue;  		if (other_p->stp_state == BR_STATE_FORWARDING && @@ -192,7 +192,8 @@ void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf)  EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats);  int ksz_port_bridge_join(struct dsa_switch *ds, int port, -			 struct net_device *br) +			 struct dsa_bridge bridge, +			 bool *tx_fwd_offload)  {  	/* port_stp_state_set() will be called after to put the port in  	 * appropriate state so there is no need to do anything. @@ -203,7 +204,7 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port,  EXPORT_SYMBOL_GPL(ksz_port_bridge_join);  void ksz_port_bridge_leave(struct dsa_switch *ds, int port, -			   struct net_device *br) +			   struct dsa_bridge bridge)  {  	/* port_stp_state_set() will be called after to put the port in  	 * forwarding state so there is no need to do anything. @@ -301,7 +302,6 @@ int ksz_port_mdb_del(struct dsa_switch *ds, int port,  	struct ksz_device *dev = ds->priv;  	struct alu_struct alu;  	int index; -	int ret = 0;  	for (index = 0; index < dev->num_statics; index++) {  		if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { @@ -323,7 +323,7 @@ int ksz_port_mdb_del(struct dsa_switch *ds, int port,  	dev->dev_ops->w_sta_mac_table(dev, index, &alu);  exit: -	return ret; +	return 0;  }  EXPORT_SYMBOL_GPL(ksz_port_mdb_del); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index 54b456bc8972..df8ae59c8525 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -155,9 +155,9 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,  int ksz_sset_count(struct dsa_switch *ds, int port, int sset);  void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf);  int ksz_port_bridge_join(struct dsa_switch *ds, int port, -			 struct net_device *br); +			 struct dsa_bridge bridge, bool *tx_fwd_offload);  void ksz_port_bridge_leave(struct dsa_switch *ds, int port, -			   struct net_device *br); +			   struct dsa_bridge bridge);  void ksz_port_fast_age(struct dsa_switch *ds, int port);  int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,  		      void *data); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 9890672a206d..b82512e5b33b 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1186,29 +1186,33 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port,  static int  mt7530_port_bridge_join(struct dsa_switch *ds, int port, -			struct net_device *bridge) +			struct dsa_bridge bridge, bool *tx_fwd_offload)  { -	struct mt7530_priv *priv = ds->priv; +	struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;  	u32 port_bitmap = BIT(MT7530_CPU_PORT); -	int i; +	struct mt7530_priv *priv = ds->priv;  	mutex_lock(&priv->reg_mutex); -	for (i = 0; i < MT7530_NUM_PORTS; i++) { +	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_is_user_port(ds, i) && i != port) { -			if (dsa_to_port(ds, i)->bridge_dev != bridge) -				continue; -			if (priv->ports[i].enable) -				mt7530_set(priv, MT7530_PCR_P(i), -					   PCR_MATRIX(BIT(port))); -			priv->ports[i].pm |= PCR_MATRIX(BIT(port)); +		if (!dsa_port_offloads_bridge(other_dp, &bridge)) +			continue; -			port_bitmap |= BIT(i); -		} +		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. */ @@ -1236,7 +1240,7 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)  	/* This is called after .port_bridge_leave when leaving a VLAN-aware  	 * bridge. Don't set standalone ports to fallback mode.  	 */ -	if (dsa_to_port(ds, port)->bridge_dev) +	if (dsa_port_bridge_dev_get(dsa_to_port(ds, port)))  		mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK,  			   MT7530_PORT_FALLBACK_MODE); @@ -1299,26 +1303,30 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)  static void  mt7530_port_bridge_leave(struct dsa_switch *ds, int port, -			 struct net_device *bridge) +			 struct dsa_bridge bridge)  { +	struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;  	struct mt7530_priv *priv = ds->priv; -	int i;  	mutex_lock(&priv->reg_mutex); -	for (i = 0; i < MT7530_NUM_PORTS; i++) { +	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_is_user_port(ds, i) && i != port) { -			if (dsa_to_port(ds, i)->bridge_dev != bridge) -				continue; -			if (priv->ports[i].enable) -				mt7530_clear(priv, MT7530_PCR_P(i), -					     PCR_MATRIX(BIT(port))); -			priv->ports[i].pm &= ~PCR_MATRIX(BIT(port)); -		} +		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 diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index cd8462d1e27c..58ca684d73f7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1241,8 +1241,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)  {  	struct dsa_switch *ds = chip->ds;  	struct dsa_switch_tree *dst = ds->dst; -	struct net_device *br; -	struct dsa_port *dp; +	struct dsa_port *dp, *other_dp;  	bool found = false;  	u16 pvlan; @@ -1251,11 +1250,9 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)  		list_for_each_entry(dp, &dst->ports, list) {  			if (dp->ds->index == dev && dp->index == port) {  				/* dp might be a DSA link or a user port, so it -				 * might or might not have a bridge_dev -				 * pointer. Use the "found" variable for both -				 * cases. +				 * might or might not have a bridge. +				 * Use the "found" variable for both cases.  				 */ -				br = dp->bridge_dev;  				found = true;  				break;  			} @@ -1263,13 +1260,14 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)  	/* dev is a virtual bridge */  	} else {  		list_for_each_entry(dp, &dst->ports, list) { -			if (dp->bridge_num < 0) +			unsigned int bridge_num = dsa_port_bridge_num_get(dp); + +			if (!bridge_num)  				continue; -			if (dp->bridge_num + 1 + dst->last_switch != dev) +			if (bridge_num + dst->last_switch != dev)  				continue; -			br = dp->bridge_dev;  			found = true;  			break;  		} @@ -1288,12 +1286,11 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)  	/* Frames from user ports can egress any local DSA links and CPU ports,  	 * as well as any local member of their bridge group.  	 */ -	list_for_each_entry(dp, &dst->ports, list) -		if (dp->ds == ds && -		    (dp->type == DSA_PORT_TYPE_CPU || -		     dp->type == DSA_PORT_TYPE_DSA || -		     (br && dp->bridge_dev == br))) -			pvlan |= BIT(dp->index); +	dsa_switch_for_each_port(other_dp, ds) +		if (other_dp->type == DSA_PORT_TYPE_CPU || +		    other_dp->type == DSA_PORT_TYPE_DSA || +		    dsa_port_bridge_same(dp, other_dp)) +			pvlan |= BIT(other_dp->index);  	return pvlan;  } @@ -1660,12 +1657,13 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)  static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  					u16 vid)  { +	struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;  	struct mv88e6xxx_chip *chip = ds->priv;  	struct mv88e6xxx_vtu_entry vlan; -	int i, err; +	int err;  	/* DSA and CPU ports have to be members of multiple vlans */ -	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) +	if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp))  		return 0;  	err = mv88e6xxx_vtu_get(chip, vid, &vlan); @@ -1675,27 +1673,22 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  	if (!vlan.valid)  		return 0; -	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { -		if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) -			continue; +	dsa_switch_for_each_user_port(other_dp, ds) { +		struct net_device *other_br; -		if (!dsa_to_port(ds, i)->slave) -			continue; - -		if (vlan.member[i] == +		if (vlan.member[other_dp->index] ==  		    MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)  			continue; -		if (dsa_to_port(ds, i)->bridge_dev == -		    dsa_to_port(ds, port)->bridge_dev) +		if (dsa_port_bridge_same(dp, other_dp))  			break; /* same bridge, check next VLAN */ -		if (!dsa_to_port(ds, i)->bridge_dev) +		other_br = dsa_port_bridge_dev_get(other_dp); +		if (!other_br)  			continue;  		dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n", -			port, vlan.vid, i, -			netdev_name(dsa_to_port(ds, i)->bridge_dev)); +			port, vlan.vid, other_dp->index, netdev_name(other_br));  		return -EOPNOTSUPP;  	} @@ -1705,13 +1698,14 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port)  {  	struct dsa_port *dp = dsa_to_port(chip->ds, port); +	struct net_device *br = dsa_port_bridge_dev_get(dp);  	struct mv88e6xxx_port *p = &chip->ports[port];  	u16 pvid = MV88E6XXX_VID_STANDALONE;  	bool drop_untagged = false;  	int err; -	if (dp->bridge_dev) { -		if (br_vlan_enabled(dp->bridge_dev)) { +	if (br) { +		if (br_vlan_enabled(br)) {  			pvid = p->bridge_pvid.vid;  			drop_untagged = !p->bridge_pvid.valid;  		} else { @@ -2429,7 +2423,7 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,  }  static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, -				struct net_device *br) +				struct dsa_bridge bridge)  {  	struct dsa_switch *ds = chip->ds;  	struct dsa_switch_tree *dst = ds->dst; @@ -2437,7 +2431,7 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,  	int err;  	list_for_each_entry(dp, &dst->ports, list) { -		if (dp->bridge_dev == br) { +		if (dsa_port_offloads_bridge(dp, &bridge)) {  			if (dp->ds == ds) {  				/* This is a local bridge group member,  				 * remap its Port VLAN Map. @@ -2460,15 +2454,29 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,  	return 0;  } +/* Treat the software bridge as a virtual single-port switch behind the + * CPU and map in the PVT. First dst->last_switch elements are taken by + * physical switches, so start from beyond that range. + */ +static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds, +					       unsigned int bridge_num) +{ +	u8 dev = bridge_num + ds->dst->last_switch; +	struct mv88e6xxx_chip *chip = ds->priv; + +	return mv88e6xxx_pvt_map(chip, dev, 0); +} +  static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, -				      struct net_device *br) +				      struct dsa_bridge bridge, +				      bool *tx_fwd_offload)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	int err;  	mv88e6xxx_reg_lock(chip); -	err = mv88e6xxx_bridge_map(chip, br); +	err = mv88e6xxx_bridge_map(chip, bridge);  	if (err)  		goto unlock; @@ -2476,6 +2484,14 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,  	if (err)  		goto unlock; +	if (mv88e6xxx_has_pvt(chip)) { +		err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num); +		if (err) +			goto unlock; + +		*tx_fwd_offload = true; +	} +  unlock:  	mv88e6xxx_reg_unlock(chip); @@ -2483,14 +2499,18 @@ unlock:  }  static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, -					struct net_device *br) +					struct dsa_bridge bridge)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	int err;  	mv88e6xxx_reg_lock(chip); -	if (mv88e6xxx_bridge_map(chip, br) || +	if (bridge.tx_fwd_offload && +	    mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num)) +		dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); + +	if (mv88e6xxx_bridge_map(chip, bridge) ||  	    mv88e6xxx_port_vlan_map(chip, port))  		dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); @@ -2505,7 +2525,7 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,  static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,  					   int tree_index, int sw_index, -					   int port, struct net_device *br) +					   int port, struct dsa_bridge bridge)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	int err; @@ -2515,6 +2535,7 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,  	mv88e6xxx_reg_lock(chip);  	err = mv88e6xxx_pvt_map(chip, sw_index, port); +	err = err ? : mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num);  	mv88e6xxx_reg_unlock(chip);  	return err; @@ -2522,7 +2543,7 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,  static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds,  					     int tree_index, int sw_index, -					     int port, struct net_device *br) +					     int port, struct dsa_bridge bridge)  {  	struct mv88e6xxx_chip *chip = ds->priv; @@ -2530,49 +2551,12 @@ static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds,  		return;  	mv88e6xxx_reg_lock(chip); -	if (mv88e6xxx_pvt_map(chip, sw_index, port)) +	if (mv88e6xxx_pvt_map(chip, sw_index, port) || +	    mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num))  		dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");  	mv88e6xxx_reg_unlock(chip);  } -/* Treat the software bridge as a virtual single-port switch behind the - * CPU and map in the PVT. First dst->last_switch elements are taken by - * physical switches, so start from beyond that range. - */ -static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds, -					       int bridge_num) -{ -	u8 dev = bridge_num + ds->dst->last_switch + 1; -	struct mv88e6xxx_chip *chip = ds->priv; -	int err; - -	mv88e6xxx_reg_lock(chip); -	err = mv88e6xxx_pvt_map(chip, dev, 0); -	mv88e6xxx_reg_unlock(chip); - -	return err; -} - -static int mv88e6xxx_bridge_tx_fwd_offload(struct dsa_switch *ds, int port, -					   struct net_device *br, -					   int bridge_num) -{ -	return mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num); -} - -static void mv88e6xxx_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port, -					      struct net_device *br, -					      int bridge_num) -{ -	int err; - -	err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num); -	if (err) { -		dev_err(ds->dev, "failed to remap cross-chip Port VLAN: %pe\n", -			ERR_PTR(err)); -	} -} -  static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip)  {  	if (chip->info->ops->reset) @@ -3199,8 +3183,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)  	 * time.  	 */  	if (mv88e6xxx_has_pvt(chip)) -		ds->num_fwd_offloading_bridges = MV88E6XXX_MAX_PVT_SWITCHES - -						 ds->dst->last_switch - 1; +		ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES - +				      ds->dst->last_switch - 1;  	mv88e6xxx_reg_lock(chip); @@ -6292,8 +6276,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {  	.crosschip_lag_change	= mv88e6xxx_crosschip_lag_change,  	.crosschip_lag_join	= mv88e6xxx_crosschip_lag_join,  	.crosschip_lag_leave	= mv88e6xxx_crosschip_lag_leave, -	.port_bridge_tx_fwd_offload = mv88e6xxx_bridge_tx_fwd_offload, -	.port_bridge_tx_fwd_unoffload = mv88e6xxx_bridge_tx_fwd_unoffload,  };  static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index 8f74ffc7a279..389f8a6ec0ab 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -100,10 +100,6 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port,  	 */  	clear_bit_unlock(MV88E6XXX_HWTSTAMP_ENABLED, &ps->state); -	/* reserved for future extensions */ -	if (config->flags) -		return -EINVAL; -  	switch (config->tx_type) {  	case HWTSTAMP_TX_OFF:  		tstamp_enable = false; diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index 9948544ba1c4..220b0b027b55 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -21,6 +21,7 @@ config NET_DSA_MSCC_SEVILLE  	depends on NET_VENDOR_MICROSEMI  	depends on HAS_IOMEM  	depends on PTP_1588_CLOCK_OPTIONAL +	select MDIO_MSCC_MIIM  	select MSCC_OCELOT_SWITCH_LIB  	select NET_DSA_TAG_OCELOT_8021Q  	select NET_DSA_TAG_OCELOT diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index f1a05e7dc818..9957772201d5 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -21,7 +21,6 @@  #include <linux/of_net.h>  #include <linux/pci.h>  #include <linux/of.h> -#include <linux/pcs-lynx.h>  #include <net/pkt_sched.h>  #include <net/dsa.h>  #include "felix.h" @@ -240,24 +239,32 @@ static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)   */  static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port)  { +	mutex_lock(&ocelot->fwd_domain_lock); +  	ocelot->ports[port]->is_dsa_8021q_cpu = true;  	ocelot->npi = -1;  	/* Overwrite PGID_CPU with the non-tagging port */  	ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU); -	ocelot_apply_bridge_fwd_mask(ocelot); +	ocelot_apply_bridge_fwd_mask(ocelot, true); + +	mutex_unlock(&ocelot->fwd_domain_lock);  }  static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)  { +	mutex_lock(&ocelot->fwd_domain_lock); +  	ocelot->ports[port]->is_dsa_8021q_cpu = false;  	/* Restore PGID_CPU */  	ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,  			 PGID_CPU); -	ocelot_apply_bridge_fwd_mask(ocelot); +	ocelot_apply_bridge_fwd_mask(ocelot, true); + +	mutex_unlock(&ocelot->fwd_domain_lock);  }  /* Set up a VCAP IS2 rule for delivering PTP frames to the CPU port module. @@ -632,6 +639,17 @@ static int felix_set_ageing_time(struct dsa_switch *ds,  	return 0;  } +static void felix_port_fast_age(struct dsa_switch *ds, int port) +{ +	struct ocelot *ocelot = ds->priv; +	int err; + +	err = ocelot_mact_flush(ocelot, port); +	if (err) +		dev_err(ds->dev, "Flushing MAC table on port %d returned %pe\n", +			port, ERR_PTR(err)); +} +  static int felix_fdb_dump(struct dsa_switch *ds, int port,  			  dsa_fdb_dump_cb_t *cb, void *data)  { @@ -701,21 +719,21 @@ static int felix_bridge_flags(struct dsa_switch *ds, int port,  }  static int felix_bridge_join(struct dsa_switch *ds, int port, -			     struct net_device *br) +			     struct dsa_bridge bridge, bool *tx_fwd_offload)  {  	struct ocelot *ocelot = ds->priv; -	ocelot_port_bridge_join(ocelot, port, br); +	ocelot_port_bridge_join(ocelot, port, bridge.dev);  	return 0;  }  static void felix_bridge_leave(struct dsa_switch *ds, int port, -			       struct net_device *br) +			       struct dsa_bridge bridge)  {  	struct ocelot *ocelot = ds->priv; -	ocelot_port_bridge_leave(ocelot, port, br); +	ocelot_port_bridge_leave(ocelot, port, bridge.dev);  }  static int felix_lag_join(struct dsa_switch *ds, int port, @@ -823,8 +841,8 @@ static void felix_phylink_mac_config(struct dsa_switch *ds, int port,  	struct felix *felix = ocelot_to_felix(ocelot);  	struct dsa_port *dp = dsa_to_port(ds, port); -	if (felix->pcs[port]) -		phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs); +	if (felix->pcs && felix->pcs[port]) +		phylink_set_pcs(dp->pl, felix->pcs[port]);  }  static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, @@ -992,6 +1010,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  	ocelot->num_stats	= felix->info->num_stats;  	ocelot->num_mact_rows	= felix->info->num_mact_rows;  	ocelot->vcap		= felix->info->vcap; +	ocelot->vcap_pol.base	= felix->info->vcap_pol_base; +	ocelot->vcap_pol.max	= felix->info->vcap_pol_max; +	ocelot->vcap_pol.base2	= felix->info->vcap_pol_base2; +	ocelot->vcap_pol.max2	= felix->info->vcap_pol_max2;  	ocelot->ops		= felix->info->ops;  	ocelot->npi_inj_prefix	= OCELOT_TAG_PREFIX_SHORT;  	ocelot->npi_xtr_prefix	= OCELOT_TAG_PREFIX_SHORT; @@ -1019,7 +1041,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  		res.start += felix->switch_base;  		res.end += felix->switch_base; -		target = ocelot_regmap_init(ocelot, &res); +		target = felix->info->init_regmap(ocelot, &res);  		if (IS_ERR(target)) {  			dev_err(ocelot->dev,  				"Failed to map device memory space\n"); @@ -1056,7 +1078,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  		res.start += felix->switch_base;  		res.end += felix->switch_base; -		target = ocelot_regmap_init(ocelot, &res); +		target = felix->info->init_regmap(ocelot, &res);  		if (IS_ERR(target)) {  			dev_err(ocelot->dev,  				"Failed to map memory space for port %d\n", @@ -1143,38 +1165,22 @@ static void felix_port_deferred_xmit(struct kthread_work *work)  	kfree(xmit_work);  } -static int felix_port_setup_tagger_data(struct dsa_switch *ds, int port) +static int felix_connect_tag_protocol(struct dsa_switch *ds, +				      enum dsa_tag_protocol proto)  { -	struct dsa_port *dp = dsa_to_port(ds, port); -	struct ocelot *ocelot = ds->priv; -	struct felix *felix = ocelot_to_felix(ocelot); -	struct felix_port *felix_port; +	struct ocelot_8021q_tagger_data *tagger_data; -	if (!dsa_port_is_user(dp)) +	switch (proto) { +	case DSA_TAG_PROTO_OCELOT_8021Q: +		tagger_data = ocelot_8021q_tagger_data(ds); +		tagger_data->xmit_work_fn = felix_port_deferred_xmit;  		return 0; - -	felix_port = kzalloc(sizeof(*felix_port), GFP_KERNEL); -	if (!felix_port) -		return -ENOMEM; - -	felix_port->xmit_worker = felix->xmit_worker; -	felix_port->xmit_work_fn = felix_port_deferred_xmit; - -	dp->priv = felix_port; - -	return 0; -} - -static void felix_port_teardown_tagger_data(struct dsa_switch *ds, int port) -{ -	struct dsa_port *dp = dsa_to_port(ds, port); -	struct felix_port *felix_port = dp->priv; - -	if (!felix_port) -		return; - -	dp->priv = NULL; -	kfree(felix_port); +	case DSA_TAG_PROTO_OCELOT: +	case DSA_TAG_PROTO_SEVILLE: +		return 0; +	default: +		return -EPROTONOSUPPORT; +	}  }  /* Hardware initialization done here so that we can allocate structures with @@ -1205,12 +1211,6 @@ static int felix_setup(struct dsa_switch *ds)  		}  	} -	felix->xmit_worker = kthread_create_worker(0, "felix_xmit"); -	if (IS_ERR(felix->xmit_worker)) { -		err = PTR_ERR(felix->xmit_worker); -		goto out_deinit_timestamp; -	} -  	for (port = 0; port < ds->num_ports; port++) {  		if (dsa_is_unused_port(ds, port))  			continue; @@ -1221,14 +1221,6 @@ static int felix_setup(struct dsa_switch *ds)  		 * bits of vlan tag.  		 */  		felix_port_qos_map_init(ocelot, port); - -		err = felix_port_setup_tagger_data(ds, port); -		if (err) { -			dev_err(ds->dev, -				"port %d failed to set up tagger data: %pe\n", -				port, ERR_PTR(err)); -			goto out_deinit_ports; -		}  	}  	err = ocelot_devlink_sb_register(ocelot); @@ -1256,13 +1248,9 @@ out_deinit_ports:  		if (dsa_is_unused_port(ds, port))  			continue; -		felix_port_teardown_tagger_data(ds, port);  		ocelot_deinit_port(ocelot, port);  	} -	kthread_destroy_worker(felix->xmit_worker); - -out_deinit_timestamp:  	ocelot_deinit_timestamp(ocelot);  	ocelot_deinit(ocelot); @@ -1291,12 +1279,9 @@ static void felix_teardown(struct dsa_switch *ds)  		if (dsa_is_unused_port(ds, port))  			continue; -		felix_port_teardown_tagger_data(ds, port);  		ocelot_deinit_port(ocelot, port);  	} -	kthread_destroy_worker(felix->xmit_worker); -  	ocelot_devlink_sb_unregister(ocelot);  	ocelot_deinit_timestamp(ocelot);  	ocelot_deinit(ocelot); @@ -1636,6 +1621,7 @@ felix_mrp_del_ring_role(struct dsa_switch *ds, int port,  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,  	.setup				= felix_setup,  	.teardown			= felix_teardown,  	.set_ageing_time		= felix_set_ageing_time, @@ -1647,6 +1633,7 @@ const struct dsa_switch_ops felix_switch_ops = {  	.phylink_mac_config		= felix_phylink_mac_config,  	.phylink_mac_link_down		= felix_phylink_mac_link_down,  	.phylink_mac_link_up		= felix_phylink_mac_link_up, +	.port_fast_age			= felix_port_fast_age,  	.port_fdb_dump			= felix_fdb_dump,  	.port_fdb_add			= felix_fdb_add,  	.port_fdb_del			= felix_fdb_del, diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index be3e42e135c0..9395ac119d33 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -21,8 +21,10 @@ struct felix_info {  	int				num_ports;  	int				num_tx_queues;  	struct vcap_props		*vcap; -	int				switch_pci_bar; -	int				imdio_pci_bar; +	u16				vcap_pol_base; +	u16				vcap_pol_max; +	u16				vcap_pol_base2; +	u16				vcap_pol_max2;  	const struct ptp_clock_info	*ptp_caps;  	/* Some Ocelot switches are integrated into the SoC without the @@ -48,6 +50,8 @@ struct felix_info {  				 enum tc_setup_type type, void *type_data);  	void	(*port_sched_speed_set)(struct ocelot *ocelot, int port,  					u32 speed); +	struct regmap *(*init_regmap)(struct ocelot *ocelot, +				      struct resource *res);  };  extern const struct dsa_switch_ops felix_switch_ops; @@ -58,7 +62,7 @@ struct felix {  	const struct felix_info		*info;  	struct ocelot			ocelot;  	struct mii_bus			*imdio; -	struct lynx_pcs			**pcs; +	struct phylink_pcs		**pcs;  	resource_size_t			switch_base;  	resource_size_t			imdio_base;  	enum dsa_tag_protocol		tag_proto; diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 45c5ec7a83ea..bf8d38239e7e 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -5,8 +5,10 @@  #include <linux/fsl/enetc_mdio.h>  #include <soc/mscc/ocelot_qsys.h>  #include <soc/mscc/ocelot_vcap.h> +#include <soc/mscc/ocelot_ana.h>  #include <soc/mscc/ocelot_ptp.h>  #include <soc/mscc/ocelot_sys.h> +#include <net/tc_act/tc_gate.h>  #include <soc/mscc/ocelot.h>  #include <linux/dsa/ocelot.h>  #include <linux/pcs-lynx.h> @@ -17,6 +19,10 @@  #include "felix.h"  #define VSC9959_TAS_GCL_ENTRY_MAX	63 +#define VSC9959_VCAP_POLICER_BASE	63 +#define VSC9959_VCAP_POLICER_MAX	383 +#define VSC9959_SWITCH_PCI_BAR		4 +#define VSC9959_IMDIO_PCI_BAR		0  static const u32 vsc9959_ana_regmap[] = {  	REG(ANA_ADVLEARN,			0x0089a0), @@ -292,7 +298,7 @@ static const u32 vsc9959_sys_regmap[] = {  	REG_RESERVED(SYS_MMGT_FAST),  	REG_RESERVED(SYS_EVENTS_DIF),  	REG_RESERVED(SYS_EVENTS_CORE), -	REG_RESERVED(SYS_CNT), +	REG(SYS_CNT,				0x000000),  	REG(SYS_PTP_STATUS,			0x000f14),  	REG(SYS_PTP_TXSTAMP,			0x000f18),  	REG(SYS_PTP_NXT,			0x000f1c), @@ -1020,15 +1026,6 @@ static void vsc9959_wm_stat(u32 val, u32 *inuse, u32 *maxuse)  	*maxuse = val & GENMASK(11, 0);  } -static const struct ocelot_ops vsc9959_ops = { -	.reset			= vsc9959_reset, -	.wm_enc			= vsc9959_wm_enc, -	.wm_dec			= vsc9959_wm_dec, -	.wm_stat		= vsc9959_wm_stat, -	.port_to_netdev		= felix_port_to_netdev, -	.netdev_to_port		= felix_netdev_to_port, -}; -  static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  {  	struct felix *felix = ocelot_to_felix(ocelot); @@ -1042,7 +1039,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  	int rc;  	felix->pcs = devm_kcalloc(dev, felix->info->num_ports, -				  sizeof(struct lynx_pcs *), +				  sizeof(struct phylink_pcs *),  				  GFP_KERNEL);  	if (!felix->pcs) {  		dev_err(dev, "failed to allocate array for PCS PHYs\n"); @@ -1091,8 +1088,8 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  	for (port = 0; port < felix->info->num_ports; port++) {  		struct ocelot_port *ocelot_port = ocelot->ports[port]; -		struct mdio_device *pcs; -		struct lynx_pcs *lynx; +		struct phylink_pcs *phylink_pcs; +		struct mdio_device *mdio_device;  		if (dsa_is_unused_port(felix->ds, port))  			continue; @@ -1100,17 +1097,17 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)  			continue; -		pcs = mdio_device_create(felix->imdio, port); -		if (IS_ERR(pcs)) +		mdio_device = mdio_device_create(felix->imdio, port); +		if (IS_ERR(mdio_device))  			continue; -		lynx = lynx_pcs_create(pcs); -		if (!lynx) { -			mdio_device_free(pcs); +		phylink_pcs = lynx_pcs_create(mdio_device); +		if (!phylink_pcs) { +			mdio_device_free(mdio_device);  			continue;  		} -		felix->pcs[port] = lynx; +		felix->pcs[port] = phylink_pcs;  		dev_info(dev, "Found PCS at internal MDIO address %d\n", port);  	} @@ -1124,13 +1121,15 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)  	int port;  	for (port = 0; port < ocelot->num_phys_ports; port++) { -		struct lynx_pcs *pcs = felix->pcs[port]; +		struct phylink_pcs *phylink_pcs = felix->pcs[port]; +		struct mdio_device *mdio_device; -		if (!pcs) +		if (!phylink_pcs)  			continue; -		mdio_device_free(pcs->mdio); -		lynx_pcs_destroy(pcs); +		mdio_device = lynx_get_mdio_device(phylink_pcs); +		mdio_device_free(mdio_device); +		lynx_pcs_destroy(phylink_pcs);  	}  	mdiobus_unregister(felix->imdio);  } @@ -1344,6 +1343,877 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port,  	}  } +#define VSC9959_PSFP_SFID_MAX			175 +#define VSC9959_PSFP_GATE_ID_MAX		183 +#define VSC9959_PSFP_POLICER_BASE		63 +#define VSC9959_PSFP_POLICER_MAX		383 +#define VSC9959_PSFP_GATE_LIST_NUM		4 +#define VSC9959_PSFP_GATE_CYCLETIME_MIN		5000 + +struct felix_stream { +	struct list_head list; +	unsigned long id; +	bool dummy; +	int ports; +	int port; +	u8 dmac[ETH_ALEN]; +	u16 vid; +	s8 prio; +	u8 sfid_valid; +	u8 ssid_valid; +	u32 sfid; +	u32 ssid; +}; + +struct felix_stream_filter { +	struct list_head list; +	refcount_t refcount; +	u32 index; +	u8 enable; +	int portmask; +	u8 sg_valid; +	u32 sgid; +	u8 fm_valid; +	u32 fmid; +	u8 prio_valid; +	u8 prio; +	u32 maxsdu; +}; + +struct felix_stream_filter_counters { +	u32 match; +	u32 not_pass_gate; +	u32 not_pass_sdu; +	u32 red; +}; + +struct felix_stream_gate { +	u32 index; +	u8 enable; +	u8 ipv_valid; +	u8 init_ipv; +	u64 basetime; +	u64 cycletime; +	u64 cycletime_ext; +	u32 num_entries; +	struct action_gate_entry entries[]; +}; + +struct felix_stream_gate_entry { +	struct list_head list; +	refcount_t refcount; +	u32 index; +}; + +static int vsc9959_stream_identify(struct flow_cls_offload *f, +				   struct felix_stream *stream) +{ +	struct flow_rule *rule = flow_cls_offload_flow_rule(f); +	struct flow_dissector *dissector = rule->match.dissector; + +	if (dissector->used_keys & +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | +	      BIT(FLOW_DISSECTOR_KEY_BASIC) | +	      BIT(FLOW_DISSECTOR_KEY_VLAN) | +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) +		return -EOPNOTSUPP; + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { +		struct flow_match_eth_addrs match; + +		flow_rule_match_eth_addrs(rule, &match); +		ether_addr_copy(stream->dmac, match.key->dst); +		if (!is_zero_ether_addr(match.mask->src)) +			return -EOPNOTSUPP; +	} else { +		return -EOPNOTSUPP; +	} + +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { +		struct flow_match_vlan match; + +		flow_rule_match_vlan(rule, &match); +		if (match.mask->vlan_priority) +			stream->prio = match.key->vlan_priority; +		else +			stream->prio = -1; + +		if (!match.mask->vlan_id) +			return -EOPNOTSUPP; +		stream->vid = match.key->vlan_id; +	} else { +		return -EOPNOTSUPP; +	} + +	stream->id = f->cookie; + +	return 0; +} + +static int vsc9959_mact_stream_set(struct ocelot *ocelot, +				   struct felix_stream *stream, +				   struct netlink_ext_ack *extack) +{ +	enum macaccess_entry_type type; +	int ret, sfid, ssid; +	u32 vid, dst_idx; +	u8 mac[ETH_ALEN]; + +	ether_addr_copy(mac, stream->dmac); +	vid = stream->vid; + +	/* Stream identification desn't support to add a stream with non +	 * existent MAC (The MAC entry has not been learned in MAC table). +	 */ +	ret = ocelot_mact_lookup(ocelot, &dst_idx, mac, vid, &type); +	if (ret) { +		if (extack) +			NL_SET_ERR_MSG_MOD(extack, "Stream is not learned in MAC table"); +		return -EOPNOTSUPP; +	} + +	if ((stream->sfid_valid || stream->ssid_valid) && +	    type == ENTRYTYPE_NORMAL) +		type = ENTRYTYPE_LOCKED; + +	sfid = stream->sfid_valid ? stream->sfid : -1; +	ssid = stream->ssid_valid ? stream->ssid : -1; + +	ret = ocelot_mact_learn_streamdata(ocelot, dst_idx, mac, vid, type, +					   sfid, ssid); + +	return ret; +} + +static struct felix_stream * +vsc9959_stream_table_lookup(struct list_head *stream_list, +			    struct felix_stream *stream) +{ +	struct felix_stream *tmp; + +	list_for_each_entry(tmp, stream_list, list) +		if (ether_addr_equal(tmp->dmac, stream->dmac) && +		    tmp->vid == stream->vid) +			return tmp; + +	return NULL; +} + +static int vsc9959_stream_table_add(struct ocelot *ocelot, +				    struct list_head *stream_list, +				    struct felix_stream *stream, +				    struct netlink_ext_ack *extack) +{ +	struct felix_stream *stream_entry; +	int ret; + +	stream_entry = kmemdup(stream, sizeof(*stream_entry), GFP_KERNEL); +	if (!stream_entry) +		return -ENOMEM; + +	if (!stream->dummy) { +		ret = vsc9959_mact_stream_set(ocelot, stream_entry, extack); +		if (ret) { +			kfree(stream_entry); +			return ret; +		} +	} + +	list_add_tail(&stream_entry->list, stream_list); + +	return 0; +} + +static struct felix_stream * +vsc9959_stream_table_get(struct list_head *stream_list, unsigned long id) +{ +	struct felix_stream *tmp; + +	list_for_each_entry(tmp, stream_list, list) +		if (tmp->id == id) +			return tmp; + +	return NULL; +} + +static void vsc9959_stream_table_del(struct ocelot *ocelot, +				     struct felix_stream *stream) +{ +	if (!stream->dummy) +		vsc9959_mact_stream_set(ocelot, stream, NULL); + +	list_del(&stream->list); +	kfree(stream); +} + +static u32 vsc9959_sfi_access_status(struct ocelot *ocelot) +{ +	return ocelot_read(ocelot, ANA_TABLES_SFIDACCESS); +} + +static int vsc9959_psfp_sfi_set(struct ocelot *ocelot, +				struct felix_stream_filter *sfi) +{ +	u32 val; + +	if (sfi->index > VSC9959_PSFP_SFID_MAX) +		return -EINVAL; + +	if (!sfi->enable) { +		ocelot_write(ocelot, ANA_TABLES_SFIDTIDX_SFID_INDEX(sfi->index), +			     ANA_TABLES_SFIDTIDX); + +		val = ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE); +		ocelot_write(ocelot, val, ANA_TABLES_SFIDACCESS); + +		return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val, +					  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)), +					  10, 100000); +	} + +	if (sfi->sgid > VSC9959_PSFP_GATE_ID_MAX || +	    sfi->fmid > VSC9959_PSFP_POLICER_MAX) +		return -EINVAL; + +	ocelot_write(ocelot, +		     (sfi->sg_valid ? ANA_TABLES_SFIDTIDX_SGID_VALID : 0) | +		     ANA_TABLES_SFIDTIDX_SGID(sfi->sgid) | +		     (sfi->fm_valid ? ANA_TABLES_SFIDTIDX_POL_ENA : 0) | +		     ANA_TABLES_SFIDTIDX_POL_IDX(sfi->fmid) | +		     ANA_TABLES_SFIDTIDX_SFID_INDEX(sfi->index), +		     ANA_TABLES_SFIDTIDX); + +	ocelot_write(ocelot, +		     (sfi->prio_valid ? ANA_TABLES_SFIDACCESS_IGR_PRIO_MATCH_ENA : 0) | +		     ANA_TABLES_SFIDACCESS_IGR_PRIO(sfi->prio) | +		     ANA_TABLES_SFIDACCESS_MAX_SDU_LEN(sfi->maxsdu) | +		     ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE), +		     ANA_TABLES_SFIDACCESS); + +	return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val, +				  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)), +				  10, 100000); +} + +static int vsc9959_psfp_sfidmask_set(struct ocelot *ocelot, u32 sfid, int ports) +{ +	u32 val; + +	ocelot_rmw(ocelot, +		   ANA_TABLES_SFIDTIDX_SFID_INDEX(sfid), +		   ANA_TABLES_SFIDTIDX_SFID_INDEX_M, +		   ANA_TABLES_SFIDTIDX); + +	ocelot_write(ocelot, +		     ANA_TABLES_SFID_MASK_IGR_PORT_MASK(ports) | +		     ANA_TABLES_SFID_MASK_IGR_SRCPORT_MATCH_ENA, +		     ANA_TABLES_SFID_MASK); + +	ocelot_rmw(ocelot, +		   ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(SFIDACCESS_CMD_WRITE), +		   ANA_TABLES_SFIDACCESS_SFID_TBL_CMD_M, +		   ANA_TABLES_SFIDACCESS); + +	return readx_poll_timeout(vsc9959_sfi_access_status, ocelot, val, +				  (!ANA_TABLES_SFIDACCESS_SFID_TBL_CMD(val)), +				  10, 100000); +} + +static int vsc9959_psfp_sfi_list_add(struct ocelot *ocelot, +				     struct felix_stream_filter *sfi, +				     struct list_head *pos) +{ +	struct felix_stream_filter *sfi_entry; +	int ret; + +	sfi_entry = kmemdup(sfi, sizeof(*sfi_entry), GFP_KERNEL); +	if (!sfi_entry) +		return -ENOMEM; + +	refcount_set(&sfi_entry->refcount, 1); + +	ret = vsc9959_psfp_sfi_set(ocelot, sfi_entry); +	if (ret) { +		kfree(sfi_entry); +		return ret; +	} + +	vsc9959_psfp_sfidmask_set(ocelot, sfi->index, sfi->portmask); + +	list_add(&sfi_entry->list, pos); + +	return 0; +} + +static int vsc9959_psfp_sfi_table_add(struct ocelot *ocelot, +				      struct felix_stream_filter *sfi) +{ +	struct list_head *pos, *q, *last; +	struct felix_stream_filter *tmp; +	struct ocelot_psfp_list *psfp; +	u32 insert = 0; + +	psfp = &ocelot->psfp; +	last = &psfp->sfi_list; + +	list_for_each_safe(pos, q, &psfp->sfi_list) { +		tmp = list_entry(pos, struct felix_stream_filter, list); +		if (sfi->sg_valid == tmp->sg_valid && +		    sfi->fm_valid == tmp->fm_valid && +		    sfi->portmask == tmp->portmask && +		    tmp->sgid == sfi->sgid && +		    tmp->fmid == sfi->fmid) { +			sfi->index = tmp->index; +			refcount_inc(&tmp->refcount); +			return 0; +		} +		/* Make sure that the index is increasing in order. */ +		if (tmp->index == insert) { +			last = pos; +			insert++; +		} +	} +	sfi->index = insert; + +	return vsc9959_psfp_sfi_list_add(ocelot, sfi, last); +} + +static int vsc9959_psfp_sfi_table_add2(struct ocelot *ocelot, +				       struct felix_stream_filter *sfi, +				       struct felix_stream_filter *sfi2) +{ +	struct felix_stream_filter *tmp; +	struct list_head *pos, *q, *last; +	struct ocelot_psfp_list *psfp; +	u32 insert = 0; +	int ret; + +	psfp = &ocelot->psfp; +	last = &psfp->sfi_list; + +	list_for_each_safe(pos, q, &psfp->sfi_list) { +		tmp = list_entry(pos, struct felix_stream_filter, list); +		/* Make sure that the index is increasing in order. */ +		if (tmp->index >= insert + 2) +			break; + +		insert = tmp->index + 1; +		last = pos; +	} +	sfi->index = insert; + +	ret = vsc9959_psfp_sfi_list_add(ocelot, sfi, last); +	if (ret) +		return ret; + +	sfi2->index = insert + 1; + +	return vsc9959_psfp_sfi_list_add(ocelot, sfi2, last->next); +} + +static struct felix_stream_filter * +vsc9959_psfp_sfi_table_get(struct list_head *sfi_list, u32 index) +{ +	struct felix_stream_filter *tmp; + +	list_for_each_entry(tmp, sfi_list, list) +		if (tmp->index == index) +			return tmp; + +	return NULL; +} + +static void vsc9959_psfp_sfi_table_del(struct ocelot *ocelot, u32 index) +{ +	struct felix_stream_filter *tmp, *n; +	struct ocelot_psfp_list *psfp; +	u8 z; + +	psfp = &ocelot->psfp; + +	list_for_each_entry_safe(tmp, n, &psfp->sfi_list, list) +		if (tmp->index == index) { +			z = refcount_dec_and_test(&tmp->refcount); +			if (z) { +				tmp->enable = 0; +				vsc9959_psfp_sfi_set(ocelot, tmp); +				list_del(&tmp->list); +				kfree(tmp); +			} +			break; +		} +} + +static void vsc9959_psfp_parse_gate(const struct flow_action_entry *entry, +				    struct felix_stream_gate *sgi) +{ +	sgi->index = entry->hw_index; +	sgi->ipv_valid = (entry->gate.prio < 0) ? 0 : 1; +	sgi->init_ipv = (sgi->ipv_valid) ? entry->gate.prio : 0; +	sgi->basetime = entry->gate.basetime; +	sgi->cycletime = entry->gate.cycletime; +	sgi->num_entries = entry->gate.num_entries; +	sgi->enable = 1; + +	memcpy(sgi->entries, entry->gate.entries, +	       entry->gate.num_entries * sizeof(struct action_gate_entry)); +} + +static u32 vsc9959_sgi_cfg_status(struct ocelot *ocelot) +{ +	return ocelot_read(ocelot, ANA_SG_ACCESS_CTRL); +} + +static int vsc9959_psfp_sgi_set(struct ocelot *ocelot, +				struct felix_stream_gate *sgi) +{ +	struct action_gate_entry *e; +	struct timespec64 base_ts; +	u32 interval_sum = 0; +	u32 val; +	int i; + +	if (sgi->index > VSC9959_PSFP_GATE_ID_MAX) +		return -EINVAL; + +	ocelot_write(ocelot, ANA_SG_ACCESS_CTRL_SGID(sgi->index), +		     ANA_SG_ACCESS_CTRL); + +	if (!sgi->enable) { +		ocelot_rmw(ocelot, ANA_SG_CONFIG_REG_3_INIT_GATE_STATE, +			   ANA_SG_CONFIG_REG_3_INIT_GATE_STATE | +			   ANA_SG_CONFIG_REG_3_GATE_ENABLE, +			   ANA_SG_CONFIG_REG_3); + +		return 0; +	} + +	if (sgi->cycletime < VSC9959_PSFP_GATE_CYCLETIME_MIN || +	    sgi->cycletime > NSEC_PER_SEC) +		return -EINVAL; + +	if (sgi->num_entries > VSC9959_PSFP_GATE_LIST_NUM) +		return -EINVAL; + +	vsc9959_new_base_time(ocelot, sgi->basetime, sgi->cycletime, &base_ts); +	ocelot_write(ocelot, base_ts.tv_nsec, ANA_SG_CONFIG_REG_1); +	val = lower_32_bits(base_ts.tv_sec); +	ocelot_write(ocelot, val, ANA_SG_CONFIG_REG_2); + +	val = upper_32_bits(base_ts.tv_sec); +	ocelot_write(ocelot, +		     (sgi->ipv_valid ? ANA_SG_CONFIG_REG_3_IPV_VALID : 0) | +		     ANA_SG_CONFIG_REG_3_INIT_IPV(sgi->init_ipv) | +		     ANA_SG_CONFIG_REG_3_GATE_ENABLE | +		     ANA_SG_CONFIG_REG_3_LIST_LENGTH(sgi->num_entries) | +		     ANA_SG_CONFIG_REG_3_INIT_GATE_STATE | +		     ANA_SG_CONFIG_REG_3_BASE_TIME_SEC_MSB(val), +		     ANA_SG_CONFIG_REG_3); + +	ocelot_write(ocelot, sgi->cycletime, ANA_SG_CONFIG_REG_4); + +	e = sgi->entries; +	for (i = 0; i < sgi->num_entries; i++) { +		u32 ips = (e[i].ipv < 0) ? 0 : (e[i].ipv + 8); + +		ocelot_write_rix(ocelot, ANA_SG_GCL_GS_CONFIG_IPS(ips) | +				 (e[i].gate_state ? +				  ANA_SG_GCL_GS_CONFIG_GATE_STATE : 0), +				 ANA_SG_GCL_GS_CONFIG, i); + +		interval_sum += e[i].interval; +		ocelot_write_rix(ocelot, interval_sum, ANA_SG_GCL_TI_CONFIG, i); +	} + +	ocelot_rmw(ocelot, ANA_SG_ACCESS_CTRL_CONFIG_CHANGE, +		   ANA_SG_ACCESS_CTRL_CONFIG_CHANGE, +		   ANA_SG_ACCESS_CTRL); + +	return readx_poll_timeout(vsc9959_sgi_cfg_status, ocelot, val, +				  (!(ANA_SG_ACCESS_CTRL_CONFIG_CHANGE & val)), +				  10, 100000); +} + +static int vsc9959_psfp_sgi_table_add(struct ocelot *ocelot, +				      struct felix_stream_gate *sgi) +{ +	struct felix_stream_gate_entry *tmp; +	struct ocelot_psfp_list *psfp; +	int ret; + +	psfp = &ocelot->psfp; + +	list_for_each_entry(tmp, &psfp->sgi_list, list) +		if (tmp->index == sgi->index) { +			refcount_inc(&tmp->refcount); +			return 0; +		} + +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); +	if (!tmp) +		return -ENOMEM; + +	ret = vsc9959_psfp_sgi_set(ocelot, sgi); +	if (ret) { +		kfree(tmp); +		return ret; +	} + +	tmp->index = sgi->index; +	refcount_set(&tmp->refcount, 1); +	list_add_tail(&tmp->list, &psfp->sgi_list); + +	return 0; +} + +static void vsc9959_psfp_sgi_table_del(struct ocelot *ocelot, +				       u32 index) +{ +	struct felix_stream_gate_entry *tmp, *n; +	struct felix_stream_gate sgi = {0}; +	struct ocelot_psfp_list *psfp; +	u8 z; + +	psfp = &ocelot->psfp; + +	list_for_each_entry_safe(tmp, n, &psfp->sgi_list, list) +		if (tmp->index == index) { +			z = refcount_dec_and_test(&tmp->refcount); +			if (z) { +				sgi.index = index; +				sgi.enable = 0; +				vsc9959_psfp_sgi_set(ocelot, &sgi); +				list_del(&tmp->list); +				kfree(tmp); +			} +			break; +		} +} + +static void vsc9959_psfp_counters_get(struct ocelot *ocelot, u32 index, +				      struct felix_stream_filter_counters *counters) +{ +	ocelot_rmw(ocelot, SYS_STAT_CFG_STAT_VIEW(index), +		   SYS_STAT_CFG_STAT_VIEW_M, +		   SYS_STAT_CFG); + +	counters->match = ocelot_read_gix(ocelot, SYS_CNT, 0x200); +	counters->not_pass_gate = ocelot_read_gix(ocelot, SYS_CNT, 0x201); +	counters->not_pass_sdu = ocelot_read_gix(ocelot, SYS_CNT, 0x202); +	counters->red = ocelot_read_gix(ocelot, SYS_CNT, 0x203); + +	/* Clear the PSFP counter. */ +	ocelot_write(ocelot, +		     SYS_STAT_CFG_STAT_VIEW(index) | +		     SYS_STAT_CFG_STAT_CLEAR_SHOT(0x10), +		     SYS_STAT_CFG); +} + +static int vsc9959_psfp_filter_add(struct ocelot *ocelot, int port, +				   struct flow_cls_offload *f) +{ +	struct netlink_ext_ack *extack = f->common.extack; +	struct felix_stream_filter old_sfi, *sfi_entry; +	struct felix_stream_filter sfi = {0}; +	const struct flow_action_entry *a; +	struct felix_stream *stream_entry; +	struct felix_stream stream = {0}; +	struct felix_stream_gate *sgi; +	struct ocelot_psfp_list *psfp; +	struct ocelot_policer pol; +	int ret, i, size; +	u64 rate, burst; +	u32 index; + +	psfp = &ocelot->psfp; + +	ret = vsc9959_stream_identify(f, &stream); +	if (ret) { +		NL_SET_ERR_MSG_MOD(extack, "Only can match on VID, PCP, and dest MAC"); +		return ret; +	} + +	flow_action_for_each(i, a, &f->rule->action) { +		switch (a->id) { +		case FLOW_ACTION_GATE: +			size = struct_size(sgi, entries, a->gate.num_entries); +			sgi = kzalloc(size, GFP_KERNEL); +			vsc9959_psfp_parse_gate(a, sgi); +			ret = vsc9959_psfp_sgi_table_add(ocelot, sgi); +			if (ret) { +				kfree(sgi); +				goto err; +			} +			sfi.sg_valid = 1; +			sfi.sgid = sgi->index; +			kfree(sgi); +			break; +		case FLOW_ACTION_POLICE: +			index = a->hw_index + VSC9959_PSFP_POLICER_BASE; +			if (index > VSC9959_PSFP_POLICER_MAX) { +				ret = -EINVAL; +				goto err; +			} + +			rate = a->police.rate_bytes_ps; +			burst = rate * PSCHED_NS2TICKS(a->police.burst); +			pol = (struct ocelot_policer) { +				.burst = div_u64(burst, PSCHED_TICKS_PER_SEC), +				.rate = div_u64(rate, 1000) * 8, +			}; +			ret = ocelot_vcap_policer_add(ocelot, index, &pol); +			if (ret) +				goto err; + +			sfi.fm_valid = 1; +			sfi.fmid = index; +			sfi.maxsdu = a->police.mtu; +			break; +		default: +			return -EOPNOTSUPP; +		} +	} + +	stream.ports = BIT(port); +	stream.port = port; + +	sfi.portmask = stream.ports; +	sfi.prio_valid = (stream.prio < 0 ? 0 : 1); +	sfi.prio = (sfi.prio_valid ? stream.prio : 0); +	sfi.enable = 1; + +	/* Check if stream is set. */ +	stream_entry = vsc9959_stream_table_lookup(&psfp->stream_list, &stream); +	if (stream_entry) { +		if (stream_entry->ports & BIT(port)) { +			NL_SET_ERR_MSG_MOD(extack, +					   "The stream is added on this port"); +			ret = -EEXIST; +			goto err; +		} + +		if (stream_entry->ports != BIT(stream_entry->port)) { +			NL_SET_ERR_MSG_MOD(extack, +					   "The stream is added on two ports"); +			ret = -EEXIST; +			goto err; +		} + +		stream_entry->ports |= BIT(port); +		stream.ports = stream_entry->ports; + +		sfi_entry = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, +						       stream_entry->sfid); +		memcpy(&old_sfi, sfi_entry, sizeof(old_sfi)); + +		vsc9959_psfp_sfi_table_del(ocelot, stream_entry->sfid); + +		old_sfi.portmask = stream_entry->ports; +		sfi.portmask = stream.ports; + +		if (stream_entry->port > port) { +			ret = vsc9959_psfp_sfi_table_add2(ocelot, &sfi, +							  &old_sfi); +			stream_entry->dummy = true; +		} else { +			ret = vsc9959_psfp_sfi_table_add2(ocelot, &old_sfi, +							  &sfi); +			stream.dummy = true; +		} +		if (ret) +			goto err; + +		stream_entry->sfid = old_sfi.index; +	} else { +		ret = vsc9959_psfp_sfi_table_add(ocelot, &sfi); +		if (ret) +			goto err; +	} + +	stream.sfid = sfi.index; +	stream.sfid_valid = 1; +	ret = vsc9959_stream_table_add(ocelot, &psfp->stream_list, +				       &stream, extack); +	if (ret) { +		vsc9959_psfp_sfi_table_del(ocelot, stream.sfid); +		goto err; +	} + +	return 0; + +err: +	if (sfi.sg_valid) +		vsc9959_psfp_sgi_table_del(ocelot, sfi.sgid); + +	if (sfi.fm_valid) +		ocelot_vcap_policer_del(ocelot, sfi.fmid); + +	return ret; +} + +static int vsc9959_psfp_filter_del(struct ocelot *ocelot, +				   struct flow_cls_offload *f) +{ +	struct felix_stream *stream, tmp, *stream_entry; +	static struct felix_stream_filter *sfi; +	struct ocelot_psfp_list *psfp; + +	psfp = &ocelot->psfp; + +	stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie); +	if (!stream) +		return -ENOMEM; + +	sfi = vsc9959_psfp_sfi_table_get(&psfp->sfi_list, stream->sfid); +	if (!sfi) +		return -ENOMEM; + +	if (sfi->sg_valid) +		vsc9959_psfp_sgi_table_del(ocelot, sfi->sgid); + +	if (sfi->fm_valid) +		ocelot_vcap_policer_del(ocelot, sfi->fmid); + +	vsc9959_psfp_sfi_table_del(ocelot, stream->sfid); + +	memcpy(&tmp, stream, sizeof(tmp)); + +	stream->sfid_valid = 0; +	vsc9959_stream_table_del(ocelot, stream); + +	stream_entry = vsc9959_stream_table_lookup(&psfp->stream_list, &tmp); +	if (stream_entry) { +		stream_entry->ports = BIT(stream_entry->port); +		if (stream_entry->dummy) { +			stream_entry->dummy = false; +			vsc9959_mact_stream_set(ocelot, stream_entry, NULL); +		} +		vsc9959_psfp_sfidmask_set(ocelot, stream_entry->sfid, +					  stream_entry->ports); +	} + +	return 0; +} + +static int vsc9959_psfp_stats_get(struct ocelot *ocelot, +				  struct flow_cls_offload *f, +				  struct flow_stats *stats) +{ +	struct felix_stream_filter_counters counters; +	struct ocelot_psfp_list *psfp; +	struct felix_stream *stream; + +	psfp = &ocelot->psfp; +	stream = vsc9959_stream_table_get(&psfp->stream_list, f->cookie); +	if (!stream) +		return -ENOMEM; + +	vsc9959_psfp_counters_get(ocelot, stream->sfid, &counters); + +	stats->pkts = counters.match; +	stats->drops = counters.not_pass_gate + counters.not_pass_sdu + +		       counters.red; + +	return 0; +} + +static void vsc9959_psfp_init(struct ocelot *ocelot) +{ +	struct ocelot_psfp_list *psfp = &ocelot->psfp; + +	INIT_LIST_HEAD(&psfp->stream_list); +	INIT_LIST_HEAD(&psfp->sfi_list); +	INIT_LIST_HEAD(&psfp->sgi_list); +} + +/* When using cut-through forwarding and the egress port runs at a higher data + * rate than the ingress port, the packet currently under transmission would + * suffer an underrun since it would be transmitted faster than it is received. + * The Felix switch implementation of cut-through forwarding does not check in + * hardware whether this condition is satisfied or not, so we must restrict the + * list of ports that have cut-through forwarding enabled on egress to only be + * the ports operating at the lowest link speed within their respective + * forwarding domain. + */ +static void vsc9959_cut_through_fwd(struct ocelot *ocelot) +{ +	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_switch *ds = felix->ds; +	int port, other_port; + +	lockdep_assert_held(&ocelot->fwd_domain_lock); + +	for (port = 0; port < ocelot->num_phys_ports; port++) { +		struct ocelot_port *ocelot_port = ocelot->ports[port]; +		int min_speed = ocelot_port->speed; +		unsigned long mask = 0; +		u32 tmp, val = 0; + +		/* Disable cut-through on ports that are down */ +		if (ocelot_port->speed <= 0) +			goto set; + +		if (dsa_is_cpu_port(ds, port)) { +			/* Ocelot switches forward from the NPI port towards +			 * any port, regardless of it being in the NPI port's +			 * forwarding domain or not. +			 */ +			mask = dsa_user_ports(ds); +		} else { +			mask = ocelot_get_bridge_fwd_mask(ocelot, port); +			mask &= ~BIT(port); +			if (ocelot->npi >= 0) +				mask |= BIT(ocelot->npi); +			else +				mask |= ocelot_get_dsa_8021q_cpu_mask(ocelot); +		} + +		/* Calculate the minimum link speed, among the ports that are +		 * up, of this source port's forwarding domain. +		 */ +		for_each_set_bit(other_port, &mask, ocelot->num_phys_ports) { +			struct ocelot_port *other_ocelot_port; + +			other_ocelot_port = ocelot->ports[other_port]; +			if (other_ocelot_port->speed <= 0) +				continue; + +			if (min_speed > other_ocelot_port->speed) +				min_speed = other_ocelot_port->speed; +		} + +		/* Enable cut-through forwarding for all traffic classes. */ +		if (ocelot_port->speed == min_speed) +			val = GENMASK(7, 0); + +set: +		tmp = ocelot_read_rix(ocelot, ANA_CUT_THRU_CFG, port); +		if (tmp == val) +			continue; + +		dev_dbg(ocelot->dev, +			"port %d fwd mask 0x%lx speed %d min_speed %d, %s cut-through forwarding\n", +			port, mask, ocelot_port->speed, min_speed, +			val ? "enabling" : "disabling"); + +		ocelot_write_rix(ocelot, val, ANA_CUT_THRU_CFG, port); +	} +} + +static const struct ocelot_ops vsc9959_ops = { +	.reset			= vsc9959_reset, +	.wm_enc			= vsc9959_wm_enc, +	.wm_dec			= vsc9959_wm_dec, +	.wm_stat		= vsc9959_wm_stat, +	.port_to_netdev		= felix_port_to_netdev, +	.netdev_to_port		= felix_netdev_to_port, +	.psfp_init		= vsc9959_psfp_init, +	.psfp_filter_add	= vsc9959_psfp_filter_add, +	.psfp_filter_del	= vsc9959_psfp_filter_del, +	.psfp_stats_get		= vsc9959_psfp_stats_get, +	.cut_through_fwd	= vsc9959_cut_through_fwd, +}; +  static const struct felix_info felix_info_vsc9959 = {  	.target_io_res		= vsc9959_target_io_res,  	.port_io_res		= vsc9959_port_io_res, @@ -1354,11 +2224,13 @@ static const struct felix_info felix_info_vsc9959 = {  	.stats_layout		= vsc9959_stats_layout,  	.num_stats		= ARRAY_SIZE(vsc9959_stats_layout),  	.vcap			= vsc9959_vcap_props, +	.vcap_pol_base		= VSC9959_VCAP_POLICER_BASE, +	.vcap_pol_max		= VSC9959_VCAP_POLICER_MAX, +	.vcap_pol_base2		= 0, +	.vcap_pol_max2		= 0,  	.num_mact_rows		= 2048,  	.num_ports		= 6,  	.num_tx_queues		= OCELOT_NUM_TC, -	.switch_pci_bar		= 4, -	.imdio_pci_bar		= 0,  	.quirk_no_xtr_irq	= true,  	.ptp_caps		= &vsc9959_ptp_caps,  	.mdio_bus_alloc		= vsc9959_mdio_bus_alloc, @@ -1367,6 +2239,7 @@ static const struct felix_info felix_info_vsc9959 = {  	.prevalidate_phy_mode	= vsc9959_prevalidate_phy_mode,  	.port_setup_tc		= vsc9959_port_setup_tc,  	.port_sched_speed_set	= vsc9959_sched_speed_set, +	.init_regmap		= ocelot_regmap_init,  };  static irqreturn_t felix_irq_handler(int irq, void *data) @@ -1417,10 +2290,8 @@ static int felix_pci_probe(struct pci_dev *pdev,  	ocelot->dev = &pdev->dev;  	ocelot->num_flooding_pgids = OCELOT_NUM_TC;  	felix->info = &felix_info_vsc9959; -	felix->switch_base = pci_resource_start(pdev, -						felix->info->switch_pci_bar); -	felix->imdio_base = pci_resource_start(pdev, -					       felix->info->imdio_pci_bar); +	felix->switch_base = pci_resource_start(pdev, VSC9959_SWITCH_PCI_BAR); +	felix->imdio_base = pci_resource_start(pdev, VSC9959_IMDIO_PCI_BAR);  	pci_set_master(pdev); diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 92eae63150ea..8c1c9da61602 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -6,18 +6,18 @@  #include <soc/mscc/ocelot_vcap.h>  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot.h> +#include <linux/mdio/mdio-mscc-miim.h> +#include <linux/of_mdio.h>  #include <linux/of_platform.h>  #include <linux/pcs-lynx.h>  #include <linux/dsa/ocelot.h>  #include <linux/iopoll.h>  #include "felix.h" -#define MSCC_MIIM_CMD_OPR_WRITE			BIT(1) -#define MSCC_MIIM_CMD_OPR_READ			BIT(2) -#define MSCC_MIIM_CMD_WRDATA_SHIFT		4 -#define MSCC_MIIM_CMD_REGAD_SHIFT		20 -#define MSCC_MIIM_CMD_PHYAD_SHIFT		25 -#define MSCC_MIIM_CMD_VLD			BIT(31) +#define VSC9953_VCAP_POLICER_BASE		11 +#define VSC9953_VCAP_POLICER_MAX		31 +#define VSC9953_VCAP_POLICER_BASE2		120 +#define VSC9953_VCAP_POLICER_MAX2		161  static const u32 vsc9953_ana_regmap[] = {  	REG(ANA_ADVLEARN,			0x00b500), @@ -857,7 +857,6 @@ static struct vcap_props vsc9953_vcap_props[] = {  #define VSC9953_INIT_TIMEOUT			50000  #define VSC9953_GCB_RST_SLEEP			100  #define VSC9953_SYS_RAMINIT_SLEEP		80 -#define VCS9953_MII_TIMEOUT			10000  static int vsc9953_gcb_soft_rst_status(struct ocelot *ocelot)  { @@ -877,82 +876,6 @@ static int vsc9953_sys_ram_init_status(struct ocelot *ocelot)  	return val;  } -static int vsc9953_gcb_miim_pending_status(struct ocelot *ocelot) -{ -	int val; - -	ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_PENDING, &val); - -	return val; -} - -static int vsc9953_gcb_miim_busy_status(struct ocelot *ocelot) -{ -	int val; - -	ocelot_field_read(ocelot, GCB_MIIM_MII_STATUS_BUSY, &val); - -	return val; -} - -static int vsc9953_mdio_write(struct mii_bus *bus, int phy_id, int regnum, -			      u16 value) -{ -	struct ocelot *ocelot = bus->priv; -	int err, cmd, val; - -	/* Wait while MIIM controller becomes idle */ -	err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot, -				 val, !val, 10, VCS9953_MII_TIMEOUT); -	if (err) { -		dev_err(ocelot->dev, "MDIO write: pending timeout\n"); -		goto out; -	} - -	cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | -	      (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | -	      (value << MSCC_MIIM_CMD_WRDATA_SHIFT) | -	      MSCC_MIIM_CMD_OPR_WRITE; - -	ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD); - -out: -	return err; -} - -static int vsc9953_mdio_read(struct mii_bus *bus, int phy_id, int regnum) -{ -	struct ocelot *ocelot = bus->priv; -	int err, cmd, val; - -	/* Wait until MIIM controller becomes idle */ -	err = readx_poll_timeout(vsc9953_gcb_miim_pending_status, ocelot, -				 val, !val, 10, VCS9953_MII_TIMEOUT); -	if (err) { -		dev_err(ocelot->dev, "MDIO read: pending timeout\n"); -		goto out; -	} - -	/* Write the MIIM COMMAND register */ -	cmd = MSCC_MIIM_CMD_VLD | (phy_id << MSCC_MIIM_CMD_PHYAD_SHIFT) | -	      (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ; - -	ocelot_write(ocelot, cmd, GCB_MIIM_MII_CMD); - -	/* Wait while read operation via the MIIM controller is in progress */ -	err = readx_poll_timeout(vsc9953_gcb_miim_busy_status, ocelot, -				 val, !val, 10, VCS9953_MII_TIMEOUT); -	if (err) { -		dev_err(ocelot->dev, "MDIO read: busy timeout\n"); -		goto out; -	} - -	val = ocelot_read(ocelot, GCB_MIIM_MII_DATA); - -	err = val & 0xFFFF; -out: -	return err; -}  /* CORE_ENA is in SYS:SYSTEM:RESET_CFG   * MEM_INIT is in SYS:SYSTEM:RESET_CFG @@ -1089,26 +1012,24 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)  	int rc;  	felix->pcs = devm_kcalloc(dev, felix->info->num_ports, -				  sizeof(struct phy_device *), +				  sizeof(struct phylink_pcs *),  				  GFP_KERNEL);  	if (!felix->pcs) {  		dev_err(dev, "failed to allocate array for PCS PHYs\n");  		return -ENOMEM;  	} -	bus = devm_mdiobus_alloc(dev); -	if (!bus) -		return -ENOMEM; +	rc = mscc_miim_setup(dev, &bus, "VSC9953 internal MDIO bus", +			     ocelot->targets[GCB], +			     ocelot->map[GCB][GCB_MIIM_MII_STATUS & REG_MASK]); -	bus->name = "VSC9953 internal MDIO bus"; -	bus->read = vsc9953_mdio_read; -	bus->write = vsc9953_mdio_write; -	bus->parent = dev; -	bus->priv = ocelot; -	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); +	if (rc) { +		dev_err(dev, "failed to setup MDIO bus\n"); +		return rc; +	}  	/* Needed in order to initialize the bus mutex lock */ -	rc = mdiobus_register(bus); +	rc = of_mdiobus_register(bus, NULL);  	if (rc < 0) {  		dev_err(dev, "failed to register MDIO bus\n");  		return rc; @@ -1118,9 +1039,9 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)  	for (port = 0; port < felix->info->num_ports; port++) {  		struct ocelot_port *ocelot_port = ocelot->ports[port]; +		struct phylink_pcs *phylink_pcs; +		struct mdio_device *mdio_device;  		int addr = port + 4; -		struct mdio_device *pcs; -		struct lynx_pcs *lynx;  		if (dsa_is_unused_port(felix->ds, port))  			continue; @@ -1128,17 +1049,17 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)  		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)  			continue; -		pcs = mdio_device_create(felix->imdio, addr); -		if (IS_ERR(pcs)) +		mdio_device = mdio_device_create(felix->imdio, addr); +		if (IS_ERR(mdio_device))  			continue; -		lynx = lynx_pcs_create(pcs); -		if (!lynx) { -			mdio_device_free(pcs); +		phylink_pcs = lynx_pcs_create(mdio_device); +		if (!phylink_pcs) { +			mdio_device_free(mdio_device);  			continue;  		} -		felix->pcs[port] = lynx; +		felix->pcs[port] = phylink_pcs;  		dev_info(dev, "Found PCS at internal MDIO address %d\n", addr);  	} @@ -1152,13 +1073,15 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)  	int port;  	for (port = 0; port < ocelot->num_phys_ports; port++) { -		struct lynx_pcs *pcs = felix->pcs[port]; +		struct phylink_pcs *phylink_pcs = felix->pcs[port]; +		struct mdio_device *mdio_device; -		if (!pcs) +		if (!phylink_pcs)  			continue; -		mdio_device_free(pcs->mdio); -		lynx_pcs_destroy(pcs); +		mdio_device = lynx_get_mdio_device(phylink_pcs); +		mdio_device_free(mdio_device); +		lynx_pcs_destroy(phylink_pcs);  	}  	mdiobus_unregister(felix->imdio);  } @@ -1172,6 +1095,10 @@ static const struct felix_info seville_info_vsc9953 = {  	.stats_layout		= vsc9953_stats_layout,  	.num_stats		= ARRAY_SIZE(vsc9953_stats_layout),  	.vcap			= vsc9953_vcap_props, +	.vcap_pol_base		= VSC9953_VCAP_POLICER_BASE, +	.vcap_pol_max		= VSC9953_VCAP_POLICER_MAX, +	.vcap_pol_base2		= VSC9953_VCAP_POLICER_BASE2, +	.vcap_pol_max2		= VSC9953_VCAP_POLICER_MAX2,  	.num_mact_rows		= 2048,  	.num_ports		= 10,  	.num_tx_queues		= OCELOT_NUM_TC, @@ -1179,6 +1106,7 @@ static const struct felix_info seville_info_vsc9953 = {  	.mdio_bus_free		= vsc9953_mdio_bus_free,  	.phylink_validate	= vsc9953_phylink_validate,  	.prevalidate_phy_mode	= vsc9953_prevalidate_phy_mode, +	.init_regmap		= ocelot_regmap_init,  };  static int seville_probe(struct platform_device *pdev) diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 147ca39531a3..039694518788 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -9,6 +9,8 @@  #include <linux/module.h>  #include <linux/phy.h>  #include <linux/netdevice.h> +#include <linux/bitfield.h> +#include <linux/regmap.h>  #include <net/dsa.h>  #include <linux/of_net.h>  #include <linux/of_mdio.h> @@ -68,6 +70,8 @@ static const struct qca8k_mib_desc ar8327_mib[] = {  	MIB_DESC(1, 0x9c, "TxExcDefer"),  	MIB_DESC(1, 0xa0, "TxDefer"),  	MIB_DESC(1, 0xa4, "TxLateCol"), +	MIB_DESC(1, 0xa8, "RXUnicast"), +	MIB_DESC(1, 0xac, "TXUnicast"),  };  /* The 32bit switch registers are accessed indirectly. To achieve this we need @@ -151,6 +155,25 @@ qca8k_set_page(struct mii_bus *bus, u16 page)  static int  qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val)  { +	return regmap_read(priv->regmap, reg, val); +} + +static int +qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) +{ +	return regmap_write(priv->regmap, reg, val); +} + +static int +qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +{ +	return regmap_update_bits(priv->regmap, reg, mask, write_val); +} + +static int +qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) +{ +	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;  	struct mii_bus *bus = priv->bus;  	u16 r1, r2, page;  	int ret; @@ -171,8 +194,9 @@ exit:  }  static int -qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) +qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)  { +	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;  	struct mii_bus *bus = priv->bus;  	u16 r1, r2, page;  	int ret; @@ -193,8 +217,9 @@ exit:  }  static int -qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) +qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_val)  { +	struct qca8k_priv *priv = (struct qca8k_priv *)ctx;  	struct mii_bus *bus = priv->bus;  	u16 r1, r2, page;  	u32 val; @@ -222,34 +247,6 @@ exit:  	return ret;  } -static int -qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val) -{ -	return qca8k_rmw(priv, reg, 0, val); -} - -static int -qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val) -{ -	return qca8k_rmw(priv, reg, val, 0); -} - -static int -qca8k_regmap_read(void *ctx, uint32_t reg, uint32_t *val) -{ -	struct qca8k_priv *priv = (struct qca8k_priv *)ctx; - -	return qca8k_read(priv, reg, val); -} - -static int -qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val) -{ -	struct qca8k_priv *priv = (struct qca8k_priv *)ctx; - -	return qca8k_write(priv, reg, val); -} -  static const struct regmap_range qca8k_readable_ranges[] = {  	regmap_reg_range(0x0000, 0x00e4), /* Global control */  	regmap_reg_range(0x0100, 0x0168), /* EEE control */ @@ -281,26 +278,19 @@ static struct regmap_config qca8k_regmap_config = {  	.max_register = 0x16ac, /* end MIB - Port6 range */  	.reg_read = qca8k_regmap_read,  	.reg_write = qca8k_regmap_write, +	.reg_update_bits = qca8k_regmap_update_bits,  	.rd_table = &qca8k_readable_table, +	.disable_locking = true, /* Locking is handled by qca8k read/write */ +	.cache_type = REGCACHE_NONE, /* Explicitly disable CACHE */  };  static int  qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask)  { -	int ret, ret1;  	u32 val; -	ret = read_poll_timeout(qca8k_read, ret1, !(val & mask), -				0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, -				priv, reg, &val); - -	/* Check if qca8k_read has failed for a different reason -	 * before returning -ETIMEDOUT -	 */ -	if (ret < 0 && ret1 < 0) -		return ret1; - -	return ret; +	return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, +				       QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC);  }  static int @@ -319,18 +309,18 @@ qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)  	}  	/* vid - 83:72 */ -	fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M; +	fdb->vid = FIELD_GET(QCA8K_ATU_VID_MASK, reg[2]);  	/* aging - 67:64 */ -	fdb->aging = reg[2] & QCA8K_ATU_STATUS_M; +	fdb->aging = FIELD_GET(QCA8K_ATU_STATUS_MASK, reg[2]);  	/* portmask - 54:48 */ -	fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M; +	fdb->port_mask = FIELD_GET(QCA8K_ATU_PORT_MASK, reg[1]);  	/* mac - 47:0 */ -	fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff; -	fdb->mac[1] = reg[1] & 0xff; -	fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff; -	fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff; -	fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff; -	fdb->mac[5] = reg[0] & 0xff; +	fdb->mac[0] = FIELD_GET(QCA8K_ATU_ADDR0_MASK, reg[1]); +	fdb->mac[1] = FIELD_GET(QCA8K_ATU_ADDR1_MASK, reg[1]); +	fdb->mac[2] = FIELD_GET(QCA8K_ATU_ADDR2_MASK, reg[0]); +	fdb->mac[3] = FIELD_GET(QCA8K_ATU_ADDR3_MASK, reg[0]); +	fdb->mac[4] = FIELD_GET(QCA8K_ATU_ADDR4_MASK, reg[0]); +	fdb->mac[5] = FIELD_GET(QCA8K_ATU_ADDR5_MASK, reg[0]);  	return 0;  } @@ -343,18 +333,18 @@ qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,  	int i;  	/* vid - 83:72 */ -	reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S; +	reg[2] = FIELD_PREP(QCA8K_ATU_VID_MASK, vid);  	/* aging - 67:64 */ -	reg[2] |= aging & QCA8K_ATU_STATUS_M; +	reg[2] |= FIELD_PREP(QCA8K_ATU_STATUS_MASK, aging);  	/* portmask - 54:48 */ -	reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S; +	reg[1] = FIELD_PREP(QCA8K_ATU_PORT_MASK, port_mask);  	/* mac - 47:0 */ -	reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S; -	reg[1] |= mac[1]; -	reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S; -	reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S; -	reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S; -	reg[0] |= mac[5]; +	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR0_MASK, mac[0]); +	reg[1] |= FIELD_PREP(QCA8K_ATU_ADDR1_MASK, mac[1]); +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR2_MASK, mac[2]); +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR3_MASK, mac[3]); +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR4_MASK, mac[4]); +	reg[0] |= FIELD_PREP(QCA8K_ATU_ADDR5_MASK, mac[5]);  	/* load the array into the ARL table */  	for (i = 0; i < 3; i++) @@ -372,7 +362,7 @@ qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)  	reg |= cmd;  	if (port >= 0) {  		reg |= QCA8K_ATU_FUNC_PORT_EN; -		reg |= (port & QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S; +		reg |= FIELD_PREP(QCA8K_ATU_FUNC_PORT_MASK, port);  	}  	/* Write the function register triggering the table access */ @@ -446,6 +436,81 @@ qca8k_fdb_flush(struct qca8k_priv *priv)  }  static int +qca8k_fdb_search_and_insert(struct qca8k_priv *priv, u8 port_mask, +			    const u8 *mac, u16 vid) +{ +	struct qca8k_fdb fdb = { 0 }; +	int ret; + +	mutex_lock(&priv->reg_mutex); + +	qca8k_fdb_write(priv, vid, 0, mac, 0); +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +	if (ret < 0) +		goto exit; + +	ret = qca8k_fdb_read(priv, &fdb); +	if (ret < 0) +		goto exit; + +	/* Rule exist. Delete first */ +	if (!fdb.aging) { +		ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +		if (ret) +			goto exit; +	} + +	/* Add port to fdb portmask */ +	fdb.port_mask |= port_mask; + +	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); + +exit: +	mutex_unlock(&priv->reg_mutex); +	return ret; +} + +static int +qca8k_fdb_search_and_del(struct qca8k_priv *priv, u8 port_mask, +			 const u8 *mac, u16 vid) +{ +	struct qca8k_fdb fdb = { 0 }; +	int ret; + +	mutex_lock(&priv->reg_mutex); + +	qca8k_fdb_write(priv, vid, 0, mac, 0); +	ret = qca8k_fdb_access(priv, QCA8K_FDB_SEARCH, -1); +	if (ret < 0) +		goto exit; + +	/* Rule doesn't exist. Why delete? */ +	if (!fdb.aging) { +		ret = -EINVAL; +		goto exit; +	} + +	ret = qca8k_fdb_access(priv, QCA8K_FDB_PURGE, -1); +	if (ret) +		goto exit; + +	/* Only port in the rule is this port. Don't re insert */ +	if (fdb.port_mask == port_mask) +		goto exit; + +	/* Remove port from port mask */ +	fdb.port_mask &= ~port_mask; + +	qca8k_fdb_write(priv, vid, fdb.port_mask, mac, fdb.aging); +	ret = qca8k_fdb_access(priv, QCA8K_FDB_LOAD, -1); + +exit: +	mutex_unlock(&priv->reg_mutex); +	return ret; +} + +static int  qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)  {  	u32 reg; @@ -454,7 +519,7 @@ qca8k_vlan_access(struct qca8k_priv *priv, enum qca8k_vlan_cmd cmd, u16 vid)  	/* Set the command and VLAN index */  	reg = QCA8K_VTU_FUNC1_BUSY;  	reg |= cmd; -	reg |= vid << QCA8K_VTU_FUNC1_VID_S; +	reg |= FIELD_PREP(QCA8K_VTU_FUNC1_VID_MASK, vid);  	/* Write the function register triggering the table access */  	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC1, reg); @@ -500,13 +565,11 @@ qca8k_vlan_add(struct qca8k_priv *priv, u8 port, u16 vid, bool untagged)  	if (ret < 0)  		goto out;  	reg |= QCA8K_VTU_FUNC0_VALID | QCA8K_VTU_FUNC0_IVL_EN; -	reg &= ~(QCA8K_VTU_FUNC0_EG_MODE_MASK << QCA8K_VTU_FUNC0_EG_MODE_S(port)); +	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port);  	if (untagged) -		reg |= QCA8K_VTU_FUNC0_EG_MODE_UNTAG << -				QCA8K_VTU_FUNC0_EG_MODE_S(port); +		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(port);  	else -		reg |= QCA8K_VTU_FUNC0_EG_MODE_TAG << -				QCA8K_VTU_FUNC0_EG_MODE_S(port); +		reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(port);  	ret = qca8k_write(priv, QCA8K_REG_VTU_FUNC0, reg);  	if (ret) @@ -534,15 +597,13 @@ qca8k_vlan_del(struct qca8k_priv *priv, u8 port, u16 vid)  	ret = qca8k_read(priv, QCA8K_REG_VTU_FUNC0, ®);  	if (ret < 0)  		goto out; -	reg &= ~(3 << QCA8K_VTU_FUNC0_EG_MODE_S(port)); -	reg |= QCA8K_VTU_FUNC0_EG_MODE_NOT << -			QCA8K_VTU_FUNC0_EG_MODE_S(port); +	reg &= ~QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(port); +	reg |= QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(port);  	/* Check if we're the last member to be removed */  	del = true;  	for (i = 0; i < QCA8K_NUM_PORTS; i++) { -		mask = QCA8K_VTU_FUNC0_EG_MODE_NOT; -		mask <<= QCA8K_VTU_FUNC0_EG_MODE_S(i); +		mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i);  		if ((reg & mask) != mask) {  			del = false; @@ -571,7 +632,7 @@ qca8k_mib_init(struct qca8k_priv *priv)  	int ret;  	mutex_lock(&priv->reg_mutex); -	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY); +	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_FLUSH | QCA8K_MIB_BUSY);  	if (ret)  		goto exit; @@ -579,7 +640,7 @@ qca8k_mib_init(struct qca8k_priv *priv)  	if (ret)  		goto exit; -	ret = qca8k_reg_set(priv, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); +	ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP);  	if (ret)  		goto exit; @@ -600,9 +661,9 @@ qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable)  		mask |= QCA8K_PORT_STATUS_LINK_AUTO;  	if (enable) -		qca8k_reg_set(priv, QCA8K_REG_PORT_STATUS(port), mask); +		regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);  	else -		qca8k_reg_clear(priv, QCA8K_REG_PORT_STATUS(port), mask); +		regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask);  }  static u32 @@ -864,8 +925,8 @@ qca8k_setup_mdio_bus(struct qca8k_priv *priv)  		 * a dt-overlay and driver reload changed the configuration  		 */ -		return qca8k_reg_clear(priv, QCA8K_MDIO_MASTER_CTRL, -				       QCA8K_MDIO_MASTER_EN); +		return regmap_clear_bits(priv->regmap, QCA8K_MDIO_MASTER_CTRL, +					 QCA8K_MDIO_MASTER_EN);  	}  	/* Check if the devicetree declare the port:phy mapping */ @@ -983,7 +1044,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)  	u32 delay;  	/* We have 2 CPU port. Check them */ -	for (port = 0; port < QCA8K_NUM_PORTS && cpu_port_index < QCA8K_NUM_CPU_PORTS; port++) { +	for (port = 0; port < QCA8K_NUM_PORTS; port++) {  		/* Skip every other port */  		if (port != 0 && port != 6)  			continue; @@ -1014,7 +1075,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)  				 mode == PHY_INTERFACE_MODE_RGMII_TXID)  				delay = 1; -			if (delay > QCA8K_MAX_DELAY) { +			if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, delay)) {  				dev_err(priv->dev, "rgmii tx delay is limited to a max value of 3ns, setting to the max value");  				delay = 3;  			} @@ -1030,7 +1091,7 @@ qca8k_parse_port_config(struct qca8k_priv *priv)  				 mode == PHY_INTERFACE_MODE_RGMII_RXID)  				delay = 2; -			if (delay > QCA8K_MAX_DELAY) { +			if (!FIELD_FIT(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, delay)) {  				dev_err(priv->dev, "rgmii rx delay is limited to a max value of 3ns, setting to the max value");  				delay = 3;  			} @@ -1089,14 +1150,6 @@ qca8k_setup(struct dsa_switch *ds)  	if (ret)  		return ret; -	mutex_init(&priv->reg_mutex); - -	/* Start by setting up the register mapping */ -	priv->regmap = devm_regmap_init(ds->dev, NULL, priv, -					&qca8k_regmap_config); -	if (IS_ERR(priv->regmap)) -		dev_warn(priv->dev, "regmap initialization failed"); -  	ret = qca8k_setup_mdio_bus(priv);  	if (ret)  		return ret; @@ -1110,16 +1163,16 @@ qca8k_setup(struct dsa_switch *ds)  		return ret;  	/* Make sure MAC06 is disabled */ -	ret = qca8k_reg_clear(priv, QCA8K_REG_PORT0_PAD_CTRL, -			      QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN); +	ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT0_PAD_CTRL, +				QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN);  	if (ret) {  		dev_err(priv->dev, "failed disabling MAC06 exchange");  		return ret;  	}  	/* Enable CPU Port */ -	ret = qca8k_reg_set(priv, QCA8K_REG_GLOBAL_FW_CTRL0, -			    QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN); +	ret = regmap_set_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +			      QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN);  	if (ret) {  		dev_err(priv->dev, "failed enabling CPU port");  		return ret; @@ -1141,8 +1194,8 @@ qca8k_setup(struct dsa_switch *ds)  		/* Enable QCA header mode on all cpu ports */  		if (dsa_is_cpu_port(ds, i)) {  			ret = qca8k_write(priv, QCA8K_REG_PORT_HDR_CTRL(i), -					  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_TX_S | -					  QCA8K_PORT_HDR_CTRL_ALL << QCA8K_PORT_HDR_CTRL_RX_S); +					  FIELD_PREP(QCA8K_PORT_HDR_CTRL_TX_MASK, QCA8K_PORT_HDR_CTRL_ALL) | +					  FIELD_PREP(QCA8K_PORT_HDR_CTRL_RX_MASK, QCA8K_PORT_HDR_CTRL_ALL));  			if (ret) {  				dev_err(priv->dev, "failed enabling QCA header mode");  				return ret; @@ -1159,10 +1212,10 @@ qca8k_setup(struct dsa_switch *ds)  	 * for igmp, unknown, multicast and broadcast packet  	 */  	ret = qca8k_write(priv, QCA8K_REG_GLOBAL_FW_CTRL1, -			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S | -			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_BC_DP_S | -			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_MC_DP_S | -			  BIT(cpu_port) << QCA8K_GLOBAL_FW_CTRL1_UC_DP_S); +			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK, BIT(cpu_port)) | +			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK, BIT(cpu_port)) | +			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK, BIT(cpu_port)) | +			  FIELD_PREP(QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK, BIT(cpu_port)));  	if (ret)  		return ret; @@ -1180,8 +1233,6 @@ qca8k_setup(struct dsa_switch *ds)  		/* Individual user ports get connected to CPU port only */  		if (dsa_is_user_port(ds, i)) { -			int shift = 16 * (i % 2); -  			ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(i),  					QCA8K_PORT_LOOKUP_MEMBER,  					BIT(cpu_port)); @@ -1189,8 +1240,8 @@ qca8k_setup(struct dsa_switch *ds)  				return ret;  			/* Enable ARP Auto-learning by default */ -			ret = qca8k_reg_set(priv, QCA8K_PORT_LOOKUP_CTRL(i), -					    QCA8K_PORT_LOOKUP_LEARN); +			ret = regmap_set_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(i), +					      QCA8K_PORT_LOOKUP_LEARN);  			if (ret)  				return ret; @@ -1198,8 +1249,8 @@ qca8k_setup(struct dsa_switch *ds)  			 * default egress vid  			 */  			ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(i), -					0xfff << shift, -					QCA8K_PORT_VID_DEF << shift); +					QCA8K_EGREES_VLAN_PORT_MASK(i), +					QCA8K_EGREES_VLAN_PORT(i, QCA8K_PORT_VID_DEF));  			if (ret)  				return ret; @@ -1246,7 +1297,7 @@ qca8k_setup(struct dsa_switch *ds)  			QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |  			QCA8K_PORT_HOL_CTRL1_WRED_EN;  			qca8k_rmw(priv, QCA8K_REG_PORT_HOL_CTRL1(i), -				  QCA8K_PORT_HOL_CTRL1_ING_BUF | +				  QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK |  				  QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN |  				  QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN |  				  QCA8K_PORT_HOL_CTRL1_WRED_EN, @@ -1269,8 +1320,8 @@ qca8k_setup(struct dsa_switch *ds)  		mask = QCA8K_GLOBAL_FC_GOL_XON_THRES(288) |  		       QCA8K_GLOBAL_FC_GOL_XOFF_THRES(496);  		qca8k_rmw(priv, QCA8K_REG_GLOBAL_FC_THRESH, -			  QCA8K_GLOBAL_FC_GOL_XON_THRES_S | -			  QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S, +			  QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK | +			  QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK,  			  mask);  	} @@ -1285,6 +1336,13 @@ qca8k_setup(struct dsa_switch *ds)  	/* We don't have interrupts for link changes, so we need to poll */  	ds->pcs_poll = true; +	/* Set min a max ageing value supported */ +	ds->ageing_time_min = 7000; +	ds->ageing_time_max = 458745000; + +	/* Set max number of LAGs supported */ +	ds->num_lag_ids = QCA8K_NUM_LAGS; +  	return 0;  } @@ -1631,12 +1689,16 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,  static void  qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data)  { +	const struct qca8k_match_data *match_data; +	struct qca8k_priv *priv = ds->priv;  	int i;  	if (stringset != ETH_SS_STATS)  		return; -	for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) +	match_data = of_device_get_match_data(priv->dev); + +	for (i = 0; i < match_data->mib_count; i++)  		strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name,  			ETH_GSTRING_LEN);  } @@ -1646,12 +1708,15 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,  			uint64_t *data)  {  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; +	const struct qca8k_match_data *match_data;  	const struct qca8k_mib_desc *mib;  	u32 reg, i, val;  	u32 hi = 0;  	int ret; -	for (i = 0; i < ARRAY_SIZE(ar8327_mib); i++) { +	match_data = of_device_get_match_data(priv->dev); + +	for (i = 0; i < match_data->mib_count; i++) {  		mib = &ar8327_mib[i];  		reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; @@ -1674,10 +1739,15 @@ qca8k_get_ethtool_stats(struct dsa_switch *ds, int port,  static int  qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset)  { +	const struct qca8k_match_data *match_data; +	struct qca8k_priv *priv = ds->priv; +  	if (sset != ETH_SS_STATS)  		return 0; -	return ARRAY_SIZE(ar8327_mib); +	match_data = of_device_get_match_data(priv->dev); + +	return match_data->mib_count;  }  static int @@ -1740,8 +1810,9 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)  		  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);  } -static int -qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) +static int qca8k_port_bridge_join(struct dsa_switch *ds, int port, +				  struct dsa_bridge bridge, +				  bool *tx_fwd_offload)  {  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;  	int port_mask, cpu_port; @@ -1753,14 +1824,14 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)  	for (i = 0; i < QCA8K_NUM_PORTS; i++) {  		if (dsa_is_cpu_port(ds, i))  			continue; -		if (dsa_to_port(ds, i)->bridge_dev != br) +		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 = qca8k_reg_set(priv, -				    QCA8K_PORT_LOOKUP_CTRL(i), -				    BIT(port)); +		ret = regmap_set_bits(priv->regmap, +				      QCA8K_PORT_LOOKUP_CTRL(i), +				      BIT(port));  		if (ret)  			return ret;  		if (i != port) @@ -1774,8 +1845,8 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)  	return ret;  } -static void -qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) +static void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, +				    struct dsa_bridge bridge)  {  	struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;  	int cpu_port, i; @@ -1785,14 +1856,14 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)  	for (i = 0; i < QCA8K_NUM_PORTS; i++) {  		if (dsa_is_cpu_port(ds, i))  			continue; -		if (dsa_to_port(ds, i)->bridge_dev != br) +		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  		 */ -		qca8k_reg_clear(priv, -				QCA8K_PORT_LOOKUP_CTRL(i), -				BIT(port)); +		regmap_clear_bits(priv->regmap, +				  QCA8K_PORT_LOOKUP_CTRL(i), +				  BIT(port));  	}  	/* Set the cpu port to be the only one in the portvlan mask of @@ -1802,6 +1873,36 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)  		  QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port));  } +static void +qca8k_port_fast_age(struct dsa_switch *ds, int port) +{ +	struct qca8k_priv *priv = ds->priv; + +	mutex_lock(&priv->reg_mutex); +	qca8k_fdb_access(priv, QCA8K_FDB_FLUSH_PORT, port); +	mutex_unlock(&priv->reg_mutex); +} + +static int +qca8k_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ +	struct qca8k_priv *priv = ds->priv; +	unsigned int secs = msecs / 1000; +	u32 val; + +	/* AGE_TIME reg is set in 7s step */ +	val = secs / 7; + +	/* Handle case with 0 as val to NOT disable +	 * learning +	 */ +	if (!val) +		val = 1; + +	return regmap_update_bits(priv->regmap, QCA8K_REG_ATU_CTRL, QCA8K_ATU_AGE_TIME_MASK, +				  QCA8K_ATU_AGE_TIME(val)); +} +  static int  qca8k_port_enable(struct dsa_switch *ds, int port,  		  struct phy_device *phy) @@ -1908,6 +2009,121 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,  }  static int +qca8k_port_mdb_add(struct dsa_switch *ds, int port, +		   const struct switchdev_obj_port_mdb *mdb) +{ +	struct qca8k_priv *priv = ds->priv; +	const u8 *addr = mdb->addr; +	u16 vid = mdb->vid; + +	return qca8k_fdb_search_and_insert(priv, BIT(port), addr, vid); +} + +static int +qca8k_port_mdb_del(struct dsa_switch *ds, int port, +		   const struct switchdev_obj_port_mdb *mdb) +{ +	struct qca8k_priv *priv = ds->priv; +	const u8 *addr = mdb->addr; +	u16 vid = mdb->vid; + +	return qca8k_fdb_search_and_del(priv, BIT(port), addr, vid); +} + +static int +qca8k_port_mirror_add(struct dsa_switch *ds, int port, +		      struct dsa_mall_mirror_tc_entry *mirror, +		      bool ingress) +{ +	struct qca8k_priv *priv = ds->priv; +	int monitor_port, ret; +	u32 reg, val; + +	/* Check for existent entry */ +	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) +		return -EEXIST; + +	ret = regmap_read(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, &val); +	if (ret) +		return ret; + +	/* QCA83xx can have only one port set to mirror mode. +	 * Check that the correct port is requested and return error otherwise. +	 * When no mirror port is set, the values is set to 0xF +	 */ +	monitor_port = FIELD_GET(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +	if (monitor_port != 0xF && monitor_port != mirror->to_local_port) +		return -EEXIST; + +	/* Set the monitor port */ +	val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, +			 mirror->to_local_port); +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +				 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +	if (ret) +		return ret; + +	if (ingress) { +		reg = QCA8K_PORT_LOOKUP_CTRL(port); +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +	} else { +		reg = QCA8K_REG_PORT_HOL_CTRL1(port); +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +	} + +	ret = regmap_update_bits(priv->regmap, reg, val, val); +	if (ret) +		return ret; + +	/* Track mirror port for tx and rx to decide when the +	 * mirror port has to be disabled. +	 */ +	if (ingress) +		priv->mirror_rx |= BIT(port); +	else +		priv->mirror_tx |= BIT(port); + +	return 0; +} + +static void +qca8k_port_mirror_del(struct dsa_switch *ds, int port, +		      struct dsa_mall_mirror_tc_entry *mirror) +{ +	struct qca8k_priv *priv = ds->priv; +	u32 reg, val; +	int ret; + +	if (mirror->ingress) { +		reg = QCA8K_PORT_LOOKUP_CTRL(port); +		val = QCA8K_PORT_LOOKUP_ING_MIRROR_EN; +	} else { +		reg = QCA8K_REG_PORT_HOL_CTRL1(port); +		val = QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN; +	} + +	ret = regmap_clear_bits(priv->regmap, reg, val); +	if (ret) +		goto err; + +	if (mirror->ingress) +		priv->mirror_rx &= ~BIT(port); +	else +		priv->mirror_tx &= ~BIT(port); + +	/* No port set to send packet to mirror port. Disable mirror port */ +	if (!priv->mirror_rx && !priv->mirror_tx) { +		val = FIELD_PREP(QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, 0xF); +		ret = regmap_update_bits(priv->regmap, QCA8K_REG_GLOBAL_FW_CTRL0, +					 QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM, val); +		if (ret) +			goto err; +	} +err: +	dev_err(priv->dev, "Failed to del mirror port from %d", port); +} + +static int  qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,  			  struct netlink_ext_ack *extack)  { @@ -1916,11 +2132,11 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,  	if (vlan_filtering) {  		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -				QCA8K_PORT_LOOKUP_VLAN_MODE, +				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,  				QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE);  	} else {  		ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), -				QCA8K_PORT_LOOKUP_VLAN_MODE, +				QCA8K_PORT_LOOKUP_VLAN_MODE_MASK,  				QCA8K_PORT_LOOKUP_VLAN_MODE_NONE);  	} @@ -1944,10 +2160,9 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port,  	}  	if (pvid) { -		int shift = 16 * (port % 2); -  		ret = qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port), -				0xfff << shift, vlan->vid << shift); +				QCA8K_EGREES_VLAN_PORT_MASK(port), +				QCA8K_EGREES_VLAN_PORT(port, vlan->vid));  		if (ret)  			return ret; @@ -1996,12 +2211,185 @@ qca8k_get_tag_protocol(struct dsa_switch *ds, int port,  	return DSA_TAG_PROTO_QCA;  } +static bool +qca8k_lag_can_offload(struct dsa_switch *ds, +		      struct net_device *lag, +		      struct netdev_lag_upper_info *info) +{ +	struct dsa_port *dp; +	int id, members = 0; + +	id = dsa_lag_id(ds->dst, lag); +	if (id < 0 || id >= ds->num_lag_ids) +		return false; + +	dsa_lag_foreach_port(dp, ds->dst, lag) +		/* Includes the port joining the LAG */ +		members++; + +	if (members > QCA8K_NUM_PORTS_FOR_LAG) +		return false; + +	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) +		return false; + +	if (info->hash_type != NETDEV_LAG_HASH_L2 && +	    info->hash_type != NETDEV_LAG_HASH_L23) +		return false; + +	return true; +} + +static int +qca8k_lag_setup_hash(struct dsa_switch *ds, +		     struct net_device *lag, +		     struct netdev_lag_upper_info *info) +{ +	struct qca8k_priv *priv = ds->priv; +	bool unique_lag = true; +	u32 hash = 0; +	int i, id; + +	id = dsa_lag_id(ds->dst, lag); + +	switch (info->hash_type) { +	case NETDEV_LAG_HASH_L23: +		hash |= QCA8K_TRUNK_HASH_SIP_EN; +		hash |= QCA8K_TRUNK_HASH_DIP_EN; +		fallthrough; +	case NETDEV_LAG_HASH_L2: +		hash |= QCA8K_TRUNK_HASH_SA_EN; +		hash |= QCA8K_TRUNK_HASH_DA_EN; +		break; +	default: /* We should NEVER reach this */ +		return -EOPNOTSUPP; +	} + +	/* Check if we are the unique configured LAG */ +	dsa_lags_foreach_id(i, ds->dst) +		if (i != id && dsa_lag_dev(ds->dst, i)) { +			unique_lag = false; +			break; +		} + +	/* Hash Mode is global. Make sure the same Hash Mode +	 * is set to all the 4 possible lag. +	 * If we are the unique LAG we can set whatever hash +	 * mode we want. +	 * To change hash mode it's needed to remove all LAG +	 * and change the mode with the latest. +	 */ +	if (unique_lag) { +		priv->lag_hash_mode = hash; +	} else if (priv->lag_hash_mode != hash) { +		netdev_err(lag, "Error: Mismatched Hash Mode across different lag is not supported\n"); +		return -EOPNOTSUPP; +	} + +	return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL, +				  QCA8K_TRUNK_HASH_MASK, hash); +} + +static int +qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port, +			  struct net_device *lag, bool delete) +{ +	struct qca8k_priv *priv = ds->priv; +	int ret, id, i; +	u32 val; + +	id = dsa_lag_id(ds->dst, lag); + +	/* Read current port member */ +	ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val); +	if (ret) +		return ret; + +	/* Shift val to the correct trunk */ +	val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id); +	val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK; +	if (delete) +		val &= ~BIT(port); +	else +		val |= BIT(port); + +	/* Update port member. With empty portmap disable trunk */ +	ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, +				 QCA8K_REG_GOL_TRUNK_MEMBER(id) | +				 QCA8K_REG_GOL_TRUNK_EN(id), +				 !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) | +				 val << QCA8K_REG_GOL_TRUNK_SHIFT(id)); + +	/* Search empty member if adding or port on deleting */ +	for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) { +		ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val); +		if (ret) +			return ret; + +		val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i); +		val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK; + +		if (delete) { +			/* If port flagged to be disabled assume this member is +			 * empty +			 */ +			if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +				continue; + +			val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK; +			if (val != port) +				continue; +		} else { +			/* If port flagged to be enabled assume this member is +			 * already set +			 */ +			if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK) +				continue; +		} + +		/* We have found the member to add/remove */ +		break; +	} + +	/* Set port in the correct port mask or disable port if in delete mode */ +	return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), +				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) | +				  QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i), +				  !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) | +				  port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i)); +} + +static int +qca8k_port_lag_join(struct dsa_switch *ds, int port, +		    struct net_device *lag, +		    struct netdev_lag_upper_info *info) +{ +	int ret; + +	if (!qca8k_lag_can_offload(ds, lag, info)) +		return -EOPNOTSUPP; + +	ret = qca8k_lag_setup_hash(ds, lag, info); +	if (ret) +		return ret; + +	return qca8k_lag_refresh_portmap(ds, port, lag, false); +} + +static int +qca8k_port_lag_leave(struct dsa_switch *ds, int port, +		     struct net_device *lag) +{ +	return qca8k_lag_refresh_portmap(ds, port, lag, true); +} +  static const struct dsa_switch_ops qca8k_switch_ops = {  	.get_tag_protocol	= qca8k_get_tag_protocol,  	.setup			= qca8k_setup,  	.get_strings		= qca8k_get_strings,  	.get_ethtool_stats	= qca8k_get_ethtool_stats,  	.get_sset_count		= qca8k_get_sset_count, +	.set_ageing_time	= qca8k_set_ageing_time,  	.get_mac_eee		= qca8k_get_mac_eee,  	.set_mac_eee		= qca8k_set_mac_eee,  	.port_enable		= qca8k_port_enable, @@ -2011,9 +2399,14 @@ static const struct dsa_switch_ops qca8k_switch_ops = {  	.port_stp_state_set	= qca8k_port_stp_state_set,  	.port_bridge_join	= qca8k_port_bridge_join,  	.port_bridge_leave	= qca8k_port_bridge_leave, +	.port_fast_age		= qca8k_port_fast_age,  	.port_fdb_add		= qca8k_port_fdb_add,  	.port_fdb_del		= qca8k_port_fdb_del,  	.port_fdb_dump		= qca8k_port_fdb_dump, +	.port_mdb_add		= qca8k_port_mdb_add, +	.port_mdb_del		= qca8k_port_mdb_del, +	.port_mirror_add	= qca8k_port_mirror_add, +	.port_mirror_del	= qca8k_port_mirror_del,  	.port_vlan_filtering	= qca8k_port_vlan_filtering,  	.port_vlan_add		= qca8k_port_vlan_add,  	.port_vlan_del		= qca8k_port_vlan_del, @@ -2023,6 +2416,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = {  	.phylink_mac_link_down	= qca8k_phylink_mac_link_down,  	.phylink_mac_link_up	= qca8k_phylink_mac_link_up,  	.get_phy_flags		= qca8k_get_phy_flags, +	.port_lag_join		= qca8k_port_lag_join, +	.port_lag_leave		= qca8k_port_lag_leave,  };  static int qca8k_read_switch_id(struct qca8k_priv *priv) @@ -2041,7 +2436,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)  	if (ret < 0)  		return -ENODEV; -	id = QCA8K_MASK_CTRL_DEVICE_ID(val & QCA8K_MASK_CTRL_DEVICE_ID_MASK); +	id = QCA8K_MASK_CTRL_DEVICE_ID(val);  	if (id != data->id) {  		dev_err(priv->dev, "Switch id detected %x but expected %x", id, data->id);  		return -ENODEV; @@ -2050,7 +2445,7 @@ static int qca8k_read_switch_id(struct qca8k_priv *priv)  	priv->switch_id = id;  	/* Save revision to communicate to the internal PHY driver */ -	priv->switch_revision = (val & QCA8K_MASK_CTRL_REV_ID_MASK); +	priv->switch_revision = QCA8K_MASK_CTRL_REV_ID(val);  	return 0;  } @@ -2085,6 +2480,14 @@ qca8k_sw_probe(struct mdio_device *mdiodev)  		gpiod_set_value_cansleep(priv->reset_gpio, 0);  	} +	/* Start by setting up the register mapping */ +	priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, priv, +					&qca8k_regmap_config); +	if (IS_ERR(priv->regmap)) { +		dev_err(priv->dev, "regmap initialization failed"); +		return PTR_ERR(priv->regmap); +	} +  	/* Check the detected switch id */  	ret = qca8k_read_switch_id(priv);  	if (ret) @@ -2173,14 +2576,17 @@ static SIMPLE_DEV_PM_OPS(qca8k_pm_ops,  static const struct qca8k_match_data qca8327 = {  	.id = QCA8K_ID_QCA8327,  	.reduced_package = true, +	.mib_count = QCA8K_QCA832X_MIB_COUNT,  };  static const struct qca8k_match_data qca8328 = {  	.id = QCA8K_ID_QCA8327, +	.mib_count = QCA8K_QCA832X_MIB_COUNT,  };  static const struct qca8k_match_data qca833x = {  	.id = QCA8K_ID_QCA8337, +	.mib_count = QCA8K_QCA833X_MIB_COUNT,  };  static const struct of_device_id qca8k_of_match[] = { diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 128b8cf85e08..ab4a417b25a9 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -15,12 +15,17 @@  #define QCA8K_NUM_PORTS					7  #define QCA8K_NUM_CPU_PORTS				2  #define QCA8K_MAX_MTU					9000 +#define QCA8K_NUM_LAGS					4 +#define QCA8K_NUM_PORTS_FOR_LAG				4  #define PHY_ID_QCA8327					0x004dd034  #define QCA8K_ID_QCA8327				0x12  #define PHY_ID_QCA8337					0x004dd036  #define QCA8K_ID_QCA8337				0x13 +#define QCA8K_QCA832X_MIB_COUNT				39 +#define QCA8K_QCA833X_MIB_COUNT				41 +  #define QCA8K_BUSY_WAIT_TIMEOUT				2000  #define QCA8K_NUM_FDB_RECORDS				2048 @@ -30,9 +35,9 @@  /* Global control registers */  #define QCA8K_REG_MASK_CTRL				0x000  #define   QCA8K_MASK_CTRL_REV_ID_MASK			GENMASK(7, 0) -#define   QCA8K_MASK_CTRL_REV_ID(x)			((x) >> 0) +#define   QCA8K_MASK_CTRL_REV_ID(x)			FIELD_GET(QCA8K_MASK_CTRL_REV_ID_MASK, x)  #define   QCA8K_MASK_CTRL_DEVICE_ID_MASK		GENMASK(15, 8) -#define   QCA8K_MASK_CTRL_DEVICE_ID(x)			((x) >> 8) +#define   QCA8K_MASK_CTRL_DEVICE_ID(x)			FIELD_GET(QCA8K_MASK_CTRL_DEVICE_ID_MASK, x)  #define QCA8K_REG_PORT0_PAD_CTRL			0x004  #define   QCA8K_PORT0_PAD_MAC06_EXCHANGE_EN		BIT(31)  #define   QCA8K_PORT0_PAD_SGMII_RXCLK_FALLING_EDGE	BIT(19) @@ -41,12 +46,11 @@  #define QCA8K_REG_PORT6_PAD_CTRL			0x00c  #define   QCA8K_PORT_PAD_RGMII_EN			BIT(26)  #define   QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK		GENMASK(23, 22) -#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		((x) << 22) +#define   QCA8K_PORT_PAD_RGMII_TX_DELAY(x)		FIELD_PREP(QCA8K_PORT_PAD_RGMII_TX_DELAY_MASK, x)  #define   QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK		GENMASK(21, 20) -#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		((x) << 20) +#define   QCA8K_PORT_PAD_RGMII_RX_DELAY(x)		FIELD_PREP(QCA8K_PORT_PAD_RGMII_RX_DELAY_MASK, x)  #define	  QCA8K_PORT_PAD_RGMII_TX_DELAY_EN		BIT(25)  #define   QCA8K_PORT_PAD_RGMII_RX_DELAY_EN		BIT(24) -#define   QCA8K_MAX_DELAY				3  #define   QCA8K_PORT_PAD_SGMII_EN			BIT(7)  #define QCA8K_REG_PWS					0x010  #define   QCA8K_PWS_POWER_ON_SEL			BIT(31) @@ -68,10 +72,12 @@  #define   QCA8K_MDIO_MASTER_READ			BIT(27)  #define   QCA8K_MDIO_MASTER_WRITE			0  #define   QCA8K_MDIO_MASTER_SUP_PRE			BIT(26) -#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)			((x) << 21) -#define   QCA8K_MDIO_MASTER_REG_ADDR(x)			((x) << 16) -#define   QCA8K_MDIO_MASTER_DATA(x)			(x) +#define   QCA8K_MDIO_MASTER_PHY_ADDR_MASK		GENMASK(25, 21) +#define   QCA8K_MDIO_MASTER_PHY_ADDR(x)			FIELD_PREP(QCA8K_MDIO_MASTER_PHY_ADDR_MASK, x) +#define   QCA8K_MDIO_MASTER_REG_ADDR_MASK		GENMASK(20, 16) +#define   QCA8K_MDIO_MASTER_REG_ADDR(x)			FIELD_PREP(QCA8K_MDIO_MASTER_REG_ADDR_MASK, x)  #define   QCA8K_MDIO_MASTER_DATA_MASK			GENMASK(15, 0) +#define   QCA8K_MDIO_MASTER_DATA(x)			FIELD_PREP(QCA8K_MDIO_MASTER_DATA_MASK, x)  #define   QCA8K_MDIO_MASTER_MAX_PORTS			5  #define   QCA8K_MDIO_MASTER_MAX_REG			32  #define QCA8K_GOL_MAC_ADDR0				0x60 @@ -93,9 +99,7 @@  #define   QCA8K_PORT_STATUS_FLOW_AUTO			BIT(12)  #define QCA8K_REG_PORT_HDR_CTRL(_i)			(0x9c + (_i * 4))  #define   QCA8K_PORT_HDR_CTRL_RX_MASK			GENMASK(3, 2) -#define   QCA8K_PORT_HDR_CTRL_RX_S			2  #define   QCA8K_PORT_HDR_CTRL_TX_MASK			GENMASK(1, 0) -#define   QCA8K_PORT_HDR_CTRL_TX_S			0  #define   QCA8K_PORT_HDR_CTRL_ALL			2  #define   QCA8K_PORT_HDR_CTRL_MGMT			1  #define   QCA8K_PORT_HDR_CTRL_NONE			0 @@ -105,10 +109,11 @@  #define   QCA8K_SGMII_EN_TX				BIT(3)  #define   QCA8K_SGMII_EN_SD				BIT(4)  #define   QCA8K_SGMII_CLK125M_DELAY			BIT(7) -#define   QCA8K_SGMII_MODE_CTRL_MASK			(BIT(22) | BIT(23)) -#define   QCA8K_SGMII_MODE_CTRL_BASEX			(0 << 22) -#define   QCA8K_SGMII_MODE_CTRL_PHY			(1 << 22) -#define   QCA8K_SGMII_MODE_CTRL_MAC			(2 << 22) +#define   QCA8K_SGMII_MODE_CTRL_MASK			GENMASK(23, 22) +#define   QCA8K_SGMII_MODE_CTRL(x)			FIELD_PREP(QCA8K_SGMII_MODE_CTRL_MASK, x) +#define   QCA8K_SGMII_MODE_CTRL_BASEX			QCA8K_SGMII_MODE_CTRL(0x0) +#define   QCA8K_SGMII_MODE_CTRL_PHY			QCA8K_SGMII_MODE_CTRL(0x1) +#define   QCA8K_SGMII_MODE_CTRL_MAC			QCA8K_SGMII_MODE_CTRL(0x2)  /* MAC_PWR_SEL registers */  #define QCA8K_REG_MAC_PWR_SEL				0x0e4 @@ -119,102 +124,152 @@  #define QCA8K_REG_EEE_CTRL				0x100  #define  QCA8K_REG_EEE_CTRL_LPI_EN(_i)			((_i + 1) * 2) +/* TRUNK_HASH_EN registers */ +#define QCA8K_TRUNK_HASH_EN_CTRL			0x270 +#define   QCA8K_TRUNK_HASH_SIP_EN			BIT(3) +#define   QCA8K_TRUNK_HASH_DIP_EN			BIT(2) +#define   QCA8K_TRUNK_HASH_SA_EN			BIT(1) +#define   QCA8K_TRUNK_HASH_DA_EN			BIT(0) +#define   QCA8K_TRUNK_HASH_MASK				GENMASK(3, 0) +  /* ACL registers */  #define QCA8K_REG_PORT_VLAN_CTRL0(_i)			(0x420 + (_i * 8)) -#define   QCA8K_PORT_VLAN_CVID(x)			(x << 16) -#define   QCA8K_PORT_VLAN_SVID(x)			x +#define   QCA8K_PORT_VLAN_CVID_MASK			GENMASK(27, 16) +#define   QCA8K_PORT_VLAN_CVID(x)			FIELD_PREP(QCA8K_PORT_VLAN_CVID_MASK, x) +#define   QCA8K_PORT_VLAN_SVID_MASK			GENMASK(11, 0) +#define   QCA8K_PORT_VLAN_SVID(x)			FIELD_PREP(QCA8K_PORT_VLAN_SVID_MASK, x)  #define QCA8K_REG_PORT_VLAN_CTRL1(_i)			(0x424 + (_i * 8))  #define QCA8K_REG_IPV4_PRI_BASE_ADDR			0x470  #define QCA8K_REG_IPV4_PRI_ADDR_MASK			0x474  /* Lookup registers */  #define QCA8K_REG_ATU_DATA0				0x600 -#define   QCA8K_ATU_ADDR2_S				24 -#define   QCA8K_ATU_ADDR3_S				16 -#define   QCA8K_ATU_ADDR4_S				8 +#define   QCA8K_ATU_ADDR2_MASK				GENMASK(31, 24) +#define   QCA8K_ATU_ADDR3_MASK				GENMASK(23, 16) +#define   QCA8K_ATU_ADDR4_MASK				GENMASK(15, 8) +#define   QCA8K_ATU_ADDR5_MASK				GENMASK(7, 0)  #define QCA8K_REG_ATU_DATA1				0x604 -#define   QCA8K_ATU_PORT_M				0x7f -#define   QCA8K_ATU_PORT_S				16 -#define   QCA8K_ATU_ADDR0_S				8 +#define   QCA8K_ATU_PORT_MASK				GENMASK(22, 16) +#define   QCA8K_ATU_ADDR0_MASK				GENMASK(15, 8) +#define   QCA8K_ATU_ADDR1_MASK				GENMASK(7, 0)  #define QCA8K_REG_ATU_DATA2				0x608 -#define   QCA8K_ATU_VID_M				0xfff -#define   QCA8K_ATU_VID_S				8 -#define   QCA8K_ATU_STATUS_M				0xf +#define   QCA8K_ATU_VID_MASK				GENMASK(19, 8) +#define   QCA8K_ATU_STATUS_MASK				GENMASK(3, 0)  #define   QCA8K_ATU_STATUS_STATIC			0xf  #define QCA8K_REG_ATU_FUNC				0x60c  #define   QCA8K_ATU_FUNC_BUSY				BIT(31)  #define   QCA8K_ATU_FUNC_PORT_EN			BIT(14)  #define   QCA8K_ATU_FUNC_MULTI_EN			BIT(13)  #define   QCA8K_ATU_FUNC_FULL				BIT(12) -#define   QCA8K_ATU_FUNC_PORT_M				0xf -#define   QCA8K_ATU_FUNC_PORT_S				8 +#define   QCA8K_ATU_FUNC_PORT_MASK			GENMASK(11, 8)  #define QCA8K_REG_VTU_FUNC0				0x610  #define   QCA8K_VTU_FUNC0_VALID				BIT(20)  #define   QCA8K_VTU_FUNC0_IVL_EN			BIT(19) -#define   QCA8K_VTU_FUNC0_EG_MODE_S(_i)			(4 + (_i) * 2) -#define   QCA8K_VTU_FUNC0_EG_MODE_MASK			3 -#define   QCA8K_VTU_FUNC0_EG_MODE_UNMOD			0 -#define   QCA8K_VTU_FUNC0_EG_MODE_UNTAG			1 -#define   QCA8K_VTU_FUNC0_EG_MODE_TAG			2 -#define   QCA8K_VTU_FUNC0_EG_MODE_NOT			3 +/*        QCA8K_VTU_FUNC0_EG_MODE_MASK			GENMASK(17, 4) + *          It does contain VLAN_MODE for each port [5:4] for port0, + *          [7:6] for port1 ... [17:16] for port6. Use virtual port + *          define to handle this. + */ +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)	(4 + (_i) * 2) +#define   QCA8K_VTU_FUNC0_EG_MODE_MASK			GENMASK(1, 0) +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_MASK(_i)		(GENMASK(1, 0) << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +#define   QCA8K_VTU_FUNC0_EG_MODE_UNMOD			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x0) +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_UNMOD(_i)	(QCA8K_VTU_FUNC0_EG_MODE_UNMOD << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +#define   QCA8K_VTU_FUNC0_EG_MODE_UNTAG			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x1) +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_UNTAG(_i)	(QCA8K_VTU_FUNC0_EG_MODE_UNTAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +#define   QCA8K_VTU_FUNC0_EG_MODE_TAG			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x2) +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_TAG(_i)		(QCA8K_VTU_FUNC0_EG_MODE_TAG << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i)) +#define   QCA8K_VTU_FUNC0_EG_MODE_NOT			FIELD_PREP(QCA8K_VTU_FUNC0_EG_MODE_MASK, 0x3) +#define   QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(_i)		(QCA8K_VTU_FUNC0_EG_MODE_NOT << QCA8K_VTU_FUNC0_EG_MODE_PORT_SHIFT(_i))  #define QCA8K_REG_VTU_FUNC1				0x614  #define   QCA8K_VTU_FUNC1_BUSY				BIT(31) -#define   QCA8K_VTU_FUNC1_VID_S				16 +#define   QCA8K_VTU_FUNC1_VID_MASK			GENMASK(27, 16)  #define   QCA8K_VTU_FUNC1_FULL				BIT(4) +#define QCA8K_REG_ATU_CTRL				0x618 +#define   QCA8K_ATU_AGE_TIME_MASK			GENMASK(15, 0) +#define   QCA8K_ATU_AGE_TIME(x)				FIELD_PREP(QCA8K_ATU_AGE_TIME_MASK, (x))  #define QCA8K_REG_GLOBAL_FW_CTRL0			0x620  #define   QCA8K_GLOBAL_FW_CTRL0_CPU_PORT_EN		BIT(10) +#define   QCA8K_GLOBAL_FW_CTRL0_MIRROR_PORT_NUM		GENMASK(7, 4)  #define QCA8K_REG_GLOBAL_FW_CTRL1			0x624 -#define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_S		24 -#define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_S			16 -#define   QCA8K_GLOBAL_FW_CTRL1_MC_DP_S			8 -#define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_S			0 +#define   QCA8K_GLOBAL_FW_CTRL1_IGMP_DP_MASK		GENMASK(30, 24) +#define   QCA8K_GLOBAL_FW_CTRL1_BC_DP_MASK		GENMASK(22, 16) +#define   QCA8K_GLOBAL_FW_CTRL1_MC_DP_MASK		GENMASK(14, 8) +#define   QCA8K_GLOBAL_FW_CTRL1_UC_DP_MASK		GENMASK(6, 0)  #define QCA8K_PORT_LOOKUP_CTRL(_i)			(0x660 + (_i) * 0xc)  #define   QCA8K_PORT_LOOKUP_MEMBER			GENMASK(6, 0) -#define   QCA8K_PORT_LOOKUP_VLAN_MODE			GENMASK(9, 8) -#define   QCA8K_PORT_LOOKUP_VLAN_MODE_NONE		(0 << 8) -#define   QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK		(1 << 8) -#define   QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK		(2 << 8) -#define   QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE		(3 << 8) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE_MASK		GENMASK(9, 8) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE(x)		FIELD_PREP(QCA8K_PORT_LOOKUP_VLAN_MODE_MASK, x) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE_NONE		QCA8K_PORT_LOOKUP_VLAN_MODE(0x0) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE_FALLBACK		QCA8K_PORT_LOOKUP_VLAN_MODE(0x1) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE_CHECK		QCA8K_PORT_LOOKUP_VLAN_MODE(0x2) +#define   QCA8K_PORT_LOOKUP_VLAN_MODE_SECURE		QCA8K_PORT_LOOKUP_VLAN_MODE(0x3)  #define   QCA8K_PORT_LOOKUP_STATE_MASK			GENMASK(18, 16) -#define   QCA8K_PORT_LOOKUP_STATE_DISABLED		(0 << 16) -#define   QCA8K_PORT_LOOKUP_STATE_BLOCKING		(1 << 16) -#define   QCA8K_PORT_LOOKUP_STATE_LISTENING		(2 << 16) -#define   QCA8K_PORT_LOOKUP_STATE_LEARNING		(3 << 16) -#define   QCA8K_PORT_LOOKUP_STATE_FORWARD		(4 << 16) -#define   QCA8K_PORT_LOOKUP_STATE			GENMASK(18, 16) +#define   QCA8K_PORT_LOOKUP_STATE(x)			FIELD_PREP(QCA8K_PORT_LOOKUP_STATE_MASK, x) +#define   QCA8K_PORT_LOOKUP_STATE_DISABLED		QCA8K_PORT_LOOKUP_STATE(0x0) +#define   QCA8K_PORT_LOOKUP_STATE_BLOCKING		QCA8K_PORT_LOOKUP_STATE(0x1) +#define   QCA8K_PORT_LOOKUP_STATE_LISTENING		QCA8K_PORT_LOOKUP_STATE(0x2) +#define   QCA8K_PORT_LOOKUP_STATE_LEARNING		QCA8K_PORT_LOOKUP_STATE(0x3) +#define   QCA8K_PORT_LOOKUP_STATE_FORWARD		QCA8K_PORT_LOOKUP_STATE(0x4)  #define   QCA8K_PORT_LOOKUP_LEARN			BIT(20) +#define   QCA8K_PORT_LOOKUP_ING_MIRROR_EN		BIT(25) + +#define QCA8K_REG_GOL_TRUNK_CTRL0			0x700 +/* 4 max trunk first + * first 6 bit for member bitmap + * 7th bit is to enable trunk port + */ +#define QCA8K_REG_GOL_TRUNK_SHIFT(_i)			((_i) * 8) +#define QCA8K_REG_GOL_TRUNK_EN_MASK			BIT(7) +#define QCA8K_REG_GOL_TRUNK_EN(_i)			(QCA8K_REG_GOL_TRUNK_EN_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) +#define QCA8K_REG_GOL_TRUNK_MEMBER_MASK			GENMASK(6, 0) +#define QCA8K_REG_GOL_TRUNK_MEMBER(_i)			(QCA8K_REG_GOL_TRUNK_MEMBER_MASK << QCA8K_REG_GOL_TRUNK_SHIFT(_i)) +/* 0x704 for TRUNK 0-1 --- 0x708 for TRUNK 2-3 */ +#define QCA8K_REG_GOL_TRUNK_CTRL(_i)			(0x704 + (((_i) / 2) * 4)) +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK		GENMASK(3, 0) +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK		BIT(3) +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK		GENMASK(2, 0) +#define QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)		(((_i) / 2) * 16) +#define QCA8K_REG_GOL_MEM_ID_SHIFT(_i)			((_i) * 4) +/* Complex shift: FIRST shift for port THEN shift for trunk */ +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)	(QCA8K_REG_GOL_MEM_ID_SHIFT(_j) + QCA8K_REG_GOL_TRUNK_ID_SHIFT(_i)) +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j)) +#define QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(_i, _j)	(QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(_i, _j))  #define QCA8K_REG_GLOBAL_FC_THRESH			0x800 -#define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		((x) << 16) -#define   QCA8K_GLOBAL_FC_GOL_XON_THRES_S		GENMASK(24, 16) -#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x)		((x) << 0) -#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES_S		GENMASK(8, 0) +#define   QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK		GENMASK(24, 16) +#define   QCA8K_GLOBAL_FC_GOL_XON_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XON_THRES_MASK, x) +#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK		GENMASK(8, 0) +#define   QCA8K_GLOBAL_FC_GOL_XOFF_THRES(x)		FIELD_PREP(QCA8K_GLOBAL_FC_GOL_XOFF_THRES_MASK, x)  #define QCA8K_REG_PORT_HOL_CTRL0(_i)			(0x970 + (_i) * 0x8) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF		GENMASK(3, 0) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0(x)		((x) << 0) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF		GENMASK(7, 4) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1(x)		((x) << 4) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF		GENMASK(11, 8) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2(x)		((x) << 8) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF		GENMASK(15, 12) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3(x)		((x) << 12) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF		GENMASK(19, 16) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4(x)		((x) << 16) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF		GENMASK(23, 20) -#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5(x)		((x) << 20) -#define   QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF		GENMASK(29, 24) -#define   QCA8K_PORT_HOL_CTRL0_EG_PORT(x)		((x) << 24) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK		GENMASK(3, 0) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI0(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI0_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK		GENMASK(7, 4) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI1(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI1_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK		GENMASK(11, 8) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI2(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI2_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK		GENMASK(15, 12) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI3(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI3_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK		GENMASK(19, 16) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI4(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI4_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK		GENMASK(23, 20) +#define   QCA8K_PORT_HOL_CTRL0_EG_PRI5(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PRI5_BUF_MASK, x) +#define   QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK		GENMASK(29, 24) +#define   QCA8K_PORT_HOL_CTRL0_EG_PORT(x)		FIELD_PREP(QCA8K_PORT_HOL_CTRL0_EG_PORT_BUF_MASK, x)  #define QCA8K_REG_PORT_HOL_CTRL1(_i)			(0x974 + (_i) * 0x8) -#define   QCA8K_PORT_HOL_CTRL1_ING_BUF			GENMASK(3, 0) -#define   QCA8K_PORT_HOL_CTRL1_ING(x)			((x) << 0) +#define   QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK		GENMASK(3, 0) +#define   QCA8K_PORT_HOL_CTRL1_ING(x)			FIELD_PREP(QCA8K_PORT_HOL_CTRL1_ING_BUF_MASK, x)  #define   QCA8K_PORT_HOL_CTRL1_EG_PRI_BUF_EN		BIT(6)  #define   QCA8K_PORT_HOL_CTRL1_EG_PORT_BUF_EN		BIT(7)  #define   QCA8K_PORT_HOL_CTRL1_WRED_EN			BIT(8)  #define   QCA8K_PORT_HOL_CTRL1_EG_MIRROR_EN		BIT(16)  /* Pkt edit registers */ +#define QCA8K_EGREES_VLAN_PORT_SHIFT(_i)		(16 * ((_i) % 2)) +#define QCA8K_EGREES_VLAN_PORT_MASK(_i)			(GENMASK(11, 0) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i)) +#define QCA8K_EGREES_VLAN_PORT(_i, x)			((x) << QCA8K_EGREES_VLAN_PORT_SHIFT(_i))  #define QCA8K_EGRESS_VLAN(x)				(0x0c70 + (4 * (x / 2)))  /* L3 registers */ @@ -244,6 +299,7 @@ enum qca8k_fdb_cmd {  	QCA8K_FDB_FLUSH	= 1,  	QCA8K_FDB_LOAD = 2,  	QCA8K_FDB_PURGE = 3, +	QCA8K_FDB_FLUSH_PORT = 5,  	QCA8K_FDB_NEXT = 6,  	QCA8K_FDB_SEARCH = 7,  }; @@ -264,6 +320,7 @@ struct ar8xxx_port_status {  struct qca8k_match_data {  	u8 id;  	bool reduced_package; +	u8 mib_count;  };  enum { @@ -282,6 +339,9 @@ struct qca8k_ports_config {  struct qca8k_priv {  	u8 switch_id;  	u8 switch_revision; +	u8 mirror_rx; +	u8 mirror_tx; +	u8 lag_hash_mode;  	bool legacy_phy_port_mapping;  	struct qca8k_ports_config ports_config;  	struct regmap *regmap; diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c index c66ebd0ee217..aae46ada8d83 100644 --- a/drivers/net/dsa/realtek-smi-core.c +++ b/drivers/net/dsa/realtek-smi-core.c @@ -456,7 +456,7 @@ static int realtek_smi_probe(struct platform_device *pdev)  	smi->ds->ops = var->ds_ops;  	ret = dsa_register_switch(smi->ds);  	if (ret) { -		dev_err(dev, "unable to register switch ret = %d\n", ret); +		dev_err_probe(dev, ret, "unable to register switch\n");  		return ret;  	}  	return 0; diff --git a/drivers/net/dsa/rtl8365mb.c b/drivers/net/dsa/rtl8365mb.c index 078ca4cd7160..3b729544798b 100644 --- a/drivers/net/dsa/rtl8365mb.c +++ b/drivers/net/dsa/rtl8365mb.c @@ -277,7 +277,7 @@  		(RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport))  #define   RTL8365MB_PORT_ISOLATION_MASK			0x07FF -/* MSTP port state registers - indexed by tree instancrSTI (tree ine */ +/* MSTP port state registers - indexed by tree instance */  #define RTL8365MB_MSTI_CTRL_BASE			0x0A00  #define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \  		(RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3)) @@ -767,7 +767,8 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port,  	 *     0 = no delay, 1 = 2 ns delay  	 *   RX delay:  	 *     0 = no delay, 7 = maximum delay -	 *     No units are specified, but there are a total of 8 steps. +	 *     Each step is approximately 0.3 ns, so the maximum delay is about +	 *     2.1 ns.  	 *  	 * The vendor driver also states that this must be configured *before*  	 * forcing the external interface into a particular mode, which is done @@ -778,10 +779,6 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port,  	 * specified. We ignore the detail of the RGMII interface mode  	 * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only  	 * property. -	 * -	 * For the RX delay, we assume that a register value of 4 corresponds to -	 * 2 ns. But this is just an educated guess, so ignore all other values -	 * to avoid too much confusion.  	 */  	if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) {  		val = val / 1000; /* convert to ns */ @@ -794,13 +791,13 @@ static int rtl8365mb_ext_config_rgmii(struct realtek_smi *smi, int port,  	}  	if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { -		val = val / 1000; /* convert to ns */ +		val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */ -		if (val == 0 || val == 2) -			rx_delay = val * 2; +		if (val <= 7) +			rx_delay = val;  		else  			dev_warn(smi->dev, -				 "EXT port RX delay must be 0 to 2 ns\n"); +				 "EXT port RX delay must be 0 to 2.1 ns\n");  	}  	ret = regmap_update_bits( @@ -903,7 +900,8 @@ static bool rtl8365mb_phy_mode_supported(struct dsa_switch *ds, int port,  {  	if (dsa_is_user_port(ds, port) &&  	    (interface == PHY_INTERFACE_MODE_NA || -	     interface == PHY_INTERFACE_MODE_INTERNAL)) +	     interface == PHY_INTERFACE_MODE_INTERNAL || +	     interface == PHY_INTERFACE_MODE_GMII))  		/* Internal PHY */  		return true;  	else if (dsa_is_cpu_port(ds, port) && diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index 03deacd83e61..ecc19bd5115f 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -1186,7 +1186,8 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port)  static int  rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port, -			   struct net_device *bridge) +			   struct dsa_bridge bridge, +			   bool *tx_fwd_offload)  {  	struct realtek_smi *smi = ds->priv;  	unsigned int port_bitmap = 0; @@ -1198,7 +1199,7 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,  		if (i == port)  			continue;  		/* Not on this bridge */ -		if (dsa_to_port(ds, i)->bridge_dev != bridge) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		/* Join this port to each other port on the bridge */  		ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), @@ -1218,7 +1219,7 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,  static void  rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port, -			    struct net_device *bridge) +			    struct dsa_bridge bridge)  {  	struct realtek_smi *smi = ds->priv;  	unsigned int port_bitmap = 0; @@ -1230,7 +1231,7 @@ rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port,  		if (i == port)  			continue;  		/* Not on this bridge */ -		if (dsa_to_port(ds, i)->bridge_dev != bridge) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		/* Remove this port from any other port on the bridge */  		ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i), diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 21dba16af097..9ba2ec2b966d 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -249,6 +249,7 @@ struct sja1105_private {  	bool fixed_link[SJA1105_MAX_NUM_PORTS];  	unsigned long ucast_egress_floods;  	unsigned long bcast_egress_floods; +	unsigned long hwts_tx_en;  	const struct sja1105_info *info;  	size_t max_xfer_len;  	struct spi_device *spidev; @@ -256,11 +257,13 @@ struct sja1105_private {  	u16 bridge_pvid[SJA1105_MAX_NUM_PORTS];  	u16 tag_8021q_pvid[SJA1105_MAX_NUM_PORTS];  	struct sja1105_flow_block flow_block; -	struct sja1105_port ports[SJA1105_MAX_NUM_PORTS];  	/* Serializes transmission of management frames so that  	 * the switch doesn't confuse them with one another.  	 */  	struct mutex mgmt_lock; +	/* PTP two-step TX timestamp ID, and its serialization lock */ +	spinlock_t ts_id_lock; +	u8 ts_id;  	/* Serializes access to the dynamic config interface */  	struct mutex dynamic_config_lock;  	struct devlink_region **regions; @@ -269,7 +272,6 @@ struct sja1105_private {  	struct mii_bus *mdio_base_tx;  	struct mii_bus *mdio_pcs;  	struct dw_xpcs *xpcs[SJA1105_MAX_NUM_PORTS]; -	struct sja1105_tagger_data tagger_data;  	struct sja1105_ptp_data ptp_data;  	struct sja1105_tas_data tas_data;  }; diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index 72b9b39b0989..7dcdd784aea4 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -379,7 +379,7 @@ int sja1105_cls_flower_add(struct dsa_switch *ds, int port,  			vl_rule = true;  			rc = sja1105_vl_gate(priv, port, extack, cookie, -					     &key, act->gate.index, +					     &key, act->hw_index,  					     act->gate.prio,  					     act->gate.basetime,  					     act->gate.cycletime, diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index c343effe2e96..b513713be610 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -118,13 +118,14 @@ static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid)  static int sja1105_commit_pvid(struct dsa_switch *ds, int port)  {  	struct dsa_port *dp = dsa_to_port(ds, port); +	struct net_device *br = dsa_port_bridge_dev_get(dp);  	struct sja1105_private *priv = ds->priv;  	struct sja1105_vlan_lookup_entry *vlan;  	bool drop_untagged = false;  	int match, rc;  	u16 pvid; -	if (dp->bridge_dev && br_vlan_enabled(dp->bridge_dev)) +	if (br && br_vlan_enabled(br))  		pvid = priv->bridge_pvid[port];  	else  		pvid = priv->tag_8021q_pvid[port]; @@ -1979,7 +1980,7 @@ static int sja1105_manage_flood_domains(struct sja1105_private *priv)  }  static int sja1105_bridge_member(struct dsa_switch *ds, int port, -				 struct net_device *br, bool member) +				 struct dsa_bridge bridge, bool member)  {  	struct sja1105_l2_forwarding_entry *l2_fwd;  	struct sja1105_private *priv = ds->priv; @@ -2004,7 +2005,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port,  		 */  		if (i == port)  			continue; -		if (dsa_to_port(ds, i)->bridge_dev != br) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		sja1105_port_allow_traffic(l2_fwd, i, port, member);  		sja1105_port_allow_traffic(l2_fwd, port, i, member); @@ -2073,15 +2074,31 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,  }  static int sja1105_bridge_join(struct dsa_switch *ds, int port, -			       struct net_device *br) +			       struct dsa_bridge bridge, +			       bool *tx_fwd_offload)  { -	return sja1105_bridge_member(ds, port, br, true); +	int rc; + +	rc = sja1105_bridge_member(ds, port, bridge, true); +	if (rc) +		return rc; + +	rc = dsa_tag_8021q_bridge_tx_fwd_offload(ds, port, bridge); +	if (rc) { +		sja1105_bridge_member(ds, port, bridge, false); +		return rc; +	} + +	*tx_fwd_offload = true; + +	return 0;  }  static void sja1105_bridge_leave(struct dsa_switch *ds, int port, -				 struct net_device *br) +				 struct dsa_bridge bridge)  { -	sja1105_bridge_member(ds, port, br, false); +	dsa_tag_8021q_bridge_tx_fwd_unoffload(ds, port, bridge); +	sja1105_bridge_member(ds, port, bridge, false);  }  #define BYTES_PER_KBIT (1000LL / 8) @@ -2587,8 +2604,9 @@ static int sja1105_prechangeupper(struct dsa_switch *ds, int port,  	if (netif_is_bridge_master(upper)) {  		list_for_each_entry(dp, &dst->ports, list) { -			if (dp->bridge_dev && dp->bridge_dev != upper && -			    br_vlan_enabled(dp->bridge_dev)) { +			struct net_device *br = dsa_port_bridge_dev_get(dp); + +			if (br && br != upper && br_vlan_enabled(br)) {  				NL_SET_ERR_MSG_MOD(extack,  						   "Only one VLAN-aware bridge is supported");  				return -EBUSY; @@ -2599,18 +2617,6 @@ static int sja1105_prechangeupper(struct dsa_switch *ds, int port,  	return 0;  } -static void sja1105_port_disable(struct dsa_switch *ds, int port) -{ -	struct sja1105_private *priv = ds->priv; -	struct sja1105_port *sp = &priv->ports[port]; - -	if (!dsa_is_user_port(ds, port)) -		return; - -	kthread_cancel_work_sync(&sp->xmit_work); -	skb_queue_purge(&sp->xmit_queue); -} -  static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,  			     struct sk_buff *skb, bool takets)  { @@ -2669,10 +2675,8 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,  	return NETDEV_TX_OK;  } -#define work_to_port(work) \ -		container_of((work), struct sja1105_port, xmit_work) -#define tagger_to_sja1105(t) \ -		container_of((t), struct sja1105_private, tagger_data) +#define work_to_xmit_work(w) \ +		container_of((w), struct sja1105_deferred_xmit_work, work)  /* Deferred work is unfortunately necessary because setting up the management   * route cannot be done from atomit context (SPI transfer takes a sleepable @@ -2680,25 +2684,41 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot,   */  static void sja1105_port_deferred_xmit(struct kthread_work *work)  { -	struct sja1105_port *sp = work_to_port(work); -	struct sja1105_tagger_data *tagger_data = sp->data; -	struct sja1105_private *priv = tagger_to_sja1105(tagger_data); -	int port = sp - priv->ports; -	struct sk_buff *skb; +	struct sja1105_deferred_xmit_work *xmit_work = work_to_xmit_work(work); +	struct sk_buff *clone, *skb = xmit_work->skb; +	struct dsa_switch *ds = xmit_work->dp->ds; +	struct sja1105_private *priv = ds->priv; +	int port = xmit_work->dp->index; -	while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { -		struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone; +	clone = SJA1105_SKB_CB(skb)->clone; -		mutex_lock(&priv->mgmt_lock); +	mutex_lock(&priv->mgmt_lock); -		sja1105_mgmt_xmit(priv->ds, port, 0, skb, !!clone); +	sja1105_mgmt_xmit(ds, port, 0, skb, !!clone); -		/* The clone, if there, was made by dsa_skb_tx_timestamp */ -		if (clone) -			sja1105_ptp_txtstamp_skb(priv->ds, port, clone); +	/* The clone, if there, was made by dsa_skb_tx_timestamp */ +	if (clone) +		sja1105_ptp_txtstamp_skb(ds, port, clone); -		mutex_unlock(&priv->mgmt_lock); -	} +	mutex_unlock(&priv->mgmt_lock); + +	kfree(xmit_work); +} + +static int sja1105_connect_tag_protocol(struct dsa_switch *ds, +					enum dsa_tag_protocol proto) +{ +	struct sja1105_private *priv = ds->priv; +	struct sja1105_tagger_data *tagger_data; + +	if (proto != priv->info->tag_proto) +		return -EPROTONOSUPPORT; + +	tagger_data = sja1105_tagger_data(ds); +	tagger_data->xmit_work_fn = sja1105_port_deferred_xmit; +	tagger_data->meta_tstamp_handler = sja1110_process_meta_tstamp; + +	return 0;  }  /* The MAXAGE setting belongs to the L2 Forwarding Parameters table, @@ -3001,58 +3021,6 @@ static int sja1105_port_bridge_flags(struct dsa_switch *ds, int port,  	return 0;  } -static void sja1105_teardown_ports(struct sja1105_private *priv) -{ -	struct dsa_switch *ds = priv->ds; -	int port; - -	for (port = 0; port < ds->num_ports; port++) { -		struct sja1105_port *sp = &priv->ports[port]; - -		if (sp->xmit_worker) -			kthread_destroy_worker(sp->xmit_worker); -	} -} - -static int sja1105_setup_ports(struct sja1105_private *priv) -{ -	struct sja1105_tagger_data *tagger_data = &priv->tagger_data; -	struct dsa_switch *ds = priv->ds; -	int port, rc; - -	/* Connections between dsa_port and sja1105_port */ -	for (port = 0; port < ds->num_ports; port++) { -		struct sja1105_port *sp = &priv->ports[port]; -		struct dsa_port *dp = dsa_to_port(ds, port); -		struct kthread_worker *worker; -		struct net_device *slave; - -		if (!dsa_port_is_user(dp)) -			continue; - -		dp->priv = sp; -		sp->data = tagger_data; -		slave = dp->slave; -		kthread_init_work(&sp->xmit_work, sja1105_port_deferred_xmit); -		worker = kthread_create_worker(0, "%s_xmit", slave->name); -		if (IS_ERR(worker)) { -			rc = PTR_ERR(worker); -			dev_err(ds->dev, -				"failed to create deferred xmit thread: %d\n", -				rc); -			goto out_destroy_workers; -		} -		sp->xmit_worker = worker; -		skb_queue_head_init(&sp->xmit_queue); -	} - -	return 0; - -out_destroy_workers: -	sja1105_teardown_ports(priv); -	return rc; -} -  /* The programming model for the SJA1105 switch is "all-at-once" via static   * configuration tables. Some of these can be dynamically modified at runtime,   * but not the xMII mode parameters table. @@ -3098,10 +3066,6 @@ static int sja1105_setup(struct dsa_switch *ds)  		}  	} -	rc = sja1105_setup_ports(priv); -	if (rc) -		goto out_static_config_free; -  	sja1105_tas_setup(ds);  	sja1105_flower_setup(ds); @@ -3139,7 +3103,7 @@ static int sja1105_setup(struct dsa_switch *ds)  	ds->vlan_filtering_is_global = true;  	ds->untag_bridge_pvid = true;  	/* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ -	ds->num_fwd_offloading_bridges = 7; +	ds->max_num_bridges = 7;  	/* Advertise the 8 egress queues */  	ds->num_tx_queues = SJA1105_NUM_TC; @@ -3158,7 +3122,6 @@ out_ptp_clock_unregister:  out_flower_teardown:  	sja1105_flower_teardown(ds);  	sja1105_tas_teardown(ds); -	sja1105_teardown_ports(priv);  out_static_config_free:  	sja1105_static_config_free(&priv->static_config); @@ -3178,12 +3141,12 @@ static void sja1105_teardown(struct dsa_switch *ds)  	sja1105_ptp_clock_unregister(ds);  	sja1105_flower_teardown(ds);  	sja1105_tas_teardown(ds); -	sja1105_teardown_ports(priv);  	sja1105_static_config_free(&priv->static_config);  }  static const struct dsa_switch_ops sja1105_switch_ops = {  	.get_tag_protocol	= sja1105_get_tag_protocol, +	.connect_tag_protocol	= sja1105_connect_tag_protocol,  	.setup			= sja1105_setup,  	.teardown		= sja1105_teardown,  	.set_ageing_time	= sja1105_set_ageing_time, @@ -3197,7 +3160,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {  	.get_ethtool_stats	= sja1105_get_ethtool_stats,  	.get_sset_count		= sja1105_get_sset_count,  	.get_ts_info		= sja1105_get_ts_info, -	.port_disable		= sja1105_port_disable,  	.port_fdb_dump		= sja1105_fdb_dump,  	.port_fdb_add		= sja1105_fdb_add,  	.port_fdb_del		= sja1105_fdb_del, @@ -3228,8 +3190,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {  	.tag_8021q_vlan_add	= sja1105_dsa_8021q_vlan_add,  	.tag_8021q_vlan_del	= sja1105_dsa_8021q_vlan_del,  	.port_prechangeupper	= sja1105_prechangeupper, -	.port_bridge_tx_fwd_offload = dsa_tag_8021q_bridge_tx_fwd_offload, -	.port_bridge_tx_fwd_unoffload = dsa_tag_8021q_bridge_tx_fwd_unoffload,  };  static const struct of_device_id sja1105_dt_ids[]; @@ -3367,6 +3327,7 @@ static int sja1105_probe(struct spi_device *spi)  	mutex_init(&priv->ptp_data.lock);  	mutex_init(&priv->dynamic_config_lock);  	mutex_init(&priv->mgmt_lock); +	spin_lock_init(&priv->ts_id_lock);  	rc = sja1105_parse_dt(priv);  	if (rc < 0) { diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 54396992a919..be3068a935af 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -58,13 +58,12 @@ enum sja1105_ptp_clk_mode {  #define ptp_data_to_sja1105(d) \  		container_of((d), struct sja1105_private, ptp_data) -/* Must be called only with priv->tagger_data.state bit - * SJA1105_HWTS_RX_EN cleared +/* Must be called only while the RX timestamping state of the tagger + * is turned off   */  static int sja1105_change_rxtstamping(struct sja1105_private *priv,  				      bool on)  { -	struct sja1105_tagger_data *tagger_data = &priv->tagger_data;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	struct sja1105_general_params_entry *general_params;  	struct sja1105_table *table; @@ -74,13 +73,8 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,  	general_params->send_meta1 = on;  	general_params->send_meta0 = on; -	/* Initialize the meta state machine to a known state */ -	if (priv->tagger_data.stampable_skb) { -		kfree_skb(priv->tagger_data.stampable_skb); -		priv->tagger_data.stampable_skb = NULL; -	}  	ptp_cancel_worker_sync(ptp_data->clock); -	skb_queue_purge(&tagger_data->skb_txtstamp_queue); +	skb_queue_purge(&ptp_data->skb_txtstamp_queue);  	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);  	return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); @@ -88,6 +82,7 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,  int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)  { +	struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds);  	struct sja1105_private *priv = ds->priv;  	struct hwtstamp_config config;  	bool rx_on; @@ -98,10 +93,10 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)  	switch (config.tx_type) {  	case HWTSTAMP_TX_OFF: -		priv->ports[port].hwts_tx_en = false; +		priv->hwts_tx_en &= ~BIT(port);  		break;  	case HWTSTAMP_TX_ON: -		priv->ports[port].hwts_tx_en = true; +		priv->hwts_tx_en |= BIT(port);  		break;  	default:  		return -ERANGE; @@ -116,8 +111,8 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)  		break;  	} -	if (rx_on != test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) { -		clear_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); +	if (rx_on != tagger_data->rxtstamp_get_state(ds)) { +		tagger_data->rxtstamp_set_state(ds, false);  		rc = sja1105_change_rxtstamping(priv, rx_on);  		if (rc < 0) { @@ -126,7 +121,7 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)  			return rc;  		}  		if (rx_on) -			set_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state); +			tagger_data->rxtstamp_set_state(ds, true);  	}  	if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) @@ -136,15 +131,16 @@ int sja1105_hwtstamp_set(struct dsa_switch *ds, int port, struct ifreq *ifr)  int sja1105_hwtstamp_get(struct dsa_switch *ds, int port, struct ifreq *ifr)  { +	struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds);  	struct sja1105_private *priv = ds->priv;  	struct hwtstamp_config config;  	config.flags = 0; -	if (priv->ports[port].hwts_tx_en) +	if (priv->hwts_tx_en & BIT(port))  		config.tx_type = HWTSTAMP_TX_ON;  	else  		config.tx_type = HWTSTAMP_TX_OFF; -	if (test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) +	if (tagger_data->rxtstamp_get_state(ds))  		config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;  	else  		config.rx_filter = HWTSTAMP_FILTER_NONE; @@ -417,10 +413,11 @@ static long sja1105_rxtstamp_work(struct ptp_clock_info *ptp)  bool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  { +	struct sja1105_tagger_data *tagger_data = sja1105_tagger_data(ds);  	struct sja1105_private *priv = ds->priv;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data; -	if (!test_bit(SJA1105_HWTS_RX_EN, &priv->tagger_data.state)) +	if (!tagger_data->rxtstamp_get_state(ds))  		return false;  	/* We need to read the full PTP clock to reconstruct the Rx @@ -453,6 +450,39 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,  	return priv->info->rxtstamp(ds, port, skb);  } +void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id, +				 enum sja1110_meta_tstamp dir, u64 tstamp) +{ +	struct sja1105_private *priv = ds->priv; +	struct sja1105_ptp_data *ptp_data = &priv->ptp_data; +	struct sk_buff *skb, *skb_tmp, *skb_match = NULL; +	struct skb_shared_hwtstamps shwt = {0}; + +	/* We don't care about RX timestamps on the CPU port */ +	if (dir == SJA1110_META_TSTAMP_RX) +		return; + +	spin_lock(&ptp_data->skb_txtstamp_queue.lock); + +	skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) { +		if (SJA1105_SKB_CB(skb)->ts_id != ts_id) +			continue; + +		__skb_unlink(skb, &ptp_data->skb_txtstamp_queue); +		skb_match = skb; + +		break; +	} + +	spin_unlock(&ptp_data->skb_txtstamp_queue.lock); + +	if (WARN_ON(!skb_match)) +		return; + +	shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(tstamp)); +	skb_complete_tx_timestamp(skb_match, &shwt); +} +  /* In addition to cloning the skb which is done by the common   * sja1105_port_txtstamp, we need to generate a timestamp ID and save the   * packet to the TX timestamping queue. @@ -461,22 +491,22 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  {  	struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone;  	struct sja1105_private *priv = ds->priv; -	struct sja1105_port *sp = &priv->ports[port]; +	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	u8 ts_id;  	skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; -	spin_lock(&sp->data->meta_lock); +	spin_lock(&priv->ts_id_lock); -	ts_id = sp->data->ts_id; +	ts_id = priv->ts_id;  	/* Deal automatically with 8-bit wraparound */ -	sp->data->ts_id++; +	priv->ts_id++;  	SJA1105_SKB_CB(clone)->ts_id = ts_id; -	spin_unlock(&sp->data->meta_lock); +	spin_unlock(&priv->ts_id_lock); -	skb_queue_tail(&sp->data->skb_txtstamp_queue, clone); +	skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone);  }  /* Called from dsa_skb_tx_timestamp. This callback is just to clone @@ -486,10 +516,9 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  void sja1105_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  {  	struct sja1105_private *priv = ds->priv; -	struct sja1105_port *sp = &priv->ports[port];  	struct sk_buff *clone; -	if (!sp->hwts_tx_en) +	if (!(priv->hwts_tx_en & BIT(port)))  		return;  	clone = skb_clone_sk(skb); @@ -896,7 +925,6 @@ static struct ptp_pin_desc sja1105_ptp_pin = {  int sja1105_ptp_clock_register(struct dsa_switch *ds)  {  	struct sja1105_private *priv = ds->priv; -	struct sja1105_tagger_data *tagger_data = &priv->tagger_data;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	ptp_data->caps = (struct ptp_clock_info) { @@ -919,8 +947,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  	/* Only used on SJA1105 */  	skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);  	/* Only used on SJA1110 */ -	skb_queue_head_init(&tagger_data->skb_txtstamp_queue); -	spin_lock_init(&tagger_data->meta_lock); +	skb_queue_head_init(&ptp_data->skb_txtstamp_queue);  	ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev);  	if (IS_ERR_OR_NULL(ptp_data->clock)) @@ -937,7 +964,6 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  void sja1105_ptp_clock_unregister(struct dsa_switch *ds)  {  	struct sja1105_private *priv = ds->priv; -	struct sja1105_tagger_data *tagger_data = &priv->tagger_data;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	if (IS_ERR_OR_NULL(ptp_data->clock)) @@ -945,7 +971,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds)  	del_timer_sync(&ptp_data->extts_timer);  	ptp_cancel_worker_sync(ptp_data->clock); -	skb_queue_purge(&tagger_data->skb_txtstamp_queue); +	skb_queue_purge(&ptp_data->skb_txtstamp_queue);  	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);  	ptp_clock_unregister(ptp_data->clock);  	ptp_data->clock = NULL; diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 3ae6b9fdd492..416461ee95d2 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -8,6 +8,21 @@  #if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) +/* Timestamps are in units of 8 ns clock ticks (equivalent to + * a fixed 125 MHz clock). + */ +#define SJA1105_TICK_NS			8 + +static inline s64 ns_to_sja1105_ticks(s64 ns) +{ +	return ns / SJA1105_TICK_NS; +} + +static inline s64 sja1105_ticks_to_ns(s64 ticks) +{ +	return ticks * SJA1105_TICK_NS; +} +  /* Calculate the first base_time in the future that satisfies this   * relationship:   * @@ -62,6 +77,10 @@ struct sja1105_ptp_data {  	struct timer_list extts_timer;  	/* Used only on SJA1105 to reconstruct partial timestamps */  	struct sk_buff_head skb_rxtstamp_queue; +	/* Used on SJA1110 where meta frames are generated only for +	 * 2-step TX timestamps +	 */ +	struct sk_buff_head skb_txtstamp_queue;  	struct ptp_clock_info caps;  	struct ptp_clock *clock;  	struct sja1105_ptp_cmd cmd; @@ -112,6 +131,9 @@ bool sja1105_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);  bool sja1110_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);  void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb); +void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id, +				 enum sja1110_meta_tstamp dir, u64 tstamp); +  #else  struct sja1105_ptp_cmd; @@ -178,6 +200,8 @@ static inline int sja1105_ptp_commit(struct dsa_switch *ds,  #define sja1110_rxtstamp NULL  #define sja1110_txtstamp NULL +#define sja1110_process_meta_tstamp NULL +  #endif /* IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) */  #endif /* _SJA1105_PTP_H */ diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index a4b1447ff055..ae55167ce0a6 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -1122,9 +1122,6 @@ static int vsc73xx_gpio_probe(struct vsc73xx *vsc)  	vsc->gc.ngpio = 4;  	vsc->gc.owner = THIS_MODULE;  	vsc->gc.parent = vsc->dev; -#if IS_ENABLED(CONFIG_OF_GPIO) -	vsc->gc.of_node = vsc->dev->of_node; -#endif  	vsc->gc.base = -1;  	vsc->gc.get = vsc73xx_gpio_get;  	vsc->gc.set = vsc73xx_gpio_set; @@ -1216,12 +1213,10 @@ int vsc73xx_probe(struct vsc73xx *vsc)  }  EXPORT_SYMBOL(vsc73xx_probe); -int vsc73xx_remove(struct vsc73xx *vsc) +void vsc73xx_remove(struct vsc73xx *vsc)  {  	dsa_unregister_switch(vsc->ds);  	gpiod_set_value(vsc->reset, 1); - -	return 0;  }  EXPORT_SYMBOL(vsc73xx_remove); diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h index 30b951504e65..30b1f0a36566 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.h +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -26,5 +26,5 @@ struct vsc73xx_ops {  int vsc73xx_is_addr_valid(u8 block, u8 subblock);  int vsc73xx_probe(struct vsc73xx *vsc); -int vsc73xx_remove(struct vsc73xx *vsc); +void vsc73xx_remove(struct vsc73xx *vsc);  void vsc73xx_shutdown(struct vsc73xx *vsc); diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index 910fcb3b252b..0730352cdd57 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -5,6 +5,7 @@   */  #include <net/dsa.h> +#include <linux/etherdevice.h>  #include <linux/if_bridge.h>  #include <linux/of_device.h>  #include <linux/netdev_features.h> @@ -501,7 +502,7 @@ static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,  }  static int xrs700x_bridge_common(struct dsa_switch *ds, int port, -				 struct net_device *bridge, bool join) +				 struct dsa_bridge bridge, bool join)  {  	unsigned int i, cpu_mask = 0, mask = 0;  	struct xrs700x *priv = ds->priv; @@ -513,14 +514,14 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port,  		cpu_mask |= BIT(i); -		if (dsa_to_port(ds, i)->bridge_dev == bridge) +		if (dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		mask |= BIT(i);  	}  	for (i = 0; i < ds->num_ports; i++) { -		if (dsa_to_port(ds, i)->bridge_dev != bridge) +		if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))  			continue;  		/* 1 = Disable forwarding to the port */ @@ -540,13 +541,13 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port,  }  static int xrs700x_bridge_join(struct dsa_switch *ds, int port, -			       struct net_device *bridge) +			       struct dsa_bridge bridge, bool *tx_fwd_offload)  {  	return xrs700x_bridge_common(ds, port, bridge, true);  }  static void xrs700x_bridge_leave(struct dsa_switch *ds, int port, -				 struct net_device *bridge) +				 struct dsa_bridge bridge)  {  	xrs700x_bridge_common(ds, port, bridge, false);  } |