diff options
Diffstat (limited to 'drivers/net/dsa')
54 files changed, 822 insertions, 203 deletions
| diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c index a533a90e3904..a7aeb3c132c9 100644 --- a/drivers/net/dsa/b53/b53_mdio.c +++ b/drivers/net/dsa/b53/b53_mdio.c @@ -351,9 +351,25 @@ static int b53_mdio_probe(struct mdio_device *mdiodev)  static void b53_mdio_remove(struct mdio_device *mdiodev)  {  	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); -	struct dsa_switch *ds = dev->ds; -	dsa_unregister_switch(ds); +	if (!dev) +		return; + +	b53_switch_remove(dev); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void b53_mdio_shutdown(struct mdio_device *mdiodev) +{ +	struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); + +	if (!dev) +		return; + +	b53_switch_shutdown(dev); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id b53_of_match[] = { @@ -373,6 +389,7 @@ MODULE_DEVICE_TABLE(of, b53_of_match);  static struct mdio_driver b53_mdio_driver = {  	.probe	= b53_mdio_probe,  	.remove	= b53_mdio_remove, +	.shutdown = b53_mdio_shutdown,  	.mdiodrv.driver = {  		.name = "bcm53xx",  		.of_match_table = b53_of_match, diff --git a/drivers/net/dsa/b53/b53_mmap.c b/drivers/net/dsa/b53/b53_mmap.c index 82680e083cc2..ae4c79d39bc0 100644 --- a/drivers/net/dsa/b53/b53_mmap.c +++ b/drivers/net/dsa/b53/b53_mmap.c @@ -316,9 +316,21 @@ static int b53_mmap_remove(struct platform_device *pdev)  	if (dev)  		b53_switch_remove(dev); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } +static void b53_mmap_shutdown(struct platform_device *pdev) +{ +	struct b53_device *dev = platform_get_drvdata(pdev); + +	if (dev) +		b53_switch_shutdown(dev); + +	platform_set_drvdata(pdev, NULL); +} +  static const struct of_device_id b53_mmap_of_table[] = {  	{ .compatible = "brcm,bcm3384-switch" },  	{ .compatible = "brcm,bcm6328-switch" }, @@ -331,6 +343,7 @@ MODULE_DEVICE_TABLE(of, b53_mmap_of_table);  static struct platform_driver b53_mmap_driver = {  	.probe = b53_mmap_probe,  	.remove = b53_mmap_remove, +	.shutdown = b53_mmap_shutdown,  	.driver = {  		.name = "b53-switch",  		.of_match_table = b53_mmap_of_table, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 5d068acf7cf8..959a52d41f0a 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -228,6 +228,11 @@ static inline void b53_switch_remove(struct b53_device *dev)  	dsa_unregister_switch(dev->ds);  } +static inline void b53_switch_shutdown(struct b53_device *dev) +{ +	dsa_switch_shutdown(dev->ds); +} +  #define b53_build_op(type_op_size, val_type)				\  static inline int b53_##type_op_size(struct b53_device *dev, u8 page,	\  				     u8 reg, val_type val)		\ diff --git a/drivers/net/dsa/b53/b53_spi.c b/drivers/net/dsa/b53/b53_spi.c index ecb9f7f6b335..01e37b75471e 100644 --- a/drivers/net/dsa/b53/b53_spi.c +++ b/drivers/net/dsa/b53/b53_spi.c @@ -321,9 +321,21 @@ static int b53_spi_remove(struct spi_device *spi)  	if (dev)  		b53_switch_remove(dev); +	spi_set_drvdata(spi, NULL); +  	return 0;  } +static void b53_spi_shutdown(struct spi_device *spi) +{ +	struct b53_device *dev = spi_get_drvdata(spi); + +	if (dev) +		b53_switch_shutdown(dev); + +	spi_set_drvdata(spi, NULL); +} +  static const struct of_device_id b53_spi_of_match[] = {  	{ .compatible = "brcm,bcm5325" },  	{ .compatible = "brcm,bcm5365" }, @@ -344,6 +356,7 @@ static struct spi_driver b53_spi_driver = {  	},  	.probe	= b53_spi_probe,  	.remove	= b53_spi_remove, +	.shutdown = b53_spi_shutdown,  };  module_spi_driver(b53_spi_driver); diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c index 3f4249de70c5..4591bb1c05d2 100644 --- a/drivers/net/dsa/b53/b53_srab.c +++ b/drivers/net/dsa/b53/b53_srab.c @@ -629,17 +629,34 @@ static int b53_srab_probe(struct platform_device *pdev)  static int b53_srab_remove(struct platform_device *pdev)  {  	struct b53_device *dev = platform_get_drvdata(pdev); -	struct b53_srab_priv *priv = dev->priv; -	b53_srab_intr_set(priv, false); +	if (!dev) +		return 0; + +	b53_srab_intr_set(dev->priv, false);  	b53_switch_remove(dev); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } +static void b53_srab_shutdown(struct platform_device *pdev) +{ +	struct b53_device *dev = platform_get_drvdata(pdev); + +	if (!dev) +		return; + +	b53_switch_shutdown(dev); + +	platform_set_drvdata(pdev, NULL); +} +  static struct platform_driver b53_srab_driver = {  	.probe = b53_srab_probe,  	.remove = b53_srab_remove, +	.shutdown = b53_srab_shutdown,  	.driver = {  		.name = "b53-srab-switch",  		.of_match_table = b53_srab_of_match, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 6ce9ec1283e0..7578a5c38df5 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -68,7 +68,7 @@ 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++) { +	for (port = 0; port < ds->num_ports; port++) {  		if (dsa_is_cpu_port(ds, port))  			continue;  		if (priv->port_sts[port].enabled) @@ -1512,6 +1512,9 @@ static int bcm_sf2_sw_remove(struct platform_device *pdev)  {  	struct bcm_sf2_priv *priv = platform_get_drvdata(pdev); +	if (!priv) +		return 0; +  	priv->wol_ports_mask = 0;  	/* Disable interrupts */  	bcm_sf2_intr_disable(priv); @@ -1523,6 +1526,8 @@ static int bcm_sf2_sw_remove(struct platform_device *pdev)  	if (priv->type == BCM7278_DEVICE_ID)  		reset_control_assert(priv->rcdev); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } @@ -1530,6 +1535,9 @@ static void bcm_sf2_sw_shutdown(struct platform_device *pdev)  {  	struct bcm_sf2_priv *priv = platform_get_drvdata(pdev); +	if (!priv) +		return; +  	/* For a kernel about to be kexec'd we want to keep the GPHY on for a  	 * successful MDIO bus scan to occur. If we did turn off the GPHY  	 * before (e.g: port_disable), this will also power it back on. @@ -1538,6 +1546,10 @@ static void bcm_sf2_sw_shutdown(struct platform_device *pdev)  	 */  	if (priv->hw_params.num_gphy == 1)  		bcm_sf2_gphy_enable_set(priv->dev->ds, true); + +	dsa_switch_shutdown(priv->dev->ds); + +	platform_set_drvdata(pdev, NULL);  }  #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index bfdf3324aac3..e638e3eea911 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -340,10 +340,29 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev)  static void dsa_loop_drv_remove(struct mdio_device *mdiodev)  {  	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); -	struct dsa_loop_priv *ps = ds->priv; +	struct dsa_loop_priv *ps; + +	if (!ds) +		return; + +	ps = ds->priv;  	dsa_unregister_switch(ds);  	dev_put(ps->netdev); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void dsa_loop_drv_shutdown(struct mdio_device *mdiodev) +{ +	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + +	if (!ds) +		return; + +	dsa_switch_shutdown(ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static struct mdio_driver dsa_loop_drv = { @@ -352,6 +371,7 @@ static struct mdio_driver dsa_loop_drv = {  	},  	.probe	= dsa_loop_drv_probe,  	.remove	= dsa_loop_drv_remove, +	.shutdown = dsa_loop_drv_shutdown,  };  #define NUM_FIXED_PHYS	(DSA_LOOP_NUM_PORTS - 2) diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 542cfc4ccb08..354655f9ed00 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -1916,6 +1916,9 @@ static int hellcreek_remove(struct platform_device *pdev)  {  	struct hellcreek *hellcreek = platform_get_drvdata(pdev); +	if (!hellcreek) +		return 0; +  	hellcreek_hwtstamp_free(hellcreek);  	hellcreek_ptp_free(hellcreek);  	dsa_unregister_switch(hellcreek->ds); @@ -1924,6 +1927,18 @@ static int hellcreek_remove(struct platform_device *pdev)  	return 0;  } +static void hellcreek_shutdown(struct platform_device *pdev) +{ +	struct hellcreek *hellcreek = platform_get_drvdata(pdev); + +	if (!hellcreek) +		return; + +	dsa_switch_shutdown(hellcreek->ds); + +	platform_set_drvdata(pdev, NULL); +} +  static const struct hellcreek_platform_data de1soc_r1_pdata = {  	.name		 = "r4c30",  	.num_ports	 = 4, @@ -1946,6 +1961,7 @@ MODULE_DEVICE_TABLE(of, hellcreek_of_match);  static struct platform_driver hellcreek_driver = {  	.probe	= hellcreek_probe,  	.remove = hellcreek_remove, +	.shutdown = hellcreek_shutdown,  	.driver = {  		.name = "hellcreek",  		.of_match_table = hellcreek_of_match, diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index d7ce281570b5..89f920289ae2 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -1379,6 +1379,12 @@ int lan9303_remove(struct lan9303 *chip)  }  EXPORT_SYMBOL(lan9303_remove); +void lan9303_shutdown(struct lan9303 *chip) +{ +	dsa_switch_shutdown(chip->ds); +} +EXPORT_SYMBOL(lan9303_shutdown); +  MODULE_AUTHOR("Juergen Borleis <kernel@pengutronix.de>");  MODULE_DESCRIPTION("Core driver for SMSC/Microchip LAN9303 three port ethernet switch");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h index 11f590b64701..c7f73efa50f0 100644 --- a/drivers/net/dsa/lan9303.h +++ b/drivers/net/dsa/lan9303.h @@ -10,3 +10,4 @@ extern const struct lan9303_phy_ops lan9303_indirect_phy_ops;  int lan9303_probe(struct lan9303 *chip, struct device_node *np);  int lan9303_remove(struct lan9303 *chip); +void lan9303_shutdown(struct lan9303 *chip); diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 9bffaef65a04..8ca4713310fa 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -67,13 +67,28 @@ static int lan9303_i2c_probe(struct i2c_client *client,  static int lan9303_i2c_remove(struct i2c_client *client)  { -	struct lan9303_i2c *sw_dev; +	struct lan9303_i2c *sw_dev = i2c_get_clientdata(client); -	sw_dev = i2c_get_clientdata(client);  	if (!sw_dev) -		return -ENODEV; +		return 0; + +	lan9303_remove(&sw_dev->chip); + +	i2c_set_clientdata(client, NULL); + +	return 0; +} + +static void lan9303_i2c_shutdown(struct i2c_client *client) +{ +	struct lan9303_i2c *sw_dev = i2c_get_clientdata(client); + +	if (!sw_dev) +		return; + +	lan9303_shutdown(&sw_dev->chip); -	return lan9303_remove(&sw_dev->chip); +	i2c_set_clientdata(client, NULL);  }  /*-------------------------------------------------------------------------*/ @@ -97,6 +112,7 @@ static struct i2c_driver lan9303_i2c_driver = {  	},  	.probe = lan9303_i2c_probe,  	.remove = lan9303_i2c_remove, +	.shutdown = lan9303_i2c_shutdown,  	.id_table = lan9303_i2c_id,  };  module_i2c_driver(lan9303_i2c_driver); diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 9cbe80460b53..bbb7032409ba 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -138,6 +138,20 @@ static void lan9303_mdio_remove(struct mdio_device *mdiodev)  		return;  	lan9303_remove(&sw_dev->chip); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void lan9303_mdio_shutdown(struct mdio_device *mdiodev) +{ +	struct lan9303_mdio *sw_dev = dev_get_drvdata(&mdiodev->dev); + +	if (!sw_dev) +		return; + +	lan9303_shutdown(&sw_dev->chip); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  /*-------------------------------------------------------------------------*/ @@ -155,6 +169,7 @@ static struct mdio_driver lan9303_mdio_driver = {  	},  	.probe  = lan9303_mdio_probe,  	.remove = lan9303_mdio_remove, +	.shutdown = lan9303_mdio_shutdown,  };  mdio_module_driver(lan9303_mdio_driver); diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 267324889dd6..3ff4b7e177f3 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -2184,6 +2184,9 @@ static int gswip_remove(struct platform_device *pdev)  	struct gswip_priv *priv = platform_get_drvdata(pdev);  	int i; +	if (!priv) +		return 0; +  	/* disable the switch */  	gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB); @@ -2197,9 +2200,23 @@ static int gswip_remove(struct platform_device *pdev)  	for (i = 0; i < priv->num_gphy_fw; i++)  		gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } +static void gswip_shutdown(struct platform_device *pdev) +{ +	struct gswip_priv *priv = platform_get_drvdata(pdev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	platform_set_drvdata(pdev, NULL); +} +  static const struct gswip_hw_info gswip_xrx200 = {  	.max_ports = 7,  	.cpu_port = 6, @@ -2223,6 +2240,7 @@ MODULE_DEVICE_TABLE(of, gswip_of_match);  static struct platform_driver gswip_driver = {  	.probe = gswip_probe,  	.remove = gswip_remove, +	.shutdown = gswip_shutdown,  	.driver = {  		.name = "gswip",  		.of_match_table = gswip_of_match, diff --git a/drivers/net/dsa/microchip/ksz8795_spi.c b/drivers/net/dsa/microchip/ksz8795_spi.c index ea7550d1b634..866767b70d65 100644 --- a/drivers/net/dsa/microchip/ksz8795_spi.c +++ b/drivers/net/dsa/microchip/ksz8795_spi.c @@ -94,6 +94,8 @@ static int ksz8795_spi_remove(struct spi_device *spi)  	if (dev)  		ksz_switch_remove(dev); +	spi_set_drvdata(spi, NULL); +  	return 0;  } @@ -101,8 +103,15 @@ static void ksz8795_spi_shutdown(struct spi_device *spi)  {  	struct ksz_device *dev = spi_get_drvdata(spi); -	if (dev && dev->dev_ops->shutdown) +	if (!dev) +		return; + +	if (dev->dev_ops->shutdown)  		dev->dev_ops->shutdown(dev); + +	dsa_switch_shutdown(dev->ds); + +	spi_set_drvdata(spi, NULL);  }  static const struct of_device_id ksz8795_dt_ids[] = { diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index 11293485138c..5883fa7edda2 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -191,6 +191,18 @@ static void ksz8863_smi_remove(struct mdio_device *mdiodev)  	if (dev)  		ksz_switch_remove(dev); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) +{ +	struct ksz_device *dev = dev_get_drvdata(&mdiodev->dev); + +	if (dev) +		dsa_switch_shutdown(dev->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id ksz8863_dt_ids[] = { @@ -203,6 +215,7 @@ MODULE_DEVICE_TABLE(of, ksz8863_dt_ids);  static struct mdio_driver ksz8863_driver = {  	.probe	= ksz8863_smi_probe,  	.remove	= ksz8863_smi_remove, +	.shutdown = ksz8863_smi_shutdown,  	.mdiodrv.driver = {  		.name	= "ksz8863-switch",  		.of_match_table = ksz8863_dt_ids, diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index 4e053a25d077..f3afb8b8c4cc 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -56,7 +56,10 @@ static int ksz9477_i2c_remove(struct i2c_client *i2c)  {  	struct ksz_device *dev = i2c_get_clientdata(i2c); -	ksz_switch_remove(dev); +	if (dev) +		ksz_switch_remove(dev); + +	i2c_set_clientdata(i2c, NULL);  	return 0;  } @@ -65,8 +68,15 @@ static void ksz9477_i2c_shutdown(struct i2c_client *i2c)  {  	struct ksz_device *dev = i2c_get_clientdata(i2c); -	if (dev && dev->dev_ops->shutdown) +	if (!dev) +		return; + +	if (dev->dev_ops->shutdown)  		dev->dev_ops->shutdown(dev); + +	dsa_switch_shutdown(dev->ds); + +	i2c_set_clientdata(i2c, NULL);  }  static const struct i2c_device_id ksz9477_i2c_id[] = { diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c index 15bc11b3cda4..e3cb0e6c9f6f 100644 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ b/drivers/net/dsa/microchip/ksz9477_spi.c @@ -72,6 +72,8 @@ static int ksz9477_spi_remove(struct spi_device *spi)  	if (dev)  		ksz_switch_remove(dev); +	spi_set_drvdata(spi, NULL); +  	return 0;  } @@ -79,8 +81,10 @@ static void ksz9477_spi_shutdown(struct spi_device *spi)  {  	struct ksz_device *dev = spi_get_drvdata(spi); -	if (dev && dev->dev_ops->shutdown) -		dev->dev_ops->shutdown(dev); +	if (dev) +		dsa_switch_shutdown(dev->ds); + +	spi_set_drvdata(spi, NULL);  }  static const struct of_device_id ksz9477_dt_ids[] = { diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 1542bfb8b5e5..7c2968a639eb 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -449,8 +449,10 @@ EXPORT_SYMBOL(ksz_switch_register);  void ksz_switch_remove(struct ksz_device *dev)  {  	/* timer started */ -	if (dev->mib_read_interval) +	if (dev->mib_read_interval) { +		dev->mib_read_interval = 0;  		cancel_delayed_work_sync(&dev->mib_read); +	}  	dev->dev_ops->exit(dev);  	dsa_unregister_switch(dev->ds); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index d0cba2d1cd68..094737e5084a 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -3286,6 +3286,9 @@ mt7530_remove(struct mdio_device *mdiodev)  	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);  	int ret = 0; +	if (!priv) +		return; +  	ret = regulator_disable(priv->core_pwr);  	if (ret < 0)  		dev_err(priv->dev, @@ -3301,11 +3304,26 @@ mt7530_remove(struct mdio_device *mdiodev)  	dsa_unregister_switch(priv->ds);  	mutex_destroy(&priv->reg_mutex); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void mt7530_shutdown(struct mdio_device *mdiodev) +{ +	struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static struct mdio_driver mt7530_mdio_driver = {  	.probe  = mt7530_probe,  	.remove = mt7530_remove, +	.shutdown = mt7530_shutdown,  	.mdiodrv.driver = {  		.name = "mt7530",  		.of_match_table = mt7530_of_match, diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 24b8219fd607..a4c6eb9a52d0 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -290,7 +290,24 @@ static void mv88e6060_remove(struct mdio_device *mdiodev)  {  	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); +	if (!ds) +		return; +  	dsa_unregister_switch(ds); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void mv88e6060_shutdown(struct mdio_device *mdiodev) +{ +	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + +	if (!ds) +		return; + +	dsa_switch_shutdown(ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id mv88e6060_of_match[] = { @@ -303,6 +320,7 @@ static const struct of_device_id mv88e6060_of_match[] = {  static struct mdio_driver mv88e6060_driver = {  	.probe	= mv88e6060_probe,  	.remove = mv88e6060_remove, +	.shutdown = mv88e6060_shutdown,  	.mdiodrv.driver = {  		.name = "mv88e6060",  		.of_match_table = mv88e6060_of_match, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c45ca2473743..8dadcae93c9b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -12,6 +12,7 @@  #include <linux/bitfield.h>  #include <linux/delay.h> +#include <linux/dsa/mv88e6xxx.h>  #include <linux/etherdevice.h>  #include <linux/ethtool.h>  #include <linux/if_bridge.h> @@ -749,7 +750,11 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,  	ops = chip->info->ops;  	mv88e6xxx_reg_lock(chip); -	if ((!mv88e6xxx_port_ppu_updates(chip, port) || +	/* Internal PHYs propagate their configuration directly to the MAC. +	 * External PHYs depend on whether the PPU is enabled for this port. +	 */ +	if (((!mv88e6xxx_phy_is_internal(ds, port) && +	      !mv88e6xxx_port_ppu_updates(chip, port)) ||  	     mode == MLO_AN_FIXED) && ops->port_sync_link)  		err = ops->port_sync_link(chip, port, mode, false);  	mv88e6xxx_reg_unlock(chip); @@ -772,7 +777,12 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,  	ops = chip->info->ops;  	mv88e6xxx_reg_lock(chip); -	if (!mv88e6xxx_port_ppu_updates(chip, port) || mode == MLO_AN_FIXED) { +	/* Internal PHYs propagate their configuration directly to the MAC. +	 * External PHYs depend on whether the PPU is enabled for this port. +	 */ +	if ((!mv88e6xxx_phy_is_internal(ds, port) && +	     !mv88e6xxx_port_ppu_updates(chip, port)) || +	    mode == MLO_AN_FIXED) {  		/* FIXME: for an automedia port, should we force the link  		 * down here - what if the link comes up due to "other" media  		 * while we're bringing the port up, how is the exclusivity @@ -1677,6 +1687,30 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,  	return 0;  } +static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port) +{ +	struct dsa_port *dp = dsa_to_port(chip->ds, port); +	struct mv88e6xxx_port *p = &chip->ports[port]; +	u16 pvid = MV88E6XXX_VID_STANDALONE; +	bool drop_untagged = false; +	int err; + +	if (dp->bridge_dev) { +		if (br_vlan_enabled(dp->bridge_dev)) { +			pvid = p->bridge_pvid.vid; +			drop_untagged = !p->bridge_pvid.valid; +		} else { +			pvid = MV88E6XXX_VID_BRIDGED; +		} +	} + +	err = mv88e6xxx_port_set_pvid(chip, port, pvid); +	if (err) +		return err; + +	return mv88e6xxx_port_drop_untagged(chip, port, drop_untagged); +} +  static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,  					 bool vlan_filtering,  					 struct netlink_ext_ack *extack) @@ -1690,7 +1724,16 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,  		return -EOPNOTSUPP;  	mv88e6xxx_reg_lock(chip); +  	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); +	if (err) +		goto unlock; + +	err = mv88e6xxx_port_commit_pvid(chip, port); +	if (err) +		goto unlock; + +unlock:  	mv88e6xxx_reg_unlock(chip);  	return err; @@ -1725,11 +1768,15 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,  	u16 fid;  	int err; -	/* Null VLAN ID corresponds to the port private database */ +	/* Ports have two private address databases: one for when the port is +	 * standalone and one for when the port is under a bridge and the +	 * 802.1Q mode is disabled. When the port is standalone, DSA wants its +	 * address database to remain 100% empty, so we never load an ATU entry +	 * into a standalone port's database. Therefore, translate the null +	 * VLAN ID into the port's database used for VLAN-unaware bridging. +	 */  	if (vid == 0) { -		err = mv88e6xxx_port_get_fid(chip, port, &fid); -		if (err) -			return err; +		fid = MV88E6XXX_FID_BRIDGED;  	} else {  		err = mv88e6xxx_vtu_get(chip, vid, &vlan);  		if (err) @@ -2123,6 +2170,7 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,  	struct mv88e6xxx_chip *chip = ds->priv;  	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;  	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; +	struct mv88e6xxx_port *p = &chip->ports[port];  	bool warn;  	u8 member;  	int err; @@ -2156,13 +2204,21 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,  	}  	if (pvid) { -		err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid); -		if (err) { -			dev_err(ds->dev, "p%d: failed to set PVID %d\n", -				port, vlan->vid); +		p->bridge_pvid.vid = vlan->vid; +		p->bridge_pvid.valid = true; + +		err = mv88e6xxx_port_commit_pvid(chip, port); +		if (err) +			goto out; +	} else if (vlan->vid && p->bridge_pvid.vid == vlan->vid) { +		/* The old pvid was reinstalled as a non-pvid VLAN */ +		p->bridge_pvid.valid = false; + +		err = mv88e6xxx_port_commit_pvid(chip, port); +		if (err)  			goto out; -		}  	} +  out:  	mv88e6xxx_reg_unlock(chip); @@ -2212,6 +2268,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,  				   const struct switchdev_obj_port_vlan *vlan)  {  	struct mv88e6xxx_chip *chip = ds->priv; +	struct mv88e6xxx_port *p = &chip->ports[port];  	int err = 0;  	u16 pvid; @@ -2229,7 +2286,9 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,  		goto unlock;  	if (vlan->vid == pvid) { -		err = mv88e6xxx_port_set_pvid(chip, port, 0); +		p->bridge_pvid.valid = false; + +		err = mv88e6xxx_port_commit_pvid(chip, port);  		if (err)  			goto unlock;  	} @@ -2393,7 +2452,16 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,  	int err;  	mv88e6xxx_reg_lock(chip); +  	err = mv88e6xxx_bridge_map(chip, br); +	if (err) +		goto unlock; + +	err = mv88e6xxx_port_commit_pvid(chip, port); +	if (err) +		goto unlock; + +unlock:  	mv88e6xxx_reg_unlock(chip);  	return err; @@ -2403,11 +2471,20 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,  					struct net_device *br)  {  	struct mv88e6xxx_chip *chip = ds->priv; +	int err;  	mv88e6xxx_reg_lock(chip); +  	if (mv88e6xxx_bridge_map(chip, br) ||  	    mv88e6xxx_port_vlan_map(chip, port))  		dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); + +	err = mv88e6xxx_port_commit_pvid(chip, port); +	if (err) +		dev_err(ds->dev, +			"port %d failed to restore standalone pvid: %pe\n", +			port, ERR_PTR(err)); +  	mv88e6xxx_reg_unlock(chip);  } @@ -2834,8 +2911,8 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	if (err)  		return err; -	/* Port Control 2: don't force a good FCS, set the maximum frame size to -	 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or +	/* Port Control 2: don't force a good FCS, set the MTU size to +	 * 10222 bytes, disable 802.1q tags checking, don't discard tagged or  	 * untagged frames on this port, do a destination address lookup on all  	 * received packets as usual, disable ARP mirroring and don't send a  	 * copy of all transmitted/received frames on this port to the CPU. @@ -2853,8 +2930,22 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	if (err)  		return err; +	/* Associate MV88E6XXX_VID_BRIDGED with MV88E6XXX_FID_BRIDGED in the +	 * ATU by virtue of the fact that mv88e6xxx_atu_new() will pick it as +	 * the first free FID after MV88E6XXX_FID_STANDALONE. This will be used +	 * as the private PVID on ports under a VLAN-unaware bridge. +	 * Shared (DSA and CPU) ports must also be members of it, to translate +	 * the VID from the DSA tag into MV88E6XXX_FID_BRIDGED, instead of +	 * relying on their port default FID. +	 */ +	err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED, +				       MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED, +				       false); +	if (err) +		return err; +  	if (chip->info->ops->port_set_jumbo_size) { -		err = chip->info->ops->port_set_jumbo_size(chip, port, 10240); +		err = chip->info->ops->port_set_jumbo_size(chip, port, 10218);  		if (err)  			return err;  	} @@ -2925,7 +3016,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)  	 * database, and allow bidirectional communication between the  	 * CPU and DSA port(s), and the other ports.  	 */ -	err = mv88e6xxx_port_set_fid(chip, port, 0); +	err = mv88e6xxx_port_set_fid(chip, port, MV88E6XXX_FID_STANDALONE);  	if (err)  		return err; @@ -2944,10 +3035,10 @@ static int mv88e6xxx_get_max_mtu(struct dsa_switch *ds, int port)  	struct mv88e6xxx_chip *chip = ds->priv;  	if (chip->info->ops->port_set_jumbo_size) -		return 10240; +		return 10240 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN;  	else if (chip->info->ops->set_max_frame_size) -		return 1632; -	return 1522; +		return 1632 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN; +	return 1522 - VLAN_ETH_HLEN - EDSA_HLEN - ETH_FCS_LEN;  }  static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu) @@ -2955,6 +3046,9 @@ static int mv88e6xxx_change_mtu(struct dsa_switch *ds, int port, int new_mtu)  	struct mv88e6xxx_chip *chip = ds->priv;  	int ret = 0; +	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port)) +		new_mtu += EDSA_HLEN; +  	mv88e6xxx_reg_lock(chip);  	if (chip->info->ops->port_set_jumbo_size)  		ret = chip->info->ops->port_set_jumbo_size(chip, port, new_mtu); @@ -3071,7 +3165,7 @@ static void mv88e6xxx_teardown(struct dsa_switch *ds)  {  	mv88e6xxx_teardown_devlink_params(ds);  	dsa_devlink_resources_unregister(ds); -	mv88e6xxx_teardown_devlink_regions(ds); +	mv88e6xxx_teardown_devlink_regions_global(ds);  }  static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -3112,6 +3206,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)  		}  	} +	err = mv88e6xxx_vtu_setup(chip); +	if (err) +		goto unlock; +  	/* Setup Switch Port Registers */  	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {  		if (dsa_is_unused_port(ds, i)) @@ -3141,10 +3239,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)  	if (err)  		goto unlock; -	err = mv88e6xxx_vtu_setup(chip); -	if (err) -		goto unlock; -  	err = mv88e6xxx_pvt_setup(chip);  	if (err)  		goto unlock; @@ -3215,7 +3309,7 @@ unlock:  	if (err)  		goto out_resources; -	err = mv88e6xxx_setup_devlink_regions(ds); +	err = mv88e6xxx_setup_devlink_regions_global(ds);  	if (err)  		goto out_params; @@ -3229,6 +3323,16 @@ out_resources:  	return err;  } +static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port) +{ +	return mv88e6xxx_setup_devlink_regions_port(ds, port); +} + +static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port) +{ +	mv88e6xxx_teardown_devlink_regions_port(ds, port); +} +  /* prod_id for switch families which do not have a PHY model number */  static const u16 family_prod_id_table[] = {  	[MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341, @@ -3715,7 +3819,6 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {  	.port_set_ucast_flood = mv88e6352_port_set_ucast_flood,  	.port_set_mcast_flood = mv88e6352_port_set_mcast_flood,  	.port_set_ether_type = mv88e6351_port_set_ether_type, -	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,  	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,  	.port_pause_limit = mv88e6097_port_pause_limit,  	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, @@ -3740,6 +3843,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {  	.avb_ops = &mv88e6165_avb_ops,  	.ptp_ops = &mv88e6165_ptp_ops,  	.phylink_validate = mv88e6185_phylink_validate, +	.set_max_frame_size = mv88e6185_g1_set_max_frame_size,  };  static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -6116,6 +6220,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {  	.change_tag_protocol	= mv88e6xxx_change_tag_protocol,  	.setup			= mv88e6xxx_setup,  	.teardown		= mv88e6xxx_teardown, +	.port_setup		= mv88e6xxx_port_setup, +	.port_teardown		= mv88e6xxx_port_teardown,  	.phylink_validate	= mv88e6xxx_validate,  	.phylink_mac_link_state	= mv88e6xxx_serdes_pcs_get_state,  	.phylink_mac_config	= mv88e6xxx_mac_config, @@ -6389,7 +6495,12 @@ out:  static void mv88e6xxx_remove(struct mdio_device *mdiodev)  {  	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); -	struct mv88e6xxx_chip *chip = ds->priv; +	struct mv88e6xxx_chip *chip; + +	if (!ds) +		return; + +	chip = ds->priv;  	if (chip->info->ptp_support) {  		mv88e6xxx_hwtstamp_free(chip); @@ -6410,6 +6521,20 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)  		mv88e6xxx_g1_irq_free(chip);  	else  		mv88e6xxx_irq_poll_free(chip); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void mv88e6xxx_shutdown(struct mdio_device *mdiodev) +{ +	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + +	if (!ds) +		return; + +	dsa_switch_shutdown(ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id mv88e6xxx_of_match[] = { @@ -6433,6 +6558,7 @@ MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);  static struct mdio_driver mv88e6xxx_driver = {  	.probe	= mv88e6xxx_probe,  	.remove = mv88e6xxx_remove, +	.shutdown = mv88e6xxx_shutdown,  	.mdiodrv.driver = {  		.name = "mv88e6085",  		.of_match_table = mv88e6xxx_of_match, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 675b1f3e43b7..8271b8aa7b71 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -18,8 +18,12 @@  #include <linux/timecounter.h>  #include <net/dsa.h> +#define EDSA_HLEN		8  #define MV88E6XXX_N_FID		4096 +#define MV88E6XXX_FID_STANDALONE	0 +#define MV88E6XXX_FID_BRIDGED		1 +  /* PVT limits for 4-bit port and 5-bit switch */  #define MV88E6XXX_MAX_PVT_SWITCHES	32  #define MV88E6XXX_MAX_PVT_PORTS		16 @@ -245,9 +249,15 @@ struct mv88e6xxx_policy {  	u16 vid;  }; +struct mv88e6xxx_vlan { +	u16	vid; +	bool	valid; +}; +  struct mv88e6xxx_port {  	struct mv88e6xxx_chip *chip;  	int port; +	struct mv88e6xxx_vlan bridge_pvid;  	u64 serdes_stats[2];  	u64 atu_member_violation;  	u64 atu_miss_violation; diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c index 0c0f5ea6680c..381068395c63 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.c +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -647,26 +647,25 @@ static struct mv88e6xxx_region mv88e6xxx_regions[] = {  	},  }; -static void -mv88e6xxx_teardown_devlink_regions_global(struct mv88e6xxx_chip *chip) +void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)  { +	struct mv88e6xxx_chip *chip = ds->priv;  	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) +void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)  { +	struct mv88e6xxx_chip *chip = ds->priv; +  	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) +int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)  { +	struct mv88e6xxx_chip *chip = ds->priv;  	struct devlink_region *region;  	region = dsa_devlink_port_region_create(ds, @@ -681,40 +680,10 @@ static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds,  	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) +int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)  {  	bool (*cond)(struct mv88e6xxx_chip *chip); +	struct mv88e6xxx_chip *chip = ds->priv;  	struct devlink_region_ops *ops;  	struct devlink_region *region;  	u64 size; @@ -753,30 +722,6 @@ out:  	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) diff --git a/drivers/net/dsa/mv88e6xxx/devlink.h b/drivers/net/dsa/mv88e6xxx/devlink.h index 3d72db3dcf95..65ce6a6858b9 100644 --- a/drivers/net/dsa/mv88e6xxx/devlink.h +++ b/drivers/net/dsa/mv88e6xxx/devlink.h @@ -12,8 +12,10 @@ 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_setup_devlink_regions_global(struct dsa_switch *ds); +void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds); +int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port); +void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port);  int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,  			       struct devlink_info_req *req, diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 815b0f681d69..5848112036b0 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -232,6 +232,8 @@ int mv88e6185_g1_set_max_frame_size(struct mv88e6xxx_chip *chip, int mtu)  	u16 val;  	int err; +	mtu += ETH_HLEN + ETH_FCS_LEN; +  	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_CTL1, &val);  	if (err)  		return err; diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index f77e2ee64a60..d9817b20ea64 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -1257,6 +1257,27 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,  	return 0;  } +int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, +				 bool drop_untagged) +{ +	u16 old, new; +	int err; + +	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old); +	if (err) +		return err; + +	if (drop_untagged) +		new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED; +	else +		new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED; + +	if (new == old) +		return 0; + +	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new); +} +  int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)  {  	u16 reg; @@ -1277,6 +1298,8 @@ int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,  	u16 reg;  	int err; +	size += VLAN_ETH_HLEN + ETH_FCS_LEN; +  	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®);  	if (err)  		return err; diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index b10e5aebacf6..03382b66f800 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -423,6 +423,8 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,  			      phy_interface_t mode);  int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);  int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); +int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port, +				 bool drop_untagged);  int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);  int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,  				     int upstream_port); diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 3656e67af789..341236dcbdb4 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/* Copyright 2019-2021 NXP Semiconductors +/* Copyright 2019-2021 NXP   *   * This is an umbrella module for all network switches that are   * register-compatible with Ocelot and that perform I/O to their host CPU @@ -266,12 +266,12 @@ static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)   */  static int felix_setup_mmio_filtering(struct felix *felix)  { -	unsigned long user_ports = 0, cpu_ports = 0; +	unsigned long user_ports = dsa_user_ports(felix->ds);  	struct ocelot_vcap_filter *redirect_rule;  	struct ocelot_vcap_filter *tagging_rule;  	struct ocelot *ocelot = &felix->ocelot;  	struct dsa_switch *ds = felix->ds; -	int port, ret; +	int cpu = -1, port, ret;  	tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);  	if (!tagging_rule) @@ -284,12 +284,15 @@ static int felix_setup_mmio_filtering(struct felix *felix)  	}  	for (port = 0; port < ocelot->num_phys_ports; port++) { -		if (dsa_is_user_port(ds, port)) -			user_ports |= BIT(port); -		if (dsa_is_cpu_port(ds, port)) -			cpu_ports |= BIT(port); +		if (dsa_is_cpu_port(ds, port)) { +			cpu = port; +			break; +		}  	} +	if (cpu < 0) +		return -EINVAL; +  	tagging_rule->key_type = OCELOT_VCAP_KEY_ETYPE;  	*(__be16 *)tagging_rule->key.etype.etype.value = htons(ETH_P_1588);  	*(__be16 *)tagging_rule->key.etype.etype.mask = htons(0xffff); @@ -325,7 +328,7 @@ static int felix_setup_mmio_filtering(struct felix *felix)  		 * the CPU port module  		 */  		redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; -		redirect_rule->action.port_mask = cpu_ports; +		redirect_rule->action.port_mask = BIT(cpu);  	} else {  		/* Trap PTP packets only to the CPU port module (which is  		 * redirected to the NPI port) @@ -1074,6 +1077,101 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)  	return 0;  } +static void ocelot_port_purge_txtstamp_skb(struct ocelot *ocelot, int port, +					   struct sk_buff *skb) +{ +	struct ocelot_port *ocelot_port = ocelot->ports[port]; +	struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone; +	struct sk_buff *skb_match = NULL, *skb_tmp; +	unsigned long flags; + +	if (!clone) +		return; + +	spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags); + +	skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) { +		if (skb != clone) +			continue; +		__skb_unlink(skb, &ocelot_port->tx_skbs); +		skb_match = skb; +		break; +	} + +	spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags); + +	WARN_ONCE(!skb_match, +		  "Could not find skb clone in TX timestamping list\n"); +} + +#define work_to_xmit_work(w) \ +		container_of((w), struct felix_deferred_xmit_work, work) + +static void felix_port_deferred_xmit(struct kthread_work *work) +{ +	struct felix_deferred_xmit_work *xmit_work = work_to_xmit_work(work); +	struct dsa_switch *ds = xmit_work->dp->ds; +	struct sk_buff *skb = xmit_work->skb; +	u32 rew_op = ocelot_ptp_rew_op(skb); +	struct ocelot *ocelot = ds->priv; +	int port = xmit_work->dp->index; +	int retries = 10; + +	do { +		if (ocelot_can_inject(ocelot, 0)) +			break; + +		cpu_relax(); +	} while (--retries); + +	if (!retries) { +		dev_err(ocelot->dev, "port %d failed to inject skb\n", +			port); +		ocelot_port_purge_txtstamp_skb(ocelot, port, skb); +		kfree_skb(skb); +		return; +	} + +	ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); + +	consume_skb(skb); +	kfree(xmit_work); +} + +static int felix_port_setup_tagger_data(struct dsa_switch *ds, int port) +{ +	struct dsa_port *dp = dsa_to_port(ds, port); +	struct ocelot *ocelot = ds->priv; +	struct felix *felix = ocelot_to_felix(ocelot); +	struct felix_port *felix_port; + +	if (!dsa_port_is_user(dp)) +		return 0; + +	felix_port = kzalloc(sizeof(*felix_port), GFP_KERNEL); +	if (!felix_port) +		return -ENOMEM; + +	felix_port->xmit_worker = felix->xmit_worker; +	felix_port->xmit_work_fn = felix_port_deferred_xmit; + +	dp->priv = felix_port; + +	return 0; +} + +static void felix_port_teardown_tagger_data(struct dsa_switch *ds, int port) +{ +	struct dsa_port *dp = dsa_to_port(ds, port); +	struct felix_port *felix_port = dp->priv; + +	if (!felix_port) +		return; + +	dp->priv = NULL; +	kfree(felix_port); +} +  /* Hardware initialization done here so that we can allocate structures with   * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing   * us to allocate structures twice (leak memory) and map PCI memory twice @@ -1102,6 +1200,12 @@ static int felix_setup(struct dsa_switch *ds)  		}  	} +	felix->xmit_worker = kthread_create_worker(0, "felix_xmit"); +	if (IS_ERR(felix->xmit_worker)) { +		err = PTR_ERR(felix->xmit_worker); +		goto out_deinit_timestamp; +	} +  	for (port = 0; port < ds->num_ports; port++) {  		if (dsa_is_unused_port(ds, port))  			continue; @@ -1112,6 +1216,14 @@ static int felix_setup(struct dsa_switch *ds)  		 * bits of vlan tag.  		 */  		felix_port_qos_map_init(ocelot, port); + +		err = felix_port_setup_tagger_data(ds, port); +		if (err) { +			dev_err(ds->dev, +				"port %d failed to set up tagger data: %pe\n", +				port, ERR_PTR(err)); +			goto out_deinit_ports; +		}  	}  	err = ocelot_devlink_sb_register(ocelot); @@ -1126,6 +1238,7 @@ static int felix_setup(struct dsa_switch *ds)  		 * there's no real point in checking for errors.  		 */  		felix_set_tag_protocol(ds, port, felix->tag_proto); +		break;  	}  	ds->mtu_enforcement_ingress = true; @@ -1138,9 +1251,13 @@ out_deinit_ports:  		if (dsa_is_unused_port(ds, port))  			continue; +		felix_port_teardown_tagger_data(ds, port);  		ocelot_deinit_port(ocelot, port);  	} +	kthread_destroy_worker(felix->xmit_worker); + +out_deinit_timestamp:  	ocelot_deinit_timestamp(ocelot);  	ocelot_deinit(ocelot); @@ -1162,19 +1279,23 @@ static void felix_teardown(struct dsa_switch *ds)  			continue;  		felix_del_tag_protocol(ds, port, felix->tag_proto); +		break;  	} -	ocelot_devlink_sb_unregister(ocelot); -	ocelot_deinit_timestamp(ocelot); -	ocelot_deinit(ocelot); -  	for (port = 0; port < ocelot->num_phys_ports; port++) {  		if (dsa_is_unused_port(ds, port))  			continue; +		felix_port_teardown_tagger_data(ds, port);  		ocelot_deinit_port(ocelot, port);  	} +	kthread_destroy_worker(felix->xmit_worker); + +	ocelot_devlink_sb_unregister(ocelot); +	ocelot_deinit_timestamp(ocelot); +	ocelot_deinit(ocelot); +  	if (felix->info->mdio_bus_free)  		felix->info->mdio_bus_free(ocelot);  } @@ -1291,8 +1412,12 @@ static void felix_txtstamp(struct dsa_switch *ds, int port,  	if (!ocelot->ptp)  		return; -	if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) +	if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) { +		dev_err_ratelimited(ds->dev, +				    "port %d delivering skb without TX timestamp\n", +				    port);  		return; +	}  	if (clone)  		OCELOT_SKB_CB(skb)->clone = clone; diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 5854bab43327..be3e42e135c0 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -1,5 +1,5 @@  /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright 2019 NXP Semiconductors +/* Copyright 2019 NXP   */  #ifndef _MSCC_FELIX_H  #define _MSCC_FELIX_H @@ -62,6 +62,7 @@ struct felix {  	resource_size_t			switch_base;  	resource_size_t			imdio_base;  	enum dsa_tag_protocol		tag_proto; +	struct kthread_worker		*xmit_worker;  };  struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index f966a253d1c7..11b42fd812e4 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: (GPL-2.0 OR MIT)  /* Copyright 2017 Microsemi Corporation - * Copyright 2018-2019 NXP Semiconductors + * Copyright 2018-2019 NXP   */  #include <linux/fsl/enetc_mdio.h>  #include <soc/mscc/ocelot_qsys.h> @@ -1472,9 +1472,10 @@ err_pci_enable:  static void felix_pci_remove(struct pci_dev *pdev)  { -	struct felix *felix; +	struct felix *felix = pci_get_drvdata(pdev); -	felix = pci_get_drvdata(pdev); +	if (!felix) +		return;  	dsa_unregister_switch(felix->ds); @@ -1482,6 +1483,20 @@ static void felix_pci_remove(struct pci_dev *pdev)  	kfree(felix);  	pci_disable_device(pdev); + +	pci_set_drvdata(pdev, NULL); +} + +static void felix_pci_shutdown(struct pci_dev *pdev) +{ +	struct felix *felix = pci_get_drvdata(pdev); + +	if (!felix) +		return; + +	dsa_switch_shutdown(felix->ds); + +	pci_set_drvdata(pdev, NULL);  }  static struct pci_device_id felix_ids[] = { @@ -1498,6 +1513,7 @@ static struct pci_driver felix_vsc9959_pci_driver = {  	.id_table	= felix_ids,  	.probe		= felix_pci_probe,  	.remove		= felix_pci_remove, +	.shutdown	= felix_pci_shutdown,  };  module_pci_driver(felix_vsc9959_pci_driver); diff --git a/drivers/net/dsa/ocelot/seville_vsc9953.c b/drivers/net/dsa/ocelot/seville_vsc9953.c index deae923c8b7a..de1d34a1f1e4 100644 --- a/drivers/net/dsa/ocelot/seville_vsc9953.c +++ b/drivers/net/dsa/ocelot/seville_vsc9953.c @@ -1245,18 +1245,33 @@ err_alloc_felix:  static int seville_remove(struct platform_device *pdev)  { -	struct felix *felix; +	struct felix *felix = platform_get_drvdata(pdev); -	felix = platform_get_drvdata(pdev); +	if (!felix) +		return 0;  	dsa_unregister_switch(felix->ds);  	kfree(felix->ds);  	kfree(felix); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } +static void seville_shutdown(struct platform_device *pdev) +{ +	struct felix *felix = platform_get_drvdata(pdev); + +	if (!felix) +		return; + +	dsa_switch_shutdown(felix->ds); + +	platform_set_drvdata(pdev, NULL); +} +  static const struct of_device_id seville_of_match[] = {  	{ .compatible = "mscc,vsc9953-switch" },  	{ }, @@ -1266,6 +1281,7 @@ MODULE_DEVICE_TABLE(of, seville_of_match);  static struct platform_driver seville_vsc9953_driver = {  	.probe		= seville_probe,  	.remove		= seville_remove, +	.shutdown	= seville_shutdown,  	.driver = {  		.name		= "mscc_seville",  		.of_match_table	= of_match_ptr(seville_of_match), diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index 563d8a279030..a6bfb6abc51a 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -1083,6 +1083,9 @@ static void ar9331_sw_remove(struct mdio_device *mdiodev)  	struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev);  	unsigned int i; +	if (!priv) +		return; +  	for (i = 0; i < ARRAY_SIZE(priv->port); i++) {  		struct ar9331_sw_port *port = &priv->port[i]; @@ -1094,6 +1097,20 @@ static void ar9331_sw_remove(struct mdio_device *mdiodev)  	dsa_unregister_switch(&priv->ds);  	reset_control_assert(priv->sw_reset); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void ar9331_sw_shutdown(struct mdio_device *mdiodev) +{ +	struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(&priv->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id ar9331_sw_of_match[] = { @@ -1104,6 +1121,7 @@ static const struct of_device_id ar9331_sw_of_match[] = {  static struct mdio_driver ar9331_sw_mdio_driver = {  	.probe = ar9331_sw_probe,  	.remove = ar9331_sw_remove, +	.shutdown = ar9331_sw_shutdown,  	.mdiodrv.driver = {  		.name = AR9331_SW_NAME,  		.of_match_table = ar9331_sw_of_match, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index bda5a9bf4f52..a984f06f6f04 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -1880,10 +1880,27 @@ qca8k_sw_remove(struct mdio_device *mdiodev)  	struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev);  	int i; +	if (!priv) +		return; +  	for (i = 0; i < QCA8K_NUM_PORTS; i++)  		qca8k_port_set_status(priv, i, 0);  	dsa_unregister_switch(priv->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void qca8k_sw_shutdown(struct mdio_device *mdiodev) +{ +	struct qca8k_priv *priv = dev_get_drvdata(&mdiodev->dev); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  #ifdef CONFIG_PM_SLEEP @@ -1940,6 +1957,7 @@ static const struct of_device_id qca8k_of_match[] = {  static struct mdio_driver qca8kmdio_driver = {  	.probe  = qca8k_sw_probe,  	.remove = qca8k_sw_remove, +	.shutdown = qca8k_sw_shutdown,  	.mdiodrv.driver = {  		.name = "qca8k",  		.of_match_table = qca8k_of_match, diff --git a/drivers/net/dsa/realtek-smi-core.c b/drivers/net/dsa/realtek-smi-core.c index 8e49d4f85d48..2fcfd917b876 100644 --- a/drivers/net/dsa/realtek-smi-core.c +++ b/drivers/net/dsa/realtek-smi-core.c @@ -368,7 +368,7 @@ int realtek_smi_setup_mdio(struct realtek_smi *smi)  	smi->slave_mii_bus->parent = smi->dev;  	smi->ds->slave_mii_bus = smi->slave_mii_bus; -	ret = of_mdiobus_register(smi->slave_mii_bus, mdio_np); +	ret = devm_of_mdiobus_register(smi->dev, smi->slave_mii_bus, mdio_np);  	if (ret) {  		dev_err(smi->dev, "unable to register MDIO bus %s\n",  			smi->slave_mii_bus->id); @@ -464,16 +464,33 @@ static int realtek_smi_probe(struct platform_device *pdev)  static int realtek_smi_remove(struct platform_device *pdev)  { -	struct realtek_smi *smi = dev_get_drvdata(&pdev->dev); +	struct realtek_smi *smi = platform_get_drvdata(pdev); + +	if (!smi) +		return 0;  	dsa_unregister_switch(smi->ds);  	if (smi->slave_mii_bus)  		of_node_put(smi->slave_mii_bus->dev.of_node);  	gpiod_set_value(smi->reset, 1); +	platform_set_drvdata(pdev, NULL); +  	return 0;  } +static void realtek_smi_shutdown(struct platform_device *pdev) +{ +	struct realtek_smi *smi = platform_get_drvdata(pdev); + +	if (!smi) +		return; + +	dsa_switch_shutdown(smi->ds); + +	platform_set_drvdata(pdev, NULL); +} +  static const struct of_device_id realtek_smi_of_match[] = {  	{  		.compatible = "realtek,rtl8366rb", @@ -495,6 +512,7 @@ static struct platform_driver realtek_smi_driver = {  	},  	.probe  = realtek_smi_probe,  	.remove = realtek_smi_remove, +	.shutdown = realtek_smi_shutdown,  };  module_platform_driver(realtek_smi_driver); diff --git a/drivers/net/dsa/sja1105/sja1105_clocking.c b/drivers/net/dsa/sja1105/sja1105_clocking.c index 387a1f2f161c..5bbf1707f2af 100644 --- a/drivers/net/dsa/sja1105/sja1105_clocking.c +++ b/drivers/net/dsa/sja1105/sja1105_clocking.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: BSD-3-Clause -/* Copyright (c) 2016-2018, NXP Semiconductors +/* Copyright 2016-2018 NXP   * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>   */  #include <linux/packing.h> diff --git a/drivers/net/dsa/sja1105/sja1105_devlink.c b/drivers/net/dsa/sja1105/sja1105_devlink.c index 05c7f4ca3b1a..0569ff066634 100644 --- a/drivers/net/dsa/sja1105/sja1105_devlink.c +++ b/drivers/net/dsa/sja1105/sja1105_devlink.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0  /* Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> - * Copyright 2020 NXP Semiconductors + * Copyright 2020 NXP   */  #include "sja1105.h" diff --git a/drivers/net/dsa/sja1105/sja1105_flower.c b/drivers/net/dsa/sja1105/sja1105_flower.c index 6c10ffa968ce..72b9b39b0989 100644 --- a/drivers/net/dsa/sja1105/sja1105_flower.c +++ b/drivers/net/dsa/sja1105/sja1105_flower.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/* Copyright 2020, NXP Semiconductors +/* Copyright 2020 NXP   */  #include "sja1105.h"  #include "sja1105_vl.h" diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 2f8cc6686c38..924c3f129992 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -3117,7 +3117,7 @@ static void sja1105_teardown(struct dsa_switch *ds)  	sja1105_static_config_free(&priv->static_config);  } -const struct dsa_switch_ops sja1105_switch_ops = { +static const struct dsa_switch_ops sja1105_switch_ops = {  	.get_tag_protocol	= sja1105_get_tag_protocol,  	.setup			= sja1105_setup,  	.teardown		= sja1105_teardown, @@ -3166,7 +3166,6 @@ const struct dsa_switch_ops sja1105_switch_ops = {  	.port_bridge_tx_fwd_offload = dsa_tag_8021q_bridge_tx_fwd_offload,  	.port_bridge_tx_fwd_unoffload = dsa_tag_8021q_bridge_tx_fwd_unoffload,  }; -EXPORT_SYMBOL_GPL(sja1105_switch_ops);  static const struct of_device_id sja1105_dt_ids[]; @@ -3335,13 +3334,29 @@ static int sja1105_probe(struct spi_device *spi)  static int sja1105_remove(struct spi_device *spi)  {  	struct sja1105_private *priv = spi_get_drvdata(spi); -	struct dsa_switch *ds = priv->ds; -	dsa_unregister_switch(ds); +	if (!priv) +		return 0; + +	dsa_unregister_switch(priv->ds); + +	spi_set_drvdata(spi, NULL);  	return 0;  } +static void sja1105_shutdown(struct spi_device *spi) +{ +	struct sja1105_private *priv = spi_get_drvdata(spi); + +	if (!priv) +		return; + +	dsa_switch_shutdown(priv->ds); + +	spi_set_drvdata(spi, NULL); +} +  static const struct of_device_id sja1105_dt_ids[] = {  	{ .compatible = "nxp,sja1105e", .data = &sja1105e_info },  	{ .compatible = "nxp,sja1105t", .data = &sja1105t_info }, @@ -3365,6 +3380,7 @@ static struct spi_driver sja1105_driver = {  	},  	.probe  = sja1105_probe,  	.remove = sja1105_remove, +	.shutdown = sja1105_shutdown,  };  module_spi_driver(sja1105_driver); diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c index 705d3900e43a..215dd17ca790 100644 --- a/drivers/net/dsa/sja1105/sja1105_mdio.c +++ b/drivers/net/dsa/sja1105/sja1105_mdio.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/* Copyright 2021, NXP Semiconductors +/* Copyright 2021 NXP   */  #include <linux/pcs/pcs-xpcs.h>  #include <linux/of_mdio.h> diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.c b/drivers/net/dsa/sja1105/sja1105_ptp.c index 691f6dd7e669..54396992a919 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.c +++ b/drivers/net/dsa/sja1105/sja1105_ptp.c @@ -64,6 +64,7 @@ enum sja1105_ptp_clk_mode {  static int sja1105_change_rxtstamping(struct sja1105_private *priv,  				      bool on)  { +	struct sja1105_tagger_data *tagger_data = &priv->tagger_data;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	struct sja1105_general_params_entry *general_params;  	struct sja1105_table *table; @@ -79,7 +80,7 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,  		priv->tagger_data.stampable_skb = NULL;  	}  	ptp_cancel_worker_sync(ptp_data->clock); -	skb_queue_purge(&ptp_data->skb_txtstamp_queue); +	skb_queue_purge(&tagger_data->skb_txtstamp_queue);  	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);  	return sja1105_static_config_reload(priv, SJA1105_RX_HWTSTAMPING); @@ -452,40 +453,6 @@ bool sja1105_port_rxtstamp(struct dsa_switch *ds, int port,  	return priv->info->rxtstamp(ds, port, skb);  } -void sja1110_process_meta_tstamp(struct dsa_switch *ds, int port, u8 ts_id, -				 enum sja1110_meta_tstamp dir, u64 tstamp) -{ -	struct sja1105_private *priv = ds->priv; -	struct sja1105_ptp_data *ptp_data = &priv->ptp_data; -	struct sk_buff *skb, *skb_tmp, *skb_match = NULL; -	struct skb_shared_hwtstamps shwt = {0}; - -	/* We don't care about RX timestamps on the CPU port */ -	if (dir == SJA1110_META_TSTAMP_RX) -		return; - -	spin_lock(&ptp_data->skb_txtstamp_queue.lock); - -	skb_queue_walk_safe(&ptp_data->skb_txtstamp_queue, skb, skb_tmp) { -		if (SJA1105_SKB_CB(skb)->ts_id != ts_id) -			continue; - -		__skb_unlink(skb, &ptp_data->skb_txtstamp_queue); -		skb_match = skb; - -		break; -	} - -	spin_unlock(&ptp_data->skb_txtstamp_queue.lock); - -	if (WARN_ON(!skb_match)) -		return; - -	shwt.hwtstamp = ns_to_ktime(sja1105_ticks_to_ns(tstamp)); -	skb_complete_tx_timestamp(skb_match, &shwt); -} -EXPORT_SYMBOL_GPL(sja1110_process_meta_tstamp); -  /* In addition to cloning the skb which is done by the common   * sja1105_port_txtstamp, we need to generate a timestamp ID and save the   * packet to the TX timestamping queue. @@ -494,7 +461,6 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  {  	struct sk_buff *clone = SJA1105_SKB_CB(skb)->clone;  	struct sja1105_private *priv = ds->priv; -	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	struct sja1105_port *sp = &priv->ports[port];  	u8 ts_id; @@ -510,7 +476,7 @@ void sja1110_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb)  	spin_unlock(&sp->data->meta_lock); -	skb_queue_tail(&ptp_data->skb_txtstamp_queue, clone); +	skb_queue_tail(&sp->data->skb_txtstamp_queue, clone);  }  /* Called from dsa_skb_tx_timestamp. This callback is just to clone @@ -953,7 +919,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  	/* Only used on SJA1105 */  	skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);  	/* Only used on SJA1110 */ -	skb_queue_head_init(&ptp_data->skb_txtstamp_queue); +	skb_queue_head_init(&tagger_data->skb_txtstamp_queue);  	spin_lock_init(&tagger_data->meta_lock);  	ptp_data->clock = ptp_clock_register(&ptp_data->caps, ds->dev); @@ -971,6 +937,7 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)  void sja1105_ptp_clock_unregister(struct dsa_switch *ds)  {  	struct sja1105_private *priv = ds->priv; +	struct sja1105_tagger_data *tagger_data = &priv->tagger_data;  	struct sja1105_ptp_data *ptp_data = &priv->ptp_data;  	if (IS_ERR_OR_NULL(ptp_data->clock)) @@ -978,7 +945,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds)  	del_timer_sync(&ptp_data->extts_timer);  	ptp_cancel_worker_sync(ptp_data->clock); -	skb_queue_purge(&ptp_data->skb_txtstamp_queue); +	skb_queue_purge(&tagger_data->skb_txtstamp_queue);  	skb_queue_purge(&ptp_data->skb_rxtstamp_queue);  	ptp_clock_unregister(ptp_data->clock);  	ptp_data->clock = NULL; diff --git a/drivers/net/dsa/sja1105/sja1105_ptp.h b/drivers/net/dsa/sja1105/sja1105_ptp.h index 3c874bb4c17b..3ae6b9fdd492 100644 --- a/drivers/net/dsa/sja1105/sja1105_ptp.h +++ b/drivers/net/dsa/sja1105/sja1105_ptp.h @@ -8,21 +8,6 @@  #if IS_ENABLED(CONFIG_NET_DSA_SJA1105_PTP) -/* Timestamps are in units of 8 ns clock ticks (equivalent to - * a fixed 125 MHz clock). - */ -#define SJA1105_TICK_NS			8 - -static inline s64 ns_to_sja1105_ticks(s64 ns) -{ -	return ns / SJA1105_TICK_NS; -} - -static inline s64 sja1105_ticks_to_ns(s64 ticks) -{ -	return ticks * SJA1105_TICK_NS; -} -  /* Calculate the first base_time in the future that satisfies this   * relationship:   * @@ -77,10 +62,6 @@ struct sja1105_ptp_data {  	struct timer_list extts_timer;  	/* Used only on SJA1105 to reconstruct partial timestamps */  	struct sk_buff_head skb_rxtstamp_queue; -	/* Used on SJA1110 where meta frames are generated only for -	 * 2-step TX timestamps -	 */ -	struct sk_buff_head skb_txtstamp_queue;  	struct ptp_clock_info caps;  	struct ptp_clock *clock;  	struct sja1105_ptp_cmd cmd; diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index d60a530d0272..d3c9ad6d39d4 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: BSD-3-Clause -/* Copyright (c) 2016-2018, NXP Semiconductors +/* Copyright 2016-2018 NXP   * Copyright (c) 2018, Sensor-Technik Wiedemann GmbH   * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>   */ diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.c b/drivers/net/dsa/sja1105/sja1105_static_config.c index 7a422ef4deb6..baba204ad62f 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.c +++ b/drivers/net/dsa/sja1105/sja1105_static_config.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: BSD-3-Clause -/* Copyright (c) 2016-2018, NXP Semiconductors +/* Copyright 2016-2018 NXP   * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>   */  #include "sja1105_static_config.h" diff --git a/drivers/net/dsa/sja1105/sja1105_static_config.h b/drivers/net/dsa/sja1105/sja1105_static_config.h index bce0f5c03d0b..6a372d5f22ae 100644 --- a/drivers/net/dsa/sja1105/sja1105_static_config.h +++ b/drivers/net/dsa/sja1105/sja1105_static_config.h @@ -1,5 +1,5 @@  /* SPDX-License-Identifier: BSD-3-Clause */ -/* Copyright (c) 2016-2018, NXP Semiconductors +/* Copyright 2016-2018 NXP   * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com>   */  #ifndef _SJA1105_STATIC_CONFIG_H diff --git a/drivers/net/dsa/sja1105/sja1105_vl.c b/drivers/net/dsa/sja1105/sja1105_vl.c index ec7b65daec20..6802f4057cc0 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.c +++ b/drivers/net/dsa/sja1105/sja1105_vl.c @@ -1,5 +1,5 @@  // SPDX-License-Identifier: GPL-2.0 -/* Copyright 2020, NXP Semiconductors +/* Copyright 2020 NXP   */  #include <net/tc_act/tc_gate.h>  #include <linux/dsa/8021q.h> diff --git a/drivers/net/dsa/sja1105/sja1105_vl.h b/drivers/net/dsa/sja1105/sja1105_vl.h index 173d78963fed..51fba0dce91a 100644 --- a/drivers/net/dsa/sja1105/sja1105_vl.h +++ b/drivers/net/dsa/sja1105/sja1105_vl.h @@ -1,5 +1,5 @@  /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright 2020, NXP Semiconductors +/* Copyright 2020 NXP   */  #ifndef _SJA1105_VL_H  #define _SJA1105_VL_H diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 19ce4aa0973b..a4b1447ff055 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -1225,6 +1225,12 @@ int vsc73xx_remove(struct vsc73xx *vsc)  }  EXPORT_SYMBOL(vsc73xx_remove); +void vsc73xx_shutdown(struct vsc73xx *vsc) +{ +	dsa_switch_shutdown(vsc->ds); +} +EXPORT_SYMBOL(vsc73xx_shutdown); +  MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");  MODULE_DESCRIPTION("Vitesse VSC7385/7388/7395/7398 driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/vitesse-vsc73xx-platform.c b/drivers/net/dsa/vitesse-vsc73xx-platform.c index 2a57f337b2a2..fe4b154a0a57 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-platform.c +++ b/drivers/net/dsa/vitesse-vsc73xx-platform.c @@ -116,7 +116,26 @@ static int vsc73xx_platform_remove(struct platform_device *pdev)  {  	struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev); -	return vsc73xx_remove(&vsc_platform->vsc); +	if (!vsc_platform) +		return 0; + +	vsc73xx_remove(&vsc_platform->vsc); + +	platform_set_drvdata(pdev, NULL); + +	return 0; +} + +static void vsc73xx_platform_shutdown(struct platform_device *pdev) +{ +	struct vsc73xx_platform *vsc_platform = platform_get_drvdata(pdev); + +	if (!vsc_platform) +		return; + +	vsc73xx_shutdown(&vsc_platform->vsc); + +	platform_set_drvdata(pdev, NULL);  }  static const struct vsc73xx_ops vsc73xx_platform_ops = { @@ -144,6 +163,7 @@ MODULE_DEVICE_TABLE(of, vsc73xx_of_match);  static struct platform_driver vsc73xx_platform_driver = {  	.probe = vsc73xx_platform_probe,  	.remove = vsc73xx_platform_remove, +	.shutdown = vsc73xx_platform_shutdown,  	.driver = {  		.name = "vsc73xx-platform",  		.of_match_table = vsc73xx_of_match, diff --git a/drivers/net/dsa/vitesse-vsc73xx-spi.c b/drivers/net/dsa/vitesse-vsc73xx-spi.c index 81eca4a5781d..645398901e05 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-spi.c +++ b/drivers/net/dsa/vitesse-vsc73xx-spi.c @@ -163,7 +163,26 @@ static int vsc73xx_spi_remove(struct spi_device *spi)  {  	struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi); -	return vsc73xx_remove(&vsc_spi->vsc); +	if (!vsc_spi) +		return 0; + +	vsc73xx_remove(&vsc_spi->vsc); + +	spi_set_drvdata(spi, NULL); + +	return 0; +} + +static void vsc73xx_spi_shutdown(struct spi_device *spi) +{ +	struct vsc73xx_spi *vsc_spi = spi_get_drvdata(spi); + +	if (!vsc_spi) +		return; + +	vsc73xx_shutdown(&vsc_spi->vsc); + +	spi_set_drvdata(spi, NULL);  }  static const struct vsc73xx_ops vsc73xx_spi_ops = { @@ -191,6 +210,7 @@ MODULE_DEVICE_TABLE(of, vsc73xx_of_match);  static struct spi_driver vsc73xx_spi_driver = {  	.probe = vsc73xx_spi_probe,  	.remove = vsc73xx_spi_remove, +	.shutdown = vsc73xx_spi_shutdown,  	.driver = {  		.name = "vsc73xx-spi",  		.of_match_table = vsc73xx_of_match, diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h index 7478f8d4e0a9..30b951504e65 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.h +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -27,3 +27,4 @@ struct vsc73xx_ops {  int vsc73xx_is_addr_valid(u8 block, u8 subblock);  int vsc73xx_probe(struct vsc73xx *vsc);  int vsc73xx_remove(struct vsc73xx *vsc); +void vsc73xx_shutdown(struct vsc73xx *vsc); diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c index 130abb0f1438..469420941054 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.c +++ b/drivers/net/dsa/xrs700x/xrs700x.c @@ -822,6 +822,12 @@ void xrs700x_switch_remove(struct xrs700x *priv)  }  EXPORT_SYMBOL(xrs700x_switch_remove); +void xrs700x_switch_shutdown(struct xrs700x *priv) +{ +	dsa_switch_shutdown(priv->ds); +} +EXPORT_SYMBOL(xrs700x_switch_shutdown); +  MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");  MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/xrs700x/xrs700x.h b/drivers/net/dsa/xrs700x/xrs700x.h index ff62cf61b091..4d58257471d2 100644 --- a/drivers/net/dsa/xrs700x/xrs700x.h +++ b/drivers/net/dsa/xrs700x/xrs700x.h @@ -40,3 +40,4 @@ struct xrs700x {  struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv);  int xrs700x_switch_register(struct xrs700x *priv);  void xrs700x_switch_remove(struct xrs700x *priv); +void xrs700x_switch_shutdown(struct xrs700x *priv); diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c index 489d9385b4f0..6deae388a0d6 100644 --- a/drivers/net/dsa/xrs700x/xrs700x_i2c.c +++ b/drivers/net/dsa/xrs700x/xrs700x_i2c.c @@ -109,11 +109,28 @@ static int xrs700x_i2c_remove(struct i2c_client *i2c)  {  	struct xrs700x *priv = i2c_get_clientdata(i2c); +	if (!priv) +		return 0; +  	xrs700x_switch_remove(priv); +	i2c_set_clientdata(i2c, NULL); +  	return 0;  } +static void xrs700x_i2c_shutdown(struct i2c_client *i2c) +{ +	struct xrs700x *priv = i2c_get_clientdata(i2c); + +	if (!priv) +		return; + +	xrs700x_switch_shutdown(priv); + +	i2c_set_clientdata(i2c, NULL); +} +  static const struct i2c_device_id xrs700x_i2c_id[] = {  	{ "xrs700x-switch", 0 },  	{}, @@ -137,6 +154,7 @@ static struct i2c_driver xrs700x_i2c_driver = {  	},  	.probe	= xrs700x_i2c_probe,  	.remove	= xrs700x_i2c_remove, +	.shutdown = xrs700x_i2c_shutdown,  	.id_table = xrs700x_i2c_id,  }; diff --git a/drivers/net/dsa/xrs700x/xrs700x_mdio.c b/drivers/net/dsa/xrs700x/xrs700x_mdio.c index 44f58bee04a4..d01cf1073d49 100644 --- a/drivers/net/dsa/xrs700x/xrs700x_mdio.c +++ b/drivers/net/dsa/xrs700x/xrs700x_mdio.c @@ -136,7 +136,24 @@ static void xrs700x_mdio_remove(struct mdio_device *mdiodev)  {  	struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev); +	if (!priv) +		return; +  	xrs700x_switch_remove(priv); + +	dev_set_drvdata(&mdiodev->dev, NULL); +} + +static void xrs700x_mdio_shutdown(struct mdio_device *mdiodev) +{ +	struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev); + +	if (!priv) +		return; + +	xrs700x_switch_shutdown(priv); + +	dev_set_drvdata(&mdiodev->dev, NULL);  }  static const struct of_device_id __maybe_unused xrs700x_mdio_dt_ids[] = { @@ -155,6 +172,7 @@ static struct mdio_driver xrs700x_mdio_driver = {  	},  	.probe	= xrs700x_mdio_probe,  	.remove	= xrs700x_mdio_remove, +	.shutdown = xrs700x_mdio_shutdown,  };  mdio_module_driver(xrs700x_mdio_driver); | 
