diff options
Diffstat (limited to 'net/dsa/dsa.c')
| -rw-r--r-- | net/dsa/dsa.c | 115 | 
1 files changed, 95 insertions, 20 deletions
| diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 76e3800765f8..1eba07feb34a 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -22,6 +22,7 @@  #include <linux/of_platform.h>  #include <linux/of_net.h>  #include <linux/sysfs.h> +#include <linux/phy_fixed.h>  #include "dsa_priv.h"  char dsa_driver_version[] = "0.1"; @@ -305,7 +306,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)  	if (ret < 0)  		goto out; -	ds->slave_mii_bus = mdiobus_alloc(); +	ds->slave_mii_bus = devm_mdiobus_alloc(parent);  	if (ds->slave_mii_bus == NULL) {  		ret = -ENOMEM;  		goto out; @@ -314,7 +315,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)  	ret = mdiobus_register(ds->slave_mii_bus);  	if (ret < 0) -		goto out_free; +		goto out;  	/* @@ -326,8 +327,8 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)  		ret = dsa_slave_create(ds, parent, i, pd->port_names[i]);  		if (ret < 0) { -			netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n", -				   index, i, pd->port_names[i]); +			netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n", +				   index, i, pd->port_names[i], ret);  			ret = 0;  		}  	} @@ -367,10 +368,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent)  	return ret; -out_free: -	mdiobus_free(ds->slave_mii_bus);  out: -	kfree(ds);  	return ret;  } @@ -400,7 +398,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,  	/*  	 * Allocate and initialise switch state.  	 */ -	ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL); +	ds = devm_kzalloc(parent, sizeof(*ds) + drv->priv_size, GFP_KERNEL);  	if (ds == NULL)  		return ERR_PTR(-ENOMEM); @@ -420,10 +418,47 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,  static void dsa_switch_destroy(struct dsa_switch *ds)  { +	struct device_node *port_dn; +	struct phy_device *phydev; +	struct dsa_chip_data *cd = ds->pd; +	int port; +  #ifdef CONFIG_NET_DSA_HWMON  	if (ds->hwmon_dev)  		hwmon_device_unregister(ds->hwmon_dev);  #endif + +	/* Disable configuration of the CPU and DSA ports */ +	for (port = 0; port < DSA_MAX_PORTS; port++) { +		if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) +			continue; + +		port_dn = cd->port_dn[port]; +		if (of_phy_is_fixed_link(port_dn)) { +			phydev = of_phy_find_device(port_dn); +			if (phydev) { +				int addr = phydev->addr; + +				phy_device_free(phydev); +				of_node_put(port_dn); +				fixed_phy_del(addr); +			} +		} +	} + +	/* Destroy network devices for physical switch ports. */ +	for (port = 0; port < DSA_MAX_PORTS; port++) { +		if (!(ds->phys_port_mask & (1 << port))) +			continue; + +		if (!ds->ports[port]) +			continue; + +		unregister_netdev(ds->ports[port]); +		free_netdev(ds->ports[port]); +	} + +	mdiobus_unregister(ds->slave_mii_bus);  }  #ifdef CONFIG_PM_SLEEP @@ -634,6 +669,10 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)  			port_index++;  		}  		kfree(pd->chip[i].rtable); + +		/* Drop our reference to the MDIO bus device */ +		if (pd->chip[i].host_dev) +			put_device(pd->chip[i].host_dev);  	}  	kfree(pd->chip);  } @@ -661,16 +700,22 @@ static int dsa_of_probe(struct device *dev)  		return -EPROBE_DEFER;  	ethernet = of_parse_phandle(np, "dsa,ethernet", 0); -	if (!ethernet) -		return -EINVAL; +	if (!ethernet) { +		ret = -EINVAL; +		goto out_put_mdio; +	}  	ethernet_dev = of_find_net_device_by_node(ethernet); -	if (!ethernet_dev) -		return -EPROBE_DEFER; +	if (!ethernet_dev) { +		ret = -EPROBE_DEFER; +		goto out_put_mdio; +	}  	pd = kzalloc(sizeof(*pd), GFP_KERNEL); -	if (!pd) -		return -ENOMEM; +	if (!pd) { +		ret = -ENOMEM; +		goto out_put_ethernet; +	}  	dev->platform_data = pd;  	pd->of_netdev = ethernet_dev; @@ -691,7 +736,9 @@ static int dsa_of_probe(struct device *dev)  		cd = &pd->chip[chip_index];  		cd->of_node = child; -		cd->host_dev = &mdio_bus->dev; + +		/* When assigning the host device, increment its refcount */ +		cd->host_dev = get_device(&mdio_bus->dev);  		sw_addr = of_get_property(child, "reg", NULL);  		if (!sw_addr) @@ -711,6 +758,12 @@ static int dsa_of_probe(struct device *dev)  				ret = -EPROBE_DEFER;  				goto out_free_chip;  			} + +			/* Drop the mdio_bus device ref, replacing the host +			 * device with the mdio_bus_switch device, keeping +			 * the refcount from of_mdio_find_bus() above. +			 */ +			put_device(cd->host_dev);  			cd->host_dev = &mdio_bus_switch->dev;  		} @@ -744,6 +797,10 @@ static int dsa_of_probe(struct device *dev)  		}  	} +	/* The individual chips hold their own refcount on the mdio bus, +	 * so drop ours */ +	put_device(&mdio_bus->dev); +  	return 0;  out_free_chip: @@ -751,6 +808,10 @@ out_free_chip:  out_free:  	kfree(pd);  	dev->platform_data = NULL; +out_put_ethernet: +	put_device(ðernet_dev->dev); +out_put_mdio: +	put_device(&mdio_bus->dev);  	return ret;  } @@ -762,6 +823,7 @@ static void dsa_of_remove(struct device *dev)  		return;  	dsa_of_free_platform_data(pd); +	put_device(&pd->of_netdev->dev);  	kfree(pd);  }  #else @@ -775,10 +837,11 @@ static inline void dsa_of_remove(struct device *dev)  }  #endif -static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, -			  struct device *parent, struct dsa_platform_data *pd) +static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, +			 struct device *parent, struct dsa_platform_data *pd)  {  	int i; +	unsigned configured = 0;  	dst->pd = pd;  	dst->master_netdev = dev; @@ -798,9 +861,17 @@ static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,  		dst->ds[i] = ds;  		if (ds->drv->poll_link != NULL)  			dst->link_poll_needed = 1; + +		++configured;  	}  	/* +	 * If no switch was found, exit cleanly +	 */ +	if (!configured) +		return -EPROBE_DEFER; + +	/*  	 * If we use a tagging format that doesn't have an ethertype  	 * field, make sure that all packets from this point on get  	 * sent to the tag format's receive function. @@ -816,6 +887,8 @@ static void dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev,  		dst->link_poll_timer.expires = round_jiffies(jiffies + HZ);  		add_timer(&dst->link_poll_timer);  	} + +	return 0;  }  static int dsa_probe(struct platform_device *pdev) @@ -856,7 +929,7 @@ static int dsa_probe(struct platform_device *pdev)  		goto out;  	} -	dst = kzalloc(sizeof(*dst), GFP_KERNEL); +	dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);  	if (dst == NULL) {  		dev_put(dev);  		ret = -ENOMEM; @@ -865,7 +938,9 @@ static int dsa_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, dst); -	dsa_setup_dst(dst, dev, &pdev->dev, pd); +	ret = dsa_setup_dst(dst, dev, &pdev->dev, pd); +	if (ret) +		goto out;  	return 0; @@ -887,7 +962,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst)  	for (i = 0; i < dst->pd->nr_chips; i++) {  		struct dsa_switch *ds = dst->ds[i]; -		if (ds != NULL) +		if (ds)  			dsa_switch_destroy(ds);  	}  } |