diff options
Diffstat (limited to 'net/dsa/dsa2.c')
| -rw-r--r-- | net/dsa/dsa2.c | 105 | 
1 files changed, 87 insertions, 18 deletions
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 3c3e56a1f34d..b71e87909f0e 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -392,7 +392,7 @@ static int dsa_port_setup(struct dsa_port *dp)  		break;  	case DSA_PORT_TYPE_USER: -		dp->mac = of_get_mac_address(dp->dn); +		of_get_mac_address(dp->dn, dp->mac);  		err = dsa_slave_create(dp);  		if (err)  			break; @@ -668,6 +668,30 @@ static const struct devlink_ops dsa_devlink_ops = {  	.sb_occ_tc_port_bind_get	= dsa_devlink_sb_occ_tc_port_bind_get,  }; +static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds) +{ +	const struct dsa_device_ops *tag_ops = ds->dst->tag_ops; +	struct dsa_switch_tree *dst = ds->dst; +	int port, err; + +	if (tag_ops->proto == dst->default_proto) +		return 0; + +	for (port = 0; port < ds->num_ports; port++) { +		if (!dsa_is_cpu_port(ds, port)) +			continue; + +		err = ds->ops->change_tag_protocol(ds, port, tag_ops->proto); +		if (err) { +			dev_err(ds->dev, "Unable to use tag protocol \"%s\": %pe\n", +				tag_ops->name, ERR_PTR(err)); +			return err; +		} +	} + +	return 0; +} +  static int dsa_switch_setup(struct dsa_switch *ds)  {  	struct dsa_devlink_priv *dl_priv; @@ -718,6 +742,10 @@ static int dsa_switch_setup(struct dsa_switch *ds)  	if (err < 0)  		goto unregister_notifier; +	err = dsa_switch_setup_tag_protocol(ds); +	if (err) +		goto teardown; +  	devlink_params_publish(ds->devlink);  	if (!ds->slave_mii_bus && ds->ops->phy_read) { @@ -1068,34 +1096,60 @@ static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp,  	return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol);  } -static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master) +static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master, +			      const char *user_protocol)  {  	struct dsa_switch *ds = dp->ds;  	struct dsa_switch_tree *dst = ds->dst;  	const struct dsa_device_ops *tag_ops; -	enum dsa_tag_protocol tag_protocol; +	enum dsa_tag_protocol default_proto; + +	/* Find out which protocol the switch would prefer. */ +	default_proto = dsa_get_tag_protocol(dp, master); +	if (dst->default_proto) { +		if (dst->default_proto != default_proto) { +			dev_err(ds->dev, +				"A DSA switch tree can have only one tagging protocol\n"); +			return -EINVAL; +		} +	} else { +		dst->default_proto = default_proto; +	} + +	/* See if the user wants to override that preference. */ +	if (user_protocol) { +		if (!ds->ops->change_tag_protocol) { +			dev_err(ds->dev, "Tag protocol cannot be modified\n"); +			return -EINVAL; +		} + +		tag_ops = dsa_find_tagger_by_name(user_protocol); +	} else { +		tag_ops = dsa_tag_driver_get(default_proto); +	} + +	if (IS_ERR(tag_ops)) { +		if (PTR_ERR(tag_ops) == -ENOPROTOOPT) +			return -EPROBE_DEFER; + +		dev_warn(ds->dev, "No tagger for this switch\n"); +		return PTR_ERR(tag_ops); +	} -	tag_protocol = dsa_get_tag_protocol(dp, master);  	if (dst->tag_ops) { -		if (dst->tag_ops->proto != tag_protocol) { +		if (dst->tag_ops != tag_ops) {  			dev_err(ds->dev,  				"A DSA switch tree can have only one tagging protocol\n"); + +			dsa_tag_driver_put(tag_ops);  			return -EINVAL;  		} +  		/* In the case of multiple CPU ports per switch, the tagging -		 * protocol is still reference-counted only per switch tree, so -		 * nothing to do here. +		 * protocol is still reference-counted only per switch tree.  		 */ +		dsa_tag_driver_put(tag_ops);  	} else { -		tag_ops = dsa_tag_driver_get(tag_protocol); -		if (IS_ERR(tag_ops)) { -			if (PTR_ERR(tag_ops) == -ENOPROTOOPT) -				return -EPROBE_DEFER; -			dev_warn(ds->dev, "No tagger for this switch\n"); -			dp->master = NULL; -			return PTR_ERR(tag_ops); -		} -  		dst->tag_ops = tag_ops;  	} @@ -1104,6 +1158,19 @@ static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *master)  	dsa_port_set_tag_protocol(dp, dst->tag_ops);  	dp->dst = dst; +	/* At this point, the tree may be configured to use a different +	 * tagger than the one chosen by the switch driver during +	 * .setup, in the case when a user selects a custom protocol +	 * through the DT. +	 * +	 * This is resolved by syncing the driver with the tree in +	 * dsa_switch_setup_tag_protocol once .setup has run and the +	 * driver is ready to accept calls to .change_tag_protocol. If +	 * the driver does not support the custom protocol at that +	 * point, the tree is wholly rejected, thereby ensuring that the +	 * tree and driver are always in agreement on the protocol to +	 * use. +	 */  	return 0;  } @@ -1117,12 +1184,14 @@ static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)  	if (ethernet) {  		struct net_device *master; +		const char *user_protocol;  		master = of_find_net_device_by_node(ethernet);  		if (!master)  			return -EPROBE_DEFER; -		return dsa_port_parse_cpu(dp, master); +		user_protocol = of_get_property(dn, "dsa-tag-protocol", NULL); +		return dsa_port_parse_cpu(dp, master, user_protocol);  	}  	if (link) @@ -1234,7 +1303,7 @@ static int dsa_port_parse(struct dsa_port *dp, const char *name,  		dev_put(master); -		return dsa_port_parse_cpu(dp, master); +		return dsa_port_parse_cpu(dp, master, NULL);  	}  	if (!strcmp(name, "dsa"))  |