diff options
| author | Mark Brown <broonie@kernel.org> | 2020-12-11 17:48:50 +0000 | 
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2020-12-11 17:48:50 +0000 | 
| commit | 58f7553fa424fd0fd74e8b796d50c66014cebebe (patch) | |
| tree | 8aecb1d047b1df2abbfa1ef323d2a724a6a61c77 /drivers/net/dsa | |
| parent | dd91c555461261fed220ae29a508f508a0afeb43 (diff) | |
| parent | 9326e4f1e5dd1a4410c429638d3c412b6fc17040 (diff) | |
Merge remote-tracking branch 'spi/for-5.10' into spi-linus
Diffstat (limited to 'drivers/net/dsa')
36 files changed, 3666 insertions, 1381 deletions
| diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 468b3c4273c5..2451f61a38e4 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -33,12 +33,12 @@ config NET_DSA_LANTIQ_GSWIP  	  the xrx200 / VR9 SoC.  config NET_DSA_MT7530 -	tristate "Mediatek MT7530 Ethernet switch support" +	tristate "MediaTek MT753x and MT7621 Ethernet switch support"  	depends on NET_DSA  	select NET_DSA_TAG_MTK  	help -	  This enables support for the Mediatek MT7530 Ethernet switch -	  chip. +	  This enables support for the MediaTek MT7530, MT7531, and MT7621 +	  Ethernet switch chips.  config NET_DSA_MV88E6060  	tristate "Marvell 88E6060 ethernet switch chip support" diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index e731db900ee0..288b5a5c3e0d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -17,8 +17,6 @@   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -  #include <linux/delay.h>  #include <linux/export.h>  #include <linux/gpio.h> @@ -767,8 +765,11 @@ static int b53_switch_reset(struct b53_device *dev)  			usleep_range(1000, 2000);  		} while (timeout-- > 0); -		if (timeout == 0) +		if (timeout == 0) { +			dev_err(dev->dev, +				"Timeout waiting for SW_RST to clear!\n");  			return -ETIMEDOUT; +		}  	}  	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt); @@ -976,6 +977,54 @@ int b53_get_sset_count(struct dsa_switch *ds, int port, int sset)  }  EXPORT_SYMBOL(b53_get_sset_count); +enum b53_devlink_resource_id { +	B53_DEVLINK_PARAM_ID_VLAN_TABLE, +}; + +static u64 b53_devlink_vlan_table_get(void *priv) +{ +	struct b53_device *dev = priv; +	struct b53_vlan *vl; +	unsigned int i; +	u64 count = 0; + +	for (i = 0; i < dev->num_vlans; i++) { +		vl = &dev->vlans[i]; +		if (vl->members) +			count++; +	} + +	return count; +} + +int b53_setup_devlink_resources(struct dsa_switch *ds) +{ +	struct devlink_resource_size_params size_params; +	struct b53_device *dev = ds->priv; +	int err; + +	devlink_resource_size_params_init(&size_params, dev->num_vlans, +					  dev->num_vlans, +					  1, DEVLINK_RESOURCE_UNIT_ENTRY); + +	err = dsa_devlink_resource_register(ds, "VLAN", dev->num_vlans, +					    B53_DEVLINK_PARAM_ID_VLAN_TABLE, +					    DEVLINK_RESOURCE_ID_PARENT_TOP, +					    &size_params); +	if (err) +		goto out; + +	dsa_devlink_resource_occ_get_register(ds, +					      B53_DEVLINK_PARAM_ID_VLAN_TABLE, +					      b53_devlink_vlan_table_get, dev); + +	return 0; +out: +	dsa_devlink_resources_unregister(ds); +	return err; +} +EXPORT_SYMBOL(b53_setup_devlink_resources); +  static int b53_setup(struct dsa_switch *ds)  {  	struct b53_device *dev = ds->priv; @@ -991,8 +1040,10 @@ static int b53_setup(struct dsa_switch *ds)  	b53_reset_mib(dev);  	ret = b53_apply_config(dev); -	if (ret) +	if (ret) {  		dev_err(ds->dev, "failed to apply configuration\n"); +		return ret; +	}  	/* Configure IMP/CPU port, disable all other ports. Enabled  	 * ports will be configured with .port_enable @@ -1011,7 +1062,12 @@ static int b53_setup(struct dsa_switch *ds)  	 */  	ds->vlan_filtering_is_global = true; -	return ret; +	return b53_setup_devlink_resources(ds); +} + +static void b53_teardown(struct dsa_switch *ds) +{ +	dsa_devlink_resources_unregister(ds);  }  static void b53_force_link(struct b53_device *dev, int port, int link) @@ -1318,26 +1374,13 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,  }  EXPORT_SYMBOL(b53_phylink_mac_link_up); -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) +int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +		       struct switchdev_trans *trans)  {  	struct b53_device *dev = ds->priv; -	u16 pvid, new_pvid; -	b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid); -	if (!vlan_filtering) { -		/* Filtering is currently enabled, use the default PVID since -		 * the bridge does not expect tagging anymore -		 */ -		dev->ports[port].pvid = pvid; -		new_pvid = b53_default_pvid(dev); -	} else { -		/* Filtering is currently disabled, restore the previous PVID */ -		new_pvid = dev->ports[port].pvid; -	} - -	if (pvid != new_pvid) -		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), -			    new_pvid); +	if (switchdev_trans_ph_prepare(trans)) +		return 0;  	b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering); @@ -2140,6 +2183,7 @@ static int b53_get_max_mtu(struct dsa_switch *ds, int port)  static const struct dsa_switch_ops b53_switch_ops = {  	.get_tag_protocol	= b53_get_tag_protocol,  	.setup			= b53_setup, +	.teardown		= b53_teardown,  	.get_strings		= b53_get_strings,  	.get_ethtool_stats	= b53_get_ethtool_stats,  	.get_sset_count		= b53_get_sset_count, @@ -2562,6 +2606,9 @@ struct b53_device *b53_switch_alloc(struct device *base,  	dev->priv = priv;  	dev->ops = ops;  	ds->ops = &b53_switch_ops; +	ds->configure_vlan_while_not_filtering = true; +	ds->untag_bridge_pvid = true; +	dev->vlan_enabled = ds->configure_vlan_while_not_filtering;  	mutex_init(&dev->reg_mutex);  	mutex_init(&dev->stats_mutex); @@ -2620,8 +2667,9 @@ int b53_switch_detect(struct b53_device *dev)  			dev->chip_id = id32;  			break;  		default: -			pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n", -			       id8, id32); +			dev_err(dev->dev, +				"unsupported switch detected (BCM53%02x/BCM%x)\n", +				id8, id32);  			return -ENODEV;  		}  	} @@ -2651,7 +2699,8 @@ int b53_switch_register(struct b53_device *dev)  	if (ret)  		return ret; -	pr_info("found switch: %s, rev %i\n", dev->name, dev->core_rev); +	dev_info(dev->dev, "found switch: %s, rev %i\n", +		 dev->name, dev->core_rev);  	return dsa_register_switch(dev->ds);  } diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index e942c60e4365..7c67409bb186 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -91,7 +91,6 @@ enum {  struct b53_port {  	u16		vlan_ctl_mask;  	struct ethtool_eee eee; -	u16		pvid;  };  struct b53_vlan { @@ -328,6 +327,7 @@ 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_egress_floods(struct dsa_switch *ds, int port,  			 bool unicast, bool multicast); +int b53_setup_devlink_resources(struct dsa_switch *ds);  void b53_port_event(struct dsa_switch *ds, int port);  void b53_phylink_validate(struct dsa_switch *ds, int port,  			  unsigned long *supported, @@ -347,7 +347,8 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,  			     struct phy_device *phydev,  			     int speed, int duplex,  			     bool tx_pause, bool rx_pause); -int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering); +int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +		       struct switchdev_trans *trans);  int b53_vlan_prepare(struct dsa_switch *ds, int port,  		     const struct switchdev_obj_port_vlan *vlan);  void b53_vlan_add(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 5ebff986a1ac..1e9a0adda2d6 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -14,6 +14,7 @@  #include <linux/phy_fixed.h>  #include <linux/phylink.h>  #include <linux/mii.h> +#include <linux/clk.h>  #include <linux/of.h>  #include <linux/of_irq.h>  #include <linux/of_address.h> @@ -31,6 +32,49 @@  #include "b53/b53_priv.h"  #include "b53/b53_regs.h" +/* Return the number of active ports, not counting the IMP (CPU) port */ +static unsigned int bcm_sf2_num_active_ports(struct dsa_switch *ds) +{ +	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); +	unsigned int port, count = 0; + +	for (port = 0; port < ARRAY_SIZE(priv->port_sts); port++) { +		if (dsa_is_cpu_port(ds, port)) +			continue; +		if (priv->port_sts[port].enabled) +			count++; +	} + +	return count; +} + +static void bcm_sf2_recalc_clock(struct dsa_switch *ds) +{ +	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); +	unsigned long new_rate; +	unsigned int ports_active; +	/* Frequenty in Mhz */ +	static const unsigned long rate_table[] = { +		59220000, +		60820000, +		62500000, +		62500000, +	}; + +	ports_active = bcm_sf2_num_active_ports(ds); +	if (ports_active == 0 || !priv->clk_mdiv) +		return; + +	/* If we overflow our table, just use the recommended operational +	 * frequency +	 */ +	if (ports_active > ARRAY_SIZE(rate_table)) +		new_rate = 90000000; +	else +		new_rate = rate_table[ports_active - 1]; +	clk_set_rate(priv->clk_mdiv, new_rate); +} +  static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)  {  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); @@ -82,6 +126,8 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)  		reg &= ~(RX_DIS | TX_DIS);  		core_writel(priv, reg, CORE_G_PCTL_PORT(port));  	} + +	priv->port_sts[port].enabled = true;  }  static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) @@ -167,6 +213,10 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port,  	if (!dsa_is_user_port(ds, port))  		return 0; +	priv->port_sts[port].enabled = true; + +	bcm_sf2_recalc_clock(ds); +  	/* Clear the memory power down */  	reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);  	reg &= ~P_TXQ_PSM_VDD(port); @@ -260,6 +310,10 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port)  	reg = core_readl(priv, CORE_MEM_PSM_VDD_CTRL);  	reg |= P_TXQ_PSM_VDD(port);  	core_writel(priv, reg, CORE_MEM_PSM_VDD_CTRL); + +	priv->port_sts[port].enabled = false; + +	bcm_sf2_recalc_clock(ds);  } @@ -403,6 +457,7 @@ static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,  {  	struct device_node *port;  	unsigned int port_num; +	struct property *prop;  	phy_interface_t mode;  	int err; @@ -429,15 +484,27 @@ static void bcm_sf2_identify_ports(struct bcm_sf2_priv *priv,  		if (of_property_read_bool(port, "brcm,use-bcm-hdr"))  			priv->brcm_tag_mask |= 1 << port_num; + +		/* Ensure that port 5 is not picked up as a DSA CPU port +		 * flavour but a regular port instead. We should be using +		 * devlink to be able to set the port flavour. +		 */ +		if (port_num == 5 && priv->type == BCM7278_DEVICE_ID) { +			prop = of_find_property(port, "ethernet", NULL); +			if (prop) +				of_remove_property(port, prop); +		}  	}  }  static int bcm_sf2_mdio_register(struct dsa_switch *ds)  {  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); -	struct device_node *dn; +	struct device_node *dn, *child; +	struct phy_device *phydev; +	struct property *prop;  	static int index; -	int err; +	int err, reg;  	/* Find our integrated MDIO bus node */  	dn = of_find_compatible_node(NULL, NULL, "brcm,unimac-mdio"); @@ -471,7 +538,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)  	 * driver.  	 */  	if (of_machine_is_compatible("brcm,bcm7445d0")) -		priv->indir_phy_mask |= (1 << BRCM_PSEUDO_PHY_ADDR); +		priv->indir_phy_mask |= (1 << BRCM_PSEUDO_PHY_ADDR) | (1 << 0);  	else  		priv->indir_phy_mask = 0; @@ -480,6 +547,31 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)  	priv->slave_mii_bus->parent = ds->dev->parent;  	priv->slave_mii_bus->phy_mask = ~priv->indir_phy_mask; +	/* We need to make sure that of_phy_connect() will not work by +	 * removing the 'phandle' and 'linux,phandle' properties and +	 * unregister the existing PHY device that was already registered. +	 */ +	for_each_available_child_of_node(dn, child) { +		if (of_property_read_u32(child, "reg", ®) || +		    reg >= PHY_MAX_ADDR) +			continue; + +		if (!(priv->indir_phy_mask & BIT(reg))) +			continue; + +		prop = of_find_property(child, "phandle", NULL); +		if (prop) +			of_remove_property(child, prop); + +		prop = of_find_property(child, "linux,phandle", NULL); +		if (prop) +			of_remove_property(child, prop); + +		phydev = of_phy_find_device(child); +		if (phydev) +			phy_device_remove(phydev); +	} +  	err = mdiobus_register(priv->slave_mii_bus);  	if (err && dn)  		of_node_put(dn); @@ -750,6 +842,9 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds)  			bcm_sf2_port_disable(ds, port);  	} +	if (!priv->wol_ports_mask) +		clk_disable_unprepare(priv->clk); +  	return 0;  } @@ -758,6 +853,9 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds)  	struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);  	int ret; +	if (!priv->wol_ports_mask) +		clk_prepare_enable(priv->clk); +  	ret = bcm_sf2_sw_rst(priv);  	if (ret) {  		pr_err("%s: failed to software reset switch\n", __func__); @@ -849,7 +947,12 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)  	b53_configure_vlan(ds);  	bcm_sf2_enable_acb(ds); -	return 0; +	return b53_setup_devlink_resources(ds); +} + +static void bcm_sf2_sw_teardown(struct dsa_switch *ds) +{ +	dsa_devlink_resources_unregister(ds);  }  /* The SWITCH_CORE register space is managed by b53 but operates on a page + @@ -986,6 +1089,7 @@ static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds, int port,  static const struct dsa_switch_ops bcm_sf2_ops = {  	.get_tag_protocol	= b53_get_tag_protocol,  	.setup			= bcm_sf2_sw_setup, +	.teardown		= bcm_sf2_sw_teardown,  	.get_strings		= bcm_sf2_sw_get_strings,  	.get_ethtool_stats	= bcm_sf2_sw_get_ethtool_stats,  	.get_sset_count		= bcm_sf2_sw_get_sset_count, @@ -1189,10 +1293,24 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)  		base++;  	} +	priv->clk = devm_clk_get_optional(&pdev->dev, "sw_switch"); +	if (IS_ERR(priv->clk)) +		return PTR_ERR(priv->clk); + +	clk_prepare_enable(priv->clk); + +	priv->clk_mdiv = devm_clk_get_optional(&pdev->dev, "sw_switch_mdiv"); +	if (IS_ERR(priv->clk_mdiv)) { +		ret = PTR_ERR(priv->clk_mdiv); +		goto out_clk; +	} + +	clk_prepare_enable(priv->clk_mdiv); +  	ret = bcm_sf2_sw_rst(priv);  	if (ret) {  		pr_err("unable to software reset switch: %d\n", ret); -		return ret; +		goto out_clk_mdiv;  	}  	bcm_sf2_gphy_enable_set(priv->dev->ds, true); @@ -1200,7 +1318,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)  	ret = bcm_sf2_mdio_register(ds);  	if (ret) {  		pr_err("failed to register MDIO bus\n"); -		return ret; +		goto out_clk_mdiv;  	}  	bcm_sf2_gphy_enable_set(priv->dev->ds, false); @@ -1267,6 +1385,10 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)  out_mdio:  	bcm_sf2_mdio_unregister(priv); +out_clk_mdiv: +	clk_disable_unprepare(priv->clk_mdiv); +out_clk: +	clk_disable_unprepare(priv->clk);  	return ret;  } @@ -1280,6 +1402,8 @@ static int bcm_sf2_sw_remove(struct platform_device *pdev)  	dsa_unregister_switch(priv->dev->ds);  	bcm_sf2_cfp_exit(priv->dev->ds);  	bcm_sf2_mdio_unregister(priv); +	clk_disable_unprepare(priv->clk_mdiv); +	clk_disable_unprepare(priv->clk);  	if (priv->type == BCM7278_DEVICE_ID && !IS_ERR(priv->rcdev))  		reset_control_assert(priv->rcdev); diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index de386dd96d66..1ed901a68536 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h @@ -45,6 +45,7 @@ struct bcm_sf2_hw_params {  struct bcm_sf2_port_status {  	unsigned int link; +	bool enabled;  };  struct bcm_sf2_cfp_priv { @@ -93,6 +94,9 @@ struct bcm_sf2_priv {  	/* Mask of ports enabled for Wake-on-LAN */  	u32				wol_ports_mask; +	struct clk			*clk; +	struct clk			*clk_mdiv; +  	/* MoCA port location */  	int				moca_port; diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index eb600b3dbf26..e38906ae8f23 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -28,6 +28,53 @@ static struct dsa_loop_mib_entry dsa_loop_mibs[] = {  static struct phy_device *phydevs[PHY_MAX_ADDR]; +enum dsa_loop_devlink_resource_id { +	DSA_LOOP_DEVLINK_PARAM_ID_VTU, +}; + +static u64 dsa_loop_devlink_vtu_get(void *priv) +{ +	struct dsa_loop_priv *ps = priv; +	unsigned int i, count = 0; +	struct dsa_loop_vlan *vl; + +	for (i = 0; i < ARRAY_SIZE(ps->vlans); i++) { +		vl = &ps->vlans[i]; +		if (vl->members) +			count++; +	} + +	return count; +} + +static int dsa_loop_setup_devlink_resources(struct dsa_switch *ds) +{ +	struct devlink_resource_size_params size_params; +	struct dsa_loop_priv *ps = ds->priv; +	int err; + +	devlink_resource_size_params_init(&size_params, ARRAY_SIZE(ps->vlans), +					  ARRAY_SIZE(ps->vlans), +					  1, DEVLINK_RESOURCE_UNIT_ENTRY); + +	err = dsa_devlink_resource_register(ds, "VTU", ARRAY_SIZE(ps->vlans), +					    DSA_LOOP_DEVLINK_PARAM_ID_VTU, +					    DEVLINK_RESOURCE_ID_PARENT_TOP, +					    &size_params); +	if (err) +		goto out; + +	dsa_devlink_resource_occ_get_register(ds, +					      DSA_LOOP_DEVLINK_PARAM_ID_VTU, +					      dsa_loop_devlink_vtu_get, ps); + +	return 0; + +out: +	dsa_devlink_resources_unregister(ds); +	return err; +} +  static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds,  						   int port,  						   enum dsa_tag_protocol mp) @@ -48,7 +95,12 @@ static int dsa_loop_setup(struct dsa_switch *ds)  	dev_dbg(ds->dev, "%s\n", __func__); -	return 0; +	return dsa_loop_setup_devlink_resources(ds); +} + +static void dsa_loop_teardown(struct dsa_switch *ds) +{ +	dsa_devlink_resources_unregister(ds);  }  static int dsa_loop_get_sset_count(struct dsa_switch *ds, int port, int sset) @@ -138,7 +190,8 @@ static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,  }  static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, -					bool vlan_filtering) +					bool vlan_filtering, +					struct switchdev_trans *trans)  {  	dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n",  		__func__, port, vlan_filtering); @@ -243,6 +296,7 @@ static int dsa_loop_port_max_mtu(struct dsa_switch *ds, int port)  static const struct dsa_switch_ops dsa_loop_driver = {  	.get_tag_protocol	= dsa_loop_get_protocol,  	.setup			= dsa_loop_setup, +	.teardown		= dsa_loop_teardown,  	.get_strings		= dsa_loop_get_strings,  	.get_ethtool_stats	= dsa_loop_get_ethtool_stats,  	.get_sset_count		= dsa_loop_get_sset_count, @@ -290,6 +344,7 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev)  	ds->dev = &mdiodev->dev;  	ds->ops = &dsa_loop_driver;  	ds->priv = ps; +	ds->configure_vlan_while_not_filtering = true;  	ps->bus = mdiodev->bus;  	dev_set_drvdata(&mdiodev->dev, ds); diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 521ebc072903..74db81dafee3 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -736,14 +736,23 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv)  }  static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port, -				     bool vlan_filtering) +				     bool vlan_filtering, +				     struct switchdev_trans *trans)  {  	struct gswip_priv *priv = ds->priv; -	struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;  	/* Do not allow changing the VLAN filtering options while in bridge */ -	if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering && bridge) -		return -EIO; +	if (switchdev_trans_ph_prepare(trans)) { +		struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev; + +		if (!bridge) +			return 0; + +		if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) +			return -EIO; + +		return 0; +	}  	if (vlan_filtering) {  		/* Use port based VLAN tag */ @@ -781,8 +790,15 @@ static int gswip_setup(struct dsa_switch *ds)  	/* disable port fetch/store dma on all ports */  	for (i = 0; i < priv->hw_info->max_ports; i++) { +		struct switchdev_trans trans; + +		/* Skip the prepare phase, this shouldn't return an error +		 * during setup. +		 */ +		trans.ph_prepare = false; +  		gswip_port_disable(ds, i); -		gswip_port_vlan_filtering(ds, i, false); +		gswip_port_vlan_filtering(ds, i, false, &trans);  	}  	/* enable Switch */ diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index f5779e152377..1e101ab56cea 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -782,10 +782,14 @@ static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port)  }  static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port, -				       bool flag) +				       bool flag, +				       struct switchdev_trans *trans)  {  	struct ksz_device *dev = ds->priv; +	if (switchdev_trans_ph_prepare(trans)) +		return 0; +  	ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);  	return 0; diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 2f5506ac7d19..abfd3802bb51 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -493,10 +493,14 @@ static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)  }  static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, -				       bool flag) +				       bool flag, +				       struct switchdev_trans *trans)  {  	struct ksz_device *dev = ds->priv; +	if (switchdev_trans_ph_prepare(trans)) +		return 0; +  	if (flag) {  		ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,  			     PORT_VLAN_LOOKUP_VID_0, true); @@ -1235,6 +1239,9 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)  			if (p->interface == PHY_INTERFACE_MODE_RGMII_ID ||  			    p->interface == PHY_INTERFACE_MODE_RGMII_TXID)  				data8 |= PORT_RGMII_ID_EG_ENABLE; +			/* On KSZ9893, disable RGMII in-band status support */ +			if (dev->features & IS_9893) +				data8 &= ~PORT_MII_MAC_MODE;  			p->phydev.speed = SPEED_1000;  			break;  		} @@ -1265,6 +1272,8 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds)  	for (i = 0; i < dev->port_cnt; i++) {  		if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) {  			phy_interface_t interface; +			const char *prev_msg; +			const char *prev_mode;  			dev->cpu_port = i;  			dev->host_mask = (1 << dev->cpu_port); @@ -1287,11 +1296,19 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds)  					p->interface = interface;  				}  			} -			if (interface && interface != p->interface) -				dev_info(dev->dev, -					 "use %s instead of %s\n", -					  phy_modes(p->interface), -					  phy_modes(interface)); +			if (interface && interface != p->interface) { +				prev_msg = " instead of "; +				prev_mode = phy_modes(interface); +			} else { +				prev_msg = ""; +				prev_mode = ""; +			} +			dev_info(dev->dev, +				 "Port%d: using phy mode %s%s%s\n", +				 i, +				 phy_modes(p->interface), +				 prev_msg, +				 prev_mode);  			/* enable cpu port */  			ksz9477_port_setup(dev, i, true); @@ -1435,10 +1452,12 @@ static int ksz9477_switch_detect(struct ksz_device *dev)  	/* Default capability is gigabit capable. */  	dev->features = GBIT_SUPPORT; +	dev_dbg(dev->dev, "Switch detect: ID=%08x%02x\n", id32, data8);  	id_hi = (u8)(id32 >> 16);  	id_lo = (u8)(id32 >> 8);  	if ((id_lo & 0xf) == 3) {  		/* Chip is from KSZ9893 design. */ +		dev_info(dev->dev, "Found KSZ9893\n");  		dev->features |= IS_9893;  		/* Chip does not support gigabit. */ @@ -1447,6 +1466,7 @@ static int ksz9477_switch_detect(struct ksz_device *dev)  		dev->mib_port_cnt = 3;  		dev->phy_port_cnt = 2;  	} else { +		dev_info(dev->dev, "Found KSZ9477 or compatible\n");  		/* Chip uses new XMII register definitions. */  		dev->features |= NEW_XMII; diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 7951f52d860d..4e053a25d077 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -80,6 +80,7 @@ static const struct of_device_id ksz9477_dt_ids[] = {  	{ .compatible = "microchip,ksz9477" },  	{ .compatible = "microchip,ksz9897" },  	{ .compatible = "microchip,ksz9893" }, +	{ .compatible = "microchip,ksz9563" },  	{ .compatible = "microchip,ksz9567" },  	{},  }; diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index c796d42730ba..0ef854911f21 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -103,14 +103,8 @@ void ksz_init_mib_timer(struct ksz_device *dev)  	INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); -	/* Read MIB counters every 30 seconds to avoid overflow. */ -	dev->mib_read_interval = msecs_to_jiffies(30000); -  	for (i = 0; i < dev->mib_port_cnt; i++)  		dev->dev_ops->port_init_cnt(dev, i); - -	/* Start the timer 2 seconds later. */ -	schedule_delayed_work(&dev->mib_read, msecs_to_jiffies(2000));  }  EXPORT_SYMBOL_GPL(ksz_init_mib_timer); @@ -143,7 +137,9 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,  	/* Read all MIB counters when the link is going down. */  	p->read = true; -	schedule_delayed_work(&dev->mib_read, 0); +	/* timer started */ +	if (dev->mib_read_interval) +		schedule_delayed_work(&dev->mib_read, 0);  }  EXPORT_SYMBOL_GPL(ksz_mac_link_down); @@ -402,8 +398,9 @@ int ksz_switch_register(struct ksz_device *dev,  	if (dev->reset_gpio) {  		gpiod_set_value_cansleep(dev->reset_gpio, 1); -		mdelay(10); +		usleep_range(10000, 12000);  		gpiod_set_value_cansleep(dev->reset_gpio, 0); +		usleep_range(100, 1000);  	}  	mutex_init(&dev->dev_mutex); @@ -450,6 +447,12 @@ int ksz_switch_register(struct ksz_device *dev,  		return ret;  	} +	/* Read MIB counters every 30 seconds to avoid overflow. */ +	dev->mib_read_interval = msecs_to_jiffies(30000); + +	/* Start the MIB timer. */ +	schedule_delayed_work(&dev->mib_read, 0); +  	return 0;  }  EXPORT_SYMBOL(ksz_switch_register); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1aaf47a0da2b..de7692b763d8 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -234,6 +234,12 @@ mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val)  }  static u32 +_mt7530_unlocked_read(struct mt7530_dummy_poll *p) +{ +	return mt7530_mii_read(p->priv, p->reg); +} + +static u32  _mt7530_read(struct mt7530_dummy_poll *p)  {  	struct mii_bus		*bus = p->priv->bus; @@ -372,8 +378,9 @@ mt7530_fdb_write(struct mt7530_priv *priv, u16 vid,  		mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]);  } +/* Setup TX circuit including relevant PAD and driving */  static int -mt7530_pad_clk_setup(struct dsa_switch *ds, int mode) +mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)  {  	struct mt7530_priv *priv = ds->priv;  	u32 ncpo1, ssc_delta, trgint, i, xtal; @@ -387,7 +394,7 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)  		return -EINVAL;  	} -	switch (mode) { +	switch (interface) {  	case PHY_INTERFACE_MODE_RGMII:  		trgint = 0;  		/* PLL frequency: 125MHz */ @@ -409,7 +416,8 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)  		}  		break;  	default: -		dev_err(priv->dev, "xMII mode %d not supported\n", mode); +		dev_err(priv->dev, "xMII interface %d not supported\n", +			interface);  		return -EINVAL;  	} @@ -481,6 +489,108 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, int mode)  	return 0;  } +static bool mt7531_dual_sgmii_supported(struct mt7530_priv *priv) +{ +	u32 val; + +	val = mt7530_read(priv, MT7531_TOP_SIG_SR); + +	return (val & PAD_DUAL_SGMII_EN) != 0; +} + +static int +mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 top_sig; +	u32 hwstrap; +	u32 xtal; +	u32 val; + +	if (mt7531_dual_sgmii_supported(priv)) +		return 0; + +	val = mt7530_read(priv, MT7531_CREV); +	top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR); +	hwstrap = mt7530_read(priv, MT7531_HWTRAP); +	if ((val & CHIP_REV_M) > 0) +		xtal = (top_sig & PAD_MCM_SMI_EN) ? HWTRAP_XTAL_FSEL_40MHZ : +						    HWTRAP_XTAL_FSEL_25MHZ; +	else +		xtal = hwstrap & HWTRAP_XTAL_FSEL_MASK; + +	/* Step 1 : Disable MT7531 COREPLL */ +	val = mt7530_read(priv, MT7531_PLLGP_EN); +	val &= ~EN_COREPLL; +	mt7530_write(priv, MT7531_PLLGP_EN, val); + +	/* Step 2: switch to XTAL output */ +	val = mt7530_read(priv, MT7531_PLLGP_EN); +	val |= SW_CLKSW; +	mt7530_write(priv, MT7531_PLLGP_EN, val); + +	val = mt7530_read(priv, MT7531_PLLGP_CR0); +	val &= ~RG_COREPLL_EN; +	mt7530_write(priv, MT7531_PLLGP_CR0, val); + +	/* Step 3: disable PLLGP and enable program PLLGP */ +	val = mt7530_read(priv, MT7531_PLLGP_EN); +	val |= SW_PLLGP; +	mt7530_write(priv, MT7531_PLLGP_EN, val); + +	/* Step 4: program COREPLL output frequency to 500MHz */ +	val = mt7530_read(priv, MT7531_PLLGP_CR0); +	val &= ~RG_COREPLL_POSDIV_M; +	val |= 2 << RG_COREPLL_POSDIV_S; +	mt7530_write(priv, MT7531_PLLGP_CR0, val); +	usleep_range(25, 35); + +	switch (xtal) { +	case HWTRAP_XTAL_FSEL_25MHZ: +		val = mt7530_read(priv, MT7531_PLLGP_CR0); +		val &= ~RG_COREPLL_SDM_PCW_M; +		val |= 0x140000 << RG_COREPLL_SDM_PCW_S; +		mt7530_write(priv, MT7531_PLLGP_CR0, val); +		break; +	case HWTRAP_XTAL_FSEL_40MHZ: +		val = mt7530_read(priv, MT7531_PLLGP_CR0); +		val &= ~RG_COREPLL_SDM_PCW_M; +		val |= 0x190000 << RG_COREPLL_SDM_PCW_S; +		mt7530_write(priv, MT7531_PLLGP_CR0, val); +		break; +	}; + +	/* Set feedback divide ratio update signal to high */ +	val = mt7530_read(priv, MT7531_PLLGP_CR0); +	val |= RG_COREPLL_SDM_PCW_CHG; +	mt7530_write(priv, MT7531_PLLGP_CR0, val); +	/* Wait for at least 16 XTAL clocks */ +	usleep_range(10, 20); + +	/* Step 5: set feedback divide ratio update signal to low */ +	val = mt7530_read(priv, MT7531_PLLGP_CR0); +	val &= ~RG_COREPLL_SDM_PCW_CHG; +	mt7530_write(priv, MT7531_PLLGP_CR0, val); + +	/* Enable 325M clock for SGMII */ +	mt7530_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); + +	/* Enable 250SSC clock for RGMII */ +	mt7530_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); + +	/* Step 6: Enable MT7531 PLL */ +	val = mt7530_read(priv, MT7531_PLLGP_CR0); +	val |= RG_COREPLL_EN; +	mt7530_write(priv, MT7531_PLLGP_CR0, val); + +	val = mt7530_read(priv, MT7531_PLLGP_EN); +	val |= EN_COREPLL; +	mt7530_write(priv, MT7531_PLLGP_EN, val); +	usleep_range(25, 35); + +	return 0; +} +  static void  mt7530_mib_reset(struct dsa_switch *ds)  { @@ -505,6 +615,217 @@ static int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum,  	return mdiobus_write_nested(priv->bus, port, regnum, val);  } +static int +mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, +			int regnum) +{ +	struct mii_bus *bus = priv->bus; +	struct mt7530_dummy_poll p; +	u32 reg, val; +	int ret; + +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_DEV_ADDR(devad) | regnum; +	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	reg = MT7531_MDIO_CL45_READ | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_DEV_ADDR(devad); +	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	ret = val & MT7531_MDIO_RW_DATA_MASK; +out: +	mutex_unlock(&bus->mdio_lock); + +	return ret; +} + +static int +mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, +			 int regnum, u32 data) +{ +	struct mii_bus *bus = priv->bus; +	struct mt7530_dummy_poll p; +	u32 val, reg; +	int ret; + +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_DEV_ADDR(devad) | regnum; +	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	reg = MT7531_MDIO_CL45_WRITE | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_DEV_ADDR(devad) | data; +	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +out: +	mutex_unlock(&bus->mdio_lock); + +	return ret; +} + +static int +mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) +{ +	struct mii_bus *bus = priv->bus; +	struct mt7530_dummy_poll p; +	int ret; +	u32 val; + +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	val = MT7531_MDIO_CL22_READ | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_REG_ADDR(regnum); + +	mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, val, +				 !(val & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	ret = val & MT7531_MDIO_RW_DATA_MASK; +out: +	mutex_unlock(&bus->mdio_lock); + +	return ret; +} + +static int +mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, +			 u16 data) +{ +	struct mii_bus *bus = priv->bus; +	struct mt7530_dummy_poll p; +	int ret; +	u32 reg; + +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7531_PHY_IAC); + +	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +				 !(reg & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +	reg = MT7531_MDIO_CL22_WRITE | MT7531_MDIO_PHY_ADDR(port) | +	      MT7531_MDIO_REG_ADDR(regnum) | data; + +	mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + +	ret = readx_poll_timeout(_mt7530_unlocked_read, &p, reg, +				 !(reg & MT7531_PHY_ACS_ST), 20, 100000); +	if (ret < 0) { +		dev_err(priv->dev, "poll timeout\n"); +		goto out; +	} + +out: +	mutex_unlock(&bus->mdio_lock); + +	return ret; +} + +static int +mt7531_ind_phy_read(struct dsa_switch *ds, int port, int regnum) +{ +	struct mt7530_priv *priv = ds->priv; +	int devad; +	int ret; + +	if (regnum & MII_ADDR_C45) { +		devad = (regnum >> MII_DEVADDR_C45_SHIFT) & 0x1f; +		ret = mt7531_ind_c45_phy_read(priv, port, devad, +					      regnum & MII_REGADDR_C45_MASK); +	} else { +		ret = mt7531_ind_c22_phy_read(priv, port, regnum); +	} + +	return ret; +} + +static int +mt7531_ind_phy_write(struct dsa_switch *ds, int port, int regnum, +		     u16 data) +{ +	struct mt7530_priv *priv = ds->priv; +	int devad; +	int ret; + +	if (regnum & MII_ADDR_C45) { +		devad = (regnum >> MII_DEVADDR_C45_SHIFT) & 0x1f; +		ret = mt7531_ind_c45_phy_write(priv, port, devad, +					       regnum & MII_REGADDR_C45_MASK, +					       data); +	} else { +		ret = mt7531_ind_c22_phy_write(priv, port, regnum, data); +	} + +	return ret; +} +  static void  mt7530_get_strings(struct dsa_switch *ds, int port, u32 stringset,  		   uint8_t *data) @@ -621,9 +942,18 @@ unlock_exit:  }  static int -mt7530_cpu_port_enable(struct mt7530_priv *priv, -		       int port) +mt753x_cpu_port_enable(struct dsa_switch *ds, int port)  { +	struct mt7530_priv *priv = ds->priv; +	int ret; + +	/* Setup max capability of CPU port at first */ +	if (priv->info->cpu_port_config) { +		ret = priv->info->cpu_port_config(ds, port); +		if (ret) +			return ret; +	} +  	/* Enable Mediatek header mode on the cpu port */  	mt7530_write(priv, MT7530_PVC_P(port),  		     PORT_SPEC_TAG); @@ -636,7 +966,7 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv,  		mt7530_rmw(priv, MT7530_MFC, CPU_MASK, CPU_EN | CPU_PORT(port));  	/* CPU port gets connected to all user ports of -	 * the switch +	 * the switch.  	 */  	mt7530_write(priv, MT7530_PCR_P(port),  		     PCR_MATRIX(dsa_user_ports(priv->ds))); @@ -959,8 +1289,12 @@ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)  static int  mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, -			   bool vlan_filtering) +			   bool vlan_filtering, +			   struct switchdev_trans *trans)  { +	if (switchdev_trans_ph_prepare(trans)) +		return 0; +  	if (vlan_filtering) {  		/* The port is being kept as VLAN-unaware port when bridge is  		 * set up with vlan_filtering not being set, Otherwise, the @@ -1130,27 +1464,42 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,  	return 0;  } -static int mt7530_port_mirror_add(struct dsa_switch *ds, int port, +static int mt753x_mirror_port_get(unsigned int id, u32 val) +{ +	return (id == ID_MT7531) ? MT7531_MIRROR_PORT_GET(val) : +				   MIRROR_PORT(val); +} + +static int mt753x_mirror_port_set(unsigned int id, u32 val) +{ +	return (id == ID_MT7531) ? MT7531_MIRROR_PORT_SET(val) : +				   MIRROR_PORT(val); +} + +static int mt753x_port_mirror_add(struct dsa_switch *ds, int port,  				  struct dsa_mall_mirror_tc_entry *mirror,  				  bool ingress)  {  	struct mt7530_priv *priv = ds->priv; +	int monitor_port;  	u32 val;  	/* Check for existent entry */  	if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port))  		return -EEXIST; -	val = mt7530_read(priv, MT7530_MFC); +	val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id));  	/* MT7530 only supports one monitor port */ -	if (val & MIRROR_EN && MIRROR_PORT(val) != mirror->to_local_port) +	monitor_port = mt753x_mirror_port_get(priv->id, val); +	if (val & MT753X_MIRROR_EN(priv->id) && +	    monitor_port != mirror->to_local_port)  		return -EEXIST; -	val |= MIRROR_EN; -	val &= ~MIRROR_MASK; -	val |= mirror->to_local_port; -	mt7530_write(priv, MT7530_MFC, val); +	val |= MT753X_MIRROR_EN(priv->id); +	val &= ~MT753X_MIRROR_MASK(priv->id); +	val |= mt753x_mirror_port_set(priv->id, mirror->to_local_port); +	mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val);  	val = mt7530_read(priv, MT7530_PCR_P(port));  	if (ingress) { @@ -1165,7 +1514,7 @@ static int mt7530_port_mirror_add(struct dsa_switch *ds, int port,  	return 0;  } -static void mt7530_port_mirror_del(struct dsa_switch *ds, int port, +static void mt753x_port_mirror_del(struct dsa_switch *ds, int port,  				   struct dsa_mall_mirror_tc_entry *mirror)  {  	struct mt7530_priv *priv = ds->priv; @@ -1182,9 +1531,9 @@ static void mt7530_port_mirror_del(struct dsa_switch *ds, int port,  	mt7530_write(priv, MT7530_PCR_P(port), val);  	if (!priv->mirror_rx && !priv->mirror_tx) { -		val = mt7530_read(priv, MT7530_MFC); -		val &= ~MIRROR_EN; -		mt7530_write(priv, MT7530_MFC, val); +		val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id)); +		val &= ~MT753X_MIRROR_EN(priv->id); +		mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val);  	}  } @@ -1290,9 +1639,11 @@ mt7530_setup(struct dsa_switch *ds)  		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK,  			   PCR_MATRIX_CLR); -		if (dsa_is_cpu_port(ds, i)) -			mt7530_cpu_port_enable(priv, i); -		else +		if (dsa_is_cpu_port(ds, i)) { +			ret = mt753x_cpu_port_enable(ds, i); +			if (ret) +				return ret; +		} else  			mt7530_port_disable(ds, i);  		/* Enable consistent egress tag */ @@ -1352,51 +1703,492 @@ mt7530_setup(struct dsa_switch *ds)  	return 0;  } -static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port, -				      unsigned int mode, -				      const struct phylink_link_state *state) +static int +mt7531_setup(struct dsa_switch *ds) +{ +	struct mt7530_priv *priv = ds->priv; +	struct mt7530_dummy_poll p; +	u32 val, id; +	int ret, i; + +	/* Reset whole chip through gpio pin or memory-mapped registers for +	 * different type of hardware +	 */ +	if (priv->mcm) { +		reset_control_assert(priv->rstc); +		usleep_range(1000, 1100); +		reset_control_deassert(priv->rstc); +	} else { +		gpiod_set_value_cansleep(priv->reset, 0); +		usleep_range(1000, 1100); +		gpiod_set_value_cansleep(priv->reset, 1); +	} + +	/* Waiting for MT7530 got to stable */ +	INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP); +	ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, +				 20, 1000000); +	if (ret < 0) { +		dev_err(priv->dev, "reset timeout\n"); +		return ret; +	} + +	id = mt7530_read(priv, MT7531_CREV); +	id >>= CHIP_NAME_SHIFT; + +	if (id != MT7531_ID) { +		dev_err(priv->dev, "chip %x can't be supported\n", id); +		return -ENODEV; +	} + +	/* Reset the switch through internal reset */ +	mt7530_write(priv, MT7530_SYS_CTRL, +		     SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | +		     SYS_CTRL_REG_RST); + +	if (mt7531_dual_sgmii_supported(priv)) { +		priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII; + +		/* Let ds->slave_mii_bus be able to access external phy. */ +		mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK, +			   MT7531_EXT_P_MDC_11); +		mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK, +			   MT7531_EXT_P_MDIO_12); +	} else { +		priv->p5_intf_sel = P5_INTF_SEL_GMAC5; +	} +	dev_dbg(ds->dev, "P5 support %s interface\n", +		p5_intf_modes(priv->p5_intf_sel)); + +	mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, +		   MT7531_GPIO0_INTERRUPT); + +	/* Let phylink decide the interface later. */ +	priv->p5_interface = PHY_INTERFACE_MODE_NA; +	priv->p6_interface = PHY_INTERFACE_MODE_NA; + +	/* Enable PHY core PLL, since phy_device has not yet been created +	 * provided for phy_[read,write]_mmd_indirect is called, we provide +	 * our own mt7531_ind_mmd_phy_[read,write] to complete this +	 * function. +	 */ +	val = mt7531_ind_c45_phy_read(priv, MT753X_CTRL_PHY_ADDR, +				      MDIO_MMD_VEND2, CORE_PLL_GROUP4); +	val |= MT7531_PHY_PLL_BYPASS_MODE; +	val &= ~MT7531_PHY_PLL_OFF; +	mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2, +				 CORE_PLL_GROUP4, val); + +	/* BPDU to CPU port */ +	mt7530_rmw(priv, MT7531_CFC, MT7531_CPU_PMAP_MASK, +		   BIT(MT7530_CPU_PORT)); +	mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, +		   MT753X_BPDU_CPU_ONLY); + +	/* Enable and reset MIB counters */ +	mt7530_mib_reset(ds); + +	for (i = 0; i < MT7530_NUM_PORTS; i++) { +		/* Disable forwarding by default on all ports */ +		mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, +			   PCR_MATRIX_CLR); + +		mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); + +		if (dsa_is_cpu_port(ds, i)) { +			ret = mt753x_cpu_port_enable(ds, i); +			if (ret) +				return ret; +		} else +			mt7530_port_disable(ds, i); + +		/* Enable consistent egress tag */ +		mt7530_rmw(priv, MT7530_PVC_P(i), PVC_EG_TAG_MASK, +			   PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); +	} + +	ds->configure_vlan_while_not_filtering = true; + +	/* Flush the FDB table */ +	ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); +	if (ret < 0) +		return ret; + +	return 0; +} + +static bool +mt7530_phy_mode_supported(struct dsa_switch *ds, int port, +			  const struct phylink_link_state *state)  {  	struct mt7530_priv *priv = ds->priv; -	u32 mcr_cur, mcr_new;  	switch (port) { -	case 0: /* Internal phy */ -	case 1: -	case 2: -	case 3: -	case 4: +	case 0 ... 4: /* Internal phy */  		if (state->interface != PHY_INTERFACE_MODE_GMII) -			return; +			return false;  		break;  	case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ -		if (priv->p5_interface == state->interface) -			break;  		if (!phy_interface_mode_is_rgmii(state->interface) &&  		    state->interface != PHY_INTERFACE_MODE_MII &&  		    state->interface != PHY_INTERFACE_MODE_GMII) -			return; +			return false; +		break; +	case 6: /* 1st cpu port */ +		if (state->interface != PHY_INTERFACE_MODE_RGMII && +		    state->interface != PHY_INTERFACE_MODE_TRGMII) +			return false; +		break; +	default: +		dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, +			port); +		return false; +	} -		mt7530_setup_port5(ds, state->interface); +	return true; +} + +static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) +{ +	return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); +} + +static bool +mt7531_phy_mode_supported(struct dsa_switch *ds, int port, +			  const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	switch (port) { +	case 0 ... 4: /* Internal phy */ +		if (state->interface != PHY_INTERFACE_MODE_GMII) +			return false; +		break; +	case 5: /* 2nd cpu port supports either rgmii or sgmii/8023z */ +		if (mt7531_is_rgmii_port(priv, port)) +			return phy_interface_mode_is_rgmii(state->interface); +		fallthrough; +	case 6: /* 1st cpu port supports sgmii/8023z only */ +		if (state->interface != PHY_INTERFACE_MODE_SGMII && +		    !phy_interface_mode_is_8023z(state->interface)) +			return false; +		break; +	default: +		dev_err(priv->dev, "%s: unsupported port: %i\n", __func__, +			port); +		return false; +	} + +	return true; +} + +static bool +mt753x_phy_mode_supported(struct dsa_switch *ds, int port, +			  const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->phy_mode_supported(ds, port, state); +} + +static int +mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->pad_setup(ds, state->interface); +} + +static int +mt7530_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +		  phy_interface_t interface) +{ +	struct mt7530_priv *priv = ds->priv; + +	/* Only need to setup port5. */ +	if (port != 5) +		return 0; + +	mt7530_setup_port5(priv->ds, interface); + +	return 0; +} + +static int mt7531_rgmii_setup(struct mt7530_priv *priv, u32 port, +			      phy_interface_t interface, +			      struct phy_device *phydev) +{ +	u32 val; + +	if (!mt7531_is_rgmii_port(priv, port)) { +		dev_err(priv->dev, "RGMII mode is not available for port %d\n", +			port); +		return -EINVAL; +	} + +	val = mt7530_read(priv, MT7531_CLKGEN_CTRL); +	val |= GP_CLK_EN; +	val &= ~GP_MODE_MASK; +	val |= GP_MODE(MT7531_GP_MODE_RGMII); +	val &= ~CLK_SKEW_IN_MASK; +	val |= CLK_SKEW_IN(MT7531_CLK_SKEW_NO_CHG); +	val &= ~CLK_SKEW_OUT_MASK; +	val |= CLK_SKEW_OUT(MT7531_CLK_SKEW_NO_CHG); +	val |= TXCLK_NO_REVERSE | RXCLK_NO_DELAY; + +	/* Do not adjust rgmii delay when vendor phy driver presents. */ +	if (!phydev || phy_driver_is_genphy(phydev)) { +		val &= ~(TXCLK_NO_REVERSE | RXCLK_NO_DELAY); +		switch (interface) { +		case PHY_INTERFACE_MODE_RGMII: +			val |= TXCLK_NO_REVERSE; +			val |= RXCLK_NO_DELAY; +			break; +		case PHY_INTERFACE_MODE_RGMII_RXID: +			val |= TXCLK_NO_REVERSE; +			break; +		case PHY_INTERFACE_MODE_RGMII_TXID: +			val |= RXCLK_NO_DELAY; +			break; +		case PHY_INTERFACE_MODE_RGMII_ID: +			break; +		default: +			return -EINVAL; +		} +	} +	mt7530_write(priv, MT7531_CLKGEN_CTRL, val); + +	return 0; +} + +static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, +				  unsigned long *supported) +{ +	/* Port5 supports ethier RGMII or SGMII. +	 * Port6 supports SGMII only. +	 */ +	switch (port) { +	case 5: +		if (mt7531_is_rgmii_port(priv, port)) +			break; +		fallthrough; +	case 6: +		phylink_set(supported, 1000baseX_Full); +		phylink_set(supported, 2500baseX_Full); +		phylink_set(supported, 2500baseT_Full); +	} +} + +static void +mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, +			   unsigned int mode, phy_interface_t interface, +			   int speed, int duplex) +{ +	struct mt7530_priv *priv = ds->priv; +	unsigned int val; + +	/* For adjusting speed and duplex of SGMII force mode. */ +	if (interface != PHY_INTERFACE_MODE_SGMII || +	    phylink_autoneg_inband(mode)) +		return; + +	/* SGMII force mode setting */ +	val = mt7530_read(priv, MT7531_SGMII_MODE(port)); +	val &= ~MT7531_SGMII_IF_MODE_MASK; + +	switch (speed) { +	case SPEED_10: +		val |= MT7531_SGMII_FORCE_SPEED_10; +		break; +	case SPEED_100: +		val |= MT7531_SGMII_FORCE_SPEED_100; +		break; +	case SPEED_1000: +		val |= MT7531_SGMII_FORCE_SPEED_1000; +		break; +	} + +	/* MT7531 SGMII 1G force mode can only work in full duplex mode, +	 * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. +	 */ +	if ((speed == SPEED_10 || speed == SPEED_100) && +	    duplex != DUPLEX_FULL) +		val |= MT7531_SGMII_FORCE_HALF_DUPLEX; + +	mt7530_write(priv, MT7531_SGMII_MODE(port), val); +} + +static bool mt753x_is_mac_port(u32 port) +{ +	return (port == 5 || port == 6); +} + +static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port, +					 phy_interface_t interface) +{ +	u32 val; + +	if (!mt753x_is_mac_port(port)) +		return -EINVAL; + +	mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), +		   MT7531_SGMII_PHYA_PWD); + +	val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port)); +	val &= ~MT7531_RG_TPHY_SPEED_MASK; +	/* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B +	 * encoding. +	 */ +	val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ? +		MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G; +	mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val); + +	mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); + +	/* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex +	 * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. +	 */ +	mt7530_rmw(priv, MT7531_SGMII_MODE(port), +		   MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS, +		   MT7531_SGMII_FORCE_SPEED_1000); + +	mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); + +	return 0; +} + +static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port, +				      phy_interface_t interface) +{ +	if (!mt753x_is_mac_port(port)) +		return -EINVAL; + +	mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), +		   MT7531_SGMII_PHYA_PWD); + +	mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), +		   MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G); + +	mt7530_set(priv, MT7531_SGMII_MODE(port), +		   MT7531_SGMII_REMOTE_FAULT_DIS | +		   MT7531_SGMII_SPEED_DUPLEX_AN); + +	mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port), +		   MT7531_SGMII_TX_CONFIG_MASK, 1); + +	mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); + +	mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART); + +	mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); + +	return 0; +} + +static void mt7531_sgmii_restart_an(struct dsa_switch *ds, int port) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 val; + +	/* Only restart AN when AN is enabled */ +	val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); +	if (val & MT7531_SGMII_AN_ENABLE) { +		val |= MT7531_SGMII_AN_RESTART; +		mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val); +	} +} + +static int +mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +		  phy_interface_t interface) +{ +	struct mt7530_priv *priv = ds->priv; +	struct phy_device *phydev; +	struct dsa_port *dp; + +	if (!mt753x_is_mac_port(port)) { +		dev_err(priv->dev, "port %d is not a MAC port\n", port); +		return -EINVAL; +	} + +	switch (interface) { +	case PHY_INTERFACE_MODE_RGMII: +	case PHY_INTERFACE_MODE_RGMII_ID: +	case PHY_INTERFACE_MODE_RGMII_RXID: +	case PHY_INTERFACE_MODE_RGMII_TXID: +		dp = dsa_to_port(ds, port); +		phydev = dp->slave->phydev; +		return mt7531_rgmii_setup(priv, port, interface, phydev); +	case PHY_INTERFACE_MODE_SGMII: +		return mt7531_sgmii_setup_mode_an(priv, port, interface); +	case PHY_INTERFACE_MODE_NA: +	case PHY_INTERFACE_MODE_1000BASEX: +	case PHY_INTERFACE_MODE_2500BASEX: +		if (phylink_autoneg_inband(mode)) +			return -EINVAL; + +		return mt7531_sgmii_setup_mode_force(priv, port, interface); +	default: +		return -EINVAL; +	} + +	return -EINVAL; +} + +static int +mt753x_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +		  const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->mac_port_config(ds, port, mode, state->interface); +} + +static void +mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, +			  const struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; +	u32 mcr_cur, mcr_new; + +	if (!mt753x_phy_mode_supported(ds, port, state)) +		goto unsupported; + +	switch (port) { +	case 0 ... 4: /* Internal phy */ +		if (state->interface != PHY_INTERFACE_MODE_GMII) +			goto unsupported; +		break; +	case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ +		if (priv->p5_interface == state->interface) +			break; + +		if (mt753x_mac_config(ds, port, mode, state) < 0) +			goto unsupported; + +		if (priv->p5_intf_sel != P5_DISABLED) +			priv->p5_interface = state->interface;  		break;  	case 6: /* 1st cpu port */  		if (priv->p6_interface == state->interface)  			break; -		if (state->interface != PHY_INTERFACE_MODE_RGMII && -		    state->interface != PHY_INTERFACE_MODE_TRGMII) -			return; +		mt753x_pad_setup(ds, state); -		/* Setup TX circuit incluing relevant PAD and driving */ -		mt7530_pad_clk_setup(ds, state->interface); +		if (mt753x_mac_config(ds, port, mode, state) < 0) +			goto unsupported;  		priv->p6_interface = state->interface;  		break;  	default: -		dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); +unsupported: +		dev_err(ds->dev, "%s: unsupported %s port: %i\n", +			__func__, phy_modes(state->interface), port);  		return;  	} -	if (phylink_autoneg_inband(mode)) { +	if (phylink_autoneg_inband(mode) && +	    state->interface != PHY_INTERFACE_MODE_SGMII) {  		dev_err(ds->dev, "%s: in-band negotiation unsupported\n",  			__func__);  		return; @@ -1406,7 +2198,7 @@ static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,  	mcr_new = mcr_cur;  	mcr_new &= ~PMCR_LINK_SETTINGS_MASK;  	mcr_new |= PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | PMCR_BACKOFF_EN | -		   PMCR_BACKPR_EN | PMCR_FORCE_MODE; +		   PMCR_BACKPR_EN | PMCR_FORCE_MODE_ID(priv->id);  	/* Are we connected to external phy */  	if (port == 5 && dsa_is_user_port(ds, 5)) @@ -1416,7 +2208,18 @@ static void mt7530_phylink_mac_config(struct dsa_switch *ds, int port,  		mt7530_write(priv, MT7530_PMCR_P(port), mcr_new);  } -static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port, +static void +mt753x_phylink_mac_an_restart(struct dsa_switch *ds, int port) +{ +	struct mt7530_priv *priv = ds->priv; + +	if (!priv->info->mac_pcs_an_restart) +		return; + +	priv->info->mac_pcs_an_restart(ds, port); +} + +static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port,  					 unsigned int mode,  					 phy_interface_t interface)  { @@ -1425,7 +2228,19 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port,  	mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);  } -static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port, +static void mt753x_mac_pcs_link_up(struct dsa_switch *ds, int port, +				   unsigned int mode, phy_interface_t interface, +				   int speed, int duplex) +{ +	struct mt7530_priv *priv = ds->priv; + +	if (!priv->info->mac_pcs_link_up) +		return; + +	priv->info->mac_pcs_link_up(ds, port, mode, interface, speed, duplex); +} + +static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,  				       unsigned int mode,  				       phy_interface_t interface,  				       struct phy_device *phydev, @@ -1435,8 +2250,19 @@ static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port,  	struct mt7530_priv *priv = ds->priv;  	u32 mcr; +	mt753x_mac_pcs_link_up(ds, port, mode, interface, speed, duplex); +  	mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; +	/* MT753x MAC works in 1G full duplex mode for all up-clocked +	 * variants. +	 */ +	if (interface == PHY_INTERFACE_MODE_TRGMII || +	    (phy_interface_mode_is_8023z(interface))) { +		speed = SPEED_1000; +		duplex = DUPLEX_FULL; +	} +  	switch (speed) {  	case SPEED_1000:  		mcr |= PMCR_FORCE_SPEED_1000; @@ -1456,66 +2282,107 @@ static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port,  	mt7530_set(priv, MT7530_PMCR_P(port), mcr);  } -static void mt7530_phylink_validate(struct dsa_switch *ds, int port, -				    unsigned long *supported, -				    struct phylink_link_state *state) +static int +mt7531_cpu_port_config(struct dsa_switch *ds, int port)  { -	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +	struct mt7530_priv *priv = ds->priv; +	phy_interface_t interface; +	int speed; +	int ret;  	switch (port) { -	case 0: /* Internal phy */ -	case 1: -	case 2: -	case 3: -	case 4: -		if (state->interface != PHY_INTERFACE_MODE_NA && -		    state->interface != PHY_INTERFACE_MODE_GMII) -			goto unsupported; -		break; -	case 5: /* 2nd cpu port with phy of port 0 or 4 / external phy */ -		if (state->interface != PHY_INTERFACE_MODE_NA && -		    !phy_interface_mode_is_rgmii(state->interface) && -		    state->interface != PHY_INTERFACE_MODE_MII && -		    state->interface != PHY_INTERFACE_MODE_GMII) -			goto unsupported; +	case 5: +		if (mt7531_is_rgmii_port(priv, port)) +			interface = PHY_INTERFACE_MODE_RGMII; +		else +			interface = PHY_INTERFACE_MODE_2500BASEX; + +		priv->p5_interface = interface;  		break; -	case 6: /* 1st cpu port */ -		if (state->interface != PHY_INTERFACE_MODE_NA && -		    state->interface != PHY_INTERFACE_MODE_RGMII && -		    state->interface != PHY_INTERFACE_MODE_TRGMII) -			goto unsupported; +	case 6: +		interface = PHY_INTERFACE_MODE_2500BASEX; + +		mt7531_pad_setup(ds, interface); + +		priv->p6_interface = interface;  		break;  	default: -		dev_err(ds->dev, "%s: unsupported port: %i\n", __func__, port); -unsupported: +		return -EINVAL; +	} + +	if (interface == PHY_INTERFACE_MODE_2500BASEX) +		speed = SPEED_2500; +	else +		speed = SPEED_1000; + +	ret = mt7531_mac_config(ds, port, MLO_AN_FIXED, interface); +	if (ret) +		return ret; +	mt7530_write(priv, MT7530_PMCR_P(port), +		     PMCR_CPU_PORT_SETTING(priv->id)); +	mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, +				   speed, DUPLEX_FULL, true, true); + +	return 0; +} + +static void +mt7530_mac_port_validate(struct dsa_switch *ds, int port, +			 unsigned long *supported) +{ +	if (port == 5) +		phylink_set(supported, 1000baseX_Full); +} + +static void mt7531_mac_port_validate(struct dsa_switch *ds, int port, +				     unsigned long *supported) +{ +	struct mt7530_priv *priv = ds->priv; + +	mt7531_sgmii_validate(priv, port, supported); +} + +static void +mt753x_phylink_validate(struct dsa_switch *ds, int port, +			unsigned long *supported, +			struct phylink_link_state *state) +{ +	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; +	struct mt7530_priv *priv = ds->priv; + +	if (state->interface != PHY_INTERFACE_MODE_NA && +	    !mt753x_phy_mode_supported(ds, port, state)) {  		linkmode_zero(supported);  		return;  	}  	phylink_set_port_modes(mask); -	phylink_set(mask, Autoneg); -	if (state->interface == PHY_INTERFACE_MODE_TRGMII) { -		phylink_set(mask, 1000baseT_Full); -	} else { +	if (state->interface != PHY_INTERFACE_MODE_TRGMII || +	    !phy_interface_mode_is_8023z(state->interface)) {  		phylink_set(mask, 10baseT_Half);  		phylink_set(mask, 10baseT_Full);  		phylink_set(mask, 100baseT_Half);  		phylink_set(mask, 100baseT_Full); - -		if (state->interface != PHY_INTERFACE_MODE_MII) { -			/* This switch only supports 1G full-duplex. */ -			phylink_set(mask, 1000baseT_Full); -			if (port == 5) -				phylink_set(mask, 1000baseX_Full); -		} +		phylink_set(mask, Autoneg);  	} +	/* This switch only supports 1G full-duplex. */ +	if (state->interface != PHY_INTERFACE_MODE_MII) +		phylink_set(mask, 1000baseT_Full); + +	priv->info->mac_port_validate(ds, port, mask); +  	phylink_set(mask, Pause);  	phylink_set(mask, Asym_Pause);  	linkmode_and(supported, supported, mask);  	linkmode_and(state->advertising, state->advertising, mask); + +	/* We can only operate at 2500BaseX or 1000BaseX.  If requested +	 * to advertise both, only report advertising at 2500BaseX. +	 */ +	phylink_helper_basex_speed(state);  }  static int @@ -1558,12 +2425,96 @@ mt7530_phylink_mac_link_state(struct dsa_switch *ds, int port,  	return 1;  } +static int +mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port, +			      struct phylink_link_state *state) +{ +	u32 status, val; +	u16 config_reg; + +	status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); +	state->link = !!(status & MT7531_SGMII_LINK_STATUS); +	if (state->interface == PHY_INTERFACE_MODE_SGMII && +	    (status & MT7531_SGMII_AN_ENABLE)) { +		val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port)); +		config_reg = val >> 16; + +		switch (config_reg & LPA_SGMII_SPD_MASK) { +		case LPA_SGMII_1000: +			state->speed = SPEED_1000; +			break; +		case LPA_SGMII_100: +			state->speed = SPEED_100; +			break; +		case LPA_SGMII_10: +			state->speed = SPEED_10; +			break; +		default: +			dev_err(priv->dev, "invalid sgmii PHY speed\n"); +			state->link = false; +			return -EINVAL; +		} + +		if (config_reg & LPA_SGMII_FULL_DUPLEX) +			state->duplex = DUPLEX_FULL; +		else +			state->duplex = DUPLEX_HALF; +	} + +	return 0; +} + +static int +mt7531_phylink_mac_link_state(struct dsa_switch *ds, int port, +			      struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	if (state->interface == PHY_INTERFACE_MODE_SGMII) +		return mt7531_sgmii_pcs_get_state_an(priv, port, state); + +	return -EOPNOTSUPP; +} + +static int +mt753x_phylink_mac_link_state(struct dsa_switch *ds, int port, +			      struct phylink_link_state *state) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->mac_port_get_state(ds, port, state); +} + +static int +mt753x_setup(struct dsa_switch *ds) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->sw_setup(ds); +} + +static int +mt753x_phy_read(struct dsa_switch *ds, int port, int regnum) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->phy_read(ds, port, regnum); +} + +static int +mt753x_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +{ +	struct mt7530_priv *priv = ds->priv; + +	return priv->info->phy_write(ds, port, regnum, val); +} +  static const struct dsa_switch_ops mt7530_switch_ops = {  	.get_tag_protocol	= mtk_get_tag_protocol, -	.setup			= mt7530_setup, +	.setup			= mt753x_setup,  	.get_strings		= mt7530_get_strings, -	.phy_read		= mt7530_phy_read, -	.phy_write		= mt7530_phy_write, +	.phy_read		= mt753x_phy_read, +	.phy_write		= mt753x_phy_write,  	.get_ethtool_stats	= mt7530_get_ethtool_stats,  	.get_sset_count		= mt7530_get_sset_count,  	.port_enable		= mt7530_port_enable, @@ -1578,18 +2529,59 @@ static const struct dsa_switch_ops mt7530_switch_ops = {  	.port_vlan_prepare	= mt7530_port_vlan_prepare,  	.port_vlan_add		= mt7530_port_vlan_add,  	.port_vlan_del		= mt7530_port_vlan_del, -	.port_mirror_add	= mt7530_port_mirror_add, -	.port_mirror_del	= mt7530_port_mirror_del, -	.phylink_validate	= mt7530_phylink_validate, -	.phylink_mac_link_state = mt7530_phylink_mac_link_state, -	.phylink_mac_config	= mt7530_phylink_mac_config, -	.phylink_mac_link_down	= mt7530_phylink_mac_link_down, -	.phylink_mac_link_up	= mt7530_phylink_mac_link_up, +	.port_mirror_add	= mt753x_port_mirror_add, +	.port_mirror_del	= mt753x_port_mirror_del, +	.phylink_validate	= mt753x_phylink_validate, +	.phylink_mac_link_state	= mt753x_phylink_mac_link_state, +	.phylink_mac_config	= mt753x_phylink_mac_config, +	.phylink_mac_an_restart	= mt753x_phylink_mac_an_restart, +	.phylink_mac_link_down	= mt753x_phylink_mac_link_down, +	.phylink_mac_link_up	= mt753x_phylink_mac_link_up, +}; + +static const struct mt753x_info mt753x_table[] = { +	[ID_MT7621] = { +		.id = ID_MT7621, +		.sw_setup = mt7530_setup, +		.phy_read = mt7530_phy_read, +		.phy_write = mt7530_phy_write, +		.pad_setup = mt7530_pad_clk_setup, +		.phy_mode_supported = mt7530_phy_mode_supported, +		.mac_port_validate = mt7530_mac_port_validate, +		.mac_port_get_state = mt7530_phylink_mac_link_state, +		.mac_port_config = mt7530_mac_config, +	}, +	[ID_MT7530] = { +		.id = ID_MT7530, +		.sw_setup = mt7530_setup, +		.phy_read = mt7530_phy_read, +		.phy_write = mt7530_phy_write, +		.pad_setup = mt7530_pad_clk_setup, +		.phy_mode_supported = mt7530_phy_mode_supported, +		.mac_port_validate = mt7530_mac_port_validate, +		.mac_port_get_state = mt7530_phylink_mac_link_state, +		.mac_port_config = mt7530_mac_config, +	}, +	[ID_MT7531] = { +		.id = ID_MT7531, +		.sw_setup = mt7531_setup, +		.phy_read = mt7531_ind_phy_read, +		.phy_write = mt7531_ind_phy_write, +		.pad_setup = mt7531_pad_setup, +		.cpu_port_config = mt7531_cpu_port_config, +		.phy_mode_supported = mt7531_phy_mode_supported, +		.mac_port_validate = mt7531_mac_port_validate, +		.mac_port_get_state = mt7531_phylink_mac_link_state, +		.mac_port_config = mt7531_mac_config, +		.mac_pcs_an_restart = mt7531_sgmii_restart_an, +		.mac_pcs_link_up = mt7531_sgmii_link_up_force, +	},  };  static const struct of_device_id mt7530_of_match[] = { -	{ .compatible = "mediatek,mt7621", .data = (void *)ID_MT7621, }, -	{ .compatible = "mediatek,mt7530", .data = (void *)ID_MT7530, }, +	{ .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, +	{ .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, +	{ .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },  	{ /* sentinel */ },  };  MODULE_DEVICE_TABLE(of, mt7530_of_match); @@ -1630,8 +2622,21 @@ mt7530_probe(struct mdio_device *mdiodev)  	/* Get the hardware identifier from the devicetree node.  	 * We will need it for some of the clock and regulator setup.  	 */ -	priv->id = (unsigned int)(unsigned long) -		of_device_get_match_data(&mdiodev->dev); +	priv->info = of_device_get_match_data(&mdiodev->dev); +	if (!priv->info) +		return -EINVAL; + +	/* Sanity check if these required device operations are filled +	 * properly. +	 */ +	if (!priv->info->sw_setup || !priv->info->pad_setup || +	    !priv->info->phy_read || !priv->info->phy_write || +	    !priv->info->phy_mode_supported || +	    !priv->info->mac_port_validate || +	    !priv->info->mac_port_get_state || !priv->info->mac_port_config) +		return -EINVAL; + +	priv->id = priv->info->id;  	if (priv->id == ID_MT7530) {  		priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 14de60d0b9ca..9278a8e3d04e 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -11,9 +11,10 @@  #define MT7530_NUM_FDB_RECORDS		2048  #define MT7530_ALL_MEMBERS		0xff -enum { +enum mt753x_id {  	ID_MT7530 = 0,  	ID_MT7621 = 1, +	ID_MT7531 = 2,  };  #define	NUM_TRGMII_CTRL			5 @@ -41,6 +42,33 @@ enum {  #define  MIRROR_PORT(x)			((x) & 0x7)  #define  MIRROR_MASK			0x7 +/* Registers for CPU forward control */ +#define MT7531_CFC			0x4 +#define  MT7531_MIRROR_EN		BIT(19) +#define  MT7531_MIRROR_MASK		(MIRROR_MASK << 16) +#define  MT7531_MIRROR_PORT_GET(x)	(((x) >> 16) & MIRROR_MASK) +#define  MT7531_MIRROR_PORT_SET(x)	(((x) & MIRROR_MASK) << 16) +#define  MT7531_CPU_PMAP_MASK		GENMASK(7, 0) + +#define MT753X_MIRROR_REG(id)		(((id) == ID_MT7531) ? \ +					 MT7531_CFC : MT7530_MFC) +#define MT753X_MIRROR_EN(id)		(((id) == ID_MT7531) ? \ +					 MT7531_MIRROR_EN : MIRROR_EN) +#define MT753X_MIRROR_MASK(id)		(((id) == ID_MT7531) ? \ +					 MT7531_MIRROR_MASK : MIRROR_MASK) + +/* Registers for BPDU and PAE frame control*/ +#define MT753X_BPC			0x24 +#define  MT753X_BPDU_PORT_FW_MASK	GENMASK(2, 0) + +enum mt753x_bpdu_port_fw { +	MT753X_BPDU_FOLLOW_MFC, +	MT753X_BPDU_CPU_EXCLUDE = 4, +	MT753X_BPDU_CPU_INCLUDE = 5, +	MT753X_BPDU_CPU_ONLY = 6, +	MT753X_BPDU_DROP = 7, +}; +  /* Registers for address table access */  #define MT7530_ATA1			0x74  #define  STATIC_EMP			0 @@ -220,10 +248,30 @@ enum mt7530_vlan_port_attr {  #define  PMCR_FORCE_LNK			BIT(0)  #define  PMCR_SPEED_MASK		(PMCR_FORCE_SPEED_100 | \  					 PMCR_FORCE_SPEED_1000) +#define  MT7531_FORCE_LNK		BIT(31) +#define  MT7531_FORCE_SPD		BIT(30) +#define  MT7531_FORCE_DPX		BIT(29) +#define  MT7531_FORCE_RX_FC		BIT(28) +#define  MT7531_FORCE_TX_FC		BIT(27) +#define  MT7531_FORCE_MODE		(MT7531_FORCE_LNK | \ +					 MT7531_FORCE_SPD | \ +					 MT7531_FORCE_DPX | \ +					 MT7531_FORCE_RX_FC | \ +					 MT7531_FORCE_TX_FC) +#define  PMCR_FORCE_MODE_ID(id)		(((id) == ID_MT7531) ? \ +					 MT7531_FORCE_MODE : \ +					 PMCR_FORCE_MODE)  #define  PMCR_LINK_SETTINGS_MASK	(PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \  					 PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \  					 PMCR_TX_FC_EN | PMCR_RX_FC_EN | \  					 PMCR_FORCE_FDX | PMCR_FORCE_LNK) +#define  PMCR_CPU_PORT_SETTING(id)	(PMCR_FORCE_MODE_ID((id)) | \ +					 PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ +					 PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \ +					 PMCR_TX_EN | PMCR_RX_EN | \ +					 PMCR_TX_FC_EN | PMCR_RX_FC_EN | \ +					 PMCR_FORCE_SPEED_1000 | \ +					 PMCR_FORCE_FDX | PMCR_FORCE_LNK)  #define MT7530_PMSR_P(x)		(0x3008 + (x) * 0x100)  #define  PMSR_EEE1G			BIT(7) @@ -237,6 +285,10 @@ enum mt7530_vlan_port_attr {  #define  PMSR_DPX			BIT(1)  #define  PMSR_LINK			BIT(0) +/* Register for port debug count */ +#define MT7531_DBG_CNT(x)		(0x3018 + (x) * 0x100) +#define  MT7531_DIS_CLR			BIT(31) +  /* Register for MIB */  #define MT7530_PORT_MIB_COUNTER(x)	(0x4000 + (x) * 0x100)  #define MT7530_MIB_CCR			0x4fe0 @@ -254,12 +306,118 @@ enum mt7530_vlan_port_attr {  					 CCR_RX_OCT_CNT_BAD | \  					 CCR_TX_OCT_CNT_GOOD | \  					 CCR_TX_OCT_CNT_BAD) + +/* MT7531 SGMII register group */ +#define MT7531_SGMII_REG_BASE		0x5000 +#define MT7531_SGMII_REG(p, r)		(MT7531_SGMII_REG_BASE + \ +					((p) - 5) * 0x1000 + (r)) + +/* Register forSGMII PCS_CONTROL_1 */ +#define MT7531_PCS_CONTROL_1(p)		MT7531_SGMII_REG(p, 0x00) +#define  MT7531_SGMII_LINK_STATUS	BIT(18) +#define  MT7531_SGMII_AN_ENABLE		BIT(12) +#define  MT7531_SGMII_AN_RESTART	BIT(9) + +/* Register for SGMII PCS_SPPED_ABILITY */ +#define MT7531_PCS_SPEED_ABILITY(p)	MT7531_SGMII_REG(p, 0x08) +#define  MT7531_SGMII_TX_CONFIG_MASK	GENMASK(15, 0) +#define  MT7531_SGMII_TX_CONFIG		BIT(0) + +/* Register for SGMII_MODE */ +#define MT7531_SGMII_MODE(p)		MT7531_SGMII_REG(p, 0x20) +#define  MT7531_SGMII_REMOTE_FAULT_DIS	BIT(8) +#define  MT7531_SGMII_IF_MODE_MASK	GENMASK(5, 1) +#define  MT7531_SGMII_FORCE_DUPLEX	BIT(4) +#define  MT7531_SGMII_FORCE_SPEED_MASK	GENMASK(3, 2) +#define  MT7531_SGMII_FORCE_SPEED_1000	BIT(3) +#define  MT7531_SGMII_FORCE_SPEED_100	BIT(2) +#define  MT7531_SGMII_FORCE_SPEED_10	0 +#define  MT7531_SGMII_SPEED_DUPLEX_AN	BIT(1) + +enum mt7531_sgmii_force_duplex { +	MT7531_SGMII_FORCE_FULL_DUPLEX = 0, +	MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10, +}; + +/* Fields of QPHY_PWR_STATE_CTRL */ +#define MT7531_QPHY_PWR_STATE_CTRL(p)	MT7531_SGMII_REG(p, 0xe8) +#define  MT7531_SGMII_PHYA_PWD		BIT(4) + +/* Values of SGMII SPEED */ +#define MT7531_PHYA_CTRL_SIGNAL3(p)	MT7531_SGMII_REG(p, 0x128) +#define  MT7531_RG_TPHY_SPEED_MASK	(BIT(2) | BIT(3)) +#define  MT7531_RG_TPHY_SPEED_1_25G	0x0 +#define  MT7531_RG_TPHY_SPEED_3_125G	BIT(2) +  /* Register for system reset */  #define MT7530_SYS_CTRL			0x7000  #define  SYS_CTRL_PHY_RST		BIT(2)  #define  SYS_CTRL_SW_RST		BIT(1)  #define  SYS_CTRL_REG_RST		BIT(0) +/* Register for PHY Indirect Access Control */ +#define MT7531_PHY_IAC			0x701C +#define  MT7531_PHY_ACS_ST		BIT(31) +#define  MT7531_MDIO_REG_ADDR_MASK	(0x1f << 25) +#define  MT7531_MDIO_PHY_ADDR_MASK	(0x1f << 20) +#define  MT7531_MDIO_CMD_MASK		(0x3 << 18) +#define  MT7531_MDIO_ST_MASK		(0x3 << 16) +#define  MT7531_MDIO_RW_DATA_MASK	(0xffff) +#define  MT7531_MDIO_REG_ADDR(x)	(((x) & 0x1f) << 25) +#define  MT7531_MDIO_DEV_ADDR(x)	(((x) & 0x1f) << 25) +#define  MT7531_MDIO_PHY_ADDR(x)	(((x) & 0x1f) << 20) +#define  MT7531_MDIO_CMD(x)		(((x) & 0x3) << 18) +#define  MT7531_MDIO_ST(x)		(((x) & 0x3) << 16) + +enum mt7531_phy_iac_cmd { +	MT7531_MDIO_ADDR = 0, +	MT7531_MDIO_WRITE = 1, +	MT7531_MDIO_READ = 2, +	MT7531_MDIO_READ_CL45 = 3, +}; + +/* MDIO_ST: MDIO start field */ +enum mt7531_mdio_st { +	MT7531_MDIO_ST_CL45 = 0, +	MT7531_MDIO_ST_CL22 = 1, +}; + +#define  MT7531_MDIO_CL22_READ		(MT7531_MDIO_ST(MT7531_MDIO_ST_CL22) | \ +					 MT7531_MDIO_CMD(MT7531_MDIO_READ)) +#define  MT7531_MDIO_CL22_WRITE		(MT7531_MDIO_ST(MT7531_MDIO_ST_CL22) | \ +					 MT7531_MDIO_CMD(MT7531_MDIO_WRITE)) +#define  MT7531_MDIO_CL45_ADDR		(MT7531_MDIO_ST(MT7531_MDIO_ST_CL45) | \ +					 MT7531_MDIO_CMD(MT7531_MDIO_ADDR)) +#define  MT7531_MDIO_CL45_READ		(MT7531_MDIO_ST(MT7531_MDIO_ST_CL45) | \ +					 MT7531_MDIO_CMD(MT7531_MDIO_READ)) +#define  MT7531_MDIO_CL45_WRITE		(MT7531_MDIO_ST(MT7531_MDIO_ST_CL45) | \ +					 MT7531_MDIO_CMD(MT7531_MDIO_WRITE)) + +/* Register for RGMII clock phase */ +#define MT7531_CLKGEN_CTRL		0x7500 +#define  CLK_SKEW_OUT(x)		(((x) & 0x3) << 8) +#define  CLK_SKEW_OUT_MASK		GENMASK(9, 8) +#define  CLK_SKEW_IN(x)			(((x) & 0x3) << 6) +#define  CLK_SKEW_IN_MASK		GENMASK(7, 6) +#define  RXCLK_NO_DELAY			BIT(5) +#define  TXCLK_NO_REVERSE		BIT(4) +#define  GP_MODE(x)			(((x) & 0x3) << 1) +#define  GP_MODE_MASK			GENMASK(2, 1) +#define  GP_CLK_EN			BIT(0) + +enum mt7531_gp_mode { +	MT7531_GP_MODE_RGMII = 0, +	MT7531_GP_MODE_MII = 1, +	MT7531_GP_MODE_REV_MII = 2 +}; + +enum mt7531_clk_skew { +	MT7531_CLK_SKEW_NO_CHG = 0, +	MT7531_CLK_SKEW_DLY_100PPS = 1, +	MT7531_CLK_SKEW_DLY_200PPS = 2, +	MT7531_CLK_SKEW_REVERSE = 3, +}; +  /* Register for hw trap status */  #define MT7530_HWTRAP			0x7800  #define  HWTRAP_XTAL_MASK		(BIT(10) | BIT(9)) @@ -267,6 +425,16 @@ enum mt7530_vlan_port_attr {  #define  HWTRAP_XTAL_40MHZ		(BIT(10))  #define  HWTRAP_XTAL_20MHZ		(BIT(9)) +#define MT7531_HWTRAP			0x7800 +#define  HWTRAP_XTAL_FSEL_MASK		BIT(7) +#define  HWTRAP_XTAL_FSEL_25MHZ		BIT(7) +#define  HWTRAP_XTAL_FSEL_40MHZ		0 +/* Unique fields of (M)HWSTRAP for MT7531 */ +#define  XTAL_FSEL_S			7 +#define  XTAL_FSEL_M			BIT(7) +#define  PHY_EN				BIT(6) +#define  CHG_STRAP			BIT(8) +  /* Register for hw trap modification */  #define MT7530_MHWTRAP			0x7804  #define  MHWTRAP_PHY0_SEL		BIT(20) @@ -281,14 +449,37 @@ enum mt7530_vlan_port_attr {  #define MT7530_TOP_SIG_CTRL		0x7808  #define  TOP_SIG_CTRL_NORMAL		(BIT(17) | BIT(16)) +#define MT7531_TOP_SIG_SR		0x780c +#define  PAD_DUAL_SGMII_EN		BIT(1) +#define  PAD_MCM_SMI_EN			BIT(0) +  #define MT7530_IO_DRV_CR		0x7810  #define  P5_IO_CLK_DRV(x)		((x) & 0x3)  #define  P5_IO_DATA_DRV(x)		(((x) & 0x3) << 4) +#define MT7531_CHIP_REV			0x781C + +#define MT7531_PLLGP_EN			0x7820 +#define  EN_COREPLL			BIT(2) +#define  SW_CLKSW			BIT(1) +#define  SW_PLLGP			BIT(0) +  #define MT7530_P6ECR			0x7830  #define  P6_INTF_MODE_MASK		0x3  #define  P6_INTF_MODE(x)		((x) & 0x3) +#define MT7531_PLLGP_CR0		0x78a8 +#define  RG_COREPLL_EN			BIT(22) +#define  RG_COREPLL_POSDIV_S		23 +#define  RG_COREPLL_POSDIV_M		0x3800000 +#define  RG_COREPLL_SDM_PCW_S		1 +#define  RG_COREPLL_SDM_PCW_M		0x3ffffe +#define  RG_COREPLL_SDM_PCW_CHG		BIT(0) + +/* Registers for RGMII and SGMII PLL clock */ +#define MT7531_ANA_PLLGP_CR2		0x78b0 +#define MT7531_ANA_PLLGP_CR5		0x78bc +  /* Registers for TRGMII on the both side */  #define MT7530_TRGMII_RCK_CTRL		0x7a00  #define  RX_RST				BIT(31) @@ -327,10 +518,25 @@ enum mt7530_vlan_port_attr {  #define MT7530_P5RGMIITXCR		0x7b04  #define  CSR_RGMII_TXC_CFG(x)		((x) & 0x1f) +/* Registers for GPIO mode */ +#define MT7531_GPIO_MODE0		0x7c0c +#define  MT7531_GPIO0_MASK		GENMASK(3, 0) +#define  MT7531_GPIO0_INTERRUPT		1 + +#define MT7531_GPIO_MODE1		0x7c10 +#define  MT7531_GPIO11_RG_RXD2_MASK	GENMASK(15, 12) +#define  MT7531_EXT_P_MDC_11		(2 << 12) +#define  MT7531_GPIO12_RG_RXD3_MASK	GENMASK(19, 16) +#define  MT7531_EXT_P_MDIO_12		(2 << 16) +  #define MT7530_CREV			0x7ffc  #define  CHIP_NAME_SHIFT		16  #define  MT7530_ID			0x7530 +#define MT7531_CREV			0x781C +#define  CHIP_REV_M			0x0f +#define  MT7531_ID			0x7531 +  /* Registers for core PLL access through mmd indirect */  #define CORE_PLL_GROUP2			0x401  #define  RG_SYSPLL_EN_NORMAL		BIT(15) @@ -347,6 +553,10 @@ enum mt7530_vlan_port_attr {  #define  RG_SYSPLL_DDSFBK_EN		BIT(12)  #define  RG_SYSPLL_BIAS_EN		BIT(11)  #define  RG_SYSPLL_BIAS_LPF_EN		BIT(10) +#define  MT7531_PHY_PLL_OFF		BIT(5) +#define  MT7531_PHY_PLL_BYPASS_MODE	BIT(4) + +#define MT753X_CTRL_PHY_ADDR		0  #define CORE_PLL_GROUP5			0x404  #define  RG_LCDDS_PCW_NCPO1(x)		((x) & 0xffff) @@ -425,6 +635,7 @@ enum p5_interface_select {  	P5_INTF_SEL_PHY_P0,  	P5_INTF_SEL_PHY_P4,  	P5_INTF_SEL_GMAC5, +	P5_INTF_SEL_GMAC5_SGMII,  };  static const char *p5_intf_modes(unsigned int p5_interface) @@ -438,11 +649,56 @@ static const char *p5_intf_modes(unsigned int p5_interface)  		return "PHY P4";  	case P5_INTF_SEL_GMAC5:  		return "GMAC5"; +	case P5_INTF_SEL_GMAC5_SGMII: +		return "GMAC5_SGMII";  	default:  		return "unknown";  	}  } +/* struct mt753x_info -	This is the main data structure for holding the specific + *			part for each supported device + * @sw_setup:		Holding the handler to a device initialization + * @phy_read:		Holding the way reading PHY port + * @phy_write:		Holding the way writing PHY port + * @pad_setup:		Holding the way setting up the bus pad for a certain + *			MAC port + * @phy_mode_supported:	Check if the PHY type is being supported on a certain + *			port + * @mac_port_validate:	Holding the way to set addition validate type for a + *			certan MAC port + * @mac_port_get_state: Holding the way getting the MAC/PCS state for a certain + *			MAC port + * @mac_port_config:	Holding the way setting up the PHY attribute to a + *			certain MAC port + * @mac_pcs_an_restart	Holding the way restarting PCS autonegotiation for a + *			certain MAC port + * @mac_pcs_link_up:	Holding the way setting up the PHY attribute to the pcs + *			of the certain MAC port + */ +struct mt753x_info { +	enum mt753x_id id; + +	int (*sw_setup)(struct dsa_switch *ds); +	int (*phy_read)(struct dsa_switch *ds, int port, int regnum); +	int (*phy_write)(struct dsa_switch *ds, int port, int regnum, u16 val); +	int (*pad_setup)(struct dsa_switch *ds, phy_interface_t interface); +	int (*cpu_port_config)(struct dsa_switch *ds, int port); +	bool (*phy_mode_supported)(struct dsa_switch *ds, int port, +				   const struct phylink_link_state *state); +	void (*mac_port_validate)(struct dsa_switch *ds, int port, +				  unsigned long *supported); +	int (*mac_port_get_state)(struct dsa_switch *ds, int port, +				  struct phylink_link_state *state); +	int (*mac_port_config)(struct dsa_switch *ds, int port, +			       unsigned int mode, +			       phy_interface_t interface); +	void (*mac_pcs_an_restart)(struct dsa_switch *ds, int port); +	void (*mac_pcs_link_up)(struct dsa_switch *ds, int port, +				unsigned int mode, phy_interface_t interface, +				int speed, int duplex); +}; +  /* struct mt7530_priv -	This is the main data structure for holding the state   *			of the driver   * @dev:		The device pointer @@ -468,6 +724,7 @@ struct mt7530_priv {  	struct regulator	*core_pwr;  	struct regulator	*io_pwr;  	struct gpio_desc	*reset; +	const struct mt753x_info *info;  	unsigned int		id;  	bool			mcm;  	phy_interface_t		p6_interface; diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index aa645ff86f64..4b080b448ce7 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -1,6 +1,7 @@  # SPDX-License-Identifier: GPL-2.0  obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o  mv88e6xxx-objs := chip.o +mv88e6xxx-objs += devlink.o  mv88e6xxx-objs += global1.o  mv88e6xxx-objs += global1_atu.o  mv88e6xxx-objs += global1_vtu.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index f0dbc05e30a4..bd297ae7cf9e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -32,6 +32,7 @@  #include <net/dsa.h>  #include "chip.h" +#include "devlink.h"  #include "global1.h"  #include "global2.h"  #include "hwtstamp.h" @@ -1465,21 +1466,21 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,  	return chip->info->ops->vtu_loadpurge(chip, entry);  } -static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap)  { -	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);  	struct mv88e6xxx_vtu_entry vlan;  	int i, err; +	u16 fid;  	bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);  	/* Set every FID bit used by the (un)bridged ports */  	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { -		err = mv88e6xxx_port_get_fid(chip, i, fid); +		err = mv88e6xxx_port_get_fid(chip, i, &fid);  		if (err)  			return err; -		set_bit(*fid, fid_bitmap); +		set_bit(fid, fid_bitmap);  	}  	/* Set every FID bit used by the VLAN entries */ @@ -1497,6 +1498,18 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)  		set_bit(vlan.fid, fid_bitmap);  	} while (vlan.vid < chip->info->max_vid); +	return 0; +} + +static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +{ +	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); +	int err; + +	err = mv88e6xxx_fid_map(chip, fid_bitmap); +	if (err) +		return err; +  	/* The reset value 0x000 is used to indicate that multiple address  	 * databases are not needed. Return the next positive available.  	 */ @@ -1508,22 +1521,6 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)  	return mv88e6xxx_g1_atu_flush(chip, *fid, true);  } -static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash) -{ -	if (chip->info->ops->atu_get_hash) -		return chip->info->ops->atu_get_hash(chip, hash); - -	return -EOPNOTSUPP; -} - -static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash) -{ -	if (chip->info->ops->atu_set_hash) -		return chip->info->ops->atu_set_hash(chip, hash); - -	return -EOPNOTSUPP; -} -  static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  					u16 vid_begin, u16 vid_end)  { @@ -1581,15 +1578,16 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  }  static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, -					 bool vlan_filtering) +					 bool vlan_filtering, +					 struct switchdev_trans *trans)  {  	struct mv88e6xxx_chip *chip = ds->priv;  	u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE :  		MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED;  	int err; -	if (!chip->info->max_vid) -		return -EOPNOTSUPP; +	if (switchdev_trans_ph_prepare(trans)) +		return chip->info->max_vid ? 0 : -EOPNOTSUPP;  	mv88e6xxx_reg_lock(chip);  	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); @@ -2837,248 +2835,11 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip)  	return mv88e6xxx_software_reset(chip);  } -enum mv88e6xxx_devlink_param_id { -	MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, -	MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, -}; - -static int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, -				       struct devlink_param_gset_ctx *ctx) -{ -	struct mv88e6xxx_chip *chip = ds->priv; -	int err; - -	mv88e6xxx_reg_lock(chip); - -	switch (id) { -	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: -		err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8); -		break; -	default: -		err = -EOPNOTSUPP; -		break; -	} - -	mv88e6xxx_reg_unlock(chip); - -	return err; -} - -static int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, -				       struct devlink_param_gset_ctx *ctx) -{ -	struct mv88e6xxx_chip *chip = ds->priv; -	int err; - -	mv88e6xxx_reg_lock(chip); - -	switch (id) { -	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: -		err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8); -		break; -	default: -		err = -EOPNOTSUPP; -		break; -	} - -	mv88e6xxx_reg_unlock(chip); - -	return err; -} - -static const struct devlink_param mv88e6xxx_devlink_params[] = { -	DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, -				 "ATU_hash", DEVLINK_PARAM_TYPE_U8, -				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)), -}; - -static int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds) -{ -	return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params, -					   ARRAY_SIZE(mv88e6xxx_devlink_params)); -} - -static void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds) -{ -	dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params, -				      ARRAY_SIZE(mv88e6xxx_devlink_params)); -} - -enum mv88e6xxx_devlink_resource_id { -	MV88E6XXX_RESOURCE_ID_ATU, -	MV88E6XXX_RESOURCE_ID_ATU_BIN_0, -	MV88E6XXX_RESOURCE_ID_ATU_BIN_1, -	MV88E6XXX_RESOURCE_ID_ATU_BIN_2, -	MV88E6XXX_RESOURCE_ID_ATU_BIN_3, -}; - -static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip, -					 u16 bin) -{ -	u16 occupancy = 0; -	int err; - -	mv88e6xxx_reg_lock(chip); - -	err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL, -					 bin); -	if (err) { -		dev_err(chip->dev, "failed to set ATU stats kind/bin\n"); -		goto unlock; -	} - -	err = mv88e6xxx_g1_atu_get_next(chip, 0); -	if (err) { -		dev_err(chip->dev, "failed to perform ATU get next\n"); -		goto unlock; -	} - -	err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy); -	if (err) { -		dev_err(chip->dev, "failed to get ATU stats\n"); -		goto unlock; -	} - -	occupancy &= MV88E6XXX_G2_ATU_STATS_MASK; - -unlock: -	mv88e6xxx_reg_unlock(chip); - -	return occupancy; -} - -static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv) -{ -	struct mv88e6xxx_chip *chip = priv; - -	return mv88e6xxx_devlink_atu_bin_get(chip, -					     MV88E6XXX_G2_ATU_STATS_BIN_0); -} - -static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv) -{ -	struct mv88e6xxx_chip *chip = priv; - -	return mv88e6xxx_devlink_atu_bin_get(chip, -					     MV88E6XXX_G2_ATU_STATS_BIN_1); -} - -static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv) -{ -	struct mv88e6xxx_chip *chip = priv; - -	return mv88e6xxx_devlink_atu_bin_get(chip, -					     MV88E6XXX_G2_ATU_STATS_BIN_2); -} - -static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv) -{ -	struct mv88e6xxx_chip *chip = priv; - -	return mv88e6xxx_devlink_atu_bin_get(chip, -					     MV88E6XXX_G2_ATU_STATS_BIN_3); -} - -static u64 mv88e6xxx_devlink_atu_get(void *priv) -{ -	return mv88e6xxx_devlink_atu_bin_0_get(priv) + -		mv88e6xxx_devlink_atu_bin_1_get(priv) + -		mv88e6xxx_devlink_atu_bin_2_get(priv) + -		mv88e6xxx_devlink_atu_bin_3_get(priv); -} - -static int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) -{ -	struct devlink_resource_size_params size_params; -	struct mv88e6xxx_chip *chip = ds->priv; -	int err; - -	devlink_resource_size_params_init(&size_params, -					  mv88e6xxx_num_macs(chip), -					  mv88e6xxx_num_macs(chip), -					  1, DEVLINK_RESOURCE_UNIT_ENTRY); - -	err = dsa_devlink_resource_register(ds, "ATU", -					    mv88e6xxx_num_macs(chip), -					    MV88E6XXX_RESOURCE_ID_ATU, -					    DEVLINK_RESOURCE_ID_PARENT_TOP, -					    &size_params); -	if (err) -		goto out; - -	devlink_resource_size_params_init(&size_params, -					  mv88e6xxx_num_macs(chip) / 4, -					  mv88e6xxx_num_macs(chip) / 4, -					  1, DEVLINK_RESOURCE_UNIT_ENTRY); - -	err = dsa_devlink_resource_register(ds, "ATU_bin_0", -					    mv88e6xxx_num_macs(chip) / 4, -					    MV88E6XXX_RESOURCE_ID_ATU_BIN_0, -					    MV88E6XXX_RESOURCE_ID_ATU, -					    &size_params); -	if (err) -		goto out; - -	err = dsa_devlink_resource_register(ds, "ATU_bin_1", -					    mv88e6xxx_num_macs(chip) / 4, -					    MV88E6XXX_RESOURCE_ID_ATU_BIN_1, -					    MV88E6XXX_RESOURCE_ID_ATU, -					    &size_params); -	if (err) -		goto out; - -	err = dsa_devlink_resource_register(ds, "ATU_bin_2", -					    mv88e6xxx_num_macs(chip) / 4, -					    MV88E6XXX_RESOURCE_ID_ATU_BIN_2, -					    MV88E6XXX_RESOURCE_ID_ATU, -					    &size_params); -	if (err) -		goto out; - -	err = dsa_devlink_resource_register(ds, "ATU_bin_3", -					    mv88e6xxx_num_macs(chip) / 4, -					    MV88E6XXX_RESOURCE_ID_ATU_BIN_3, -					    MV88E6XXX_RESOURCE_ID_ATU, -					    &size_params); -	if (err) -		goto out; - -	dsa_devlink_resource_occ_get_register(ds, -					      MV88E6XXX_RESOURCE_ID_ATU, -					      mv88e6xxx_devlink_atu_get, -					      chip); - -	dsa_devlink_resource_occ_get_register(ds, -					      MV88E6XXX_RESOURCE_ID_ATU_BIN_0, -					      mv88e6xxx_devlink_atu_bin_0_get, -					      chip); - -	dsa_devlink_resource_occ_get_register(ds, -					      MV88E6XXX_RESOURCE_ID_ATU_BIN_1, -					      mv88e6xxx_devlink_atu_bin_1_get, -					      chip); - -	dsa_devlink_resource_occ_get_register(ds, -					      MV88E6XXX_RESOURCE_ID_ATU_BIN_2, -					      mv88e6xxx_devlink_atu_bin_2_get, -					      chip); - -	dsa_devlink_resource_occ_get_register(ds, -					      MV88E6XXX_RESOURCE_ID_ATU_BIN_3, -					      mv88e6xxx_devlink_atu_bin_3_get, -					      chip); - -	return 0; - -out: -	dsa_devlink_resources_unregister(ds); -	return err; -} -  static void mv88e6xxx_teardown(struct dsa_switch *ds)  {  	mv88e6xxx_teardown_devlink_params(ds);  	dsa_devlink_resources_unregister(ds); +	mv88e6xxx_teardown_devlink_regions(ds);  }  static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -3211,7 +2972,18 @@ unlock:  	err = mv88e6xxx_setup_devlink_params(ds);  	if (err) -		dsa_devlink_resources_unregister(ds); +		goto out_resources; + +	err = mv88e6xxx_setup_devlink_regions(ds); +	if (err) +		goto out_params; + +	return 0; + +out_params: +	mv88e6xxx_teardown_devlink_params(ds); +out_resources: +	dsa_devlink_resources_unregister(ds);  	return err;  } @@ -3329,12 +3101,6 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,  	return 0;  } -static const struct of_device_id mv88e6xxx_mdio_external_match[] = { -	{ .compatible = "marvell,mv88e6xxx-mdio-external", -	  .data = (void *)true }, -	{ }, -}; -  static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)  { @@ -3354,7 +3120,6 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)  static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,  				    struct device_node *np)  { -	const struct of_device_id *match;  	struct device_node *child;  	int err; @@ -3372,8 +3137,8 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,  	 * bus.  	 */  	for_each_available_child_of_node(np, child) { -		match = of_match_node(mv88e6xxx_mdio_external_match, child); -		if (match) { +		if (of_device_is_compatible( +			    child, "marvell,mv88e6xxx-mdio-external")) {  			err = mv88e6xxx_mdio_register(chip, child, true);  			if (err) {  				mv88e6xxx_mdios_unregister(chip); @@ -5614,6 +5379,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {  	.get_ts_info		= mv88e6xxx_get_ts_info,  	.devlink_param_get	= mv88e6xxx_devlink_param_get,  	.devlink_param_set	= mv88e6xxx_devlink_param_set, +	.devlink_info_get	= mv88e6xxx_devlink_info_get,  };  static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 823ae89e5fca..81c244fc0419 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -238,6 +238,19 @@ struct mv88e6xxx_port {  	bool mirror_egress;  	unsigned int serdes_irq;  	char serdes_irq_name[64]; +	struct devlink_region *region; +}; + +enum mv88e6xxx_region_id { +	MV88E6XXX_REGION_GLOBAL1 = 0, +	MV88E6XXX_REGION_GLOBAL2, +	MV88E6XXX_REGION_ATU, + +	_MV88E6XXX_REGION_MAX, +}; + +struct mv88e6xxx_region_priv { +	enum mv88e6xxx_region_id id;  };  struct mv88e6xxx_chip { @@ -334,6 +347,9 @@ struct mv88e6xxx_chip {  	/* Array of port structures. */  	struct mv88e6xxx_port ports[DSA_MAX_PORTS]; + +	/* devlink regions */ +	struct devlink_region *regions[_MV88E6XXX_REGION_MAX];  };  struct mv88e6xxx_bus_ops { @@ -689,4 +705,6 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)  	mutex_unlock(&chip->reg_lock);  } +int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap); +  #endif /* _MV88E6XXX_CHIP_H */ diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c new file mode 100644 index 000000000000..10cd1bfd81a0 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <net/dsa.h> + +#include "chip.h" +#include "devlink.h" +#include "global1.h" +#include "global2.h" +#include "port.h" + +static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash) +{ +	if (chip->info->ops->atu_get_hash) +		return chip->info->ops->atu_get_hash(chip, hash); + +	return -EOPNOTSUPP; +} + +static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash) +{ +	if (chip->info->ops->atu_set_hash) +		return chip->info->ops->atu_set_hash(chip, hash); + +	return -EOPNOTSUPP; +} + +enum mv88e6xxx_devlink_param_id { +	MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, +	MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, +}; + +int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, +				struct devlink_param_gset_ctx *ctx) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	int err; + +	mv88e6xxx_reg_lock(chip); + +	switch (id) { +	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: +		err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8); +		break; +	default: +		err = -EOPNOTSUPP; +		break; +	} + +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, +				struct devlink_param_gset_ctx *ctx) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	int err; + +	mv88e6xxx_reg_lock(chip); + +	switch (id) { +	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: +		err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8); +		break; +	default: +		err = -EOPNOTSUPP; +		break; +	} + +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +static const struct devlink_param mv88e6xxx_devlink_params[] = { +	DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, +				 "ATU_hash", DEVLINK_PARAM_TYPE_U8, +				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)), +}; + +int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds) +{ +	return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params, +					   ARRAY_SIZE(mv88e6xxx_devlink_params)); +} + +void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds) +{ +	dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params, +				      ARRAY_SIZE(mv88e6xxx_devlink_params)); +} + +enum mv88e6xxx_devlink_resource_id { +	MV88E6XXX_RESOURCE_ID_ATU, +	MV88E6XXX_RESOURCE_ID_ATU_BIN_0, +	MV88E6XXX_RESOURCE_ID_ATU_BIN_1, +	MV88E6XXX_RESOURCE_ID_ATU_BIN_2, +	MV88E6XXX_RESOURCE_ID_ATU_BIN_3, +}; + +static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip, +					 u16 bin) +{ +	u16 occupancy = 0; +	int err; + +	mv88e6xxx_reg_lock(chip); + +	err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL, +					 bin); +	if (err) { +		dev_err(chip->dev, "failed to set ATU stats kind/bin\n"); +		goto unlock; +	} + +	err = mv88e6xxx_g1_atu_get_next(chip, 0); +	if (err) { +		dev_err(chip->dev, "failed to perform ATU get next\n"); +		goto unlock; +	} + +	err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy); +	if (err) { +		dev_err(chip->dev, "failed to get ATU stats\n"); +		goto unlock; +	} + +	occupancy &= MV88E6XXX_G2_ATU_STATS_MASK; + +unlock: +	mv88e6xxx_reg_unlock(chip); + +	return occupancy; +} + +static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv) +{ +	struct mv88e6xxx_chip *chip = priv; + +	return mv88e6xxx_devlink_atu_bin_get(chip, +					     MV88E6XXX_G2_ATU_STATS_BIN_0); +} + +static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv) +{ +	struct mv88e6xxx_chip *chip = priv; + +	return mv88e6xxx_devlink_atu_bin_get(chip, +					     MV88E6XXX_G2_ATU_STATS_BIN_1); +} + +static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv) +{ +	struct mv88e6xxx_chip *chip = priv; + +	return mv88e6xxx_devlink_atu_bin_get(chip, +					     MV88E6XXX_G2_ATU_STATS_BIN_2); +} + +static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv) +{ +	struct mv88e6xxx_chip *chip = priv; + +	return mv88e6xxx_devlink_atu_bin_get(chip, +					     MV88E6XXX_G2_ATU_STATS_BIN_3); +} + +static u64 mv88e6xxx_devlink_atu_get(void *priv) +{ +	return mv88e6xxx_devlink_atu_bin_0_get(priv) + +		mv88e6xxx_devlink_atu_bin_1_get(priv) + +		mv88e6xxx_devlink_atu_bin_2_get(priv) + +		mv88e6xxx_devlink_atu_bin_3_get(priv); +} + +int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) +{ +	struct devlink_resource_size_params size_params; +	struct mv88e6xxx_chip *chip = ds->priv; +	int err; + +	devlink_resource_size_params_init(&size_params, +					  mv88e6xxx_num_macs(chip), +					  mv88e6xxx_num_macs(chip), +					  1, DEVLINK_RESOURCE_UNIT_ENTRY); + +	err = dsa_devlink_resource_register(ds, "ATU", +					    mv88e6xxx_num_macs(chip), +					    MV88E6XXX_RESOURCE_ID_ATU, +					    DEVLINK_RESOURCE_ID_PARENT_TOP, +					    &size_params); +	if (err) +		goto out; + +	devlink_resource_size_params_init(&size_params, +					  mv88e6xxx_num_macs(chip) / 4, +					  mv88e6xxx_num_macs(chip) / 4, +					  1, DEVLINK_RESOURCE_UNIT_ENTRY); + +	err = dsa_devlink_resource_register(ds, "ATU_bin_0", +					    mv88e6xxx_num_macs(chip) / 4, +					    MV88E6XXX_RESOURCE_ID_ATU_BIN_0, +					    MV88E6XXX_RESOURCE_ID_ATU, +					    &size_params); +	if (err) +		goto out; + +	err = dsa_devlink_resource_register(ds, "ATU_bin_1", +					    mv88e6xxx_num_macs(chip) / 4, +					    MV88E6XXX_RESOURCE_ID_ATU_BIN_1, +					    MV88E6XXX_RESOURCE_ID_ATU, +					    &size_params); +	if (err) +		goto out; + +	err = dsa_devlink_resource_register(ds, "ATU_bin_2", +					    mv88e6xxx_num_macs(chip) / 4, +					    MV88E6XXX_RESOURCE_ID_ATU_BIN_2, +					    MV88E6XXX_RESOURCE_ID_ATU, +					    &size_params); +	if (err) +		goto out; + +	err = dsa_devlink_resource_register(ds, "ATU_bin_3", +					    mv88e6xxx_num_macs(chip) / 4, +					    MV88E6XXX_RESOURCE_ID_ATU_BIN_3, +					    MV88E6XXX_RESOURCE_ID_ATU, +					    &size_params); +	if (err) +		goto out; + +	dsa_devlink_resource_occ_get_register(ds, +					      MV88E6XXX_RESOURCE_ID_ATU, +					      mv88e6xxx_devlink_atu_get, +					      chip); + +	dsa_devlink_resource_occ_get_register(ds, +					      MV88E6XXX_RESOURCE_ID_ATU_BIN_0, +					      mv88e6xxx_devlink_atu_bin_0_get, +					      chip); + +	dsa_devlink_resource_occ_get_register(ds, +					      MV88E6XXX_RESOURCE_ID_ATU_BIN_1, +					      mv88e6xxx_devlink_atu_bin_1_get, +					      chip); + +	dsa_devlink_resource_occ_get_register(ds, +					      MV88E6XXX_RESOURCE_ID_ATU_BIN_2, +					      mv88e6xxx_devlink_atu_bin_2_get, +					      chip); + +	dsa_devlink_resource_occ_get_register(ds, +					      MV88E6XXX_RESOURCE_ID_ATU_BIN_3, +					      mv88e6xxx_devlink_atu_bin_3_get, +					      chip); + +	return 0; + +out: +	dsa_devlink_resources_unregister(ds); +	return err; +} + +static int mv88e6xxx_region_global_snapshot(struct devlink *dl, +					    const struct devlink_region_ops *ops, +					    struct netlink_ext_ack *extack, +					    u8 **data) +{ +	struct mv88e6xxx_region_priv *region_priv = ops->priv; +	struct dsa_switch *ds = dsa_devlink_to_ds(dl); +	struct mv88e6xxx_chip *chip = ds->priv; +	u16 *registers; +	int i, err; + +	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL); +	if (!registers) +		return -ENOMEM; + +	mv88e6xxx_reg_lock(chip); +	for (i = 0; i < 32; i++) { +		switch (region_priv->id) { +		case MV88E6XXX_REGION_GLOBAL1: +			err = mv88e6xxx_g1_read(chip, i, ®isters[i]); +			break; +		case MV88E6XXX_REGION_GLOBAL2: +			err = mv88e6xxx_g2_read(chip, i, ®isters[i]); +			break; +		default: +			err = -EOPNOTSUPP; +		} + +		if (err) { +			kfree(registers); +			goto out; +		} +	} +	*data = (u8 *)registers; +out: +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +/* The ATU entry varies between mv88e6xxx chipset generations. Define + * a generic format which covers all the current and hopefully future + * mv88e6xxx generations + */ + +struct mv88e6xxx_devlink_atu_entry { +	/* The FID is scattered over multiple registers. */ +	u16 fid; +	u16 atu_op; +	u16 atu_data; +	u16 atu_01; +	u16 atu_23; +	u16 atu_45; +}; + +static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip, +					     int fid, +					     struct mv88e6xxx_devlink_atu_entry *table, +					     int *count) +{ +	u16 atu_op, atu_data, atu_01, atu_23, atu_45; +	struct mv88e6xxx_atu_entry addr; +	int err; + +	addr.state = 0; +	eth_broadcast_addr(addr.mac); + +	do { +		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); +		if (err) +			return err; + +		if (!addr.state) +			break; + +		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &atu_op); +		if (err) +			return err; + +		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &atu_data); +		if (err) +			return err; + +		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, &atu_01); +		if (err) +			return err; + +		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, &atu_23); +		if (err) +			return err; + +		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, &atu_45); +		if (err) +			return err; + +		table[*count].fid = fid; +		table[*count].atu_op = atu_op; +		table[*count].atu_data = atu_data; +		table[*count].atu_01 = atu_01; +		table[*count].atu_23 = atu_23; +		table[*count].atu_45 = atu_45; +		(*count)++; +	} while (!is_broadcast_ether_addr(addr.mac)); + +	return 0; +} + +static int mv88e6xxx_region_atu_snapshot(struct devlink *dl, +					 const struct devlink_region_ops *ops, +					 struct netlink_ext_ack *extack, +					 u8 **data) +{ +	struct dsa_switch *ds = dsa_devlink_to_ds(dl); +	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); +	struct mv88e6xxx_devlink_atu_entry *table; +	struct mv88e6xxx_chip *chip = ds->priv; +	int fid = -1, count, err; + +	table = kmalloc_array(mv88e6xxx_num_databases(chip), +			      sizeof(struct mv88e6xxx_devlink_atu_entry), +			      GFP_KERNEL); +	if (!table) +		return -ENOMEM; + +	memset(table, 0, mv88e6xxx_num_databases(chip) * +	       sizeof(struct mv88e6xxx_devlink_atu_entry)); + +	count = 0; + +	mv88e6xxx_reg_lock(chip); + +	err = mv88e6xxx_fid_map(chip, fid_bitmap); +	if (err) +		goto out; + +	while (1) { +		fid = find_next_bit(fid_bitmap, MV88E6XXX_N_FID, fid + 1); +		if (fid == MV88E6XXX_N_FID) +			break; + +		err =  mv88e6xxx_region_atu_snapshot_fid(chip, fid, table, +							 &count); +		if (err) { +			kfree(table); +			goto out; +		} +	} +	*data = (u8 *)table; +out: +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port, +					  const struct devlink_port_region_ops *ops, +					  struct netlink_ext_ack *extack, +					  u8 **data) +{ +	struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port); +	int port = dsa_devlink_port_to_port(devlink_port); +	struct mv88e6xxx_chip *chip = ds->priv; +	u16 *registers; +	int i, err; + +	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL); +	if (!registers) +		return -ENOMEM; + +	mv88e6xxx_reg_lock(chip); +	for (i = 0; i < 32; i++) { +		err = mv88e6xxx_port_read(chip, port, i, ®isters[i]); +		if (err) { +			kfree(registers); +			goto out; +		} +	} +	*data = (u8 *)registers; +out: +	mv88e6xxx_reg_unlock(chip); + +	return err; +} + +static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = { +	.id = MV88E6XXX_REGION_GLOBAL1, +}; + +static struct devlink_region_ops mv88e6xxx_region_global1_ops = { +	.name = "global1", +	.snapshot = mv88e6xxx_region_global_snapshot, +	.destructor = kfree, +	.priv = &mv88e6xxx_region_global1_priv, +}; + +static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = { +	.id = MV88E6XXX_REGION_GLOBAL2, +}; + +static struct devlink_region_ops mv88e6xxx_region_global2_ops = { +	.name = "global2", +	.snapshot = mv88e6xxx_region_global_snapshot, +	.destructor = kfree, +	.priv = &mv88e6xxx_region_global2_priv, +}; + +static struct devlink_region_ops mv88e6xxx_region_atu_ops = { +	.name = "atu", +	.snapshot = mv88e6xxx_region_atu_snapshot, +	.destructor = kfree, +}; + +static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = { +	.name = "port", +	.snapshot = mv88e6xxx_region_port_snapshot, +	.destructor = kfree, +}; + +struct mv88e6xxx_region { +	struct devlink_region_ops *ops; +	u64 size; +}; + +static struct mv88e6xxx_region mv88e6xxx_regions[] = { +	[MV88E6XXX_REGION_GLOBAL1] = { +		.ops = &mv88e6xxx_region_global1_ops, +		.size = 32 * sizeof(u16) +	}, +	[MV88E6XXX_REGION_GLOBAL2] = { +		.ops = &mv88e6xxx_region_global2_ops, +		.size = 32 * sizeof(u16) }, +	[MV88E6XXX_REGION_ATU] = { +		.ops = &mv88e6xxx_region_atu_ops +	  /* calculated at runtime */ +	}, +}; + +static void +mv88e6xxx_teardown_devlink_regions_global(struct mv88e6xxx_chip *chip) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) +		dsa_devlink_region_destroy(chip->regions[i]); +} + +static void +mv88e6xxx_teardown_devlink_regions_port(struct mv88e6xxx_chip *chip, +					int port) +{ +	dsa_devlink_region_destroy(chip->ports[port].region); +} + +static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, +						struct mv88e6xxx_chip *chip, +						int port) +{ +	struct devlink_region *region; + +	region = dsa_devlink_port_region_create(ds, +						port, +						&mv88e6xxx_region_port_ops, 1, +						32 * sizeof(u16)); +	if (IS_ERR(region)) +		return PTR_ERR(region); + +	chip->ports[port].region = region; + +	return 0; +} + +static void +mv88e6xxx_teardown_devlink_regions_ports(struct mv88e6xxx_chip *chip) +{ +	int port; + +	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) +		mv88e6xxx_teardown_devlink_regions_port(chip, port); +} + +static int mv88e6xxx_setup_devlink_regions_ports(struct dsa_switch *ds, +						 struct mv88e6xxx_chip *chip) +{ +	int port; +	int err; + +	for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { +		err = mv88e6xxx_setup_devlink_regions_port(ds, chip, port); +		if (err) +			goto out; +	} + +	return 0; + +out: +	while (port-- > 0) +		mv88e6xxx_teardown_devlink_regions_port(chip, port); + +	return err; +} + +static int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds, +						  struct mv88e6xxx_chip *chip) +{ +	struct devlink_region_ops *ops; +	struct devlink_region *region; +	u64 size; +	int i, j; + +	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) { +		ops = mv88e6xxx_regions[i].ops; +		size = mv88e6xxx_regions[i].size; + +		if (i == MV88E6XXX_REGION_ATU) +			size = mv88e6xxx_num_databases(chip) * +				sizeof(struct mv88e6xxx_devlink_atu_entry); + +		region = dsa_devlink_region_create(ds, ops, 1, size); +		if (IS_ERR(region)) +			goto out; +		chip->regions[i] = region; +	} +	return 0; + +out: +	for (j = 0; j < i; j++) +		dsa_devlink_region_destroy(chip->regions[j]); + +	return PTR_ERR(region); +} + +int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	int err; + +	err = mv88e6xxx_setup_devlink_regions_global(ds, chip); +	if (err) +		return err; + +	err = mv88e6xxx_setup_devlink_regions_ports(ds, chip); +	if (err) +		mv88e6xxx_teardown_devlink_regions_global(chip); + +	return err; +} + +void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds) +{ +	struct mv88e6xxx_chip *chip = ds->priv; + +	mv88e6xxx_teardown_devlink_regions_ports(chip); +	mv88e6xxx_teardown_devlink_regions_global(chip); +} + +int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, +			       struct devlink_info_req *req, +			       struct netlink_ext_ack *extack) +{ +	struct mv88e6xxx_chip *chip = ds->priv; +	int err; + +	err = devlink_info_driver_name_put(req, "mv88e6xxx"); +	if (err) +		return err; + +	return devlink_info_version_fixed_put(req, +					      DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, +					      chip->info->name); +} diff --git a/drivers/net/dsa/mv88e6xxx/devlink.h b/drivers/net/dsa/mv88e6xxx/devlink.h new file mode 100644 index 000000000000..3d72db3dcf95 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/devlink.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Marvell 88E6xxx Switch devlink support. */ + +#ifndef _MV88E6XXX_DEVLINK_H +#define _MV88E6XXX_DEVLINK_H + +int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds); +void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds); +int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds); +int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, +				struct devlink_param_gset_ctx *ctx); +int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, +				struct devlink_param_gset_ctx *ctx); +int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds); +void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds); + +int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, +			       struct devlink_info_req *req, +			       struct netlink_ext_ack *extack); +#endif /* _MV88E6XXX_DEVLINK_H */ diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index a4c488b12e8f..094d17a1d037 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -211,49 +211,20 @@ int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port,  		-EFAULT : 0;  } -/* Get the start of the PTP header in this skb */ -static u8 *parse_ptp_header(struct sk_buff *skb, unsigned int type) -{ -	u8 *data = skb_mac_header(skb); -	unsigned int offset = 0; - -	if (type & PTP_CLASS_VLAN) -		offset += VLAN_HLEN; - -	switch (type & PTP_CLASS_PMASK) { -	case PTP_CLASS_IPV4: -		offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; -		break; -	case PTP_CLASS_IPV6: -		offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; -		break; -	case PTP_CLASS_L2: -		offset += ETH_HLEN; -		break; -	default: -		return NULL; -	} - -	/* Ensure that the entire header is present in this packet. */ -	if (skb->len + ETH_HLEN < offset + 34) -		return NULL; - -	return data + offset; -} -  /* Returns a pointer to the PTP header if the caller should time stamp,   * or NULL if the caller should not.   */ -static u8 *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, int port, -				   struct sk_buff *skb, unsigned int type) +static struct ptp_header *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, +						  int port, struct sk_buff *skb, +						  unsigned int type)  {  	struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; -	u8 *hdr; +	struct ptp_header *hdr;  	if (!chip->info->ptp_support)  		return NULL; -	hdr = parse_ptp_header(skb, type); +	hdr = ptp_parse_header(skb, type);  	if (!hdr)  		return NULL; @@ -275,12 +246,11 @@ static int mv88e6xxx_ts_valid(u16 status)  static int seq_match(struct sk_buff *skb, u16 ts_seqid)  {  	unsigned int type = SKB_PTP_TYPE(skb); -	u8 *hdr = parse_ptp_header(skb, type); -	__be16 *seqid; +	struct ptp_header *hdr; -	seqid = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); +	hdr = ptp_parse_header(skb, type); -	return ts_seqid == ntohs(*seqid); +	return ts_seqid == ntohs(hdr->sequence_id);  }  static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, @@ -357,9 +327,9 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip,  				   &ps->rx_queue2);  } -static int is_pdelay_resp(u8 *msgtype) +static int is_pdelay_resp(const struct ptp_header *hdr)  { -	return (*msgtype & 0xf) == 3; +	return (hdr->tsmt & 0xf) == 3;  }  bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, @@ -367,7 +337,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port,  {  	struct mv88e6xxx_port_hwtstamp *ps;  	struct mv88e6xxx_chip *chip; -	u8 *hdr; +	struct ptp_header *hdr;  	chip = ds->priv;  	ps = &chip->port_hwtstamp[port]; @@ -503,8 +473,7 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,  {  	struct mv88e6xxx_chip *chip = ds->priv;  	struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; -	__be16 *seq_ptr; -	u8 *hdr; +	struct ptp_header *hdr;  	if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP))  		return false; @@ -513,15 +482,13 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port,  	if (!hdr)  		return false; -	seq_ptr = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); -  	if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS,  				  &ps->state))  		return false;  	ps->tx_skb = clone;  	ps->tx_tstamp_start = jiffies; -	ps->tx_seq_id = be16_to_cpup(seq_ptr); +	ps->tx_seq_id = be16_to_cpu(hdr->sequence_id);  	ptp_schedule_worker(chip->ptp_clock, 0);  	return true; diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index 2d23ccef7d0e..c110e82a7973 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -8,12 +8,19 @@ config NET_DSA_MSCC_FELIX  	select MSCC_OCELOT_SWITCH_LIB  	select NET_DSA_TAG_OCELOT  	select FSL_ENETC_MDIO +	select PCS_LYNX  	help -	  This driver supports network switches from the Vitesse / -	  Microsemi / Microchip Ocelot family of switching cores that are -	  connected to their host CPU via Ethernet. -	  The following switches are supported: -	  - VSC9959 (Felix): embedded as a PCIe function of the NXP LS1028A -	    ENETC integrated endpoint. -	  - VSC9953 (Seville): embedded as a platform device on the -	    NXP T1040 SoC. +	  This driver supports the VSC9959 (Felix) switch, which is embedded as +	  a PCIe function of the NXP LS1028A ENETC RCiEP. + +config NET_DSA_MSCC_SEVILLE +	tristate "Ocelot / Seville Ethernet switch support" +	depends on NET_DSA +	depends on NET_VENDOR_MICROSEMI +	depends on HAS_IOMEM +	select MSCC_OCELOT_SWITCH_LIB +	select NET_DSA_TAG_OCELOT +	select PCS_LYNX +	help +	  This driver supports the VSC9953 (Seville) switch, which is embedded +	  as a platform device on the NXP T1040 SoC. diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile index ec57a5a12330..f6dd131e7491 100644 --- a/drivers/net/dsa/ocelot/Makefile +++ b/drivers/net/dsa/ocelot/Makefile @@ -1,7 +1,11 @@  # SPDX-License-Identifier: GPL-2.0-only  obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o +obj-$(CONFIG_NET_DSA_MSCC_SEVILLE) += mscc_seville.o  mscc_felix-objs := \  	felix.o \ -	felix_vsc9959.o \ +	felix_vsc9959.o + +mscc_seville-objs := \ +	felix.o \  	seville_vsc9953.o diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 01427cd08448..f791860d495f 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -19,6 +19,7 @@  #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" @@ -118,13 +119,12 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port,  	return 0;  } -static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) +static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, +				struct switchdev_trans *trans)  {  	struct ocelot *ocelot = ds->priv; -	ocelot_port_vlan_filtering(ocelot, port, enabled); - -	return 0; +	return ocelot_port_vlan_filtering(ocelot, port, enabled, trans);  }  static void felix_vlan_add(struct dsa_switch *ds, int port, @@ -196,27 +196,16 @@ static void felix_phylink_validate(struct dsa_switch *ds, int port,  		felix->info->phylink_validate(ocelot, port, supported, state);  } -static int felix_phylink_mac_pcs_get_state(struct dsa_switch *ds, int port, -					   struct phylink_link_state *state) -{ -	struct ocelot *ocelot = ds->priv; -	struct felix *felix = ocelot_to_felix(ocelot); - -	if (felix->info->pcs_link_state) -		felix->info->pcs_link_state(ocelot, port, state); - -	return 0; -} -  static void felix_phylink_mac_config(struct dsa_switch *ds, int port,  				     unsigned int link_an_mode,  				     const struct phylink_link_state *state)  {  	struct ocelot *ocelot = ds->priv;  	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_port *dp = dsa_to_port(ds, port); -	if (felix->info->pcs_config) -		felix->info->pcs_config(ocelot, port, link_an_mode, state); +	if (felix->pcs[port]) +		phylink_set_pcs(dp->pl, &felix->pcs[port]->pcs);  }  static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, @@ -306,10 +295,6 @@ static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,  	ocelot_fields_write(ocelot, port,  			    QSYS_SWITCH_PORT_MODE_PORT_ENA, 1); -	if (felix->info->pcs_link_up) -		felix->info->pcs_link_up(ocelot, port, link_an_mode, interface, -					 speed, duplex); -  	if (felix->info->port_sched_speed_set)  		felix->info->port_sched_speed_set(ocelot, port, speed);  } @@ -449,10 +434,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  	ocelot->num_stats	= felix->info->num_stats;  	ocelot->shared_queue_sz	= felix->info->shared_queue_sz;  	ocelot->num_mact_rows	= felix->info->num_mact_rows; -	ocelot->vcap_is2_keys	= felix->info->vcap_is2_keys; -	ocelot->vcap_is2_actions= felix->info->vcap_is2_actions;  	ocelot->vcap		= felix->info->vcap;  	ocelot->ops		= felix->info->ops; +	ocelot->inj_prefix	= OCELOT_TAG_PREFIX_SHORT; +	ocelot->xtr_prefix	= OCELOT_TAG_PREFIX_SHORT;  	port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t),  				 GFP_KERNEL); @@ -523,7 +508,7 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  			return PTR_ERR(target);  		} -		template = devm_kzalloc(ocelot->dev, OCELOT_TAG_LEN, +		template = devm_kzalloc(ocelot->dev, OCELOT_TOTAL_TAG_LEN,  					GFP_KERNEL);  		if (!template) {  			dev_err(ocelot->dev, @@ -552,22 +537,27 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  	return 0;  } -static struct ptp_clock_info ocelot_ptp_clock_info = { -	.owner		= THIS_MODULE, -	.name		= "felix ptp", -	.max_adj	= 0x7fffffff, -	.n_alarm	= 0, -	.n_ext_ts	= 0, -	.n_per_out	= OCELOT_PTP_PINS_NUM, -	.n_pins		= OCELOT_PTP_PINS_NUM, -	.pps		= 0, -	.gettime64	= ocelot_ptp_gettime64, -	.settime64	= ocelot_ptp_settime64, -	.adjtime	= ocelot_ptp_adjtime, -	.adjfine	= ocelot_ptp_adjfine, -	.verify		= ocelot_ptp_verify, -	.enable		= ocelot_ptp_enable, -}; +/* The CPU port module is connected to the Node Processor Interface (NPI). This + * is the mode through which frames can be injected from and extracted to an + * external CPU, over Ethernet. + */ +static void felix_npi_port_init(struct ocelot *ocelot, int port) +{ +	ocelot->npi = port; + +	ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | +		     QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port), +		     QSYS_EXT_CPU_CFG); + +	/* NPI port Injection/Extraction configuration */ +	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, +			    ocelot->xtr_prefix); +	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, +			    ocelot->inj_prefix); + +	/* Disable transmission of pause frames */ +	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0); +}  /* Hardware initialization done here so that we can allocate structures with   * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing @@ -590,7 +580,7 @@ static int felix_setup(struct dsa_switch *ds)  		return err;  	if (ocelot->ptp) { -		err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info); +		err = ocelot_init_timestamp(ocelot, felix->info->ptp_caps);  		if (err) {  			dev_err(ocelot->dev,  				"Timestamp initialization failed\n"); @@ -601,11 +591,8 @@ static int felix_setup(struct dsa_switch *ds)  	for (port = 0; port < ds->num_ports; port++) {  		ocelot_init_port(ocelot, port); -		/* Bring up the CPU port module and configure the NPI port */  		if (dsa_is_cpu_port(ds, port)) -			ocelot_configure_cpu(ocelot, port, -					     OCELOT_TAG_PREFIX_NONE, -					     OCELOT_TAG_PREFIX_LONG); +			felix_npi_port_init(ocelot, port);  		/* Set the default QoS Classification based on PCP and DEI  		 * bits of vlan tag. @@ -630,11 +617,6 @@ static int felix_setup(struct dsa_switch *ds)  	ds->mtu_enforcement_ingress = true;  	ds->configure_vlan_while_not_filtering = true; -	/* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040) -	 * isn't instantiated for the Felix PF. -	 * In-band AN may take a few ms to complete, so we need to poll. -	 */ -	ds->pcs_poll = true;  	return 0;  } @@ -705,8 +687,11 @@ static bool felix_txtstamp(struct dsa_switch *ds, int port,  	struct ocelot *ocelot = ds->priv;  	struct ocelot_port *ocelot_port = ocelot->ports[port]; -	if (!ocelot_port_add_txtstamp_skb(ocelot_port, clone)) +	if (ocelot->ptp && (skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP) && +	    ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { +		ocelot_port_add_txtstamp_skb(ocelot, port, clone);  		return true; +	}  	return false;  } @@ -793,7 +778,6 @@ const struct dsa_switch_ops felix_switch_ops = {  	.get_sset_count		= felix_get_sset_count,  	.get_ts_info		= felix_get_ts_info,  	.phylink_validate	= felix_phylink_validate, -	.phylink_mac_link_state	= felix_phylink_mac_pcs_get_state,  	.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, @@ -823,31 +807,27 @@ const struct dsa_switch_ops felix_switch_ops = {  	.cls_flower_add		= felix_cls_flower_add,  	.cls_flower_del		= felix_cls_flower_del,  	.cls_flower_stats	= felix_cls_flower_stats, -	.port_setup_tc          = felix_port_setup_tc, +	.port_setup_tc		= felix_port_setup_tc,  }; -static int __init felix_init(void) +struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)  { -	int err; - -	err = pci_register_driver(&felix_vsc9959_pci_driver); -	if (err) -		return err; +	struct felix *felix = ocelot_to_felix(ocelot); +	struct dsa_switch *ds = felix->ds; -	err = platform_driver_register(&seville_vsc9953_driver); -	if (err) -		return err; +	if (!dsa_is_user_port(ds, port)) +		return NULL; -	return 0; +	return dsa_to_port(ds, port)->slave;  } -module_init(felix_init); -static void __exit felix_exit(void) +int felix_netdev_to_port(struct net_device *dev)  { -	pci_unregister_driver(&felix_vsc9959_pci_driver); -	platform_driver_unregister(&seville_vsc9953_driver); -} -module_exit(felix_exit); +	struct dsa_port *dp; + +	dp = dsa_port_from_netdev(dev); +	if (IS_ERR(dp)) +		return -EINVAL; -MODULE_DESCRIPTION("Felix Switch driver"); -MODULE_LICENSE("GPL v2"); +	return dp->index; +} diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 98f14621ac23..4c717324ac2f 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -20,23 +20,13 @@ struct felix_info {  	const struct ocelot_stat_layout	*stats_layout;  	unsigned int			num_stats;  	int				num_ports; -	int                             num_tx_queues; -	struct vcap_field		*vcap_is2_keys; -	struct vcap_field		*vcap_is2_actions; -	const struct vcap_props		*vcap; +	int				num_tx_queues; +	struct vcap_props		*vcap;  	int				switch_pci_bar;  	int				imdio_pci_bar; +	const struct ptp_clock_info	*ptp_caps;  	int	(*mdio_bus_alloc)(struct ocelot *ocelot);  	void	(*mdio_bus_free)(struct ocelot *ocelot); -	void	(*pcs_config)(struct ocelot *ocelot, int port, -			      unsigned int link_an_mode, -			      const struct phylink_link_state *state); -	void	(*pcs_link_up)(struct ocelot *ocelot, int port, -			       unsigned int link_an_mode, -			       phy_interface_t interface, -			       int speed, int duplex); -	void	(*pcs_link_state)(struct ocelot *ocelot, int port, -				  struct phylink_link_state *state);  	void	(*phylink_validate)(struct ocelot *ocelot, int port,  				    unsigned long *supported,  				    struct phylink_link_state *state); @@ -50,8 +40,6 @@ struct felix_info {  };  extern const struct dsa_switch_ops felix_switch_ops; -extern struct pci_driver felix_vsc9959_pci_driver; -extern struct platform_driver seville_vsc9953_driver;  /* DSA glue / front-end for struct ocelot */  struct felix { @@ -59,20 +47,12 @@ struct felix {  	const struct felix_info		*info;  	struct ocelot			ocelot;  	struct mii_bus			*imdio; -	struct phy_device		**pcs; +	struct lynx_pcs			**pcs;  	resource_size_t			switch_base;  	resource_size_t			imdio_base;  }; -void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, -			    struct phylink_link_state *state); -void vsc9959_pcs_config(struct ocelot *ocelot, int port, -			unsigned int link_an_mode, -			const struct phylink_link_state *state); -void vsc9959_pcs_link_up(struct ocelot *ocelot, int port, -			 unsigned int link_an_mode, -			 phy_interface_t interface, -			 int speed, int duplex); -void vsc9959_mdio_bus_free(struct ocelot *ocelot); +struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port); +int felix_netdev_to_port(struct net_device *dev);  #endif diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 3a9637496407..3e925b8d5306 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -9,15 +9,13 @@  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot.h>  #include <linux/packing.h> +#include <linux/pcs-lynx.h>  #include <net/pkt_sched.h>  #include <linux/iopoll.h>  #include <linux/mdio.h>  #include <linux/pci.h>  #include "felix.h" -#define VSC9959_VCAP_IS2_CNT		1024 -#define VSC9959_VCAP_IS2_ENTRY_WIDTH	376 -#define VSC9959_VCAP_PORT_CNT		6  #define VSC9959_TAS_GCL_ENTRY_MAX	63  static const u32 vsc9959_ana_regmap[] = { @@ -137,14 +135,27 @@ static const u32 vsc9959_qs_regmap[] = {  	REG_RESERVED(QS_INH_DBG),  }; -static const u32 vsc9959_s2_regmap[] = { -	REG(S2_CORE_UPDATE_CTRL,		0x000000), -	REG(S2_CORE_MV_CFG,			0x000004), -	REG(S2_CACHE_ENTRY_DAT,			0x000008), -	REG(S2_CACHE_MASK_DAT,			0x000108), -	REG(S2_CACHE_ACTION_DAT,		0x000208), -	REG(S2_CACHE_CNT_DAT,			0x000308), -	REG(S2_CACHE_TG_DAT,			0x000388), +static const u32 vsc9959_vcap_regmap[] = { +	/* VCAP_CORE_CFG */ +	REG(VCAP_CORE_UPDATE_CTRL,		0x000000), +	REG(VCAP_CORE_MV_CFG,			0x000004), +	/* VCAP_CORE_CACHE */ +	REG(VCAP_CACHE_ENTRY_DAT,		0x000008), +	REG(VCAP_CACHE_MASK_DAT,		0x000108), +	REG(VCAP_CACHE_ACTION_DAT,		0x000208), +	REG(VCAP_CACHE_CNT_DAT,			0x000308), +	REG(VCAP_CACHE_TG_DAT,			0x000388), +	/* VCAP_CONST */ +	REG(VCAP_CONST_VCAP_VER,		0x000398), +	REG(VCAP_CONST_ENTRY_WIDTH,		0x00039c), +	REG(VCAP_CONST_ENTRY_CNT,		0x0003a0), +	REG(VCAP_CONST_ENTRY_SWCNT,		0x0003a4), +	REG(VCAP_CONST_ENTRY_TG_WIDTH,		0x0003a8), +	REG(VCAP_CONST_ACTION_DEF_CNT,		0x0003ac), +	REG(VCAP_CONST_ACTION_WIDTH,		0x0003b0), +	REG(VCAP_CONST_CNT_WIDTH,		0x0003b4), +	REG(VCAP_CONST_CORE_CNT,		0x0003b8), +	REG(VCAP_CONST_IF_CNT,			0x0003bc),  };  static const u32 vsc9959_qsys_regmap[] = { @@ -295,15 +306,15 @@ static const u32 vsc9959_sys_regmap[] = {  };  static const u32 vsc9959_ptp_regmap[] = { -	REG(PTP_PIN_CFG,                   0x000000), -	REG(PTP_PIN_TOD_SEC_MSB,           0x000004), -	REG(PTP_PIN_TOD_SEC_LSB,           0x000008), -	REG(PTP_PIN_TOD_NSEC,              0x00000c), -	REG(PTP_PIN_WF_HIGH_PERIOD,        0x000014), -	REG(PTP_PIN_WF_LOW_PERIOD,         0x000018), -	REG(PTP_CFG_MISC,                  0x0000a0), -	REG(PTP_CLK_CFG_ADJ_CFG,           0x0000a4), -	REG(PTP_CLK_CFG_ADJ_FREQ,          0x0000a8), +	REG(PTP_PIN_CFG,			0x000000), +	REG(PTP_PIN_TOD_SEC_MSB,		0x000004), +	REG(PTP_PIN_TOD_SEC_LSB,		0x000008), +	REG(PTP_PIN_TOD_NSEC,			0x00000c), +	REG(PTP_PIN_WF_HIGH_PERIOD,		0x000014), +	REG(PTP_PIN_WF_LOW_PERIOD,		0x000018), +	REG(PTP_CFG_MISC,			0x0000a0), +	REG(PTP_CLK_CFG_ADJ_CFG,		0x0000a4), +	REG(PTP_CLK_CFG_ADJ_FREQ,		0x0000a8),  };  static const u32 vsc9959_gcb_regmap[] = { @@ -358,7 +369,9 @@ static const u32 *vsc9959_regmap[TARGET_MAX] = {  	[QSYS]	= vsc9959_qsys_regmap,  	[REW]	= vsc9959_rew_regmap,  	[SYS]	= vsc9959_sys_regmap, -	[S2]	= vsc9959_s2_regmap, +	[S0]	= vsc9959_vcap_regmap, +	[S1]	= vsc9959_vcap_regmap, +	[S2]	= vsc9959_vcap_regmap,  	[PTP]	= vsc9959_ptp_regmap,  	[GCB]	= vsc9959_gcb_regmap,  	[DEV_GMII] = vsc9959_dev_gmii_regmap, @@ -391,6 +404,16 @@ static const struct resource vsc9959_target_io_res[TARGET_MAX] = {  		.end	= 0x001ffff,  		.name	= "sys",  	}, +	[S0] = { +		.start	= 0x0040000, +		.end	= 0x00403ff, +		.name	= "s0", +	}, +	[S1] = { +		.start	= 0x0050000, +		.end	= 0x00503ff, +		.name	= "s1", +	},  	[S2] = {  		.start	= 0x0060000,  		.end	= 0x00603ff, @@ -595,6 +618,113 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = {  	{ .offset = 0x111,	.name = "drop_green_prio_7", },  }; +static const struct vcap_field vsc9959_vcap_es0_keys[] = { +	[VCAP_ES0_EGR_PORT]			= {  0,  3}, +	[VCAP_ES0_IGR_PORT]			= {  3,  3}, +	[VCAP_ES0_RSV]				= {  6,  2}, +	[VCAP_ES0_L2_MC]			= {  8,  1}, +	[VCAP_ES0_L2_BC]			= {  9,  1}, +	[VCAP_ES0_VID]				= { 10, 12}, +	[VCAP_ES0_DP]				= { 22,  1}, +	[VCAP_ES0_PCP]				= { 23,  3}, +}; + +static const struct vcap_field vsc9959_vcap_es0_actions[] = { +	[VCAP_ES0_ACT_PUSH_OUTER_TAG]		= {  0,  2}, +	[VCAP_ES0_ACT_PUSH_INNER_TAG]		= {  2,  1}, +	[VCAP_ES0_ACT_TAG_A_TPID_SEL]		= {  3,  2}, +	[VCAP_ES0_ACT_TAG_A_VID_SEL]		= {  5,  1}, +	[VCAP_ES0_ACT_TAG_A_PCP_SEL]		= {  6,  2}, +	[VCAP_ES0_ACT_TAG_A_DEI_SEL]		= {  8,  2}, +	[VCAP_ES0_ACT_TAG_B_TPID_SEL]		= { 10,  2}, +	[VCAP_ES0_ACT_TAG_B_VID_SEL]		= { 12,  1}, +	[VCAP_ES0_ACT_TAG_B_PCP_SEL]		= { 13,  2}, +	[VCAP_ES0_ACT_TAG_B_DEI_SEL]		= { 15,  2}, +	[VCAP_ES0_ACT_VID_A_VAL]		= { 17, 12}, +	[VCAP_ES0_ACT_PCP_A_VAL]		= { 29,  3}, +	[VCAP_ES0_ACT_DEI_A_VAL]		= { 32,  1}, +	[VCAP_ES0_ACT_VID_B_VAL]		= { 33, 12}, +	[VCAP_ES0_ACT_PCP_B_VAL]		= { 45,  3}, +	[VCAP_ES0_ACT_DEI_B_VAL]		= { 48,  1}, +	[VCAP_ES0_ACT_RSV]			= { 49, 23}, +	[VCAP_ES0_ACT_HIT_STICKY]		= { 72,  1}, +}; + +static const struct vcap_field vsc9959_vcap_is1_keys[] = { +	[VCAP_IS1_HK_TYPE]			= {  0,   1}, +	[VCAP_IS1_HK_LOOKUP]			= {  1,   2}, +	[VCAP_IS1_HK_IGR_PORT_MASK]		= {  3,   7}, +	[VCAP_IS1_HK_RSV]			= { 10,   9}, +	[VCAP_IS1_HK_OAM_Y1731]			= { 19,   1}, +	[VCAP_IS1_HK_L2_MC]			= { 20,   1}, +	[VCAP_IS1_HK_L2_BC]			= { 21,   1}, +	[VCAP_IS1_HK_IP_MC]			= { 22,   1}, +	[VCAP_IS1_HK_VLAN_TAGGED]		= { 23,   1}, +	[VCAP_IS1_HK_VLAN_DBL_TAGGED]		= { 24,   1}, +	[VCAP_IS1_HK_TPID]			= { 25,   1}, +	[VCAP_IS1_HK_VID]			= { 26,  12}, +	[VCAP_IS1_HK_DEI]			= { 38,   1}, +	[VCAP_IS1_HK_PCP]			= { 39,   3}, +	/* Specific Fields for IS1 Half Key S1_NORMAL */ +	[VCAP_IS1_HK_L2_SMAC]			= { 42,  48}, +	[VCAP_IS1_HK_ETYPE_LEN]			= { 90,   1}, +	[VCAP_IS1_HK_ETYPE]			= { 91,  16}, +	[VCAP_IS1_HK_IP_SNAP]			= {107,   1}, +	[VCAP_IS1_HK_IP4]			= {108,   1}, +	/* Layer-3 Information */ +	[VCAP_IS1_HK_L3_FRAGMENT]		= {109,   1}, +	[VCAP_IS1_HK_L3_FRAG_OFS_GT0]		= {110,   1}, +	[VCAP_IS1_HK_L3_OPTIONS]		= {111,   1}, +	[VCAP_IS1_HK_L3_DSCP]			= {112,   6}, +	[VCAP_IS1_HK_L3_IP4_SIP]		= {118,  32}, +	/* Layer-4 Information */ +	[VCAP_IS1_HK_TCP_UDP]			= {150,   1}, +	[VCAP_IS1_HK_TCP]			= {151,   1}, +	[VCAP_IS1_HK_L4_SPORT]			= {152,  16}, +	[VCAP_IS1_HK_L4_RNG]			= {168,   8}, +	/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */ +	[VCAP_IS1_HK_IP4_INNER_TPID]            = { 42,   1}, +	[VCAP_IS1_HK_IP4_INNER_VID]		= { 43,  12}, +	[VCAP_IS1_HK_IP4_INNER_DEI]		= { 55,   1}, +	[VCAP_IS1_HK_IP4_INNER_PCP]		= { 56,   3}, +	[VCAP_IS1_HK_IP4_IP4]			= { 59,   1}, +	[VCAP_IS1_HK_IP4_L3_FRAGMENT]		= { 60,   1}, +	[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0]	= { 61,   1}, +	[VCAP_IS1_HK_IP4_L3_OPTIONS]		= { 62,   1}, +	[VCAP_IS1_HK_IP4_L3_DSCP]		= { 63,   6}, +	[VCAP_IS1_HK_IP4_L3_IP4_DIP]		= { 69,  32}, +	[VCAP_IS1_HK_IP4_L3_IP4_SIP]		= {101,  32}, +	[VCAP_IS1_HK_IP4_L3_PROTO]		= {133,   8}, +	[VCAP_IS1_HK_IP4_TCP_UDP]		= {141,   1}, +	[VCAP_IS1_HK_IP4_TCP]			= {142,   1}, +	[VCAP_IS1_HK_IP4_L4_RNG]		= {143,   8}, +	[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE]	= {151,  32}, +}; + +static const struct vcap_field vsc9959_vcap_is1_actions[] = { +	[VCAP_IS1_ACT_DSCP_ENA]			= {  0,  1}, +	[VCAP_IS1_ACT_DSCP_VAL]			= {  1,  6}, +	[VCAP_IS1_ACT_QOS_ENA]			= {  7,  1}, +	[VCAP_IS1_ACT_QOS_VAL]			= {  8,  3}, +	[VCAP_IS1_ACT_DP_ENA]			= { 11,  1}, +	[VCAP_IS1_ACT_DP_VAL]			= { 12,  1}, +	[VCAP_IS1_ACT_PAG_OVERRIDE_MASK]	= { 13,  8}, +	[VCAP_IS1_ACT_PAG_VAL]			= { 21,  8}, +	[VCAP_IS1_ACT_RSV]			= { 29,  9}, +	/* The fields below are incorrectly shifted by 2 in the manual */ +	[VCAP_IS1_ACT_VID_REPLACE_ENA]		= { 38,  1}, +	[VCAP_IS1_ACT_VID_ADD_VAL]		= { 39, 12}, +	[VCAP_IS1_ACT_FID_SEL]			= { 51,  2}, +	[VCAP_IS1_ACT_FID_VAL]			= { 53, 13}, +	[VCAP_IS1_ACT_PCP_DEI_ENA]		= { 66,  1}, +	[VCAP_IS1_ACT_PCP_VAL]			= { 67,  3}, +	[VCAP_IS1_ACT_DEI_VAL]			= { 70,  1}, +	[VCAP_IS1_ACT_VLAN_POP_CNT_ENA]		= { 71,  1}, +	[VCAP_IS1_ACT_VLAN_POP_CNT]		= { 72,  2}, +	[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA]	= { 74,  4}, +	[VCAP_IS1_ACT_HIT_STICKY]		= { 78,  1}, +}; +  static struct vcap_field vsc9959_vcap_is2_keys[] = {  	/* Common: 41 bits */  	[VCAP_IS2_TYPE]				= {  0,   4}, @@ -693,15 +823,32 @@ static struct vcap_field vsc9959_vcap_is2_actions[] = {  	[VCAP_IS2_ACT_HIT_CNT]			= { 44, 32},  }; -static const struct vcap_props vsc9959_vcap_props[] = { +static struct vcap_props vsc9959_vcap_props[] = { +	[VCAP_ES0] = { +		.action_type_width = 0, +		.action_table = { +			[ES0_ACTION_TYPE_NORMAL] = { +				.width = 72, /* HIT_STICKY not included */ +				.count = 1, +			}, +		}, +		.target = S0, +		.keys = vsc9959_vcap_es0_keys, +		.actions = vsc9959_vcap_es0_actions, +	}, +	[VCAP_IS1] = { +		.action_type_width = 0, +		.action_table = { +			[IS1_ACTION_TYPE_NORMAL] = { +				.width = 78, /* HIT_STICKY not included */ +				.count = 4, +			}, +		}, +		.target = S1, +		.keys = vsc9959_vcap_is1_keys, +		.actions = vsc9959_vcap_is1_actions, +	},  	[VCAP_IS2] = { -		.tg_width = 2, -		.sw_count = 4, -		.entry_count = VSC9959_VCAP_IS2_CNT, -		.entry_width = VSC9959_VCAP_IS2_ENTRY_WIDTH, -		.action_count = VSC9959_VCAP_IS2_CNT + -				VSC9959_VCAP_PORT_CNT + 2, -		.action_width = 89,  		.action_type_width = 1,  		.action_table = {  			[IS2_ACTION_TYPE_NORMAL] = { @@ -713,11 +860,29 @@ static const struct vcap_props vsc9959_vcap_props[] = {  				.count = 4  			},  		}, -		.counter_words = 4, -		.counter_width = 32, +		.target = S2, +		.keys = vsc9959_vcap_is2_keys, +		.actions = vsc9959_vcap_is2_actions,  	},  }; +static const struct ptp_clock_info vsc9959_ptp_caps = { +	.owner		= THIS_MODULE, +	.name		= "felix ptp", +	.max_adj	= 0x7fffffff, +	.n_alarm	= 0, +	.n_ext_ts	= 0, +	.n_per_out	= OCELOT_PTP_PINS_NUM, +	.n_pins		= OCELOT_PTP_PINS_NUM, +	.pps		= 0, +	.gettime64	= ocelot_ptp_gettime64, +	.settime64	= ocelot_ptp_settime64, +	.adjtime	= ocelot_ptp_adjtime, +	.adjfine	= ocelot_ptp_adjfine, +	.verify		= ocelot_ptp_verify, +	.enable		= ocelot_ptp_enable, +}; +  #define VSC9959_INIT_TIMEOUT			50000  #define VSC9959_GCB_RST_SLEEP			100  #define VSC9959_SYS_RAMINIT_SLEEP		80 @@ -726,7 +891,7 @@ static int vsc9959_gcb_soft_rst_status(struct ocelot *ocelot)  {  	int val; -	regmap_field_read(ocelot->regfields[GCB_SOFT_RST_SWC_RST], &val); +	ocelot_field_read(ocelot, GCB_SOFT_RST_SWC_RST, &val);  	return val;  } @@ -736,12 +901,15 @@ static int vsc9959_sys_ram_init_status(struct ocelot *ocelot)  	return ocelot_read(ocelot, SYS_RAM_INIT);  } +/* CORE_ENA is in SYS:SYSTEM:RESET_CFG + * RAM_INIT is in SYS:RAM_CTRL:RAM_INIT + */  static int vsc9959_reset(struct ocelot *ocelot)  {  	int val, err;  	/* soft-reset the switch core */ -	regmap_field_write(ocelot->regfields[GCB_SOFT_RST_SWC_RST], 1); +	ocelot_field_write(ocelot, GCB_SOFT_RST_SWC_RST, 1);  	err = readx_poll_timeout(vsc9959_gcb_soft_rst_status, ocelot, val, !val,  				 VSC9959_GCB_RST_SLEEP, VSC9959_INIT_TIMEOUT); @@ -761,352 +929,11 @@ static int vsc9959_reset(struct ocelot *ocelot)  	}  	/* enable switch core */ -	regmap_field_write(ocelot->regfields[SYS_RESET_CFG_CORE_ENA], 1); +	ocelot_field_write(ocelot, SYS_RESET_CFG_CORE_ENA, 1);  	return 0;  } -/* We enable SGMII AN only when the PHY has managed = "in-band-status" in the - * device tree. If we are in MLO_AN_PHY mode, we program directly state->speed - * into the PCS, which is retrieved out-of-band over MDIO. This also has the - * benefit of working with SGMII fixed-links, like downstream switches, where - * both link partners attempt to operate as AN slaves and therefore AN never - * completes.  But it also has the disadvantage that some PHY chips don't pass - * traffic if SGMII AN is enabled but not completed (acknowledged by us), so - * setting MLO_AN_INBAND is actually required for those. - */ -static void vsc9959_pcs_config_sgmii(struct phy_device *pcs, -				     unsigned int link_an_mode, -				     const struct phylink_link_state *state) -{ -	int bmsr, bmcr; - -	/* Some PHYs like VSC8234 don't like it when AN restarts on -	 * their system  side and they restart line side AN too, going -	 * into an endless link up/down loop.  Don't restart PCS AN if -	 * link is up already. -	 * We do check that AN is enabled just in case this is the 1st -	 * call, PCS detects a carrier but AN is disabled from power on -	 * or by boot loader. -	 */ -	bmcr = phy_read(pcs, MII_BMCR); -	if (bmcr < 0) -		return; - -	bmsr = phy_read(pcs, MII_BMSR); -	if (bmsr < 0) -		return; - -	if ((bmcr & BMCR_ANENABLE) && (bmsr & BMSR_LSTATUS)) -		return; - -	/* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001 -	 * for the MAC PCS in order to acknowledge the AN. -	 */ -	phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | -				      ADVERTISE_LPACK); - -	phy_write(pcs, ENETC_PCS_IF_MODE, -		  ENETC_PCS_IF_MODE_SGMII_EN | -		  ENETC_PCS_IF_MODE_USE_SGMII_AN); - -	/* Adjust link timer for SGMII */ -	phy_write(pcs, ENETC_PCS_LINK_TIMER1, -		  ENETC_PCS_LINK_TIMER1_VAL); -	phy_write(pcs, ENETC_PCS_LINK_TIMER2, -		  ENETC_PCS_LINK_TIMER2_VAL); - -	phy_set_bits(pcs, MII_BMCR, BMCR_ANENABLE); -} - -static void vsc9959_pcs_config_usxgmii(struct phy_device *pcs, -				       unsigned int link_an_mode, -				       const struct phylink_link_state *state) -{ -	/* Configure device ability for the USXGMII Replicator */ -	phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE, -		      MDIO_USXGMII_2500FULL | -		      MDIO_USXGMII_LINK | -		      ADVERTISE_SGMII | -		      ADVERTISE_LPACK); -} - -void vsc9959_pcs_config(struct ocelot *ocelot, int port, -			unsigned int link_an_mode, -			const struct phylink_link_state *state) -{ -	struct felix *felix = ocelot_to_felix(ocelot); -	struct phy_device *pcs = felix->pcs[port]; - -	if (!pcs) -		return; - -	/* The PCS does not implement the BMSR register fully, so capability -	 * detection via genphy_read_abilities does not work. Since we can get -	 * the PHY config word from the LPA register though, there is still -	 * value in using the generic phy_resolve_aneg_linkmode function. So -	 * populate the supported and advertising link modes manually here. -	 */ -	linkmode_set_bit_array(phy_basic_ports_array, -			       ARRAY_SIZE(phy_basic_ports_array), -			       pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, pcs->supported); -	linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, pcs->supported); -	if (pcs->interface == PHY_INTERFACE_MODE_2500BASEX || -	    pcs->interface == PHY_INTERFACE_MODE_USXGMII) -		linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, -				 pcs->supported); -	if (pcs->interface != PHY_INTERFACE_MODE_2500BASEX) -		linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, -				 pcs->supported); -	phy_advertise_supported(pcs); - -	if (!phylink_autoneg_inband(link_an_mode)) -		return; - -	switch (pcs->interface) { -	case PHY_INTERFACE_MODE_SGMII: -	case PHY_INTERFACE_MODE_QSGMII: -		vsc9959_pcs_config_sgmii(pcs, link_an_mode, state); -		break; -	case PHY_INTERFACE_MODE_2500BASEX: -		phydev_err(pcs, "AN not supported on 3.125GHz SerDes lane\n"); -		break; -	case PHY_INTERFACE_MODE_USXGMII: -		vsc9959_pcs_config_usxgmii(pcs, link_an_mode, state); -		break; -	default: -		dev_err(ocelot->dev, "Unsupported link mode %s\n", -			phy_modes(pcs->interface)); -	} -} - -static void vsc9959_pcs_link_up_sgmii(struct phy_device *pcs, -				      unsigned int link_an_mode, -				      int speed, int duplex) -{ -	u16 if_mode = ENETC_PCS_IF_MODE_SGMII_EN; - -	switch (speed) { -	case SPEED_1000: -		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_1000); -		break; -	case SPEED_100: -		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_100); -		break; -	case SPEED_10: -		if_mode |= ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_10); -		break; -	default: -		phydev_err(pcs, "Invalid PCS speed %d\n", speed); -		return; -	} - -	if (duplex == DUPLEX_HALF) -		if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF; - -	phy_write(pcs, ENETC_PCS_IF_MODE, if_mode); -	phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE); -} - -/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane - * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have - * auto-negotiation of any link parameters. Electrically it is compatible with - * a single lane of XAUI. - * The hardware reference manual wants to call this mode SGMII, but it isn't - * really, since the fundamental features of SGMII: - * - Downgrading the link speed by duplicating symbols - * - Auto-negotiation - * are not there. - * The speed is configured at 1000 in the IF_MODE and BMCR MDIO registers - * because the clock frequency is actually given by a PLL configured in the - * Reset Configuration Word (RCW). - * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o - * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a - * lower link speed on line side, the system-side interface remains fixed at - * 2500 Mbps and we do rate adaptation through pause frames. - */ -static void vsc9959_pcs_link_up_2500basex(struct phy_device *pcs, -					  unsigned int link_an_mode, -					  int speed, int duplex) -{ -	u16 if_mode = ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500) | -		      ENETC_PCS_IF_MODE_SGMII_EN; - -	if (duplex == DUPLEX_HALF) -		if_mode |= ENETC_PCS_IF_MODE_DUPLEX_HALF; - -	phy_write(pcs, ENETC_PCS_IF_MODE, if_mode); -	phy_clear_bits(pcs, MII_BMCR, BMCR_ANENABLE); -} - -void vsc9959_pcs_link_up(struct ocelot *ocelot, int port, -			 unsigned int link_an_mode, -			 phy_interface_t interface, -			 int speed, int duplex) -{ -	struct felix *felix = ocelot_to_felix(ocelot); -	struct phy_device *pcs = felix->pcs[port]; - -	if (!pcs) -		return; - -	if (phylink_autoneg_inband(link_an_mode)) -		return; - -	switch (interface) { -	case PHY_INTERFACE_MODE_SGMII: -	case PHY_INTERFACE_MODE_QSGMII: -		vsc9959_pcs_link_up_sgmii(pcs, link_an_mode, speed, duplex); -		break; -	case PHY_INTERFACE_MODE_2500BASEX: -		vsc9959_pcs_link_up_2500basex(pcs, link_an_mode, speed, -					      duplex); -		break; -	case PHY_INTERFACE_MODE_USXGMII: -		phydev_err(pcs, "USXGMII only supports in-band AN for now\n"); -		break; -	default: -		dev_err(ocelot->dev, "Unsupported link mode %s\n", -			phy_modes(pcs->interface)); -	} -} - -static void vsc9959_pcs_link_state_resolve(struct phy_device *pcs, -					   struct phylink_link_state *state) -{ -	state->an_complete = pcs->autoneg_complete; -	state->an_enabled = pcs->autoneg; -	state->link = pcs->link; -	state->duplex = pcs->duplex; -	state->speed = pcs->speed; -	/* SGMII AN does not negotiate flow control, but that's ok, -	 * since phylink already knows that, and does: -	 *	link_state.pause |= pl->phy_state.pause; -	 */ -	state->pause = MLO_PAUSE_NONE; - -	phydev_dbg(pcs, -		   "mode=%s/%s/%s adv=%*pb lpa=%*pb link=%u an_enabled=%u an_complete=%u\n", -		   phy_modes(pcs->interface), -		   phy_speed_to_str(pcs->speed), -		   phy_duplex_to_str(pcs->duplex), -		   __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->advertising, -		   __ETHTOOL_LINK_MODE_MASK_NBITS, pcs->lp_advertising, -		   pcs->link, pcs->autoneg, pcs->autoneg_complete); -} - -static void vsc9959_pcs_link_state_sgmii(struct phy_device *pcs, -					 struct phylink_link_state *state) -{ -	int err; - -	err = genphy_update_link(pcs); -	if (err < 0) -		return; - -	if (pcs->autoneg_complete) { -		u16 lpa = phy_read(pcs, MII_LPA); - -		mii_lpa_to_linkmode_lpa_sgmii(pcs->lp_advertising, lpa); - -		phy_resolve_aneg_linkmode(pcs); -	} -} - -static void vsc9959_pcs_link_state_2500basex(struct phy_device *pcs, -					     struct phylink_link_state *state) -{ -	int err; - -	err = genphy_update_link(pcs); -	if (err < 0) -		return; - -	pcs->speed = SPEED_2500; -	pcs->asym_pause = true; -	pcs->pause = true; -} - -static void vsc9959_pcs_link_state_usxgmii(struct phy_device *pcs, -					   struct phylink_link_state *state) -{ -	int status, lpa; - -	status = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_BMSR); -	if (status < 0) -		return; - -	pcs->autoneg = true; -	pcs->autoneg_complete = !!(status & BMSR_ANEGCOMPLETE); -	pcs->link = !!(status & BMSR_LSTATUS); - -	if (!pcs->link || !pcs->autoneg_complete) -		return; - -	lpa = phy_read_mmd(pcs, MDIO_MMD_VEND2, MII_LPA); -	if (lpa < 0) -		return; - -	switch (lpa & MDIO_USXGMII_SPD_MASK) { -	case MDIO_USXGMII_10: -		pcs->speed = SPEED_10; -		break; -	case MDIO_USXGMII_100: -		pcs->speed = SPEED_100; -		break; -	case MDIO_USXGMII_1000: -		pcs->speed = SPEED_1000; -		break; -	case MDIO_USXGMII_2500: -		pcs->speed = SPEED_2500; -		break; -	default: -		break; -	} - -	if (lpa & MDIO_USXGMII_FULL_DUPLEX) -		pcs->duplex = DUPLEX_FULL; -	else -		pcs->duplex = DUPLEX_HALF; -} - -void vsc9959_pcs_link_state(struct ocelot *ocelot, int port, -			    struct phylink_link_state *state) -{ -	struct felix *felix = ocelot_to_felix(ocelot); -	struct phy_device *pcs = felix->pcs[port]; - -	if (!pcs) -		return; - -	pcs->speed = SPEED_UNKNOWN; -	pcs->duplex = DUPLEX_UNKNOWN; -	pcs->pause = 0; -	pcs->asym_pause = 0; - -	switch (pcs->interface) { -	case PHY_INTERFACE_MODE_SGMII: -	case PHY_INTERFACE_MODE_QSGMII: -		vsc9959_pcs_link_state_sgmii(pcs, state); -		break; -	case PHY_INTERFACE_MODE_2500BASEX: -		vsc9959_pcs_link_state_2500basex(pcs, state); -		break; -	case PHY_INTERFACE_MODE_USXGMII: -		vsc9959_pcs_link_state_usxgmii(pcs, state); -		break; -	default: -		return; -	} - -	vsc9959_pcs_link_state_resolve(pcs, state); -} -  static void vsc9959_phylink_validate(struct ocelot *ocelot, int port,  				     unsigned long *supported,  				     struct phylink_link_state *state) @@ -1182,6 +1009,8 @@ static u16 vsc9959_wm_enc(u16 value)  static const struct ocelot_ops vsc9959_ops = {  	.reset			= vsc9959_reset,  	.wm_enc			= vsc9959_wm_enc, +	.port_to_netdev		= felix_port_to_netdev, +	.netdev_to_port		= felix_netdev_to_port,  };  static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) @@ -1197,7 +1026,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  	int rc;  	felix->pcs = devm_kcalloc(dev, felix->info->num_ports, -				  sizeof(struct phy_device *), +				  sizeof(struct lynx_pcs *),  				  GFP_KERNEL);  	if (!felix->pcs) {  		dev_err(dev, "failed to allocate array for PCS PHYs\n"); @@ -1248,18 +1077,26 @@ 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 phy_device *pcs; -		bool is_c45 = false; +		struct mdio_device *pcs; +		struct lynx_pcs *lynx; -		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_USXGMII) -			is_c45 = true; +		if (dsa_is_unused_port(felix->ds, port)) +			continue; -		pcs = get_phy_device(felix->imdio, port, is_c45); +		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL) +			continue; + +		pcs = mdio_device_create(felix->imdio, port);  		if (IS_ERR(pcs))  			continue; -		pcs->interface = ocelot_port->phy_mode; -		felix->pcs[port] = pcs; +		lynx = lynx_pcs_create(pcs); +		if (!lynx) { +			mdio_device_free(pcs); +			continue; +		} + +		felix->pcs[port] = lynx;  		dev_info(dev, "Found PCS at internal MDIO address %d\n", port);  	} @@ -1267,18 +1104,19 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)  	return 0;  } -void vsc9959_mdio_bus_free(struct ocelot *ocelot) +static void vsc9959_mdio_bus_free(struct ocelot *ocelot)  {  	struct felix *felix = ocelot_to_felix(ocelot);  	int port;  	for (port = 0; port < ocelot->num_phys_ports; port++) { -		struct phy_device *pcs = felix->pcs[port]; +		struct lynx_pcs *pcs = felix->pcs[port];  		if (!pcs)  			continue; -		put_device(&pcs->mdio.dev); +		mdio_device_free(pcs->mdio); +		lynx_pcs_destroy(pcs);  	}  	mdiobus_unregister(felix->imdio);  } @@ -1488,6 +1326,8 @@ static void vsc9959_xmit_template_populate(struct ocelot *ocelot, int port)  	struct ocelot_port *ocelot_port = ocelot->ports[port];  	u8 *template = ocelot_port->xmit_template;  	u64 bypass, dest, src; +	__be32 *prefix; +	u8 *injection;  	/* Set the source port as the CPU port module and not the  	 * NPI port @@ -1496,9 +1336,14 @@ static void vsc9959_xmit_template_populate(struct ocelot *ocelot, int port)  	dest = BIT(port);  	bypass = true; -	packing(template, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); -	packing(template, &dest,    68,  56, OCELOT_TAG_LEN, PACK, 0); -	packing(template, &src,     46,  43, OCELOT_TAG_LEN, PACK, 0); +	injection = template + OCELOT_SHORT_PREFIX_LEN; +	prefix = (__be32 *)template; + +	packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); +	packing(injection, &dest,    68,  56, OCELOT_TAG_LEN, PACK, 0); +	packing(injection, &src,     46,  43, OCELOT_TAG_LEN, PACK, 0); + +	*prefix = cpu_to_be32(0x8880000a);  }  static const struct felix_info felix_info_vsc9959 = { @@ -1510,8 +1355,6 @@ static const struct felix_info felix_info_vsc9959 = {  	.ops			= &vsc9959_ops,  	.stats_layout		= vsc9959_stats_layout,  	.num_stats		= ARRAY_SIZE(vsc9959_stats_layout), -	.vcap_is2_keys		= vsc9959_vcap_is2_keys, -	.vcap_is2_actions	= vsc9959_vcap_is2_actions,  	.vcap			= vsc9959_vcap_props,  	.shared_queue_sz	= 128 * 1024,  	.num_mact_rows		= 2048, @@ -1519,15 +1362,13 @@ static const struct felix_info felix_info_vsc9959 = {  	.num_tx_queues		= FELIX_NUM_TC,  	.switch_pci_bar		= 4,  	.imdio_pci_bar		= 0, +	.ptp_caps		= &vsc9959_ptp_caps,  	.mdio_bus_alloc		= vsc9959_mdio_bus_alloc,  	.mdio_bus_free		= vsc9959_mdio_bus_free, -	.pcs_config		= vsc9959_pcs_config, -	.pcs_link_up		= vsc9959_pcs_link_up, -	.pcs_link_state		= vsc9959_pcs_link_state,  	.phylink_validate	= vsc9959_phylink_validate,  	.prevalidate_phy_mode	= vsc9959_prevalidate_phy_mode, -	.port_setup_tc          = vsc9959_port_setup_tc, -	.port_sched_speed_set   = vsc9959_sched_speed_set, +	.port_setup_tc		= vsc9959_port_setup_tc, +	.port_sched_speed_set	= vsc9959_sched_speed_set,  	.xmit_template_populate	= vsc9959_xmit_template_populate,  }; @@ -1663,9 +1504,13 @@ static struct pci_device_id felix_ids[] = {  };  MODULE_DEVICE_TABLE(pci, felix_ids); -struct pci_driver felix_vsc9959_pci_driver = { +static struct pci_driver felix_vsc9959_pci_driver = {  	.name		= "mscc_felix",  	.id_table	= felix_ids,  	.probe		= felix_pci_probe,  	.remove		= felix_pci_remove,  }; +module_pci_driver(felix_vsc9959_pci_driver); + +MODULE_DESCRIPTION("Felix Switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index 9e9fd19e1d00..1d420c4a2f0f 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -7,31 +7,17 @@  #include <soc/mscc/ocelot_sys.h>  #include <soc/mscc/ocelot.h>  #include <linux/of_platform.h> +#include <linux/pcs-lynx.h>  #include <linux/packing.h>  #include <linux/iopoll.h>  #include "felix.h" -#define VSC9953_VCAP_IS2_CNT			1024 -#define VSC9953_VCAP_IS2_ENTRY_WIDTH		376 -#define VSC9953_VCAP_PORT_CNT			10 - -#define MSCC_MIIM_REG_STATUS			0x0 -#define		MSCC_MIIM_STATUS_STAT_BUSY	BIT(3) -#define MSCC_MIIM_REG_CMD			0x8 -#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 MSCC_MIIM_REG_DATA			0xC -#define		MSCC_MIIM_DATA_ERROR		(BIT(16) | BIT(17)) - -#define MSCC_PHY_REG_PHY_CFG		0x0 -#define		PHY_CFG_PHY_ENA		(BIT(0) | BIT(1) | BIT(2) | BIT(3)) -#define		PHY_CFG_PHY_COMMON_RESET BIT(4) -#define		PHY_CFG_PHY_RESET	(BIT(5) | BIT(6) | BIT(7) | BIT(8)) -#define MSCC_PHY_REG_PHY_STATUS		0x4 +#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)  static const u32 vsc9953_ana_regmap[] = {  	REG(ANA_ADVLEARN,			0x00b500), @@ -150,14 +136,27 @@ static const u32 vsc9953_qs_regmap[] = {  	REG_RESERVED(QS_INH_DBG),  }; -static const u32 vsc9953_s2_regmap[] = { -	REG(S2_CORE_UPDATE_CTRL,		0x000000), -	REG(S2_CORE_MV_CFG,			0x000004), -	REG(S2_CACHE_ENTRY_DAT,			0x000008), -	REG(S2_CACHE_MASK_DAT,			0x000108), -	REG(S2_CACHE_ACTION_DAT,		0x000208), -	REG(S2_CACHE_CNT_DAT,			0x000308), -	REG(S2_CACHE_TG_DAT,			0x000388), +static const u32 vsc9953_vcap_regmap[] = { +	/* VCAP_CORE_CFG */ +	REG(VCAP_CORE_UPDATE_CTRL,		0x000000), +	REG(VCAP_CORE_MV_CFG,			0x000004), +	/* VCAP_CORE_CACHE */ +	REG(VCAP_CACHE_ENTRY_DAT,		0x000008), +	REG(VCAP_CACHE_MASK_DAT,		0x000108), +	REG(VCAP_CACHE_ACTION_DAT,		0x000208), +	REG(VCAP_CACHE_CNT_DAT,			0x000308), +	REG(VCAP_CACHE_TG_DAT,			0x000388), +	/* VCAP_CONST */ +	REG(VCAP_CONST_VCAP_VER,		0x000398), +	REG(VCAP_CONST_ENTRY_WIDTH,		0x00039c), +	REG(VCAP_CONST_ENTRY_CNT,		0x0003a0), +	REG(VCAP_CONST_ENTRY_SWCNT,		0x0003a4), +	REG(VCAP_CONST_ENTRY_TG_WIDTH,		0x0003a8), +	REG(VCAP_CONST_ACTION_DEF_CNT,		0x0003ac), +	REG(VCAP_CONST_ACTION_WIDTH,		0x0003b0), +	REG(VCAP_CONST_CNT_WIDTH,		0x0003b4), +	REG_RESERVED(VCAP_CONST_CORE_CNT), +	REG_RESERVED(VCAP_CONST_IF_CNT),  };  static const u32 vsc9953_qsys_regmap[] = { @@ -362,7 +361,9 @@ static const u32 *vsc9953_regmap[TARGET_MAX] = {  	[QSYS]		= vsc9953_qsys_regmap,  	[REW]		= vsc9953_rew_regmap,  	[SYS]		= vsc9953_sys_regmap, -	[S2]		= vsc9953_s2_regmap, +	[S0]		= vsc9953_vcap_regmap, +	[S1]		= vsc9953_vcap_regmap, +	[S2]		= vsc9953_vcap_regmap,  	[GCB]		= vsc9953_gcb_regmap,  	[DEV_GMII]	= vsc9953_dev_gmii_regmap,  }; @@ -394,6 +395,16 @@ static const struct resource vsc9953_target_io_res[TARGET_MAX] = {  		.end	= 0x001ffff,  		.name	= "sys",  	}, +	[S0] = { +		.start	= 0x0040000, +		.end	= 0x00403ff, +		.name	= "s0", +	}, +	[S1] = { +		.start	= 0x0050000, +		.end	= 0x00503ff, +		.name	= "s1", +	},  	[S2] = {  		.start	= 0x0060000,  		.end	= 0x00603ff, @@ -609,6 +620,112 @@ static const struct ocelot_stat_layout vsc9953_stats_layout[] = {  	{ .offset = 0x91,	.name = "drop_green_prio_7", },  }; +static const struct vcap_field vsc9953_vcap_es0_keys[] = { +	[VCAP_ES0_EGR_PORT]			= {  0,  4}, +	[VCAP_ES0_IGR_PORT]			= {  4,  4}, +	[VCAP_ES0_RSV]				= {  8,  2}, +	[VCAP_ES0_L2_MC]			= { 10,  1}, +	[VCAP_ES0_L2_BC]			= { 11,  1}, +	[VCAP_ES0_VID]				= { 12, 12}, +	[VCAP_ES0_DP]				= { 24,  1}, +	[VCAP_ES0_PCP]				= { 25,  3}, +}; + +static const struct vcap_field vsc9953_vcap_es0_actions[] = { +	[VCAP_ES0_ACT_PUSH_OUTER_TAG]		= {  0,  2}, +	[VCAP_ES0_ACT_PUSH_INNER_TAG]		= {  2,  1}, +	[VCAP_ES0_ACT_TAG_A_TPID_SEL]		= {  3,  2}, +	[VCAP_ES0_ACT_TAG_A_VID_SEL]		= {  5,  1}, +	[VCAP_ES0_ACT_TAG_A_PCP_SEL]		= {  6,  2}, +	[VCAP_ES0_ACT_TAG_A_DEI_SEL]		= {  8,  2}, +	[VCAP_ES0_ACT_TAG_B_TPID_SEL]		= { 10,  2}, +	[VCAP_ES0_ACT_TAG_B_VID_SEL]		= { 12,  1}, +	[VCAP_ES0_ACT_TAG_B_PCP_SEL]		= { 13,  2}, +	[VCAP_ES0_ACT_TAG_B_DEI_SEL]		= { 15,  2}, +	[VCAP_ES0_ACT_VID_A_VAL]		= { 17, 12}, +	[VCAP_ES0_ACT_PCP_A_VAL]		= { 29,  3}, +	[VCAP_ES0_ACT_DEI_A_VAL]		= { 32,  1}, +	[VCAP_ES0_ACT_VID_B_VAL]		= { 33, 12}, +	[VCAP_ES0_ACT_PCP_B_VAL]		= { 45,  3}, +	[VCAP_ES0_ACT_DEI_B_VAL]		= { 48,  1}, +	[VCAP_ES0_ACT_RSV]			= { 49, 24}, +	[VCAP_ES0_ACT_HIT_STICKY]		= { 73,  1}, +}; + +static const struct vcap_field vsc9953_vcap_is1_keys[] = { +	[VCAP_IS1_HK_TYPE]			= {  0,   1}, +	[VCAP_IS1_HK_LOOKUP]			= {  1,   2}, +	[VCAP_IS1_HK_IGR_PORT_MASK]		= {  3,  11}, +	[VCAP_IS1_HK_RSV]			= { 14,  10}, +	/* VCAP_IS1_HK_OAM_Y1731 not supported */ +	[VCAP_IS1_HK_L2_MC]			= { 24,   1}, +	[VCAP_IS1_HK_L2_BC]			= { 25,   1}, +	[VCAP_IS1_HK_IP_MC]			= { 26,   1}, +	[VCAP_IS1_HK_VLAN_TAGGED]		= { 27,   1}, +	[VCAP_IS1_HK_VLAN_DBL_TAGGED]		= { 28,   1}, +	[VCAP_IS1_HK_TPID]			= { 29,   1}, +	[VCAP_IS1_HK_VID]			= { 30,  12}, +	[VCAP_IS1_HK_DEI]			= { 42,   1}, +	[VCAP_IS1_HK_PCP]			= { 43,   3}, +	/* Specific Fields for IS1 Half Key S1_NORMAL */ +	[VCAP_IS1_HK_L2_SMAC]			= { 46,  48}, +	[VCAP_IS1_HK_ETYPE_LEN]			= { 94,   1}, +	[VCAP_IS1_HK_ETYPE]			= { 95,  16}, +	[VCAP_IS1_HK_IP_SNAP]			= {111,   1}, +	[VCAP_IS1_HK_IP4]			= {112,   1}, +	/* Layer-3 Information */ +	[VCAP_IS1_HK_L3_FRAGMENT]		= {113,   1}, +	[VCAP_IS1_HK_L3_FRAG_OFS_GT0]		= {114,   1}, +	[VCAP_IS1_HK_L3_OPTIONS]		= {115,   1}, +	[VCAP_IS1_HK_L3_DSCP]			= {116,   6}, +	[VCAP_IS1_HK_L3_IP4_SIP]		= {122,  32}, +	/* Layer-4 Information */ +	[VCAP_IS1_HK_TCP_UDP]			= {154,   1}, +	[VCAP_IS1_HK_TCP]			= {155,   1}, +	[VCAP_IS1_HK_L4_SPORT]			= {156,  16}, +	[VCAP_IS1_HK_L4_RNG]			= {172,   8}, +	/* Specific Fields for IS1 Half Key S1_5TUPLE_IP4 */ +	[VCAP_IS1_HK_IP4_INNER_TPID]            = { 46,   1}, +	[VCAP_IS1_HK_IP4_INNER_VID]		= { 47,  12}, +	[VCAP_IS1_HK_IP4_INNER_DEI]		= { 59,   1}, +	[VCAP_IS1_HK_IP4_INNER_PCP]		= { 60,   3}, +	[VCAP_IS1_HK_IP4_IP4]			= { 63,   1}, +	[VCAP_IS1_HK_IP4_L3_FRAGMENT]		= { 64,   1}, +	[VCAP_IS1_HK_IP4_L3_FRAG_OFS_GT0]	= { 65,   1}, +	[VCAP_IS1_HK_IP4_L3_OPTIONS]		= { 66,   1}, +	[VCAP_IS1_HK_IP4_L3_DSCP]		= { 67,   6}, +	[VCAP_IS1_HK_IP4_L3_IP4_DIP]		= { 73,  32}, +	[VCAP_IS1_HK_IP4_L3_IP4_SIP]		= {105,  32}, +	[VCAP_IS1_HK_IP4_L3_PROTO]		= {137,   8}, +	[VCAP_IS1_HK_IP4_TCP_UDP]		= {145,   1}, +	[VCAP_IS1_HK_IP4_TCP]			= {146,   1}, +	[VCAP_IS1_HK_IP4_L4_RNG]		= {147,   8}, +	[VCAP_IS1_HK_IP4_IP_PAYLOAD_S1_5TUPLE]	= {155,  32}, +}; + +static const struct vcap_field vsc9953_vcap_is1_actions[] = { +	[VCAP_IS1_ACT_DSCP_ENA]			= {  0,  1}, +	[VCAP_IS1_ACT_DSCP_VAL]			= {  1,  6}, +	[VCAP_IS1_ACT_QOS_ENA]			= {  7,  1}, +	[VCAP_IS1_ACT_QOS_VAL]			= {  8,  3}, +	[VCAP_IS1_ACT_DP_ENA]			= { 11,  1}, +	[VCAP_IS1_ACT_DP_VAL]			= { 12,  1}, +	[VCAP_IS1_ACT_PAG_OVERRIDE_MASK]	= { 13,  8}, +	[VCAP_IS1_ACT_PAG_VAL]			= { 21,  8}, +	[VCAP_IS1_ACT_RSV]			= { 29, 11}, +	[VCAP_IS1_ACT_VID_REPLACE_ENA]		= { 40,  1}, +	[VCAP_IS1_ACT_VID_ADD_VAL]		= { 41, 12}, +	[VCAP_IS1_ACT_FID_SEL]			= { 53,  2}, +	[VCAP_IS1_ACT_FID_VAL]			= { 55, 13}, +	[VCAP_IS1_ACT_PCP_DEI_ENA]		= { 68,  1}, +	[VCAP_IS1_ACT_PCP_VAL]			= { 69,  3}, +	[VCAP_IS1_ACT_DEI_VAL]			= { 72,  1}, +	[VCAP_IS1_ACT_VLAN_POP_CNT_ENA]		= { 73,  1}, +	[VCAP_IS1_ACT_VLAN_POP_CNT]		= { 74,  2}, +	[VCAP_IS1_ACT_CUSTOM_ACE_TYPE_ENA]	= { 76,  4}, +	[VCAP_IS1_ACT_HIT_STICKY]		= { 80,  1}, +}; +  static struct vcap_field vsc9953_vcap_is2_keys[] = {  	/* Common: 41 bits */  	[VCAP_IS2_TYPE]				= {  0,   4}, @@ -694,15 +811,32 @@ static struct vcap_field vsc9953_vcap_is2_actions[] = {  	[VCAP_IS2_ACT_HIT_CNT]			= { 50, 32},  }; -static const struct vcap_props vsc9953_vcap_props[] = { +static struct vcap_props vsc9953_vcap_props[] = { +	[VCAP_ES0] = { +		.action_type_width = 0, +		.action_table = { +			[ES0_ACTION_TYPE_NORMAL] = { +				.width = 73, /* HIT_STICKY not included */ +				.count = 1, +			}, +		}, +		.target = S0, +		.keys = vsc9953_vcap_es0_keys, +		.actions = vsc9953_vcap_es0_actions, +	}, +	[VCAP_IS1] = { +		.action_type_width = 0, +		.action_table = { +			[IS1_ACTION_TYPE_NORMAL] = { +				.width = 80, /* HIT_STICKY not included */ +				.count = 4, +			}, +		}, +		.target = S1, +		.keys = vsc9953_vcap_is1_keys, +		.actions = vsc9953_vcap_is1_actions, +	},  	[VCAP_IS2] = { -		.tg_width = 2, -		.sw_count = 4, -		.entry_count = VSC9953_VCAP_IS2_CNT, -		.entry_width = VSC9953_VCAP_IS2_ENTRY_WIDTH, -		.action_count = VSC9953_VCAP_IS2_CNT + -				VSC9953_VCAP_PORT_CNT + 2, -		.action_width = 101,  		.action_type_width = 1,  		.action_table = {  			[IS2_ACTION_TYPE_NORMAL] = { @@ -714,8 +848,9 @@ static const struct vcap_props vsc9953_vcap_props[] = {  				.count = 4  			},  		}, -		.counter_words = 4, -		.counter_width = 32, +		.target = S2, +		.keys = vsc9953_vcap_is2_keys, +		.actions = vsc9953_vcap_is2_actions,  	},  }; @@ -819,6 +954,10 @@ out:  	return err;  } +/* CORE_ENA is in SYS:SYSTEM:RESET_CFG + * MEM_INIT is in SYS:SYSTEM:RESET_CFG + * MEM_ENA is in SYS:SYSTEM:RESET_CFG + */  static int vsc9953_reset(struct ocelot *ocelot)  {  	int val, err; @@ -834,8 +973,8 @@ static int vsc9953_reset(struct ocelot *ocelot)  	}  	/* initialize switch mem ~40us */ -	ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_INIT, 1);  	ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_ENA, 1); +	ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_INIT, 1);  	err = readx_poll_timeout(vsc9953_sys_ram_init_status, ocelot, val, !val,  				 VSC9953_SYS_RAMINIT_SLEEP, @@ -846,7 +985,6 @@ static int vsc9953_reset(struct ocelot *ocelot)  	}  	/* enable switch core */ -	ocelot_field_write(ocelot, SYS_RESET_CFG_MEM_ENA, 1);  	ocelot_field_write(ocelot, SYS_RESET_CFG_CORE_ENA, 1);  	return 0; @@ -922,6 +1060,8 @@ static u16 vsc9953_wm_enc(u16 value)  static const struct ocelot_ops vsc9953_ops = {  	.reset			= vsc9953_reset,  	.wm_enc			= vsc9953_wm_enc, +	.port_to_netdev		= felix_port_to_netdev, +	.netdev_to_port		= felix_netdev_to_port,  };  static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot) @@ -962,18 +1102,27 @@ 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 phy_device *pcs;  		int addr = port + 4; +		struct mdio_device *pcs; +		struct lynx_pcs *lynx; + +		if (dsa_is_unused_port(felix->ds, port)) +			continue;  		if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)  			continue; -		pcs = get_phy_device(felix->imdio, addr, false); +		pcs = mdio_device_create(felix->imdio, addr);  		if (IS_ERR(pcs))  			continue; -		pcs->interface = ocelot_port->phy_mode; -		felix->pcs[port] = pcs; +		lynx = lynx_pcs_create(pcs); +		if (!lynx) { +			mdio_device_free(pcs); +			continue; +		} + +		felix->pcs[port] = lynx;  		dev_info(dev, "Found PCS at internal MDIO address %d\n", addr);  	} @@ -981,11 +1130,30 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)  	return 0;  } +static void vsc9953_mdio_bus_free(struct ocelot *ocelot) +{ +	struct felix *felix = ocelot_to_felix(ocelot); +	int port; + +	for (port = 0; port < ocelot->num_phys_ports; port++) { +		struct lynx_pcs *pcs = felix->pcs[port]; + +		if (!pcs) +			continue; + +		mdio_device_free(pcs->mdio); +		lynx_pcs_destroy(pcs); +	} +	mdiobus_unregister(felix->imdio); +} +  static void vsc9953_xmit_template_populate(struct ocelot *ocelot, int port)  {  	struct ocelot_port *ocelot_port = ocelot->ports[port];  	u8 *template = ocelot_port->xmit_template;  	u64 bypass, dest, src; +	__be32 *prefix; +	u8 *injection;  	/* Set the source port as the CPU port module and not the  	 * NPI port @@ -994,9 +1162,14 @@ static void vsc9953_xmit_template_populate(struct ocelot *ocelot, int port)  	dest = BIT(port);  	bypass = true; -	packing(template, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); -	packing(template, &dest,    67,  57, OCELOT_TAG_LEN, PACK, 0); -	packing(template, &src,     46,  43, OCELOT_TAG_LEN, PACK, 0); +	injection = template + OCELOT_SHORT_PREFIX_LEN; +	prefix = (__be32 *)template; + +	packing(injection, &bypass, 127, 127, OCELOT_TAG_LEN, PACK, 0); +	packing(injection, &dest,    67,  57, OCELOT_TAG_LEN, PACK, 0); +	packing(injection, &src,     46,  43, OCELOT_TAG_LEN, PACK, 0); + +	*prefix = cpu_to_be32(0x88800005);  }  static const struct felix_info seville_info_vsc9953 = { @@ -1007,17 +1180,12 @@ static const struct felix_info seville_info_vsc9953 = {  	.ops			= &vsc9953_ops,  	.stats_layout		= vsc9953_stats_layout,  	.num_stats		= ARRAY_SIZE(vsc9953_stats_layout), -	.vcap_is2_keys		= vsc9953_vcap_is2_keys, -	.vcap_is2_actions	= vsc9953_vcap_is2_actions,  	.vcap			= vsc9953_vcap_props, -	.shared_queue_sz	= 2048 * 1024, +	.shared_queue_sz	= 256 * 1024,  	.num_mact_rows		= 2048,  	.num_ports		= 10,  	.mdio_bus_alloc		= vsc9953_mdio_bus_alloc, -	.mdio_bus_free		= vsc9959_mdio_bus_free, -	.pcs_config		= vsc9959_pcs_config, -	.pcs_link_up		= vsc9959_pcs_link_up, -	.pcs_link_state		= vsc9959_pcs_link_state, +	.mdio_bus_free		= vsc9953_mdio_bus_free,  	.phylink_validate	= vsc9953_phylink_validate,  	.prevalidate_phy_mode	= vsc9953_prevalidate_phy_mode,  	.xmit_template_populate	= vsc9953_xmit_template_populate, @@ -1096,7 +1264,7 @@ static const struct of_device_id seville_of_match[] = {  };  MODULE_DEVICE_TABLE(of, seville_of_match); -struct platform_driver seville_vsc9953_driver = { +static struct platform_driver seville_vsc9953_driver = {  	.probe		= seville_probe,  	.remove		= seville_remove,  	.driver = { @@ -1104,3 +1272,7 @@ struct platform_driver seville_vsc9953_driver = {  		.of_match_table	= of_match_ptr(seville_of_match),  	},  }; +module_platform_driver(seville_vsc9953_driver); + +MODULE_DESCRIPTION("Seville Switch driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index f1e484477e35..53064e0e1618 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1294,10 +1294,14 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,  }  static int -qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) +qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +			  struct switchdev_trans *trans)  {  	struct qca8k_priv *priv = ds->priv; +	if (switchdev_trans_ph_prepare(trans)) +		return 0; +  	if (vlan_filtering) {  		qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),  			  QCA8K_PORT_LOOKUP_VLAN_MODE, diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c index fae188c60191..8e49d4f85d48 100644 --- a/drivers/net/dsa/realtek-smi-core.c +++ b/drivers/net/dsa/realtek-smi-core.c @@ -394,9 +394,10 @@ static int realtek_smi_probe(struct platform_device *pdev)  	var = of_device_get_match_data(dev);  	np = dev->of_node; -	smi = devm_kzalloc(dev, sizeof(*smi), GFP_KERNEL); +	smi = devm_kzalloc(dev, sizeof(*smi) + var->chip_data_sz, GFP_KERNEL);  	if (!smi)  		return -ENOMEM; +	smi->chip_data = (void *)smi + sizeof(*smi);  	smi->map = devm_regmap_init(dev, NULL, smi,  				    &realtek_smi_mdio_regmap_config);  	if (IS_ERR(smi->map)) { diff --git a/drivers/net/dsa/realtek-smi-core.h b/drivers/net/dsa/realtek-smi-core.h index 9a63b51e1d82..6b6a3dec0984 100644 --- a/drivers/net/dsa/realtek-smi-core.h +++ b/drivers/net/dsa/realtek-smi-core.h @@ -25,6 +25,9 @@ struct rtl8366_mib_counter {  	const char	*name;  }; +/** + * struct rtl8366_vlan_mc - Virtual LAN member configuration + */  struct rtl8366_vlan_mc {  	u16	vid;  	u16	untag; @@ -68,6 +71,7 @@ struct realtek_smi {  	int			vlan4k_enabled;  	char			buf[4096]; +	void			*chip_data; /* Per-chip extra variant data */  };  /** @@ -108,6 +112,7 @@ struct realtek_smi_variant {  	unsigned int clk_delay;  	u8 cmd_read;  	u8 cmd_write; +	size_t chip_data_sz;  };  /* SMI core calls */ @@ -119,7 +124,6 @@ int realtek_smi_setup_mdio(struct realtek_smi *smi);  int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used);  int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member,  		     u32 untag, u32 fid); -int rtl8366_get_pvid(struct realtek_smi *smi, int port, int *val);  int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port,  		     unsigned int vid);  int rtl8366_enable_vlan4k(struct realtek_smi *smi, bool enable); @@ -127,7 +131,8 @@ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable);  int rtl8366_reset_vlan(struct realtek_smi *smi);  int rtl8366_init_vlan(struct realtek_smi *smi);  int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, -			   bool vlan_filtering); +			   bool vlan_filtering, +			   struct switchdev_trans *trans);  int rtl8366_vlan_prepare(struct dsa_switch *ds, int port,  			 const struct switchdev_obj_port_vlan *vlan);  void rtl8366_vlan_add(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/rtl8366.c b/drivers/net/dsa/rtl8366.c index a8c5a934c3d3..307466b90489 100644 --- a/drivers/net/dsa/rtl8366.c +++ b/drivers/net/dsa/rtl8366.c @@ -36,12 +36,113 @@ int rtl8366_mc_is_used(struct realtek_smi *smi, int mc_index, int *used)  }  EXPORT_SYMBOL_GPL(rtl8366_mc_is_used); +/** + * rtl8366_obtain_mc() - retrieve or allocate a VLAN member configuration + * @smi: the Realtek SMI device instance + * @vid: the VLAN ID to look up or allocate + * @vlanmc: the pointer will be assigned to a pointer to a valid member config + * if successful + * @return: index of a new member config or negative error number + */ +static int rtl8366_obtain_mc(struct realtek_smi *smi, int vid, +			     struct rtl8366_vlan_mc *vlanmc) +{ +	struct rtl8366_vlan_4k vlan4k; +	int ret; +	int i; + +	/* Try to find an existing member config entry for this VID */ +	for (i = 0; i < smi->num_vlan_mc; i++) { +		ret = smi->ops->get_vlan_mc(smi, i, vlanmc); +		if (ret) { +			dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", +				i, vid); +			return ret; +		} + +		if (vid == vlanmc->vid) +			return i; +	} + +	/* We have no MC entry for this VID, try to find an empty one */ +	for (i = 0; i < smi->num_vlan_mc; i++) { +		ret = smi->ops->get_vlan_mc(smi, i, vlanmc); +		if (ret) { +			dev_err(smi->dev, "error searching for VLAN MC %d for VID %d\n", +				i, vid); +			return ret; +		} + +		if (vlanmc->vid == 0 && vlanmc->member == 0) { +			/* Update the entry from the 4K table */ +			ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); +			if (ret) { +				dev_err(smi->dev, "error looking for 4K VLAN MC %d for VID %d\n", +					i, vid); +				return ret; +			} + +			vlanmc->vid = vid; +			vlanmc->member = vlan4k.member; +			vlanmc->untag = vlan4k.untag; +			vlanmc->fid = vlan4k.fid; +			ret = smi->ops->set_vlan_mc(smi, i, vlanmc); +			if (ret) { +				dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", +					i, vid); +				return ret; +			} + +			dev_dbg(smi->dev, "created new MC at index %d for VID %d\n", +				i, vid); +			return i; +		} +	} + +	/* MC table is full, try to find an unused entry and replace it */ +	for (i = 0; i < smi->num_vlan_mc; i++) { +		int used; + +		ret = rtl8366_mc_is_used(smi, i, &used); +		if (ret) +			return ret; + +		if (!used) { +			/* Update the entry from the 4K table */ +			ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); +			if (ret) +				return ret; + +			vlanmc->vid = vid; +			vlanmc->member = vlan4k.member; +			vlanmc->untag = vlan4k.untag; +			vlanmc->fid = vlan4k.fid; +			ret = smi->ops->set_vlan_mc(smi, i, vlanmc); +			if (ret) { +				dev_err(smi->dev, "unable to set/update VLAN MC %d for VID %d\n", +					i, vid); +				return ret; +			} +			dev_dbg(smi->dev, "recycled MC at index %i for VID %d\n", +				i, vid); +			return i; +		} +	} + +	dev_err(smi->dev, "all VLAN member configurations are in use\n"); +	return -ENOSPC; +} +  int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member,  		     u32 untag, u32 fid)  { +	struct rtl8366_vlan_mc vlanmc;  	struct rtl8366_vlan_4k vlan4k; +	int mc;  	int ret; -	int i; + +	if (!smi->ops->is_vlan_valid(smi, vid)) +		return -EINVAL;  	dev_dbg(smi->dev,  		"setting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n", @@ -63,133 +164,58 @@ int rtl8366_set_vlan(struct realtek_smi *smi, int vid, u32 member,  		"resulting VLAN%d 4k members: 0x%02x, untagged: 0x%02x\n",  		vid, vlan4k.member, vlan4k.untag); -	/* Try to find an existing MC entry for this VID */ -	for (i = 0; i < smi->num_vlan_mc; i++) { -		struct rtl8366_vlan_mc vlanmc; - -		ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); -		if (ret) -			return ret; - -		if (vid == vlanmc.vid) { -			/* update the MC entry */ -			vlanmc.member |= member; -			vlanmc.untag |= untag; -			vlanmc.fid = fid; - -			ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); +	/* Find or allocate a member config for this VID */ +	ret = rtl8366_obtain_mc(smi, vid, &vlanmc); +	if (ret < 0) +		return ret; +	mc = ret; -			dev_dbg(smi->dev, -				"resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", -				vid, vlanmc.member, vlanmc.untag); +	/* Update the MC entry */ +	vlanmc.member |= member; +	vlanmc.untag |= untag; +	vlanmc.fid = fid; -			break; -		} -	} +	/* Commit updates to the MC entry */ +	ret = smi->ops->set_vlan_mc(smi, mc, &vlanmc); +	if (ret) +		dev_err(smi->dev, "failed to commit changes to VLAN MC index %d for VID %d\n", +			mc, vid); +	else +		dev_dbg(smi->dev, +			"resulting VLAN%d MC members: 0x%02x, untagged: 0x%02x\n", +			vid, vlanmc.member, vlanmc.untag);  	return ret;  }  EXPORT_SYMBOL_GPL(rtl8366_set_vlan); -int rtl8366_get_pvid(struct realtek_smi *smi, int port, int *val) -{ -	struct rtl8366_vlan_mc vlanmc; -	int ret; -	int index; - -	ret = smi->ops->get_mc_index(smi, port, &index); -	if (ret) -		return ret; - -	ret = smi->ops->get_vlan_mc(smi, index, &vlanmc); -	if (ret) -		return ret; - -	*val = vlanmc.vid; -	return 0; -} -EXPORT_SYMBOL_GPL(rtl8366_get_pvid); -  int rtl8366_set_pvid(struct realtek_smi *smi, unsigned int port,  		     unsigned int vid)  {  	struct rtl8366_vlan_mc vlanmc; -	struct rtl8366_vlan_4k vlan4k; +	int mc;  	int ret; -	int i; - -	/* Try to find an existing MC entry for this VID */ -	for (i = 0; i < smi->num_vlan_mc; i++) { -		ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); -		if (ret) -			return ret; - -		if (vid == vlanmc.vid) { -			ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); -			if (ret) -				return ret; - -			ret = smi->ops->set_mc_index(smi, port, i); -			return ret; -		} -	} - -	/* We have no MC entry for this VID, try to find an empty one */ -	for (i = 0; i < smi->num_vlan_mc; i++) { -		ret = smi->ops->get_vlan_mc(smi, i, &vlanmc); -		if (ret) -			return ret; - -		if (vlanmc.vid == 0 && vlanmc.member == 0) { -			/* Update the entry from the 4K table */ -			ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); -			if (ret) -				return ret; - -			vlanmc.vid = vid; -			vlanmc.member = vlan4k.member; -			vlanmc.untag = vlan4k.untag; -			vlanmc.fid = vlan4k.fid; -			ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); -			if (ret) -				return ret; - -			ret = smi->ops->set_mc_index(smi, port, i); -			return ret; -		} -	} - -	/* MC table is full, try to find an unused entry and replace it */ -	for (i = 0; i < smi->num_vlan_mc; i++) { -		int used; - -		ret = rtl8366_mc_is_used(smi, i, &used); -		if (ret) -			return ret; -		if (!used) { -			/* Update the entry from the 4K table */ -			ret = smi->ops->get_vlan_4k(smi, vid, &vlan4k); -			if (ret) -				return ret; +	if (!smi->ops->is_vlan_valid(smi, vid)) +		return -EINVAL; -			vlanmc.vid = vid; -			vlanmc.member = vlan4k.member; -			vlanmc.untag = vlan4k.untag; -			vlanmc.fid = vlan4k.fid; -			ret = smi->ops->set_vlan_mc(smi, i, &vlanmc); -			if (ret) -				return ret; +	/* Find or allocate a member config for this VID */ +	ret = rtl8366_obtain_mc(smi, vid, &vlanmc); +	if (ret < 0) +		return ret; +	mc = ret; -			ret = smi->ops->set_mc_index(smi, port, i); -			return ret; -		} +	ret = smi->ops->set_mc_index(smi, port, mc); +	if (ret) { +		dev_err(smi->dev, "set PVID: failed to set MC index %d for port %d\n", +			mc, port); +		return ret;  	} -	dev_err(smi->dev, -		"all VLAN member configurations are in use\n"); +	dev_dbg(smi->dev, "set PVID: the PVID for port %d set to %d using existing MC index %d\n", +		port, vid, mc); -	return -ENOSPC; +	return 0;  }  EXPORT_SYMBOL_GPL(rtl8366_set_pvid); @@ -314,15 +340,20 @@ int rtl8366_init_vlan(struct realtek_smi *smi)  }  EXPORT_SYMBOL_GPL(rtl8366_init_vlan); -int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering) +int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, +			   struct switchdev_trans *trans)  {  	struct realtek_smi *smi = ds->priv;  	struct rtl8366_vlan_4k vlan4k;  	int ret;  	/* Use VLAN nr port + 1 since VLAN0 is not valid */ -	if (!smi->ops->is_vlan_valid(smi, port + 1)) -		return -EINVAL; +	if (switchdev_trans_ph_prepare(trans)) { +		if (!smi->ops->is_vlan_valid(smi, port + 1)) +			return -EINVAL; + +		return 0; +	}  	dev_info(smi->dev, "%s filtering on port %d\n",  		 vlan_filtering ? "enable" : "disable", @@ -389,7 +420,8 @@ void rtl8366_vlan_add(struct dsa_switch *ds, int port,  		if (!smi->ops->is_vlan_valid(smi, vid))  			return; -	dev_info(smi->dev, "add VLAN on port %d, %s, %s\n", +	dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n", +		 vlan->vid_begin,  		 port,  		 untagged ? "untagged" : "tagged",  		 pvid ? " PVID" : "no PVID"); @@ -398,34 +430,29 @@ void rtl8366_vlan_add(struct dsa_switch *ds, int port,  		dev_err(smi->dev, "port is DSA or CPU port\n");  	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { -		int pvid_val = 0; - -		dev_info(smi->dev, "add VLAN %04x\n", vid);  		member |= BIT(port);  		if (untagged)  			untag |= BIT(port); -		/* To ensure that we have a valid MC entry for this VLAN, -		 * initialize the port VLAN ID here. -		 */ -		ret = rtl8366_get_pvid(smi, port, &pvid_val); -		if (ret < 0) { -			dev_err(smi->dev, "could not lookup PVID for port %d\n", -				port); -			return; -		} -		if (pvid_val == 0) { -			ret = rtl8366_set_pvid(smi, port, vid); -			if (ret < 0) -				return; -		} -  		ret = rtl8366_set_vlan(smi, vid, member, untag, 0);  		if (ret)  			dev_err(smi->dev,  				"failed to set up VLAN %04x",  				vid); + +		if (!pvid) +			continue; + +		ret = rtl8366_set_pvid(smi, port, vid); +		if (ret) +			dev_err(smi->dev, +				"failed to set PVID on port %d to VLAN %04x", +				port, vid); + +		if (!ret) +			dev_dbg(smi->dev, "VLAN add: added VLAN %d with PVID on port %d\n", +				vid, port);  	}  }  EXPORT_SYMBOL_GPL(rtl8366_vlan_add); diff --git a/drivers/net/dsa/rtl8366rb.c b/drivers/net/dsa/rtl8366rb.c index 48f1ff746799..cfe56960f44b 100644 --- a/drivers/net/dsa/rtl8366rb.c +++ b/drivers/net/dsa/rtl8366rb.c @@ -35,7 +35,7 @@  #define RTL8366RB_SGCR_MAX_LENGTH_1522		RTL8366RB_SGCR_MAX_LENGTH(0x0)  #define RTL8366RB_SGCR_MAX_LENGTH_1536		RTL8366RB_SGCR_MAX_LENGTH(0x1)  #define RTL8366RB_SGCR_MAX_LENGTH_1552		RTL8366RB_SGCR_MAX_LENGTH(0x2) -#define RTL8366RB_SGCR_MAX_LENGTH_9216		RTL8366RB_SGCR_MAX_LENGTH(0x3) +#define RTL8366RB_SGCR_MAX_LENGTH_16000		RTL8366RB_SGCR_MAX_LENGTH(0x3)  #define RTL8366RB_SGCR_EN_VLAN			BIT(13)  #define RTL8366RB_SGCR_EN_VLAN_4KTB		BIT(14) @@ -311,6 +311,14 @@  #define RTL8366RB_GREEN_FEATURE_TX	BIT(0)  #define RTL8366RB_GREEN_FEATURE_RX	BIT(2) +/** + * struct rtl8366rb - RTL8366RB-specific data + * @max_mtu: per-port max MTU setting + */ +struct rtl8366rb { +	unsigned int max_mtu[RTL8366RB_NUM_PORTS]; +}; +  static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {  	{ 0,  0, 4, "IfInOctets"				},  	{ 0,  4, 4, "EtherStatsOctets"				}, @@ -712,6 +720,7 @@ static int rtl8366rb_setup(struct dsa_switch *ds)  {  	struct realtek_smi *smi = ds->priv;  	const u16 *jam_table; +	struct rtl8366rb *rb;  	u32 chip_ver = 0;  	u32 chip_id = 0;  	int jam_size; @@ -719,6 +728,8 @@ static int rtl8366rb_setup(struct dsa_switch *ds)  	int ret;  	int i; +	rb = smi->chip_data; +  	ret = regmap_read(smi->map, RTL8366RB_CHIP_ID_REG, &chip_id);  	if (ret) {  		dev_err(smi->dev, "unable to read chip id\n"); @@ -868,6 +879,9 @@ static int rtl8366rb_setup(struct dsa_switch *ds)  				 RTL8366RB_SGCR_MAX_LENGTH_1536);  	if (ret)  		return ret; +	for (i = 0; i < RTL8366RB_NUM_PORTS; i++) +		/* layer 2 size, see rtl8366rb_change_mtu() */ +		rb->max_mtu[i] = 1532;  	/* Enable learning for all ports */  	ret = regmap_write(smi->map, RTL8366RB_SSCR0, 0); @@ -969,8 +983,10 @@ static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct dsa_switch *ds,  	return DSA_TAG_PROTO_RTL4_A;  } -static void rtl8366rb_adjust_link(struct dsa_switch *ds, int port, -				  struct phy_device *phydev) +static void +rtl8366rb_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, +		      phy_interface_t interface, struct phy_device *phydev, +		      int speed, int duplex, bool tx_pause, bool rx_pause)  {  	struct realtek_smi *smi = ds->priv;  	int ret; @@ -978,25 +994,52 @@ static void rtl8366rb_adjust_link(struct dsa_switch *ds, int port,  	if (port != smi->cpu_port)  		return; -	dev_info(smi->dev, "adjust link on CPU port (%d)\n", port); +	dev_dbg(smi->dev, "MAC link up on CPU port (%d)\n", port);  	/* Force the fixed CPU port into 1Gbit mode, no autonegotiation */  	ret = regmap_update_bits(smi->map, RTL8366RB_MAC_FORCE_CTRL_REG,  				 BIT(port), BIT(port)); -	if (ret) +	if (ret) { +		dev_err(smi->dev, "failed to force 1Gbit on CPU port\n");  		return; +	}  	ret = regmap_update_bits(smi->map, RTL8366RB_PAACR2,  				 0xFF00U,  				 RTL8366RB_PAACR_CPU_PORT << 8); -	if (ret) +	if (ret) { +		dev_err(smi->dev, "failed to set PAACR on CPU port\n");  		return; +	}  	/* Enable the CPU port */  	ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port),  				 0); -	if (ret) +	if (ret) { +		dev_err(smi->dev, "failed to enable the CPU port\n");  		return; +	} +} + +static void +rtl8366rb_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, +			phy_interface_t interface) +{ +	struct realtek_smi *smi = ds->priv; +	int ret; + +	if (port != smi->cpu_port) +		return; + +	dev_dbg(smi->dev, "MAC link down on CPU port (%d)\n", port); + +	/* Disable the CPU port */ +	ret = regmap_update_bits(smi->map, RTL8366RB_PECR, BIT(port), +				 BIT(port)); +	if (ret) { +		dev_err(smi->dev, "failed to disable the CPU port\n"); +		return; +	}  }  static void rb8366rb_set_port_led(struct realtek_smi *smi, @@ -1077,6 +1120,56 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port)  	rb8366rb_set_port_led(smi, port, false);  } +static int rtl8366rb_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ +	struct realtek_smi *smi = ds->priv; +	struct rtl8366rb *rb; +	unsigned int max_mtu; +	u32 len; +	int i; + +	/* Cache the per-port MTU setting */ +	rb = smi->chip_data; +	rb->max_mtu[port] = new_mtu; + +	/* Roof out the MTU for the entire switch to the greatest +	 * common denominator: the biggest set for any one port will +	 * be the biggest MTU for the switch. +	 * +	 * The first setting, 1522 bytes, is max IP packet 1500 bytes, +	 * plus ethernet header, 1518 bytes, plus CPU tag, 4 bytes. +	 * This function should consider the parameter an SDU, so the +	 * MTU passed for this setting is 1518 bytes. The same logic +	 * of subtracting the DSA tag of 4 bytes apply to the other +	 * settings. +	 */ +	max_mtu = 1518; +	for (i = 0; i < RTL8366RB_NUM_PORTS; i++) { +		if (rb->max_mtu[i] > max_mtu) +			max_mtu = rb->max_mtu[i]; +	} +	if (max_mtu <= 1518) +		len = RTL8366RB_SGCR_MAX_LENGTH_1522; +	else if (max_mtu > 1518 && max_mtu <= 1532) +		len = RTL8366RB_SGCR_MAX_LENGTH_1536; +	else if (max_mtu > 1532 && max_mtu <= 1548) +		len = RTL8366RB_SGCR_MAX_LENGTH_1552; +	else +		len = RTL8366RB_SGCR_MAX_LENGTH_16000; + +	return regmap_update_bits(smi->map, RTL8366RB_SGCR, +				  RTL8366RB_SGCR_MAX_LENGTH_MASK, +				  len); +} + +static int rtl8366rb_max_mtu(struct dsa_switch *ds, int port) +{ +	/* The max MTU is 16000 bytes, so we subtract the CPU tag +	 * and the max presented to the system is 15996 bytes. +	 */ +	return 15996; +} +  static int rtl8366rb_get_vlan_4k(struct realtek_smi *smi, u32 vid,  				 struct rtl8366_vlan_4k *vlan4k)  { @@ -1255,7 +1348,7 @@ static bool rtl8366rb_is_vlan_valid(struct realtek_smi *smi, unsigned int vlan)  	if (smi->vlan4k_enabled)  		max = RTL8366RB_NUM_VIDS - 1; -	if (vlan == 0 || vlan >= max) +	if (vlan == 0 || vlan > max)  		return false;  	return true; @@ -1405,7 +1498,8 @@ static int rtl8366rb_detect(struct realtek_smi *smi)  static const struct dsa_switch_ops rtl8366rb_switch_ops = {  	.get_tag_protocol = rtl8366_get_tag_protocol,  	.setup = rtl8366rb_setup, -	.adjust_link = rtl8366rb_adjust_link, +	.phylink_mac_link_up = rtl8366rb_mac_link_up, +	.phylink_mac_link_down = rtl8366rb_mac_link_down,  	.get_strings = rtl8366_get_strings,  	.get_ethtool_stats = rtl8366_get_ethtool_stats,  	.get_sset_count = rtl8366_get_sset_count, @@ -1415,6 +1509,8 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = {  	.port_vlan_del = rtl8366_vlan_del,  	.port_enable = rtl8366rb_port_enable,  	.port_disable = rtl8366rb_port_disable, +	.port_change_mtu = rtl8366rb_change_mtu, +	.port_max_mtu = rtl8366rb_max_mtu,  };  static const struct realtek_smi_ops rtl8366rb_smi_ops = { @@ -1439,5 +1535,6 @@ const struct realtek_smi_variant rtl8366rb_variant = {  	.clk_delay = 10,  	.cmd_read = 0xa9,  	.cmd_write = 0xa8, +	.chip_data_sz = sizeof(struct rtl8366rb),  };  EXPORT_SYMBOL_GPL(rtl8366rb_variant); diff --git a/drivers/net/dsa/sja1105/Makefile b/drivers/net/dsa/sja1105/Makefile index c88e56a29db8..a860e3a910be 100644 --- a/drivers/net/dsa/sja1105/Makefile +++ b/drivers/net/dsa/sja1105/Makefile @@ -6,6 +6,7 @@ sja1105-objs := \      sja1105_main.o \      sja1105_flower.o \      sja1105_ethtool.o \ +    sja1105_devlink.o \      sja1105_clocking.o \      sja1105_static_config.o \      sja1105_dynamic_config.o \ diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index ba70b40a9a95..4ebc4a5a7b35 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -210,15 +210,15 @@ struct sja1105_private {  	struct dsa_switch *ds;  	struct list_head dsa_8021q_vlans;  	struct list_head bridge_vlans; -	struct list_head crosschip_links;  	struct sja1105_flow_block flow_block;  	struct sja1105_port ports[SJA1105_NUM_PORTS];  	/* Serializes transmission of management frames so that  	 * the switch doesn't confuse them with one another.  	 */  	struct mutex mgmt_lock; -	bool expect_dsa_8021q; +	struct dsa_8021q_context *dsa_8021q_ctx;  	enum sja1105_vlan_state vlan_state; +	struct devlink_region **regions;  	struct sja1105_cbs_entry *cbs;  	struct sja1105_tagger_data tagger_data;  	struct sja1105_ptp_data ptp_data; @@ -245,9 +245,21 @@ enum sja1105_reset_reason {  int sja1105_static_config_reload(struct sja1105_private *priv,  				 enum sja1105_reset_reason reason); - +int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, +			   struct switchdev_trans *trans);  void sja1105_frame_memory_partitioning(struct sja1105_private *priv); +/* From sja1105_devlink.c */ +int sja1105_devlink_setup(struct dsa_switch *ds); +void sja1105_devlink_teardown(struct dsa_switch *ds); +int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id, +			      struct devlink_param_gset_ctx *ctx); +int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id, +			      struct devlink_param_gset_ctx *ctx); +int sja1105_devlink_info_get(struct dsa_switch *ds, +			     struct devlink_info_req *req, +			     struct netlink_ext_ack *extack); +  /* From sja1105_spi.c */  int sja1105_xfer_buf(const struct sja1105_private *priv,  		     sja1105_spi_rw_mode_t rw, u64 reg_addr, @@ -258,6 +270,8 @@ int sja1105_xfer_u32(const struct sja1105_private *priv,  int sja1105_xfer_u64(const struct sja1105_private *priv,  		     sja1105_spi_rw_mode_t rw, u64 reg_addr, u64 *value,  		     struct ptp_system_timestamp *ptp_sts); +int static_config_buf_prepare_for_upload(struct sja1105_private *priv, +					 void *config_buf, int buf_len);  int sja1105_static_config_upload(struct sja1105_private *priv);  int sja1105_inhibit_tx(const struct sja1105_private *priv,  		       unsigned long port_bitmap, bool tx_inhibited); diff --git a/drivers/net/dsa/sja1105/sja1105_devlink.c b/drivers/net/dsa/sja1105/sja1105_devlink.c new file mode 100644 index 000000000000..4a2ec395bcb0 --- /dev/null +++ b/drivers/net/dsa/sja1105/sja1105_devlink.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> + * Copyright 2020 NXP Semiconductors + */ +#include "sja1105.h" + +/* Since devlink regions have a fixed size and the static config has a variable + * size, we need to calculate the maximum possible static config size by + * creating a dummy config with all table entries populated to the max, and get + * its packed length. This is done dynamically as opposed to simply hardcoding + * a number, since currently not all static config tables are implemented, so + * we are avoiding a possible code desynchronization. + */ +static size_t sja1105_static_config_get_max_size(struct sja1105_private *priv) +{ +	struct sja1105_static_config config; +	enum sja1105_blk_idx blk_idx; +	int rc; + +	rc = sja1105_static_config_init(&config, +					priv->info->static_ops, +					priv->info->device_id); +	if (rc) +		return 0; + +	for (blk_idx = 0; blk_idx < BLK_IDX_MAX; blk_idx++) { +		struct sja1105_table *table = &config.tables[blk_idx]; + +		table->entry_count = table->ops->max_entry_count; +	} + +	return sja1105_static_config_get_length(&config); +} + +static int +sja1105_region_static_config_snapshot(struct devlink *dl, +				      const struct devlink_region_ops *ops, +				      struct netlink_ext_ack *extack, +				      u8 **data) +{ +	struct dsa_switch *ds = dsa_devlink_to_ds(dl); +	struct sja1105_private *priv = ds->priv; +	size_t max_len, len; + +	len = sja1105_static_config_get_length(&priv->static_config); +	max_len = sja1105_static_config_get_max_size(priv); + +	*data = kcalloc(max_len, sizeof(u8), GFP_KERNEL); +	if (!*data) +		return -ENOMEM; + +	return static_config_buf_prepare_for_upload(priv, *data, len); +} + +static struct devlink_region_ops sja1105_region_static_config_ops = { +	.name = "static-config", +	.snapshot = sja1105_region_static_config_snapshot, +	.destructor = kfree, +}; + +enum sja1105_region_id { +	SJA1105_REGION_STATIC_CONFIG = 0, +}; + +struct sja1105_region { +	const struct devlink_region_ops *ops; +	size_t (*get_size)(struct sja1105_private *priv); +}; + +static struct sja1105_region sja1105_regions[] = { +	[SJA1105_REGION_STATIC_CONFIG] = { +		.ops = &sja1105_region_static_config_ops, +		.get_size = sja1105_static_config_get_max_size, +	}, +}; + +static int sja1105_setup_devlink_regions(struct dsa_switch *ds) +{ +	int i, num_regions = ARRAY_SIZE(sja1105_regions); +	struct sja1105_private *priv = ds->priv; +	const struct devlink_region_ops *ops; +	struct devlink_region *region; +	u64 size; + +	priv->regions = kcalloc(num_regions, sizeof(struct devlink_region *), +				GFP_KERNEL); +	if (!priv->regions) +		return -ENOMEM; + +	for (i = 0; i < num_regions; i++) { +		size = sja1105_regions[i].get_size(priv); +		ops = sja1105_regions[i].ops; + +		region = dsa_devlink_region_create(ds, ops, 1, size); +		if (IS_ERR(region)) { +			while (i-- >= 0) +				dsa_devlink_region_destroy(priv->regions[i]); +			return PTR_ERR(region); +		} + +		priv->regions[i] = region; +	} + +	return 0; +} + +static void sja1105_teardown_devlink_regions(struct dsa_switch *ds) +{ +	int i, num_regions = ARRAY_SIZE(sja1105_regions); +	struct sja1105_private *priv = ds->priv; + +	for (i = 0; i < num_regions; i++) +		dsa_devlink_region_destroy(priv->regions[i]); + +	kfree(priv->regions); +} + +static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv, +						  bool *be_vlan) +{ +	*be_vlan = priv->best_effort_vlan_filtering; + +	return 0; +} + +static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv, +						  bool be_vlan) +{ +	struct dsa_switch *ds = priv->ds; +	bool vlan_filtering; +	int port; +	int rc; + +	priv->best_effort_vlan_filtering = be_vlan; + +	rtnl_lock(); +	for (port = 0; port < ds->num_ports; port++) { +		struct switchdev_trans trans; +		struct dsa_port *dp; + +		if (!dsa_is_user_port(ds, port)) +			continue; + +		dp = dsa_to_port(ds, port); +		vlan_filtering = dsa_port_is_vlan_filtering(dp); + +		trans.ph_prepare = true; +		rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans); +		if (rc) +			break; + +		trans.ph_prepare = false; +		rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans); +		if (rc) +			break; +	} +	rtnl_unlock(); + +	return rc; +} + +enum sja1105_devlink_param_id { +	SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, +	SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, +}; + +int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id, +			      struct devlink_param_gset_ctx *ctx) +{ +	struct sja1105_private *priv = ds->priv; +	int err; + +	switch (id) { +	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: +		err = sja1105_best_effort_vlan_filtering_get(priv, +							     &ctx->val.vbool); +		break; +	default: +		err = -EOPNOTSUPP; +		break; +	} + +	return err; +} + +int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id, +			      struct devlink_param_gset_ctx *ctx) +{ +	struct sja1105_private *priv = ds->priv; +	int err; + +	switch (id) { +	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: +		err = sja1105_best_effort_vlan_filtering_set(priv, +							     ctx->val.vbool); +		break; +	default: +		err = -EOPNOTSUPP; +		break; +	} + +	return err; +} + +static const struct devlink_param sja1105_devlink_params[] = { +	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, +				 "best_effort_vlan_filtering", +				 DEVLINK_PARAM_TYPE_BOOL, +				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)), +}; + +static int sja1105_setup_devlink_params(struct dsa_switch *ds) +{ +	return dsa_devlink_params_register(ds, sja1105_devlink_params, +					   ARRAY_SIZE(sja1105_devlink_params)); +} + +static void sja1105_teardown_devlink_params(struct dsa_switch *ds) +{ +	dsa_devlink_params_unregister(ds, sja1105_devlink_params, +				      ARRAY_SIZE(sja1105_devlink_params)); +} + +int sja1105_devlink_info_get(struct dsa_switch *ds, +			     struct devlink_info_req *req, +			     struct netlink_ext_ack *extack) +{ +	struct sja1105_private *priv = ds->priv; +	int rc; + +	rc = devlink_info_driver_name_put(req, "sja1105"); +	if (rc) +		return rc; + +	rc = devlink_info_version_fixed_put(req, +					    DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, +					    priv->info->name); +	return rc; +} + +int sja1105_devlink_setup(struct dsa_switch *ds) +{ +	int rc; + +	rc = sja1105_setup_devlink_params(ds); +	if (rc) +		return rc; + +	rc = sja1105_setup_devlink_regions(ds); +	if (rc < 0) { +		sja1105_teardown_devlink_params(ds); +		return rc; +	} + +	return 0; +} + +void sja1105_devlink_teardown(struct dsa_switch *ds) +{ +	sja1105_teardown_devlink_params(ds); +	sja1105_teardown_devlink_regions(ds); +} diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 75247f342124..b777d3f37573 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -97,10 +97,10 @@  #define SJA1105_SIZE_DYN_CMD					4 -#define SJA1105ET_SJA1105_SIZE_VL_LOOKUP_DYN_CMD		\ +#define SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD			\  	SJA1105_SIZE_DYN_CMD -#define SJA1105PQRS_SJA1105_SIZE_VL_LOOKUP_DYN_CMD		\ +#define SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD			\  	(SJA1105_SIZE_DYN_CMD + SJA1105_SIZE_VL_LOOKUP_ENTRY)  #define SJA1105ET_SIZE_MAC_CONFIG_DYN_ENTRY			\ @@ -183,7 +183,7 @@ static size_t sja1105et_vl_lookup_entry_packing(void *buf, void *entry_ptr,  						enum packing_op op)  {  	struct sja1105_vl_lookup_entry *entry = entry_ptr; -	const int size = SJA1105ET_SJA1105_SIZE_VL_LOOKUP_DYN_CMD; +	const int size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD;  	sja1105_packing(buf, &entry->egrmirr,  21, 17, size, op);  	sja1105_packing(buf, &entry->ingrmirr, 16, 16, size, op); @@ -644,7 +644,7 @@ const struct sja1105_dynamic_table_ops sja1105et_dyn_ops[BLK_IDX_MAX_DYN] = {  		.cmd_packing = sja1105_vl_lookup_cmd_packing,  		.access = OP_WRITE,  		.max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, -		.packed_size = SJA1105ET_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, +		.packed_size = SJA1105ET_SIZE_VL_LOOKUP_DYN_CMD,  		.addr = 0x35,  	},  	[BLK_IDX_L2_LOOKUP] = { @@ -728,7 +728,7 @@ const struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {  		.cmd_packing = sja1105_vl_lookup_cmd_packing,  		.access = (OP_READ | OP_WRITE),  		.max_entry_count = SJA1105_MAX_VL_LOOKUP_COUNT, -		.packed_size = SJA1105PQRS_SJA1105_SIZE_VL_LOOKUP_DYN_CMD, +		.packed_size = SJA1105PQRS_SIZE_VL_LOOKUP_DYN_CMD,  		.addr = 0x47,  	},  	[BLK_IDX_L2_LOOKUP] = { diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 5a28dfb36ec3..4ca029650993 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1880,19 +1880,17 @@ static int sja1105_crosschip_bridge_join(struct dsa_switch *ds,  		if (dsa_to_port(ds, port)->bridge_dev != br)  			continue; -		other_priv->expect_dsa_8021q = true; -		rc = dsa_8021q_crosschip_bridge_join(ds, port, other_ds, -						     other_port, -						     &priv->crosschip_links); -		other_priv->expect_dsa_8021q = false; +		rc = dsa_8021q_crosschip_bridge_join(priv->dsa_8021q_ctx, +						     port, +						     other_priv->dsa_8021q_ctx, +						     other_port);  		if (rc)  			return rc; -		priv->expect_dsa_8021q = true; -		rc = dsa_8021q_crosschip_bridge_join(other_ds, other_port, ds, -						     port, -						     &other_priv->crosschip_links); -		priv->expect_dsa_8021q = false; +		rc = dsa_8021q_crosschip_bridge_join(other_priv->dsa_8021q_ctx, +						     other_port, +						     priv->dsa_8021q_ctx, +						     port);  		if (rc)  			return rc;  	} @@ -1919,33 +1917,24 @@ static void sja1105_crosschip_bridge_leave(struct dsa_switch *ds,  		if (dsa_to_port(ds, port)->bridge_dev != br)  			continue; -		other_priv->expect_dsa_8021q = true; -		dsa_8021q_crosschip_bridge_leave(ds, port, other_ds, other_port, -						 &priv->crosschip_links); -		other_priv->expect_dsa_8021q = false; +		dsa_8021q_crosschip_bridge_leave(priv->dsa_8021q_ctx, port, +						 other_priv->dsa_8021q_ctx, +						 other_port); -		priv->expect_dsa_8021q = true; -		dsa_8021q_crosschip_bridge_leave(other_ds, other_port, ds, port, -						 &other_priv->crosschip_links); -		priv->expect_dsa_8021q = false; +		dsa_8021q_crosschip_bridge_leave(other_priv->dsa_8021q_ctx, +						 other_port, +						 priv->dsa_8021q_ctx, port);  	}  }  static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled)  {  	struct sja1105_private *priv = ds->priv; -	int rc, i; +	int rc; -	for (i = 0; i < SJA1105_NUM_PORTS; i++) { -		priv->expect_dsa_8021q = true; -		rc = dsa_port_setup_8021q_tagging(ds, i, enabled); -		priv->expect_dsa_8021q = false; -		if (rc < 0) { -			dev_err(ds->dev, "Failed to setup VLAN tagging for port %d: %d\n", -				i, rc); -			return rc; -		} -	} +	rc = dsa_8021q_setup(priv->dsa_8021q_ctx, enabled); +	if (rc) +		return rc;  	dev_info(ds->dev, "%s switch tagging\n",  		 enabled ? "Enabled" : "Disabled"); @@ -2149,12 +2138,12 @@ struct sja1105_crosschip_vlan {  	bool untagged;  	int port;  	int other_port; -	struct dsa_switch *other_ds; +	struct dsa_8021q_context *other_ctx;  };  struct sja1105_crosschip_switch {  	struct list_head list; -	struct dsa_switch *other_ds; +	struct dsa_8021q_context *other_ctx;  };  static int sja1105_commit_pvid(struct sja1105_private *priv) @@ -2330,8 +2319,8 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,  	INIT_LIST_HEAD(&crosschip_vlans); -	list_for_each_entry(c, &priv->crosschip_links, list) { -		struct sja1105_private *other_priv = c->other_ds->priv; +	list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) { +		struct sja1105_private *other_priv = c->other_ctx->ds->priv;  		if (other_priv->vlan_state == SJA1105_VLAN_FILTERING_FULL)  			continue; @@ -2341,7 +2330,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,  		 */  		if (!dsa_is_user_port(priv->ds, c->port))  			continue; -		if (!dsa_is_user_port(c->other_ds, c->other_port)) +		if (!dsa_is_user_port(c->other_ctx->ds, c->other_port))  			continue;  		/* Search for VLANs on the remote port */ @@ -2376,7 +2365,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,  				    tmp->untagged == v->untagged &&  				    tmp->port == c->port &&  				    tmp->other_port == v->port && -				    tmp->other_ds == c->other_ds) { +				    tmp->other_ctx == c->other_ctx) {  					already_added = true;  					break;  				} @@ -2394,14 +2383,14 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,  			tmp->vid = v->vid;  			tmp->port = c->port;  			tmp->other_port = v->port; -			tmp->other_ds = c->other_ds; +			tmp->other_ctx = c->other_ctx;  			tmp->untagged = v->untagged;  			list_add(&tmp->list, &crosschip_vlans);  		}  	}  	list_for_each_entry(tmp, &crosschip_vlans, list) { -		struct sja1105_private *other_priv = tmp->other_ds->priv; +		struct sja1105_private *other_priv = tmp->other_ctx->ds->priv;  		int upstream = dsa_upstream_port(priv->ds, tmp->port);  		int match, subvlan;  		u16 rx_vid; @@ -2418,7 +2407,7 @@ sja1105_build_crosschip_subvlans(struct sja1105_private *priv,  			goto out;  		} -		rx_vid = dsa_8021q_rx_vid_subvlan(tmp->other_ds, +		rx_vid = dsa_8021q_rx_vid_subvlan(tmp->other_ctx->ds,  						  tmp->other_port,  						  subvlan); @@ -2493,11 +2482,11 @@ static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)  	INIT_LIST_HEAD(&crosschip_switches); -	list_for_each_entry(c, &priv->crosschip_links, list) { +	list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) {  		bool already_added = false;  		list_for_each_entry(s, &crosschip_switches, list) { -			if (s->other_ds == c->other_ds) { +			if (s->other_ctx == c->other_ctx) {  				already_added = true;  				break;  			} @@ -2512,12 +2501,12 @@ static int sja1105_notify_crosschip_switches(struct sja1105_private *priv)  			rc = -ENOMEM;  			goto out;  		} -		s->other_ds = c->other_ds; +		s->other_ctx = c->other_ctx;  		list_add(&s->list, &crosschip_switches);  	}  	list_for_each_entry(s, &crosschip_switches, list) { -		struct sja1105_private *other_priv = s->other_ds->priv; +		struct sja1105_private *other_priv = s->other_ctx->ds->priv;  		rc = sja1105_build_vlan_table(other_priv, false);  		if (rc) @@ -2618,16 +2607,6 @@ out:  	return rc;  } -/* Select the list to which we should add this VLAN. */ -static struct list_head *sja1105_classify_vlan(struct sja1105_private *priv, -					       u16 vid) -{ -	if (priv->expect_dsa_8021q) -		return &priv->dsa_8021q_vlans; - -	return &priv->bridge_vlans; -} -  static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,  				const struct switchdev_obj_port_vlan *vlan)  { @@ -2642,7 +2621,7 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,  	 * configuration done by dsa_8021q.  	 */  	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { -		if (!priv->expect_dsa_8021q && vid_is_dsa_8021q(vid)) { +		if (vid_is_dsa_8021q(vid)) {  			dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n");  			return -EBUSY;  		} @@ -2655,7 +2634,8 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,   * which can only be partially reconfigured at runtime (and not the TPID).   * So a switch reset is required.   */ -static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) +int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, +			   struct switchdev_trans *trans)  {  	struct sja1105_l2_lookup_params_entry *l2_lookup_params;  	struct sja1105_general_params_entry *general_params; @@ -2667,12 +2647,16 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)  	u16 tpid, tpid2;  	int rc; -	list_for_each_entry(rule, &priv->flow_block.rules, list) { -		if (rule->type == SJA1105_RULE_VL) { -			dev_err(ds->dev, -				"Cannot change VLAN filtering state while VL rules are active\n"); -			return -EBUSY; +	if (switchdev_trans_ph_prepare(trans)) { +		list_for_each_entry(rule, &priv->flow_block.rules, list) { +			if (rule->type == SJA1105_RULE_VL) { +				dev_err(ds->dev, +					"Cannot change VLAN filtering with active VL rules\n"); +				return -EBUSY; +			}  		} + +		return 0;  	}  	if (enabled) { @@ -2762,6 +2746,54 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)  	return sja1105_setup_8021q_tagging(ds, want_tagging);  } +/* Returns number of VLANs added (0 or 1) on success, + * or a negative error code. + */ +static int sja1105_vlan_add_one(struct dsa_switch *ds, int port, u16 vid, +				u16 flags, struct list_head *vlan_list) +{ +	bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED; +	bool pvid = flags & BRIDGE_VLAN_INFO_PVID; +	struct sja1105_bridge_vlan *v; + +	list_for_each_entry(v, vlan_list, list) +		if (v->port == port && v->vid == vid && +		    v->untagged == untagged && v->pvid == pvid) +			/* Already added */ +			return 0; + +	v = kzalloc(sizeof(*v), GFP_KERNEL); +	if (!v) { +		dev_err(ds->dev, "Out of memory while storing VLAN\n"); +		return -ENOMEM; +	} + +	v->port = port; +	v->vid = vid; +	v->untagged = untagged; +	v->pvid = pvid; +	list_add(&v->list, vlan_list); + +	return 1; +} + +/* Returns number of VLANs deleted (0 or 1) */ +static int sja1105_vlan_del_one(struct dsa_switch *ds, int port, u16 vid, +				struct list_head *vlan_list) +{ +	struct sja1105_bridge_vlan *v, *n; + +	list_for_each_entry_safe(v, n, vlan_list, list) { +		if (v->port == port && v->vid == vid) { +			list_del(&v->list); +			kfree(v); +			return 1; +		} +	} + +	return 0; +} +  static void sja1105_vlan_add(struct dsa_switch *ds, int port,  			     const struct switchdev_obj_port_vlan *vlan)  { @@ -2771,38 +2803,12 @@ static void sja1105_vlan_add(struct dsa_switch *ds, int port,  	int rc;  	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { -		bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; -		bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; -		struct sja1105_bridge_vlan *v; -		struct list_head *vlan_list; -		bool already_added = false; - -		vlan_list = sja1105_classify_vlan(priv, vid); - -		list_for_each_entry(v, vlan_list, list) { -			if (v->port == port && v->vid == vid && -			    v->untagged == untagged && v->pvid == pvid) { -				already_added = true; -				break; -			} -		} - -		if (already_added) -			continue; - -		v = kzalloc(sizeof(*v), GFP_KERNEL); -		if (!v) { -			dev_err(ds->dev, "Out of memory while storing VLAN\n"); +		rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags, +					  &priv->bridge_vlans); +		if (rc < 0)  			return; -		} - -		v->port = port; -		v->vid = vid; -		v->untagged = untagged; -		v->pvid = pvid; -		list_add(&v->list, vlan_list); - -		vlan_table_changed = true; +		if (rc > 0) +			vlan_table_changed = true;  	}  	if (!vlan_table_changed) @@ -2819,21 +2825,12 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,  	struct sja1105_private *priv = ds->priv;  	bool vlan_table_changed = false;  	u16 vid; +	int rc;  	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { -		struct sja1105_bridge_vlan *v, *n; -		struct list_head *vlan_list; - -		vlan_list = sja1105_classify_vlan(priv, vid); - -		list_for_each_entry_safe(v, n, vlan_list, list) { -			if (v->port == port && v->vid == vid) { -				list_del(&v->list); -				kfree(v); -				vlan_table_changed = true; -				break; -			} -		} +		rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans); +		if (rc > 0) +			vlan_table_changed = true;  	}  	if (!vlan_table_changed) @@ -2842,105 +2839,36 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,  	return sja1105_build_vlan_table(priv, true);  } -static int sja1105_best_effort_vlan_filtering_get(struct sja1105_private *priv, -						  bool *be_vlan) -{ -	*be_vlan = priv->best_effort_vlan_filtering; - -	return 0; -} - -static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv, -						  bool be_vlan) -{ -	struct dsa_switch *ds = priv->ds; -	bool vlan_filtering; -	int port; -	int rc; - -	priv->best_effort_vlan_filtering = be_vlan; - -	rtnl_lock(); -	for (port = 0; port < ds->num_ports; port++) { -		struct dsa_port *dp; - -		if (!dsa_is_user_port(ds, port)) -			continue; - -		dp = dsa_to_port(ds, port); -		vlan_filtering = dsa_port_is_vlan_filtering(dp); - -		rc = sja1105_vlan_filtering(ds, port, vlan_filtering); -		if (rc) -			break; -	} -	rtnl_unlock(); - -	return rc; -} - -enum sja1105_devlink_param_id { -	SJA1105_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, -	SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, -}; - -static int sja1105_devlink_param_get(struct dsa_switch *ds, u32 id, -				     struct devlink_param_gset_ctx *ctx) +static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, +				      u16 flags)  {  	struct sja1105_private *priv = ds->priv; -	int err; +	int rc; -	switch (id) { -	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: -		err = sja1105_best_effort_vlan_filtering_get(priv, -							     &ctx->val.vbool); -		break; -	default: -		err = -EOPNOTSUPP; -		break; -	} +	rc = sja1105_vlan_add_one(ds, port, vid, flags, &priv->dsa_8021q_vlans); +	if (rc <= 0) +		return rc; -	return err; +	return sja1105_build_vlan_table(priv, true);  } -static int sja1105_devlink_param_set(struct dsa_switch *ds, u32 id, -				     struct devlink_param_gset_ctx *ctx) +static int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)  {  	struct sja1105_private *priv = ds->priv; -	int err; +	int rc; -	switch (id) { -	case SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING: -		err = sja1105_best_effort_vlan_filtering_set(priv, -							     ctx->val.vbool); -		break; -	default: -		err = -EOPNOTSUPP; -		break; -	} +	rc = sja1105_vlan_del_one(ds, port, vid, &priv->dsa_8021q_vlans); +	if (!rc) +		return 0; -	return err; +	return sja1105_build_vlan_table(priv, true);  } -static const struct devlink_param sja1105_devlink_params[] = { -	DSA_DEVLINK_PARAM_DRIVER(SJA1105_DEVLINK_PARAM_ID_BEST_EFFORT_VLAN_FILTERING, -				 "best_effort_vlan_filtering", -				 DEVLINK_PARAM_TYPE_BOOL, -				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)), +static const struct dsa_8021q_ops sja1105_dsa_8021q_ops = { +	.vlan_add	= sja1105_dsa_8021q_vlan_add, +	.vlan_del	= sja1105_dsa_8021q_vlan_del,  }; -static int sja1105_setup_devlink_params(struct dsa_switch *ds) -{ -	return dsa_devlink_params_register(ds, sja1105_devlink_params, -					   ARRAY_SIZE(sja1105_devlink_params)); -} - -static void sja1105_teardown_devlink_params(struct dsa_switch *ds) -{ -	dsa_devlink_params_unregister(ds, sja1105_devlink_params, -				      ARRAY_SIZE(sja1105_devlink_params)); -} -  /* 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. @@ -3008,7 +2936,7 @@ static int sja1105_setup(struct dsa_switch *ds)  	ds->configure_vlan_while_not_filtering = true; -	rc = sja1105_setup_devlink_params(ds); +	rc = sja1105_devlink_setup(ds);  	if (rc < 0)  		return rc; @@ -3016,7 +2944,11 @@ static int sja1105_setup(struct dsa_switch *ds)  	 * default, and that means vlan_filtering is 0 since they're not under  	 * a bridge, so it's safe to set up switch tagging at this time.  	 */ -	return sja1105_setup_8021q_tagging(ds, true); +	rtnl_lock(); +	rc = sja1105_setup_8021q_tagging(ds, true); +	rtnl_unlock(); + +	return rc;  }  static void sja1105_teardown(struct dsa_switch *ds) @@ -3035,7 +2967,7 @@ static void sja1105_teardown(struct dsa_switch *ds)  			kthread_destroy_worker(sp->xmit_worker);  	} -	sja1105_teardown_devlink_params(ds); +	sja1105_devlink_teardown(ds);  	sja1105_flower_teardown(ds);  	sja1105_tas_teardown(ds);  	sja1105_ptp_clock_unregister(ds); @@ -3389,6 +3321,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {  	.crosschip_bridge_leave	= sja1105_crosschip_bridge_leave,  	.devlink_param_get	= sja1105_devlink_param_get,  	.devlink_param_set	= sja1105_devlink_param_set, +	.devlink_info_get	= sja1105_devlink_info_get,  };  static const struct of_device_id sja1105_dt_ids[]; @@ -3504,7 +3437,16 @@ static int sja1105_probe(struct spi_device *spi)  	mutex_init(&priv->ptp_data.lock);  	mutex_init(&priv->mgmt_lock); -	INIT_LIST_HEAD(&priv->crosschip_links); +	priv->dsa_8021q_ctx = devm_kzalloc(dev, sizeof(*priv->dsa_8021q_ctx), +					   GFP_KERNEL); +	if (!priv->dsa_8021q_ctx) +		return -ENOMEM; + +	priv->dsa_8021q_ctx->ops = &sja1105_dsa_8021q_ops; +	priv->dsa_8021q_ctx->proto = htons(ETH_P_8021Q); +	priv->dsa_8021q_ctx->ds = ds; + +	INIT_LIST_HEAD(&priv->dsa_8021q_ctx->crosschip_links);  	INIT_LIST_HEAD(&priv->bridge_vlans);  	INIT_LIST_HEAD(&priv->dsa_8021q_vlans); diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 704dcf1d1c01..591c5734747d 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -302,9 +302,8 @@ static int sja1105_status_get(struct sja1105_private *priv,   * for upload requires the recalculation of table CRCs and updating the   * structures with these.   */ -static int -static_config_buf_prepare_for_upload(struct sja1105_private *priv, -				     void *config_buf, int buf_len) +int static_config_buf_prepare_for_upload(struct sja1105_private *priv, +					 void *config_buf, int buf_len)  {  	struct sja1105_static_config *config = &priv->static_config;  	struct sja1105_table_header final_header; | 
