xref: /openbmc/linux/net/dsa/port.c (revision 06b9cce4)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a40c175bSVivien Didelot /*
3a40c175bSVivien Didelot  * Handling of a single switch port
4a40c175bSVivien Didelot  *
5a40c175bSVivien Didelot  * Copyright (c) 2017 Savoir-faire Linux Inc.
6a40c175bSVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7a40c175bSVivien Didelot  */
8a40c175bSVivien Didelot 
9a40c175bSVivien Didelot #include <linux/if_bridge.h>
10cfbed329SVivien Didelot #include <linux/notifier.h>
1157ab1ca2SVivien Didelot #include <linux/of_mdio.h>
1257ab1ca2SVivien Didelot #include <linux/of_net.h>
13a40c175bSVivien Didelot 
14a40c175bSVivien Didelot #include "dsa_priv.h"
15a40c175bSVivien Didelot 
16886f8e26SVladimir Oltean /**
17886f8e26SVladimir Oltean  * dsa_port_notify - Notify the switching fabric of changes to a port
18886f8e26SVladimir Oltean  * @dp: port on which change occurred
19886f8e26SVladimir Oltean  * @e: event, must be of type DSA_NOTIFIER_*
20886f8e26SVladimir Oltean  * @v: event-specific value.
21886f8e26SVladimir Oltean  *
22886f8e26SVladimir Oltean  * Notify all switches in the DSA tree that this port's switch belongs to,
23886f8e26SVladimir Oltean  * including this switch itself, of an event. Allows the other switches to
24886f8e26SVladimir Oltean  * reconfigure themselves for cross-chip operations. Can also be used to
25886f8e26SVladimir Oltean  * reconfigure ports without net_devices (CPU ports, DSA links) whenever
26886f8e26SVladimir Oltean  * a user port's state changes.
27886f8e26SVladimir Oltean  */
28bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
29cfbed329SVivien Didelot {
30886f8e26SVladimir Oltean 	return dsa_tree_notify(dp->ds->dst, e, v);
31cfbed329SVivien Didelot }
32cfbed329SVivien Didelot 
339264e4adSVladimir Oltean static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp)
349264e4adSVladimir Oltean {
359264e4adSVladimir Oltean 	struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
369264e4adSVladimir Oltean 	struct switchdev_notifier_fdb_info info = {
379264e4adSVladimir Oltean 		/* flush all VLANs */
389264e4adSVladimir Oltean 		.vid = 0,
399264e4adSVladimir Oltean 	};
409264e4adSVladimir Oltean 
419264e4adSVladimir Oltean 	/* When the port becomes standalone it has already left the bridge.
429264e4adSVladimir Oltean 	 * Don't notify the bridge in that case.
439264e4adSVladimir Oltean 	 */
449264e4adSVladimir Oltean 	if (!brport_dev)
459264e4adSVladimir Oltean 		return;
469264e4adSVladimir Oltean 
479264e4adSVladimir Oltean 	call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
489264e4adSVladimir Oltean 				 brport_dev, &info.info, NULL);
499264e4adSVladimir Oltean }
509264e4adSVladimir Oltean 
51045c45d1SVladimir Oltean static void dsa_port_fast_age(const struct dsa_port *dp)
52045c45d1SVladimir Oltean {
53045c45d1SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
54045c45d1SVladimir Oltean 
55045c45d1SVladimir Oltean 	if (!ds->ops->port_fast_age)
56045c45d1SVladimir Oltean 		return;
57045c45d1SVladimir Oltean 
58045c45d1SVladimir Oltean 	ds->ops->port_fast_age(ds, dp->index);
599264e4adSVladimir Oltean 
609264e4adSVladimir Oltean 	dsa_port_notify_bridge_fdb_flush(dp);
61045c45d1SVladimir Oltean }
62045c45d1SVladimir Oltean 
63a4ffe09fSVladimir Oltean static bool dsa_port_can_configure_learning(struct dsa_port *dp)
64a4ffe09fSVladimir Oltean {
65a4ffe09fSVladimir Oltean 	struct switchdev_brport_flags flags = {
66a4ffe09fSVladimir Oltean 		.mask = BR_LEARNING,
67a4ffe09fSVladimir Oltean 	};
68a4ffe09fSVladimir Oltean 	struct dsa_switch *ds = dp->ds;
69a4ffe09fSVladimir Oltean 	int err;
70a4ffe09fSVladimir Oltean 
71a4ffe09fSVladimir Oltean 	if (!ds->ops->port_bridge_flags || !ds->ops->port_pre_bridge_flags)
72a4ffe09fSVladimir Oltean 		return false;
73a4ffe09fSVladimir Oltean 
74a4ffe09fSVladimir Oltean 	err = ds->ops->port_pre_bridge_flags(ds, dp->index, flags, NULL);
75a4ffe09fSVladimir Oltean 	return !err;
76a4ffe09fSVladimir Oltean }
77a4ffe09fSVladimir Oltean 
7839f32101SVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age)
79a40c175bSVivien Didelot {
80a40c175bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
81a40c175bSVivien Didelot 	int port = dp->index;
82a40c175bSVivien Didelot 
83bae33f2bSVladimir Oltean 	if (!ds->ops->port_stp_state_set)
84bae33f2bSVladimir Oltean 		return -EOPNOTSUPP;
85a40c175bSVivien Didelot 
86a40c175bSVivien Didelot 	ds->ops->port_stp_state_set(ds, port, state);
87a40c175bSVivien Didelot 
88a4ffe09fSVladimir Oltean 	if (!dsa_port_can_configure_learning(dp) ||
89a4ffe09fSVladimir Oltean 	    (do_fast_age && dp->learning)) {
90a40c175bSVivien Didelot 		/* Fast age FDB entries or flush appropriate forwarding database
91a40c175bSVivien Didelot 		 * for the given port, if we are moving it from Learning or
92a40c175bSVivien Didelot 		 * Forwarding state, to Disabled or Blocking or Listening state.
9339f32101SVladimir Oltean 		 * Ports that were standalone before the STP state change don't
9439f32101SVladimir Oltean 		 * need to fast age the FDB, since address learning is off in
9539f32101SVladimir Oltean 		 * standalone mode.
96a40c175bSVivien Didelot 		 */
97a40c175bSVivien Didelot 
98a40c175bSVivien Didelot 		if ((dp->stp_state == BR_STATE_LEARNING ||
99a40c175bSVivien Didelot 		     dp->stp_state == BR_STATE_FORWARDING) &&
100a40c175bSVivien Didelot 		    (state == BR_STATE_DISABLED ||
101a40c175bSVivien Didelot 		     state == BR_STATE_BLOCKING ||
102a40c175bSVivien Didelot 		     state == BR_STATE_LISTENING))
103045c45d1SVladimir Oltean 			dsa_port_fast_age(dp);
104a40c175bSVivien Didelot 	}
105a40c175bSVivien Didelot 
106a40c175bSVivien Didelot 	dp->stp_state = state;
107a40c175bSVivien Didelot 
108a40c175bSVivien Didelot 	return 0;
109a40c175bSVivien Didelot }
110a40c175bSVivien Didelot 
11139f32101SVladimir Oltean static void dsa_port_set_state_now(struct dsa_port *dp, u8 state,
11239f32101SVladimir Oltean 				   bool do_fast_age)
113a40c175bSVivien Didelot {
114a40c175bSVivien Didelot 	int err;
115a40c175bSVivien Didelot 
11639f32101SVladimir Oltean 	err = dsa_port_set_state(dp, state, do_fast_age);
117a40c175bSVivien Didelot 	if (err)
118a40c175bSVivien Didelot 		pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
119a40c175bSVivien Didelot }
120cfbed329SVivien Didelot 
1218640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
122fb8a6a2bSVivien Didelot {
123fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
124fb8a6a2bSVivien Didelot 	int port = dp->index;
125fb8a6a2bSVivien Didelot 	int err;
126fb8a6a2bSVivien Didelot 
127fb8a6a2bSVivien Didelot 	if (ds->ops->port_enable) {
128fb8a6a2bSVivien Didelot 		err = ds->ops->port_enable(ds, port, phy);
129fb8a6a2bSVivien Didelot 		if (err)
130fb8a6a2bSVivien Didelot 			return err;
131fb8a6a2bSVivien Didelot 	}
132fb8a6a2bSVivien Didelot 
133d3eed0e5SVladimir Oltean 	if (!dp->bridge)
13439f32101SVladimir Oltean 		dsa_port_set_state_now(dp, BR_STATE_FORWARDING, false);
135fb8a6a2bSVivien Didelot 
1368640f8dcSRussell King 	if (dp->pl)
1378640f8dcSRussell King 		phylink_start(dp->pl);
1388640f8dcSRussell King 
139fb8a6a2bSVivien Didelot 	return 0;
140fb8a6a2bSVivien Didelot }
141fb8a6a2bSVivien Didelot 
1428640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
1438640f8dcSRussell King {
1448640f8dcSRussell King 	int err;
1458640f8dcSRussell King 
1468640f8dcSRussell King 	rtnl_lock();
1478640f8dcSRussell King 	err = dsa_port_enable_rt(dp, phy);
1488640f8dcSRussell King 	rtnl_unlock();
1498640f8dcSRussell King 
1508640f8dcSRussell King 	return err;
1518640f8dcSRussell King }
1528640f8dcSRussell King 
1538640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp)
154fb8a6a2bSVivien Didelot {
155fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
156fb8a6a2bSVivien Didelot 	int port = dp->index;
157fb8a6a2bSVivien Didelot 
1588640f8dcSRussell King 	if (dp->pl)
1598640f8dcSRussell King 		phylink_stop(dp->pl);
1608640f8dcSRussell King 
161d3eed0e5SVladimir Oltean 	if (!dp->bridge)
16239f32101SVladimir Oltean 		dsa_port_set_state_now(dp, BR_STATE_DISABLED, false);
163fb8a6a2bSVivien Didelot 
164fb8a6a2bSVivien Didelot 	if (ds->ops->port_disable)
16575104db0SAndrew Lunn 		ds->ops->port_disable(ds, port);
166fb8a6a2bSVivien Didelot }
167fb8a6a2bSVivien Didelot 
1688640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp)
1698640f8dcSRussell King {
1708640f8dcSRussell King 	rtnl_lock();
1718640f8dcSRussell King 	dsa_port_disable_rt(dp);
1728640f8dcSRussell King 	rtnl_unlock();
1738640f8dcSRussell King }
1748640f8dcSRussell King 
1755961d6a1SVladimir Oltean static int dsa_port_inherit_brport_flags(struct dsa_port *dp,
1765961d6a1SVladimir Oltean 					 struct netlink_ext_ack *extack)
1775e38c158SVladimir Oltean {
1785961d6a1SVladimir Oltean 	const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
179b9e8b58fSHans Schultz 				   BR_BCAST_FLOOD | BR_PORT_LOCKED;
1805961d6a1SVladimir Oltean 	struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
1815961d6a1SVladimir Oltean 	int flag, err;
1825e38c158SVladimir Oltean 
1835961d6a1SVladimir Oltean 	for_each_set_bit(flag, &mask, 32) {
1845961d6a1SVladimir Oltean 		struct switchdev_brport_flags flags = {0};
1855e38c158SVladimir Oltean 
1865961d6a1SVladimir Oltean 		flags.mask = BIT(flag);
1875e38c158SVladimir Oltean 
1885961d6a1SVladimir Oltean 		if (br_port_flag_is_set(brport_dev, BIT(flag)))
1895961d6a1SVladimir Oltean 			flags.val = BIT(flag);
190e18f4c18SVladimir Oltean 
1915961d6a1SVladimir Oltean 		err = dsa_port_bridge_flags(dp, flags, extack);
1925961d6a1SVladimir Oltean 		if (err && err != -EOPNOTSUPP)
1935961d6a1SVladimir Oltean 			return err;
1945e38c158SVladimir Oltean 	}
1955961d6a1SVladimir Oltean 
1965961d6a1SVladimir Oltean 	return 0;
1975961d6a1SVladimir Oltean }
1985961d6a1SVladimir Oltean 
1995961d6a1SVladimir Oltean static void dsa_port_clear_brport_flags(struct dsa_port *dp)
2005961d6a1SVladimir Oltean {
2015961d6a1SVladimir Oltean 	const unsigned long val = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
2025961d6a1SVladimir Oltean 	const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
203b9e8b58fSHans Schultz 				   BR_BCAST_FLOOD | BR_PORT_LOCKED;
2045961d6a1SVladimir Oltean 	int flag, err;
2055961d6a1SVladimir Oltean 
2065961d6a1SVladimir Oltean 	for_each_set_bit(flag, &mask, 32) {
2075961d6a1SVladimir Oltean 		struct switchdev_brport_flags flags = {0};
2085961d6a1SVladimir Oltean 
2095961d6a1SVladimir Oltean 		flags.mask = BIT(flag);
2105961d6a1SVladimir Oltean 		flags.val = val & BIT(flag);
2115961d6a1SVladimir Oltean 
2125961d6a1SVladimir Oltean 		err = dsa_port_bridge_flags(dp, flags, NULL);
2135961d6a1SVladimir Oltean 		if (err && err != -EOPNOTSUPP)
2145961d6a1SVladimir Oltean 			dev_err(dp->ds->dev,
2155961d6a1SVladimir Oltean 				"failed to clear bridge port flag %lu: %pe\n",
2165961d6a1SVladimir Oltean 				flags.val, ERR_PTR(err));
2175961d6a1SVladimir Oltean 	}
2185961d6a1SVladimir Oltean }
2195961d6a1SVladimir Oltean 
2204e51bf44SVladimir Oltean static int dsa_port_switchdev_sync_attrs(struct dsa_port *dp,
2215961d6a1SVladimir Oltean 					 struct netlink_ext_ack *extack)
2225961d6a1SVladimir Oltean {
223010e269fSVladimir Oltean 	struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
22436cbf39bSVladimir Oltean 	struct net_device *br = dsa_port_bridge_dev_get(dp);
2255961d6a1SVladimir Oltean 	int err;
2265961d6a1SVladimir Oltean 
2275961d6a1SVladimir Oltean 	err = dsa_port_inherit_brport_flags(dp, extack);
2285961d6a1SVladimir Oltean 	if (err)
2295961d6a1SVladimir Oltean 		return err;
2305961d6a1SVladimir Oltean 
23139f32101SVladimir Oltean 	err = dsa_port_set_state(dp, br_port_get_stp_state(brport_dev), false);
232010e269fSVladimir Oltean 	if (err && err != -EOPNOTSUPP)
233010e269fSVladimir Oltean 		return err;
234010e269fSVladimir Oltean 
235010e269fSVladimir Oltean 	err = dsa_port_vlan_filtering(dp, br_vlan_enabled(br), extack);
236010e269fSVladimir Oltean 	if (err && err != -EOPNOTSUPP)
237010e269fSVladimir Oltean 		return err;
238010e269fSVladimir Oltean 
239010e269fSVladimir Oltean 	err = dsa_port_ageing_time(dp, br_get_ageing_time(br));
240010e269fSVladimir Oltean 	if (err && err != -EOPNOTSUPP)
241010e269fSVladimir Oltean 		return err;
242010e269fSVladimir Oltean 
24374918945SVladimir Oltean 	return 0;
24474918945SVladimir Oltean }
24574918945SVladimir Oltean 
24674918945SVladimir Oltean static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
2475961d6a1SVladimir Oltean {
2485961d6a1SVladimir Oltean 	/* Configure the port for standalone mode (no address learning,
2495961d6a1SVladimir Oltean 	 * flood everything).
2505961d6a1SVladimir Oltean 	 * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events
2515961d6a1SVladimir Oltean 	 * when the user requests it through netlink or sysfs, but not
2525961d6a1SVladimir Oltean 	 * automatically at port join or leave, so we need to handle resetting
2535961d6a1SVladimir Oltean 	 * the brport flags ourselves. But we even prefer it that way, because
2545961d6a1SVladimir Oltean 	 * otherwise, some setups might never get the notification they need,
2555961d6a1SVladimir Oltean 	 * for example, when a port leaves a LAG that offloads the bridge,
2565961d6a1SVladimir Oltean 	 * it becomes standalone, but as far as the bridge is concerned, no
2575961d6a1SVladimir Oltean 	 * port ever left.
2585961d6a1SVladimir Oltean 	 */
2595961d6a1SVladimir Oltean 	dsa_port_clear_brport_flags(dp);
2605961d6a1SVladimir Oltean 
2615961d6a1SVladimir Oltean 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
2625961d6a1SVladimir Oltean 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
2635961d6a1SVladimir Oltean 	 */
26439f32101SVladimir Oltean 	dsa_port_set_state_now(dp, BR_STATE_FORWARDING, true);
265010e269fSVladimir Oltean 
266010e269fSVladimir Oltean 	/* VLAN filtering is handled by dsa_switch_bridge_leave */
267010e269fSVladimir Oltean 
268010e269fSVladimir Oltean 	/* Ageing time may be global to the switch chip, so don't change it
269010e269fSVladimir Oltean 	 * here because we have no good reason (or value) to change it to.
270010e269fSVladimir Oltean 	 */
2715e38c158SVladimir Oltean }
2725e38c158SVladimir Oltean 
273947c8746SVladimir Oltean static int dsa_port_bridge_create(struct dsa_port *dp,
274947c8746SVladimir Oltean 				  struct net_device *br,
275947c8746SVladimir Oltean 				  struct netlink_ext_ack *extack)
276947c8746SVladimir Oltean {
277947c8746SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
278d3eed0e5SVladimir Oltean 	struct dsa_bridge *bridge;
279947c8746SVladimir Oltean 
280d3eed0e5SVladimir Oltean 	bridge = dsa_tree_bridge_find(ds->dst, br);
281d3eed0e5SVladimir Oltean 	if (bridge) {
282d3eed0e5SVladimir Oltean 		refcount_inc(&bridge->refcount);
283d3eed0e5SVladimir Oltean 		dp->bridge = bridge;
284947c8746SVladimir Oltean 		return 0;
285d3eed0e5SVladimir Oltean 	}
286947c8746SVladimir Oltean 
287d3eed0e5SVladimir Oltean 	bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
288d3eed0e5SVladimir Oltean 	if (!bridge)
289d3eed0e5SVladimir Oltean 		return -ENOMEM;
290d3eed0e5SVladimir Oltean 
291d3eed0e5SVladimir Oltean 	refcount_set(&bridge->refcount, 1);
292d3eed0e5SVladimir Oltean 
293d3eed0e5SVladimir Oltean 	bridge->dev = br;
294d3eed0e5SVladimir Oltean 
295d3eed0e5SVladimir Oltean 	bridge->num = dsa_bridge_num_get(br, ds->max_num_bridges);
296d3eed0e5SVladimir Oltean 	if (ds->max_num_bridges && !bridge->num) {
297947c8746SVladimir Oltean 		NL_SET_ERR_MSG_MOD(extack,
298947c8746SVladimir Oltean 				   "Range of offloadable bridges exceeded");
299d3eed0e5SVladimir Oltean 		kfree(bridge);
300947c8746SVladimir Oltean 		return -EOPNOTSUPP;
301947c8746SVladimir Oltean 	}
302947c8746SVladimir Oltean 
303d3eed0e5SVladimir Oltean 	dp->bridge = bridge;
304947c8746SVladimir Oltean 
305947c8746SVladimir Oltean 	return 0;
306947c8746SVladimir Oltean }
307947c8746SVladimir Oltean 
308947c8746SVladimir Oltean static void dsa_port_bridge_destroy(struct dsa_port *dp,
309947c8746SVladimir Oltean 				    const struct net_device *br)
310947c8746SVladimir Oltean {
311d3eed0e5SVladimir Oltean 	struct dsa_bridge *bridge = dp->bridge;
312947c8746SVladimir Oltean 
313d3eed0e5SVladimir Oltean 	dp->bridge = NULL;
314947c8746SVladimir Oltean 
315d3eed0e5SVladimir Oltean 	if (!refcount_dec_and_test(&bridge->refcount))
316d3eed0e5SVladimir Oltean 		return;
317947c8746SVladimir Oltean 
318d3eed0e5SVladimir Oltean 	if (bridge->num)
319d3eed0e5SVladimir Oltean 		dsa_bridge_num_put(br, bridge->num);
320d3eed0e5SVladimir Oltean 
321d3eed0e5SVladimir Oltean 	kfree(bridge);
322123abc06SVladimir Oltean }
323123abc06SVladimir Oltean 
3242afc526aSVladimir Oltean int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
3252afc526aSVladimir Oltean 			 struct netlink_ext_ack *extack)
326cfbed329SVivien Didelot {
327cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
328f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
329cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
330cfbed329SVivien Didelot 		.port = dp->index,
331*06b9cce4SVladimir Oltean 		.extack = extack,
332cfbed329SVivien Didelot 	};
3332f5dc00fSVladimir Oltean 	struct net_device *dev = dp->slave;
3342f5dc00fSVladimir Oltean 	struct net_device *brport_dev;
335cfbed329SVivien Didelot 	int err;
336cfbed329SVivien Didelot 
337c1388063SRussell King 	/* Here the interface is already bridged. Reflect the current
338c1388063SRussell King 	 * configuration so that drivers can program their chips accordingly.
339cfbed329SVivien Didelot 	 */
340947c8746SVladimir Oltean 	err = dsa_port_bridge_create(dp, br, extack);
341947c8746SVladimir Oltean 	if (err)
342947c8746SVladimir Oltean 		return err;
343cfbed329SVivien Didelot 
3442f5dc00fSVladimir Oltean 	brport_dev = dsa_port_to_bridge_port(dp);
3452f5dc00fSVladimir Oltean 
346d3eed0e5SVladimir Oltean 	info.bridge = *dp->bridge;
347f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
3485961d6a1SVladimir Oltean 	if (err)
3495961d6a1SVladimir Oltean 		goto out_rollback;
350cfbed329SVivien Didelot 
351857fdd74SVladimir Oltean 	/* Drivers which support bridge TX forwarding should set this */
352857fdd74SVladimir Oltean 	dp->bridge->tx_fwd_offload = info.tx_fwd_offload;
353123abc06SVladimir Oltean 
3544e51bf44SVladimir Oltean 	err = switchdev_bridge_port_offload(brport_dev, dev, dp,
3554e51bf44SVladimir Oltean 					    &dsa_slave_switchdev_notifier,
3564e51bf44SVladimir Oltean 					    &dsa_slave_switchdev_blocking_notifier,
357857fdd74SVladimir Oltean 					    dp->bridge->tx_fwd_offload, extack);
3585961d6a1SVladimir Oltean 	if (err)
3595961d6a1SVladimir Oltean 		goto out_rollback_unbridge;
3605961d6a1SVladimir Oltean 
3614e51bf44SVladimir Oltean 	err = dsa_port_switchdev_sync_attrs(dp, extack);
3622f5dc00fSVladimir Oltean 	if (err)
3632f5dc00fSVladimir Oltean 		goto out_rollback_unoffload;
3642f5dc00fSVladimir Oltean 
3655961d6a1SVladimir Oltean 	return 0;
3665961d6a1SVladimir Oltean 
3672f5dc00fSVladimir Oltean out_rollback_unoffload:
3684e51bf44SVladimir Oltean 	switchdev_bridge_port_unoffload(brport_dev, dp,
3694e51bf44SVladimir Oltean 					&dsa_slave_switchdev_notifier,
3704e51bf44SVladimir Oltean 					&dsa_slave_switchdev_blocking_notifier);
3715961d6a1SVladimir Oltean out_rollback_unbridge:
3725961d6a1SVladimir Oltean 	dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
3735961d6a1SVladimir Oltean out_rollback:
374947c8746SVladimir Oltean 	dsa_port_bridge_destroy(dp, br);
375cfbed329SVivien Didelot 	return err;
376cfbed329SVivien Didelot }
377cfbed329SVivien Didelot 
3784e51bf44SVladimir Oltean void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br)
37974918945SVladimir Oltean {
3802f5dc00fSVladimir Oltean 	struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
3812f5dc00fSVladimir Oltean 
38209dba21bSVladimir Oltean 	/* Don't try to unoffload something that is not offloaded */
38309dba21bSVladimir Oltean 	if (!brport_dev)
38409dba21bSVladimir Oltean 		return;
38509dba21bSVladimir Oltean 
3864e51bf44SVladimir Oltean 	switchdev_bridge_port_unoffload(brport_dev, dp,
3874e51bf44SVladimir Oltean 					&dsa_slave_switchdev_notifier,
3884e51bf44SVladimir Oltean 					&dsa_slave_switchdev_blocking_notifier);
389d7d0d423SVladimir Oltean 
390d7d0d423SVladimir Oltean 	dsa_flush_workqueue();
39174918945SVladimir Oltean }
39274918945SVladimir Oltean 
393cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
394cfbed329SVivien Didelot {
395cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
396f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
397cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
398cfbed329SVivien Didelot 		.port = dp->index,
399cfbed329SVivien Didelot 	};
400cfbed329SVivien Didelot 	int err;
401cfbed329SVivien Didelot 
402342b6419SAlvin Šipraga 	/* If the port could not be offloaded to begin with, then
403342b6419SAlvin Šipraga 	 * there is nothing to do.
404342b6419SAlvin Šipraga 	 */
405342b6419SAlvin Šipraga 	if (!dp->bridge)
406342b6419SAlvin Šipraga 		return;
407342b6419SAlvin Šipraga 
408342b6419SAlvin Šipraga 	info.bridge = *dp->bridge;
409342b6419SAlvin Šipraga 
410cfbed329SVivien Didelot 	/* Here the port is already unbridged. Reflect the current configuration
411cfbed329SVivien Didelot 	 * so that drivers can program their chips accordingly.
412cfbed329SVivien Didelot 	 */
413947c8746SVladimir Oltean 	dsa_port_bridge_destroy(dp, br);
414cfbed329SVivien Didelot 
415f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
416cfbed329SVivien Didelot 	if (err)
417ab97462bSVladimir Oltean 		dev_err(dp->ds->dev,
418ab97462bSVladimir Oltean 			"port %d failed to notify DSA_NOTIFIER_BRIDGE_LEAVE: %pe\n",
419ab97462bSVladimir Oltean 			dp->index, ERR_PTR(err));
420cfbed329SVivien Didelot 
42174918945SVladimir Oltean 	dsa_port_switchdev_unsync_attrs(dp);
422cfbed329SVivien Didelot }
4234d61d304SVivien Didelot 
424058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp,
425058102a6STobias Waldekranz 			struct netdev_lag_lower_state_info *linfo)
426058102a6STobias Waldekranz {
427058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
428058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
429058102a6STobias Waldekranz 		.port = dp->index,
430058102a6STobias Waldekranz 	};
431058102a6STobias Waldekranz 	bool tx_enabled;
432058102a6STobias Waldekranz 
433dedd6a00SVladimir Oltean 	if (!dp->lag)
434058102a6STobias Waldekranz 		return 0;
435058102a6STobias Waldekranz 
436058102a6STobias Waldekranz 	/* On statically configured aggregates (e.g. loadbalance
437058102a6STobias Waldekranz 	 * without LACP) ports will always be tx_enabled, even if the
438058102a6STobias Waldekranz 	 * link is down. Thus we require both link_up and tx_enabled
439058102a6STobias Waldekranz 	 * in order to include it in the tx set.
440058102a6STobias Waldekranz 	 */
441058102a6STobias Waldekranz 	tx_enabled = linfo->link_up && linfo->tx_enabled;
442058102a6STobias Waldekranz 
443058102a6STobias Waldekranz 	if (tx_enabled == dp->lag_tx_enabled)
444058102a6STobias Waldekranz 		return 0;
445058102a6STobias Waldekranz 
446058102a6STobias Waldekranz 	dp->lag_tx_enabled = tx_enabled;
447058102a6STobias Waldekranz 
448058102a6STobias Waldekranz 	return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
449058102a6STobias Waldekranz }
450058102a6STobias Waldekranz 
451dedd6a00SVladimir Oltean static int dsa_port_lag_create(struct dsa_port *dp,
452dedd6a00SVladimir Oltean 			       struct net_device *lag_dev)
453dedd6a00SVladimir Oltean {
454dedd6a00SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
455dedd6a00SVladimir Oltean 	struct dsa_lag *lag;
456dedd6a00SVladimir Oltean 
457dedd6a00SVladimir Oltean 	lag = dsa_tree_lag_find(ds->dst, lag_dev);
458dedd6a00SVladimir Oltean 	if (lag) {
459dedd6a00SVladimir Oltean 		refcount_inc(&lag->refcount);
460dedd6a00SVladimir Oltean 		dp->lag = lag;
461dedd6a00SVladimir Oltean 		return 0;
462dedd6a00SVladimir Oltean 	}
463dedd6a00SVladimir Oltean 
464dedd6a00SVladimir Oltean 	lag = kzalloc(sizeof(*lag), GFP_KERNEL);
465dedd6a00SVladimir Oltean 	if (!lag)
466dedd6a00SVladimir Oltean 		return -ENOMEM;
467dedd6a00SVladimir Oltean 
468dedd6a00SVladimir Oltean 	refcount_set(&lag->refcount, 1);
469e212fa7cSVladimir Oltean 	mutex_init(&lag->fdb_lock);
470e212fa7cSVladimir Oltean 	INIT_LIST_HEAD(&lag->fdbs);
471dedd6a00SVladimir Oltean 	lag->dev = lag_dev;
472dedd6a00SVladimir Oltean 	dsa_lag_map(ds->dst, lag);
473dedd6a00SVladimir Oltean 	dp->lag = lag;
474dedd6a00SVladimir Oltean 
475dedd6a00SVladimir Oltean 	return 0;
476dedd6a00SVladimir Oltean }
477dedd6a00SVladimir Oltean 
478dedd6a00SVladimir Oltean static void dsa_port_lag_destroy(struct dsa_port *dp)
479dedd6a00SVladimir Oltean {
480dedd6a00SVladimir Oltean 	struct dsa_lag *lag = dp->lag;
481dedd6a00SVladimir Oltean 
482dedd6a00SVladimir Oltean 	dp->lag = NULL;
483dedd6a00SVladimir Oltean 	dp->lag_tx_enabled = false;
484dedd6a00SVladimir Oltean 
485dedd6a00SVladimir Oltean 	if (!refcount_dec_and_test(&lag->refcount))
486dedd6a00SVladimir Oltean 		return;
487dedd6a00SVladimir Oltean 
488e212fa7cSVladimir Oltean 	WARN_ON(!list_empty(&lag->fdbs));
489dedd6a00SVladimir Oltean 	dsa_lag_unmap(dp->ds->dst, lag);
490dedd6a00SVladimir Oltean 	kfree(lag);
491dedd6a00SVladimir Oltean }
492dedd6a00SVladimir Oltean 
49346a76724SVladimir Oltean int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
4942afc526aSVladimir Oltean 		      struct netdev_lag_upper_info *uinfo,
4952afc526aSVladimir Oltean 		      struct netlink_ext_ack *extack)
496058102a6STobias Waldekranz {
497058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
498058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
499058102a6STobias Waldekranz 		.port = dp->index,
500058102a6STobias Waldekranz 		.info = uinfo,
501058102a6STobias Waldekranz 	};
502185c9a76SVladimir Oltean 	struct net_device *bridge_dev;
503058102a6STobias Waldekranz 	int err;
504058102a6STobias Waldekranz 
505dedd6a00SVladimir Oltean 	err = dsa_port_lag_create(dp, lag_dev);
506dedd6a00SVladimir Oltean 	if (err)
507dedd6a00SVladimir Oltean 		goto err_lag_create;
508058102a6STobias Waldekranz 
509dedd6a00SVladimir Oltean 	info.lag = *dp->lag;
510058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
511185c9a76SVladimir Oltean 	if (err)
512185c9a76SVladimir Oltean 		goto err_lag_join;
513185c9a76SVladimir Oltean 
51446a76724SVladimir Oltean 	bridge_dev = netdev_master_upper_dev_get(lag_dev);
515185c9a76SVladimir Oltean 	if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
516185c9a76SVladimir Oltean 		return 0;
517185c9a76SVladimir Oltean 
5182afc526aSVladimir Oltean 	err = dsa_port_bridge_join(dp, bridge_dev, extack);
519185c9a76SVladimir Oltean 	if (err)
520185c9a76SVladimir Oltean 		goto err_bridge_join;
521185c9a76SVladimir Oltean 
522185c9a76SVladimir Oltean 	return 0;
523185c9a76SVladimir Oltean 
524185c9a76SVladimir Oltean err_bridge_join:
525185c9a76SVladimir Oltean 	dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
526185c9a76SVladimir Oltean err_lag_join:
527dedd6a00SVladimir Oltean 	dsa_port_lag_destroy(dp);
528dedd6a00SVladimir Oltean err_lag_create:
529058102a6STobias Waldekranz 	return err;
530058102a6STobias Waldekranz }
531058102a6STobias Waldekranz 
53246a76724SVladimir Oltean void dsa_port_pre_lag_leave(struct dsa_port *dp, struct net_device *lag_dev)
53374918945SVladimir Oltean {
53436cbf39bSVladimir Oltean 	struct net_device *br = dsa_port_bridge_dev_get(dp);
53536cbf39bSVladimir Oltean 
53636cbf39bSVladimir Oltean 	if (br)
53736cbf39bSVladimir Oltean 		dsa_port_pre_bridge_leave(dp, br);
53874918945SVladimir Oltean }
53974918945SVladimir Oltean 
54046a76724SVladimir Oltean void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev)
541058102a6STobias Waldekranz {
54236cbf39bSVladimir Oltean 	struct net_device *br = dsa_port_bridge_dev_get(dp);
543058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
544058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
545058102a6STobias Waldekranz 		.port = dp->index,
546058102a6STobias Waldekranz 	};
547058102a6STobias Waldekranz 	int err;
548058102a6STobias Waldekranz 
549dedd6a00SVladimir Oltean 	if (!dp->lag)
550058102a6STobias Waldekranz 		return;
551058102a6STobias Waldekranz 
552058102a6STobias Waldekranz 	/* Port might have been part of a LAG that in turn was
553058102a6STobias Waldekranz 	 * attached to a bridge.
554058102a6STobias Waldekranz 	 */
55536cbf39bSVladimir Oltean 	if (br)
55636cbf39bSVladimir Oltean 		dsa_port_bridge_leave(dp, br);
557058102a6STobias Waldekranz 
558dedd6a00SVladimir Oltean 	info.lag = *dp->lag;
559dedd6a00SVladimir Oltean 
560dedd6a00SVladimir Oltean 	dsa_port_lag_destroy(dp);
561058102a6STobias Waldekranz 
562058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
563058102a6STobias Waldekranz 	if (err)
564ab97462bSVladimir Oltean 		dev_err(dp->ds->dev,
565ab97462bSVladimir Oltean 			"port %d failed to notify DSA_NOTIFIER_LAG_LEAVE: %pe\n",
566ab97462bSVladimir Oltean 			dp->index, ERR_PTR(err));
567058102a6STobias Waldekranz }
568058102a6STobias Waldekranz 
569adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */
5708f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
57189153ed6SVladimir Oltean 					      bool vlan_filtering,
57289153ed6SVladimir Oltean 					      struct netlink_ext_ack *extack)
5738f5d16f6SVladimir Oltean {
5748f5d16f6SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
575d0004a02SVladimir Oltean 	struct dsa_port *other_dp;
576d0004a02SVladimir Oltean 	int err;
577adb256ebSVladimir Oltean 
578adb256ebSVladimir Oltean 	/* VLAN awareness was off, so the question is "can we turn it on".
579adb256ebSVladimir Oltean 	 * We may have had 8021q uppers, those need to go. Make sure we don't
580adb256ebSVladimir Oltean 	 * enter an inconsistent state: deny changing the VLAN awareness state
581adb256ebSVladimir Oltean 	 * as long as we have 8021q uppers.
582adb256ebSVladimir Oltean 	 */
58357d77986SVladimir Oltean 	if (vlan_filtering && dsa_port_is_user(dp)) {
58436cbf39bSVladimir Oltean 		struct net_device *br = dsa_port_bridge_dev_get(dp);
585adb256ebSVladimir Oltean 		struct net_device *upper_dev, *slave = dp->slave;
586adb256ebSVladimir Oltean 		struct list_head *iter;
587adb256ebSVladimir Oltean 
588adb256ebSVladimir Oltean 		netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
589adb256ebSVladimir Oltean 			struct bridge_vlan_info br_info;
590adb256ebSVladimir Oltean 			u16 vid;
591adb256ebSVladimir Oltean 
592adb256ebSVladimir Oltean 			if (!is_vlan_dev(upper_dev))
593adb256ebSVladimir Oltean 				continue;
594adb256ebSVladimir Oltean 
595adb256ebSVladimir Oltean 			vid = vlan_dev_vlan_id(upper_dev);
596adb256ebSVladimir Oltean 
597adb256ebSVladimir Oltean 			/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
598adb256ebSVladimir Oltean 			 * device, respectively the VID is not found, returning
599adb256ebSVladimir Oltean 			 * 0 means success, which is a failure for us here.
600adb256ebSVladimir Oltean 			 */
601adb256ebSVladimir Oltean 			err = br_vlan_get_info(br, vid, &br_info);
602adb256ebSVladimir Oltean 			if (err == 0) {
60389153ed6SVladimir Oltean 				NL_SET_ERR_MSG_MOD(extack,
60489153ed6SVladimir Oltean 						   "Must first remove VLAN uppers having VIDs also present in bridge");
605adb256ebSVladimir Oltean 				return false;
606adb256ebSVladimir Oltean 			}
607adb256ebSVladimir Oltean 		}
608adb256ebSVladimir Oltean 	}
6098f5d16f6SVladimir Oltean 
6108f5d16f6SVladimir Oltean 	if (!ds->vlan_filtering_is_global)
6118f5d16f6SVladimir Oltean 		return true;
6128f5d16f6SVladimir Oltean 
6138f5d16f6SVladimir Oltean 	/* For cases where enabling/disabling VLAN awareness is global to the
6148f5d16f6SVladimir Oltean 	 * switch, we need to handle the case where multiple bridges span
6158f5d16f6SVladimir Oltean 	 * different ports of the same switch device and one of them has a
6168f5d16f6SVladimir Oltean 	 * different setting than what is being requested.
6178f5d16f6SVladimir Oltean 	 */
618d0004a02SVladimir Oltean 	dsa_switch_for_each_port(other_dp, ds) {
61936cbf39bSVladimir Oltean 		struct net_device *other_br = dsa_port_bridge_dev_get(other_dp);
6208f5d16f6SVladimir Oltean 
6218f5d16f6SVladimir Oltean 		/* If it's the same bridge, it also has same
6228f5d16f6SVladimir Oltean 		 * vlan_filtering setting => no need to check
6238f5d16f6SVladimir Oltean 		 */
62436cbf39bSVladimir Oltean 		if (!other_br || other_br == dsa_port_bridge_dev_get(dp))
6258f5d16f6SVladimir Oltean 			continue;
62636cbf39bSVladimir Oltean 
62736cbf39bSVladimir Oltean 		if (br_vlan_enabled(other_br) != vlan_filtering) {
62889153ed6SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
62989153ed6SVladimir Oltean 					   "VLAN filtering is a global setting");
6308f5d16f6SVladimir Oltean 			return false;
6318f5d16f6SVladimir Oltean 		}
6328f5d16f6SVladimir Oltean 	}
6338f5d16f6SVladimir Oltean 	return true;
6348f5d16f6SVladimir Oltean }
6358f5d16f6SVladimir Oltean 
63689153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
63789153ed6SVladimir Oltean 			    struct netlink_ext_ack *extack)
6384d61d304SVivien Didelot {
63906cfb2dfSVladimir Oltean 	bool old_vlan_filtering = dsa_port_is_vlan_filtering(dp);
6404d61d304SVivien Didelot 	struct dsa_switch *ds = dp->ds;
641adb256ebSVladimir Oltean 	bool apply;
642bae33f2bSVladimir Oltean 	int err;
643adb256ebSVladimir Oltean 
6448f5d16f6SVladimir Oltean 	if (!ds->ops->port_vlan_filtering)
645707ec383SVladimir Oltean 		return -EOPNOTSUPP;
6468f5d16f6SVladimir Oltean 
647adb256ebSVladimir Oltean 	/* We are called from dsa_slave_switchdev_blocking_event(),
648adb256ebSVladimir Oltean 	 * which is not under rcu_read_lock(), unlike
649adb256ebSVladimir Oltean 	 * dsa_slave_switchdev_event().
650adb256ebSVladimir Oltean 	 */
651adb256ebSVladimir Oltean 	rcu_read_lock();
65289153ed6SVladimir Oltean 	apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack);
653adb256ebSVladimir Oltean 	rcu_read_unlock();
654adb256ebSVladimir Oltean 	if (!apply)
6558f5d16f6SVladimir Oltean 		return -EINVAL;
656707ec383SVladimir Oltean 
657ec9121e7SVladimir Oltean 	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
658ec9121e7SVladimir Oltean 		return 0;
659ec9121e7SVladimir Oltean 
66089153ed6SVladimir Oltean 	err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
66189153ed6SVladimir Oltean 					   extack);
66233162e9aSVladimir Oltean 	if (err)
66333162e9aSVladimir Oltean 		return err;
6648f5d16f6SVladimir Oltean 
66506cfb2dfSVladimir Oltean 	if (ds->vlan_filtering_is_global) {
666d0004a02SVladimir Oltean 		struct dsa_port *other_dp;
66706cfb2dfSVladimir Oltean 
66814574676SVladimir Oltean 		ds->vlan_filtering = vlan_filtering;
66906cfb2dfSVladimir Oltean 
670d0004a02SVladimir Oltean 		dsa_switch_for_each_user_port(other_dp, ds) {
671d0004a02SVladimir Oltean 			struct net_device *slave = dp->slave;
67206cfb2dfSVladimir Oltean 
67306cfb2dfSVladimir Oltean 			/* We might be called in the unbind path, so not
67406cfb2dfSVladimir Oltean 			 * all slave devices might still be registered.
67506cfb2dfSVladimir Oltean 			 */
67606cfb2dfSVladimir Oltean 			if (!slave)
67706cfb2dfSVladimir Oltean 				continue;
67806cfb2dfSVladimir Oltean 
67906cfb2dfSVladimir Oltean 			err = dsa_slave_manage_vlan_filtering(slave,
68006cfb2dfSVladimir Oltean 							      vlan_filtering);
68106cfb2dfSVladimir Oltean 			if (err)
68206cfb2dfSVladimir Oltean 				goto restore;
68306cfb2dfSVladimir Oltean 		}
68406cfb2dfSVladimir Oltean 	} else {
68533162e9aSVladimir Oltean 		dp->vlan_filtering = vlan_filtering;
6862e554a7aSVladimir Oltean 
68706cfb2dfSVladimir Oltean 		err = dsa_slave_manage_vlan_filtering(dp->slave,
68806cfb2dfSVladimir Oltean 						      vlan_filtering);
68906cfb2dfSVladimir Oltean 		if (err)
69006cfb2dfSVladimir Oltean 			goto restore;
69106cfb2dfSVladimir Oltean 	}
69206cfb2dfSVladimir Oltean 
6934d61d304SVivien Didelot 	return 0;
69406cfb2dfSVladimir Oltean 
69506cfb2dfSVladimir Oltean restore:
69606cfb2dfSVladimir Oltean 	ds->ops->port_vlan_filtering(ds, dp->index, old_vlan_filtering, NULL);
69706cfb2dfSVladimir Oltean 
69806cfb2dfSVladimir Oltean 	if (ds->vlan_filtering_is_global)
69906cfb2dfSVladimir Oltean 		ds->vlan_filtering = old_vlan_filtering;
70006cfb2dfSVladimir Oltean 	else
70106cfb2dfSVladimir Oltean 		dp->vlan_filtering = old_vlan_filtering;
70206cfb2dfSVladimir Oltean 
70306cfb2dfSVladimir Oltean 	return err;
7044d61d304SVivien Didelot }
705d87bd94eSVivien Didelot 
70654a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't
70754a0ed0dSRussell King  * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0
70854a0ed0dSRussell King  */
70954a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
71054a0ed0dSRussell King {
71136cbf39bSVladimir Oltean 	struct net_device *br = dsa_port_bridge_dev_get(dp);
71254a0ed0dSRussell King 	struct dsa_switch *ds = dp->ds;
71354a0ed0dSRussell King 
71436cbf39bSVladimir Oltean 	if (!br)
71554a0ed0dSRussell King 		return false;
71654a0ed0dSRussell King 
71736cbf39bSVladimir Oltean 	return !ds->configure_vlan_while_not_filtering && !br_vlan_enabled(br);
71854a0ed0dSRussell King }
71954a0ed0dSRussell King 
720bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
721d87bd94eSVivien Didelot {
722d87bd94eSVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
723d87bd94eSVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
724bae33f2bSVladimir Oltean 	struct dsa_notifier_ageing_time_info info;
725bae33f2bSVladimir Oltean 	int err;
726d87bd94eSVivien Didelot 
727bae33f2bSVladimir Oltean 	info.ageing_time = ageing_time;
728bae33f2bSVladimir Oltean 
729bae33f2bSVladimir Oltean 	err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
730bae33f2bSVladimir Oltean 	if (err)
731bae33f2bSVladimir Oltean 		return err;
732d87bd94eSVivien Didelot 
733d87bd94eSVivien Didelot 	dp->ageing_time = ageing_time;
734d87bd94eSVivien Didelot 
73577b61365SVladimir Oltean 	return 0;
736d87bd94eSVivien Didelot }
737d1cffff0SVivien Didelot 
738e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
739a8b659e7SVladimir Oltean 			      struct switchdev_brport_flags flags,
740a8b659e7SVladimir Oltean 			      struct netlink_ext_ack *extack)
741ea87005aSFlorian Fainelli {
742ea87005aSFlorian Fainelli 	struct dsa_switch *ds = dp->ds;
743ea87005aSFlorian Fainelli 
744a8b659e7SVladimir Oltean 	if (!ds->ops->port_pre_bridge_flags)
745ea87005aSFlorian Fainelli 		return -EINVAL;
746ea87005aSFlorian Fainelli 
747a8b659e7SVladimir Oltean 	return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack);
748ea87005aSFlorian Fainelli }
749ea87005aSFlorian Fainelli 
750045c45d1SVladimir Oltean int dsa_port_bridge_flags(struct dsa_port *dp,
751a8b659e7SVladimir Oltean 			  struct switchdev_brport_flags flags,
752a8b659e7SVladimir Oltean 			  struct netlink_ext_ack *extack)
75357652796SRussell King {
75457652796SRussell King 	struct dsa_switch *ds = dp->ds;
755045c45d1SVladimir Oltean 	int err;
75657652796SRussell King 
757a8b659e7SVladimir Oltean 	if (!ds->ops->port_bridge_flags)
75870a7c484SOleksij Rempel 		return -EOPNOTSUPP;
75957652796SRussell King 
760045c45d1SVladimir Oltean 	err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack);
761045c45d1SVladimir Oltean 	if (err)
762045c45d1SVladimir Oltean 		return err;
763045c45d1SVladimir Oltean 
764045c45d1SVladimir Oltean 	if (flags.mask & BR_LEARNING) {
765045c45d1SVladimir Oltean 		bool learning = flags.val & BR_LEARNING;
766045c45d1SVladimir Oltean 
767045c45d1SVladimir Oltean 		if (learning == dp->learning)
768045c45d1SVladimir Oltean 			return 0;
769045c45d1SVladimir Oltean 
770bee7c577SVladimir Oltean 		if ((dp->learning && !learning) &&
771bee7c577SVladimir Oltean 		    (dp->stp_state == BR_STATE_LEARNING ||
772bee7c577SVladimir Oltean 		     dp->stp_state == BR_STATE_FORWARDING))
773045c45d1SVladimir Oltean 			dsa_port_fast_age(dp);
774045c45d1SVladimir Oltean 
775045c45d1SVladimir Oltean 		dp->learning = learning;
776045c45d1SVladimir Oltean 	}
777045c45d1SVladimir Oltean 
778045c45d1SVladimir Oltean 	return 0;
77957652796SRussell King }
78057652796SRussell King 
781bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
78288faba20SVladimir Oltean 			bool targeted_match)
783bfcb8132SVladimir Oltean {
784bfcb8132SVladimir Oltean 	struct dsa_notifier_mtu_info info = {
785bfcb8132SVladimir Oltean 		.sw_index = dp->ds->index,
78688faba20SVladimir Oltean 		.targeted_match = targeted_match,
787bfcb8132SVladimir Oltean 		.port = dp->index,
788bfcb8132SVladimir Oltean 		.mtu = new_mtu,
789bfcb8132SVladimir Oltean 	};
790bfcb8132SVladimir Oltean 
791bfcb8132SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info);
792bfcb8132SVladimir Oltean }
793bfcb8132SVladimir Oltean 
7942acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
7952acf4e6aSArkadi Sharshevsky 		     u16 vid)
796d1cffff0SVivien Didelot {
797685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
798685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
799685fb6a4SVivien Didelot 		.port = dp->index,
8002acf4e6aSArkadi Sharshevsky 		.addr = addr,
8012acf4e6aSArkadi Sharshevsky 		.vid = vid,
802c2693363SVladimir Oltean 		.db = {
803c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
804c2693363SVladimir Oltean 			.bridge = *dp->bridge,
805c2693363SVladimir Oltean 		},
806685fb6a4SVivien Didelot 	};
807d1cffff0SVivien Didelot 
808c2693363SVladimir Oltean 	/* Refcounting takes bridge.num as a key, and should be global for all
809c2693363SVladimir Oltean 	 * bridges in the absence of FDB isolation, and per bridge otherwise.
810c2693363SVladimir Oltean 	 * Force the bridge.num to zero here in the absence of FDB isolation.
811c2693363SVladimir Oltean 	 */
812c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
813c2693363SVladimir Oltean 		info.db.bridge.num = 0;
814c2693363SVladimir Oltean 
815685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
816d1cffff0SVivien Didelot }
817d1cffff0SVivien Didelot 
8182acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
8192acf4e6aSArkadi Sharshevsky 		     u16 vid)
820d1cffff0SVivien Didelot {
821685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
822685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
823685fb6a4SVivien Didelot 		.port = dp->index,
8242acf4e6aSArkadi Sharshevsky 		.addr = addr,
8252acf4e6aSArkadi Sharshevsky 		.vid = vid,
826c2693363SVladimir Oltean 		.db = {
827c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
828c2693363SVladimir Oltean 			.bridge = *dp->bridge,
829c2693363SVladimir Oltean 		},
830685fb6a4SVivien Didelot 	};
831d1cffff0SVivien Didelot 
832c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
833c2693363SVladimir Oltean 		info.db.bridge.num = 0;
834c2693363SVladimir Oltean 
835685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
836d1cffff0SVivien Didelot }
837d1cffff0SVivien Didelot 
8383dc80afcSVladimir Oltean int dsa_port_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
8393dc80afcSVladimir Oltean 			  u16 vid)
8403dc80afcSVladimir Oltean {
8413dc80afcSVladimir Oltean 	struct dsa_notifier_fdb_info info = {
8423dc80afcSVladimir Oltean 		.sw_index = dp->ds->index,
8433dc80afcSVladimir Oltean 		.port = dp->index,
8443dc80afcSVladimir Oltean 		.addr = addr,
8453dc80afcSVladimir Oltean 		.vid = vid,
846c2693363SVladimir Oltean 		.db = {
847c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
848c2693363SVladimir Oltean 			.bridge = *dp->bridge,
849c2693363SVladimir Oltean 		},
8503dc80afcSVladimir Oltean 	};
85126ee7b06SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
85226ee7b06SVladimir Oltean 	int err;
85326ee7b06SVladimir Oltean 
8548940e6b6SVladimir Oltean 	/* Avoid a call to __dev_set_promiscuity() on the master, which
8558940e6b6SVladimir Oltean 	 * requires rtnl_lock(), since we can't guarantee that is held here,
8568940e6b6SVladimir Oltean 	 * and we can't take it either.
8578940e6b6SVladimir Oltean 	 */
8588940e6b6SVladimir Oltean 	if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) {
85926ee7b06SVladimir Oltean 		err = dev_uc_add(cpu_dp->master, addr);
86026ee7b06SVladimir Oltean 		if (err)
86126ee7b06SVladimir Oltean 			return err;
8628940e6b6SVladimir Oltean 	}
8633dc80afcSVladimir Oltean 
864c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
865c2693363SVladimir Oltean 		info.db.bridge.num = 0;
866c2693363SVladimir Oltean 
8673dc80afcSVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info);
8683dc80afcSVladimir Oltean }
8693dc80afcSVladimir Oltean 
8703dc80afcSVladimir Oltean int dsa_port_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
8713dc80afcSVladimir Oltean 			  u16 vid)
8723dc80afcSVladimir Oltean {
8733dc80afcSVladimir Oltean 	struct dsa_notifier_fdb_info info = {
8743dc80afcSVladimir Oltean 		.sw_index = dp->ds->index,
8753dc80afcSVladimir Oltean 		.port = dp->index,
8763dc80afcSVladimir Oltean 		.addr = addr,
8773dc80afcSVladimir Oltean 		.vid = vid,
878c2693363SVladimir Oltean 		.db = {
879c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
880c2693363SVladimir Oltean 			.bridge = *dp->bridge,
881c2693363SVladimir Oltean 		},
8823dc80afcSVladimir Oltean 	};
88326ee7b06SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
88426ee7b06SVladimir Oltean 	int err;
88526ee7b06SVladimir Oltean 
8868940e6b6SVladimir Oltean 	if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) {
88726ee7b06SVladimir Oltean 		err = dev_uc_del(cpu_dp->master, addr);
88826ee7b06SVladimir Oltean 		if (err)
88926ee7b06SVladimir Oltean 			return err;
8908940e6b6SVladimir Oltean 	}
8913dc80afcSVladimir Oltean 
892c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
893c2693363SVladimir Oltean 		info.db.bridge.num = 0;
894c2693363SVladimir Oltean 
8953dc80afcSVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info);
8963dc80afcSVladimir Oltean }
8973dc80afcSVladimir Oltean 
898e212fa7cSVladimir Oltean int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr,
899e212fa7cSVladimir Oltean 			 u16 vid)
900e212fa7cSVladimir Oltean {
901e212fa7cSVladimir Oltean 	struct dsa_notifier_lag_fdb_info info = {
902e212fa7cSVladimir Oltean 		.lag = dp->lag,
903e212fa7cSVladimir Oltean 		.addr = addr,
904e212fa7cSVladimir Oltean 		.vid = vid,
905c2693363SVladimir Oltean 		.db = {
906c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
907c2693363SVladimir Oltean 			.bridge = *dp->bridge,
908c2693363SVladimir Oltean 		},
909e212fa7cSVladimir Oltean 	};
910e212fa7cSVladimir Oltean 
911c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
912c2693363SVladimir Oltean 		info.db.bridge.num = 0;
913c2693363SVladimir Oltean 
914e212fa7cSVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_ADD, &info);
915e212fa7cSVladimir Oltean }
916e212fa7cSVladimir Oltean 
917e212fa7cSVladimir Oltean int dsa_port_lag_fdb_del(struct dsa_port *dp, const unsigned char *addr,
918e212fa7cSVladimir Oltean 			 u16 vid)
919e212fa7cSVladimir Oltean {
920e212fa7cSVladimir Oltean 	struct dsa_notifier_lag_fdb_info info = {
921e212fa7cSVladimir Oltean 		.lag = dp->lag,
922e212fa7cSVladimir Oltean 		.addr = addr,
923e212fa7cSVladimir Oltean 		.vid = vid,
924c2693363SVladimir Oltean 		.db = {
925c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
926c2693363SVladimir Oltean 			.bridge = *dp->bridge,
927c2693363SVladimir Oltean 		},
928e212fa7cSVladimir Oltean 	};
929e212fa7cSVladimir Oltean 
930c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
931c2693363SVladimir Oltean 		info.db.bridge.num = 0;
932c2693363SVladimir Oltean 
933e212fa7cSVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_LAG_FDB_DEL, &info);
934e212fa7cSVladimir Oltean }
935e212fa7cSVladimir Oltean 
936de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
937de40fc5dSVivien Didelot {
938de40fc5dSVivien Didelot 	struct dsa_switch *ds = dp->ds;
939de40fc5dSVivien Didelot 	int port = dp->index;
940de40fc5dSVivien Didelot 
941de40fc5dSVivien Didelot 	if (!ds->ops->port_fdb_dump)
942de40fc5dSVivien Didelot 		return -EOPNOTSUPP;
943de40fc5dSVivien Didelot 
944de40fc5dSVivien Didelot 	return ds->ops->port_fdb_dump(ds, port, cb, data);
945de40fc5dSVivien Didelot }
946de40fc5dSVivien Didelot 
947bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp,
948ffb68fc5SVladimir Oltean 		     const struct switchdev_obj_port_mdb *mdb)
9493a9afea3SVivien Didelot {
9508ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
9518ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
9528ae5bcdcSVivien Didelot 		.port = dp->index,
9538ae5bcdcSVivien Didelot 		.mdb = mdb,
954c2693363SVladimir Oltean 		.db = {
955c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
956c2693363SVladimir Oltean 			.bridge = *dp->bridge,
957c2693363SVladimir Oltean 		},
9588ae5bcdcSVivien Didelot 	};
9593a9afea3SVivien Didelot 
960c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
961c2693363SVladimir Oltean 		info.db.bridge.num = 0;
962c2693363SVladimir Oltean 
9638ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
9643a9afea3SVivien Didelot }
9653a9afea3SVivien Didelot 
966bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp,
9673a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb)
9683a9afea3SVivien Didelot {
9698ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
9708ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
9718ae5bcdcSVivien Didelot 		.port = dp->index,
9728ae5bcdcSVivien Didelot 		.mdb = mdb,
973c2693363SVladimir Oltean 		.db = {
974c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
975c2693363SVladimir Oltean 			.bridge = *dp->bridge,
976c2693363SVladimir Oltean 		},
9778ae5bcdcSVivien Didelot 	};
9783a9afea3SVivien Didelot 
979c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
980c2693363SVladimir Oltean 		info.db.bridge.num = 0;
981c2693363SVladimir Oltean 
9828ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
9833a9afea3SVivien Didelot }
9843a9afea3SVivien Didelot 
985b8e997c4SVladimir Oltean int dsa_port_host_mdb_add(const struct dsa_port *dp,
986b8e997c4SVladimir Oltean 			  const struct switchdev_obj_port_mdb *mdb)
987b8e997c4SVladimir Oltean {
988b8e997c4SVladimir Oltean 	struct dsa_notifier_mdb_info info = {
989b8e997c4SVladimir Oltean 		.sw_index = dp->ds->index,
990b8e997c4SVladimir Oltean 		.port = dp->index,
991b8e997c4SVladimir Oltean 		.mdb = mdb,
992c2693363SVladimir Oltean 		.db = {
993c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
994c2693363SVladimir Oltean 			.bridge = *dp->bridge,
995c2693363SVladimir Oltean 		},
996b8e997c4SVladimir Oltean 	};
99726ee7b06SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
99826ee7b06SVladimir Oltean 	int err;
99926ee7b06SVladimir Oltean 
100026ee7b06SVladimir Oltean 	err = dev_mc_add(cpu_dp->master, mdb->addr);
100126ee7b06SVladimir Oltean 	if (err)
100226ee7b06SVladimir Oltean 		return err;
1003b8e997c4SVladimir Oltean 
1004c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
1005c2693363SVladimir Oltean 		info.db.bridge.num = 0;
1006c2693363SVladimir Oltean 
1007b8e997c4SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);
1008b8e997c4SVladimir Oltean }
1009b8e997c4SVladimir Oltean 
1010b8e997c4SVladimir Oltean int dsa_port_host_mdb_del(const struct dsa_port *dp,
1011b8e997c4SVladimir Oltean 			  const struct switchdev_obj_port_mdb *mdb)
1012b8e997c4SVladimir Oltean {
1013b8e997c4SVladimir Oltean 	struct dsa_notifier_mdb_info info = {
1014b8e997c4SVladimir Oltean 		.sw_index = dp->ds->index,
1015b8e997c4SVladimir Oltean 		.port = dp->index,
1016b8e997c4SVladimir Oltean 		.mdb = mdb,
1017c2693363SVladimir Oltean 		.db = {
1018c2693363SVladimir Oltean 			.type = DSA_DB_BRIDGE,
1019c2693363SVladimir Oltean 			.bridge = *dp->bridge,
1020c2693363SVladimir Oltean 		},
1021b8e997c4SVladimir Oltean 	};
102226ee7b06SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
102326ee7b06SVladimir Oltean 	int err;
102426ee7b06SVladimir Oltean 
102526ee7b06SVladimir Oltean 	err = dev_mc_del(cpu_dp->master, mdb->addr);
102626ee7b06SVladimir Oltean 	if (err)
102726ee7b06SVladimir Oltean 		return err;
1028b8e997c4SVladimir Oltean 
1029c2693363SVladimir Oltean 	if (!dp->ds->fdb_isolation)
1030c2693363SVladimir Oltean 		info.db.bridge.num = 0;
1031c2693363SVladimir Oltean 
1032b8e997c4SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);
1033b8e997c4SVladimir Oltean }
1034b8e997c4SVladimir Oltean 
1035076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp,
103631046a5fSVladimir Oltean 		      const struct switchdev_obj_port_vlan *vlan,
103731046a5fSVladimir Oltean 		      struct netlink_ext_ack *extack)
1038076e7133SVivien Didelot {
1039d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
1040d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
1041d0c627b8SVivien Didelot 		.port = dp->index,
1042d0c627b8SVivien Didelot 		.vlan = vlan,
104331046a5fSVladimir Oltean 		.extack = extack,
1044d0c627b8SVivien Didelot 	};
1045076e7133SVivien Didelot 
1046d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
1047076e7133SVivien Didelot }
1048076e7133SVivien Didelot 
1049076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp,
1050076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan)
1051076e7133SVivien Didelot {
1052d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
1053d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
1054d0c627b8SVivien Didelot 		.port = dp->index,
1055d0c627b8SVivien Didelot 		.vlan = vlan,
1056d0c627b8SVivien Didelot 	};
1057076e7133SVivien Didelot 
1058d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
1059076e7133SVivien Didelot }
106057ab1ca2SVivien Didelot 
1061134ef238SVladimir Oltean int dsa_port_host_vlan_add(struct dsa_port *dp,
1062134ef238SVladimir Oltean 			   const struct switchdev_obj_port_vlan *vlan,
1063134ef238SVladimir Oltean 			   struct netlink_ext_ack *extack)
1064134ef238SVladimir Oltean {
1065134ef238SVladimir Oltean 	struct dsa_notifier_vlan_info info = {
1066134ef238SVladimir Oltean 		.sw_index = dp->ds->index,
1067134ef238SVladimir Oltean 		.port = dp->index,
1068134ef238SVladimir Oltean 		.vlan = vlan,
1069134ef238SVladimir Oltean 		.extack = extack,
1070134ef238SVladimir Oltean 	};
1071134ef238SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
1072134ef238SVladimir Oltean 	int err;
1073134ef238SVladimir Oltean 
1074134ef238SVladimir Oltean 	err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_ADD, &info);
1075134ef238SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
1076134ef238SVladimir Oltean 		return err;
1077134ef238SVladimir Oltean 
1078134ef238SVladimir Oltean 	vlan_vid_add(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid);
1079134ef238SVladimir Oltean 
1080134ef238SVladimir Oltean 	return err;
1081134ef238SVladimir Oltean }
1082134ef238SVladimir Oltean 
1083134ef238SVladimir Oltean int dsa_port_host_vlan_del(struct dsa_port *dp,
1084134ef238SVladimir Oltean 			   const struct switchdev_obj_port_vlan *vlan)
1085134ef238SVladimir Oltean {
1086134ef238SVladimir Oltean 	struct dsa_notifier_vlan_info info = {
1087134ef238SVladimir Oltean 		.sw_index = dp->ds->index,
1088134ef238SVladimir Oltean 		.port = dp->index,
1089134ef238SVladimir Oltean 		.vlan = vlan,
1090134ef238SVladimir Oltean 	};
1091134ef238SVladimir Oltean 	struct dsa_port *cpu_dp = dp->cpu_dp;
1092134ef238SVladimir Oltean 	int err;
1093134ef238SVladimir Oltean 
1094134ef238SVladimir Oltean 	err = dsa_port_notify(dp, DSA_NOTIFIER_HOST_VLAN_DEL, &info);
1095134ef238SVladimir Oltean 	if (err && err != -EOPNOTSUPP)
1096134ef238SVladimir Oltean 		return err;
1097134ef238SVladimir Oltean 
1098134ef238SVladimir Oltean 	vlan_vid_del(cpu_dp->master, htons(ETH_P_8021Q), vlan->vid);
1099134ef238SVladimir Oltean 
1100134ef238SVladimir Oltean 	return err;
1101134ef238SVladimir Oltean }
1102134ef238SVladimir Oltean 
1103c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp,
1104c595c433SHoratiu Vultur 		     const struct switchdev_obj_mrp *mrp)
1105c595c433SHoratiu Vultur {
1106cad69019SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1107c595c433SHoratiu Vultur 
1108cad69019SVladimir Oltean 	if (!ds->ops->port_mrp_add)
1109cad69019SVladimir Oltean 		return -EOPNOTSUPP;
1110cad69019SVladimir Oltean 
1111cad69019SVladimir Oltean 	return ds->ops->port_mrp_add(ds, dp->index, mrp);
1112c595c433SHoratiu Vultur }
1113c595c433SHoratiu Vultur 
1114c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp,
1115c595c433SHoratiu Vultur 		     const struct switchdev_obj_mrp *mrp)
1116c595c433SHoratiu Vultur {
1117cad69019SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1118c595c433SHoratiu Vultur 
1119cad69019SVladimir Oltean 	if (!ds->ops->port_mrp_del)
1120cad69019SVladimir Oltean 		return -EOPNOTSUPP;
1121cad69019SVladimir Oltean 
1122cad69019SVladimir Oltean 	return ds->ops->port_mrp_del(ds, dp->index, mrp);
1123c595c433SHoratiu Vultur }
1124c595c433SHoratiu Vultur 
1125c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp,
1126c595c433SHoratiu Vultur 			       const struct switchdev_obj_ring_role_mrp *mrp)
1127c595c433SHoratiu Vultur {
1128cad69019SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1129c595c433SHoratiu Vultur 
1130cad69019SVladimir Oltean 	if (!ds->ops->port_mrp_add_ring_role)
1131cad69019SVladimir Oltean 		return -EOPNOTSUPP;
1132cad69019SVladimir Oltean 
1133cad69019SVladimir Oltean 	return ds->ops->port_mrp_add_ring_role(ds, dp->index, mrp);
1134c595c433SHoratiu Vultur }
1135c595c433SHoratiu Vultur 
1136c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
1137c595c433SHoratiu Vultur 			       const struct switchdev_obj_ring_role_mrp *mrp)
1138c595c433SHoratiu Vultur {
1139cad69019SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1140c595c433SHoratiu Vultur 
1141cad69019SVladimir Oltean 	if (!ds->ops->port_mrp_del_ring_role)
1142cad69019SVladimir Oltean 		return -EOPNOTSUPP;
1143cad69019SVladimir Oltean 
1144cad69019SVladimir Oltean 	return ds->ops->port_mrp_del_ring_role(ds, dp->index, mrp);
1145c595c433SHoratiu Vultur }
1146c595c433SHoratiu Vultur 
114753da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
114853da0ebaSVladimir Oltean 			       const struct dsa_device_ops *tag_ops)
114953da0ebaSVladimir Oltean {
115053da0ebaSVladimir Oltean 	cpu_dp->rcv = tag_ops->rcv;
115153da0ebaSVladimir Oltean 	cpu_dp->tag_ops = tag_ops;
115253da0ebaSVladimir Oltean }
115353da0ebaSVladimir Oltean 
11546207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
11556207a78cSFlorian Fainelli {
11566207a78cSFlorian Fainelli 	struct device_node *phy_dn;
11576207a78cSFlorian Fainelli 	struct phy_device *phydev;
11586207a78cSFlorian Fainelli 
11596207a78cSFlorian Fainelli 	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
11606207a78cSFlorian Fainelli 	if (!phy_dn)
11616207a78cSFlorian Fainelli 		return NULL;
11626207a78cSFlorian Fainelli 
11636207a78cSFlorian Fainelli 	phydev = of_phy_find_device(phy_dn);
11646207a78cSFlorian Fainelli 	if (!phydev) {
11656207a78cSFlorian Fainelli 		of_node_put(phy_dn);
11666207a78cSFlorian Fainelli 		return ERR_PTR(-EPROBE_DEFER);
11676207a78cSFlorian Fainelli 	}
11686207a78cSFlorian Fainelli 
11699919a363SWen Yang 	of_node_put(phy_dn);
11706207a78cSFlorian Fainelli 	return phydev;
11716207a78cSFlorian Fainelli }
11726207a78cSFlorian Fainelli 
11738ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config,
117477373d49SIoana Ciornei 				      unsigned long *supported,
117577373d49SIoana Ciornei 				      struct phylink_link_state *state)
117677373d49SIoana Ciornei {
117777373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
117877373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
117977373d49SIoana Ciornei 
11805938bce4SRussell King (Oracle) 	if (!ds->ops->phylink_validate) {
11815938bce4SRussell King (Oracle) 		if (config->mac_capabilities)
11825938bce4SRussell King (Oracle) 			phylink_generic_validate(config, supported, state);
118377373d49SIoana Ciornei 		return;
11845938bce4SRussell King (Oracle) 	}
118577373d49SIoana Ciornei 
118677373d49SIoana Ciornei 	ds->ops->phylink_validate(ds, dp->index, supported, state);
118777373d49SIoana Ciornei }
118877373d49SIoana Ciornei 
11898ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
119077373d49SIoana Ciornei 					       struct phylink_link_state *state)
119177373d49SIoana Ciornei {
119277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
119377373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
119487615c96SRussell King 	int err;
119577373d49SIoana Ciornei 
1196d46b7e4fSRussell King 	/* Only called for inband modes */
1197d46b7e4fSRussell King 	if (!ds->ops->phylink_mac_link_state) {
1198d46b7e4fSRussell King 		state->link = 0;
1199d46b7e4fSRussell King 		return;
120077373d49SIoana Ciornei 	}
1201d46b7e4fSRussell King 
120287615c96SRussell King 	err = ds->ops->phylink_mac_link_state(ds, dp->index, state);
120387615c96SRussell King 	if (err < 0) {
120487615c96SRussell King 		dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n",
120587615c96SRussell King 			dp->index, err);
1206d46b7e4fSRussell King 		state->link = 0;
1207d46b7e4fSRussell King 	}
120887615c96SRussell King }
120977373d49SIoana Ciornei 
1210bde01822SRussell King (Oracle) static struct phylink_pcs *
1211bde01822SRussell King (Oracle) dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
1212bde01822SRussell King (Oracle) 				phy_interface_t interface)
1213bde01822SRussell King (Oracle) {
1214bde01822SRussell King (Oracle) 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
121510544570SRussell King (Oracle) 	struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
1216bde01822SRussell King (Oracle) 	struct dsa_switch *ds = dp->ds;
1217bde01822SRussell King (Oracle) 
1218bde01822SRussell King (Oracle) 	if (ds->ops->phylink_mac_select_pcs)
1219bde01822SRussell King (Oracle) 		pcs = ds->ops->phylink_mac_select_pcs(ds, dp->index, interface);
1220bde01822SRussell King (Oracle) 
1221bde01822SRussell King (Oracle) 	return pcs;
1222bde01822SRussell King (Oracle) }
1223bde01822SRussell King (Oracle) 
12248ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config,
122577373d49SIoana Ciornei 					unsigned int mode,
122677373d49SIoana Ciornei 					const struct phylink_link_state *state)
122777373d49SIoana Ciornei {
122877373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
122977373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
123077373d49SIoana Ciornei 
123177373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_config)
123277373d49SIoana Ciornei 		return;
123377373d49SIoana Ciornei 
123477373d49SIoana Ciornei 	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
123577373d49SIoana Ciornei }
123677373d49SIoana Ciornei 
12378ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
123877373d49SIoana Ciornei {
123977373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
124077373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
124177373d49SIoana Ciornei 
124277373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_an_restart)
124377373d49SIoana Ciornei 		return;
124477373d49SIoana Ciornei 
124577373d49SIoana Ciornei 	ds->ops->phylink_mac_an_restart(ds, dp->index);
124677373d49SIoana Ciornei }
124777373d49SIoana Ciornei 
12488ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
124977373d49SIoana Ciornei 					   unsigned int mode,
125077373d49SIoana Ciornei 					   phy_interface_t interface)
125177373d49SIoana Ciornei {
125277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
12530e279218SIoana Ciornei 	struct phy_device *phydev = NULL;
125477373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
125577373d49SIoana Ciornei 
125657d77986SVladimir Oltean 	if (dsa_port_is_user(dp))
12570e279218SIoana Ciornei 		phydev = dp->slave->phydev;
12580e279218SIoana Ciornei 
125977373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_down) {
12600e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
12610e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
126277373d49SIoana Ciornei 		return;
126377373d49SIoana Ciornei 	}
126477373d49SIoana Ciornei 
126577373d49SIoana Ciornei 	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
126677373d49SIoana Ciornei }
126777373d49SIoana Ciornei 
12688ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
126991a208f2SRussell King 					 struct phy_device *phydev,
127077373d49SIoana Ciornei 					 unsigned int mode,
127177373d49SIoana Ciornei 					 phy_interface_t interface,
127291a208f2SRussell King 					 int speed, int duplex,
127391a208f2SRussell King 					 bool tx_pause, bool rx_pause)
127477373d49SIoana Ciornei {
127577373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
127677373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
127777373d49SIoana Ciornei 
127877373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_up) {
12790e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
12800e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
128177373d49SIoana Ciornei 		return;
128277373d49SIoana Ciornei 	}
128377373d49SIoana Ciornei 
12845b502a7bSRussell King 	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
12855b502a7bSRussell King 				     speed, duplex, tx_pause, rx_pause);
128677373d49SIoana Ciornei }
128777373d49SIoana Ciornei 
128821bd64bdSRussell King (Oracle) static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
128977373d49SIoana Ciornei 	.validate = dsa_port_phylink_validate,
1290bde01822SRussell King (Oracle) 	.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
1291d46b7e4fSRussell King 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
129277373d49SIoana Ciornei 	.mac_config = dsa_port_phylink_mac_config,
129377373d49SIoana Ciornei 	.mac_an_restart = dsa_port_phylink_mac_an_restart,
129477373d49SIoana Ciornei 	.mac_link_down = dsa_port_phylink_mac_link_down,
129577373d49SIoana Ciornei 	.mac_link_up = dsa_port_phylink_mac_link_up,
129677373d49SIoana Ciornei };
129777373d49SIoana Ciornei 
129821bd64bdSRussell King (Oracle) int dsa_port_phylink_create(struct dsa_port *dp)
129921bd64bdSRussell King (Oracle) {
130021bd64bdSRussell King (Oracle) 	struct dsa_switch *ds = dp->ds;
130121bd64bdSRussell King (Oracle) 	phy_interface_t mode;
130221bd64bdSRussell King (Oracle) 	int err;
130321bd64bdSRussell King (Oracle) 
130421bd64bdSRussell King (Oracle) 	err = of_get_phy_mode(dp->dn, &mode);
130521bd64bdSRussell King (Oracle) 	if (err)
130621bd64bdSRussell King (Oracle) 		mode = PHY_INTERFACE_MODE_NA;
130721bd64bdSRussell King (Oracle) 
13080a9f0794SRussell King (Oracle) 	/* Presence of phylink_mac_link_state or phylink_mac_an_restart is
13090a9f0794SRussell King (Oracle) 	 * an indicator of a legacy phylink driver.
13100a9f0794SRussell King (Oracle) 	 */
13110a9f0794SRussell King (Oracle) 	if (ds->ops->phylink_mac_link_state ||
13120a9f0794SRussell King (Oracle) 	    ds->ops->phylink_mac_an_restart)
13130a9f0794SRussell King (Oracle) 		dp->pl_config.legacy_pre_march2020 = true;
13140a9f0794SRussell King (Oracle) 
1315072eea6cSRussell King (Oracle) 	if (ds->ops->phylink_get_caps)
1316072eea6cSRussell King (Oracle) 		ds->ops->phylink_get_caps(ds, dp->index, &dp->pl_config);
131721bd64bdSRussell King (Oracle) 
131821bd64bdSRussell King (Oracle) 	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
131921bd64bdSRussell King (Oracle) 				mode, &dsa_port_phylink_mac_ops);
132021bd64bdSRussell King (Oracle) 	if (IS_ERR(dp->pl)) {
132121bd64bdSRussell King (Oracle) 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
132221bd64bdSRussell King (Oracle) 		return PTR_ERR(dp->pl);
132321bd64bdSRussell King (Oracle) 	}
132421bd64bdSRussell King (Oracle) 
132521bd64bdSRussell King (Oracle) 	return 0;
132621bd64bdSRussell King (Oracle) }
132721bd64bdSRussell King (Oracle) 
132833615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
132933615367SSebastian Reichel {
133033615367SSebastian Reichel 	struct dsa_switch *ds = dp->ds;
133133615367SSebastian Reichel 	struct phy_device *phydev;
133233615367SSebastian Reichel 	int port = dp->index;
133333615367SSebastian Reichel 	int err = 0;
133433615367SSebastian Reichel 
13356207a78cSFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
13366207a78cSFlorian Fainelli 	if (!phydev)
133733615367SSebastian Reichel 		return 0;
133833615367SSebastian Reichel 
13396207a78cSFlorian Fainelli 	if (IS_ERR(phydev))
13406207a78cSFlorian Fainelli 		return PTR_ERR(phydev);
134133615367SSebastian Reichel 
134233615367SSebastian Reichel 	if (enable) {
134333615367SSebastian Reichel 		err = genphy_resume(phydev);
134433615367SSebastian Reichel 		if (err < 0)
134533615367SSebastian Reichel 			goto err_put_dev;
134633615367SSebastian Reichel 
134733615367SSebastian Reichel 		err = genphy_read_status(phydev);
134833615367SSebastian Reichel 		if (err < 0)
134933615367SSebastian Reichel 			goto err_put_dev;
135033615367SSebastian Reichel 	} else {
135133615367SSebastian Reichel 		err = genphy_suspend(phydev);
135233615367SSebastian Reichel 		if (err < 0)
135333615367SSebastian Reichel 			goto err_put_dev;
135433615367SSebastian Reichel 	}
135533615367SSebastian Reichel 
135633615367SSebastian Reichel 	if (ds->ops->adjust_link)
135733615367SSebastian Reichel 		ds->ops->adjust_link(ds, port, phydev);
135833615367SSebastian Reichel 
135933615367SSebastian Reichel 	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
136033615367SSebastian Reichel 
136133615367SSebastian Reichel err_put_dev:
136233615367SSebastian Reichel 	put_device(&phydev->mdio.dev);
136333615367SSebastian Reichel 	return err;
136433615367SSebastian Reichel }
136533615367SSebastian Reichel 
136633615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
136757ab1ca2SVivien Didelot {
136857ab1ca2SVivien Didelot 	struct device_node *dn = dp->dn;
136957ab1ca2SVivien Didelot 	struct dsa_switch *ds = dp->ds;
137057ab1ca2SVivien Didelot 	struct phy_device *phydev;
137157ab1ca2SVivien Didelot 	int port = dp->index;
13720c65b2b9SAndrew Lunn 	phy_interface_t mode;
137357ab1ca2SVivien Didelot 	int err;
137457ab1ca2SVivien Didelot 
137557ab1ca2SVivien Didelot 	err = of_phy_register_fixed_link(dn);
137657ab1ca2SVivien Didelot 	if (err) {
137757ab1ca2SVivien Didelot 		dev_err(ds->dev,
137857ab1ca2SVivien Didelot 			"failed to register the fixed PHY of port %d\n",
137957ab1ca2SVivien Didelot 			port);
138057ab1ca2SVivien Didelot 		return err;
138157ab1ca2SVivien Didelot 	}
138257ab1ca2SVivien Didelot 
138357ab1ca2SVivien Didelot 	phydev = of_phy_find_device(dn);
138457ab1ca2SVivien Didelot 
13850c65b2b9SAndrew Lunn 	err = of_get_phy_mode(dn, &mode);
13860c65b2b9SAndrew Lunn 	if (err)
138757ab1ca2SVivien Didelot 		mode = PHY_INTERFACE_MODE_NA;
138857ab1ca2SVivien Didelot 	phydev->interface = mode;
138957ab1ca2SVivien Didelot 
139057ab1ca2SVivien Didelot 	genphy_read_status(phydev);
139157ab1ca2SVivien Didelot 
139257ab1ca2SVivien Didelot 	if (ds->ops->adjust_link)
139357ab1ca2SVivien Didelot 		ds->ops->adjust_link(ds, port, phydev);
139457ab1ca2SVivien Didelot 
139557ab1ca2SVivien Didelot 	put_device(&phydev->mdio.dev);
139657ab1ca2SVivien Didelot 
139757ab1ca2SVivien Didelot 	return 0;
139857ab1ca2SVivien Didelot }
139957ab1ca2SVivien Didelot 
14000e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp)
14010e279218SIoana Ciornei {
14020e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
14030e279218SIoana Ciornei 	struct device_node *port_dn = dp->dn;
14040c65b2b9SAndrew Lunn 	int err;
14050e279218SIoana Ciornei 
14060e279218SIoana Ciornei 	dp->pl_config.dev = ds->dev;
14070e279218SIoana Ciornei 	dp->pl_config.type = PHYLINK_DEV;
14080e279218SIoana Ciornei 
140921bd64bdSRussell King (Oracle) 	err = dsa_port_phylink_create(dp);
141021bd64bdSRussell King (Oracle) 	if (err)
141121bd64bdSRussell King (Oracle) 		return err;
14120e279218SIoana Ciornei 
14130e279218SIoana Ciornei 	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
14142131fba5SFlorian Fainelli 	if (err && err != -ENODEV) {
14150e279218SIoana Ciornei 		pr_err("could not attach to PHY: %d\n", err);
14160e279218SIoana Ciornei 		goto err_phy_connect;
14170e279218SIoana Ciornei 	}
14180e279218SIoana Ciornei 
14190e279218SIoana Ciornei 	return 0;
14200e279218SIoana Ciornei 
14210e279218SIoana Ciornei err_phy_connect:
14220e279218SIoana Ciornei 	phylink_destroy(dp->pl);
14230e279218SIoana Ciornei 	return err;
14240e279218SIoana Ciornei }
14250e279218SIoana Ciornei 
142633615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp)
142757ab1ca2SVivien Didelot {
14280e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
1429a20f9970SAndrew Lunn 	struct device_node *phy_np;
14303be98b2dSAndrew Lunn 	int port = dp->index;
14310e279218SIoana Ciornei 
1432a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link) {
1433a20f9970SAndrew Lunn 		phy_np = of_parse_phandle(dp->dn, "phy-handle", 0);
14343be98b2dSAndrew Lunn 		if (of_phy_is_fixed_link(dp->dn) || phy_np) {
14353be98b2dSAndrew Lunn 			if (ds->ops->phylink_mac_link_down)
14363be98b2dSAndrew Lunn 				ds->ops->phylink_mac_link_down(ds, port,
14373be98b2dSAndrew Lunn 					MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
14380e279218SIoana Ciornei 			return dsa_port_phylink_register(dp);
14393be98b2dSAndrew Lunn 		}
1440a20f9970SAndrew Lunn 		return 0;
1441a20f9970SAndrew Lunn 	}
14420e279218SIoana Ciornei 
14430e279218SIoana Ciornei 	dev_warn(ds->dev,
14440e279218SIoana Ciornei 		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
14450e279218SIoana Ciornei 
144633615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
144733615367SSebastian Reichel 		return dsa_port_fixed_link_register_of(dp);
144833615367SSebastian Reichel 	else
144933615367SSebastian Reichel 		return dsa_port_setup_phy_of(dp, true);
145033615367SSebastian Reichel }
145157ab1ca2SVivien Didelot 
145233615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp)
145333615367SSebastian Reichel {
14540e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
14550e279218SIoana Ciornei 
1456a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link && dp->pl) {
14570e279218SIoana Ciornei 		rtnl_lock();
14580e279218SIoana Ciornei 		phylink_disconnect_phy(dp->pl);
14590e279218SIoana Ciornei 		rtnl_unlock();
14600e279218SIoana Ciornei 		phylink_destroy(dp->pl);
1461a20f9970SAndrew Lunn 		dp->pl = NULL;
14620e279218SIoana Ciornei 		return;
14630e279218SIoana Ciornei 	}
14640e279218SIoana Ciornei 
146533615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
146633615367SSebastian Reichel 		of_phy_deregister_fixed_link(dp->dn);
146733615367SSebastian Reichel 	else
146833615367SSebastian Reichel 		dsa_port_setup_phy_of(dp, false);
146957ab1ca2SVivien Didelot }
1470cf963573SFlorian Fainelli 
147118596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr)
147218596f50SGeorge McCollister {
1473a68dc7b9SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
147418596f50SGeorge McCollister 	int err;
147518596f50SGeorge McCollister 
1476a68dc7b9SVladimir Oltean 	if (!ds->ops->port_hsr_join)
1477a68dc7b9SVladimir Oltean 		return -EOPNOTSUPP;
1478a68dc7b9SVladimir Oltean 
147918596f50SGeorge McCollister 	dp->hsr_dev = hsr;
148018596f50SGeorge McCollister 
1481a68dc7b9SVladimir Oltean 	err = ds->ops->port_hsr_join(ds, dp->index, hsr);
148218596f50SGeorge McCollister 	if (err)
148318596f50SGeorge McCollister 		dp->hsr_dev = NULL;
148418596f50SGeorge McCollister 
148518596f50SGeorge McCollister 	return err;
148618596f50SGeorge McCollister }
148718596f50SGeorge McCollister 
148818596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr)
148918596f50SGeorge McCollister {
1490a68dc7b9SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
149118596f50SGeorge McCollister 	int err;
149218596f50SGeorge McCollister 
149318596f50SGeorge McCollister 	dp->hsr_dev = NULL;
149418596f50SGeorge McCollister 
1495a68dc7b9SVladimir Oltean 	if (ds->ops->port_hsr_leave) {
1496a68dc7b9SVladimir Oltean 		err = ds->ops->port_hsr_leave(ds, dp->index, hsr);
149718596f50SGeorge McCollister 		if (err)
1498ab97462bSVladimir Oltean 			dev_err(dp->ds->dev,
1499a68dc7b9SVladimir Oltean 				"port %d failed to leave HSR %s: %pe\n",
1500a68dc7b9SVladimir Oltean 				dp->index, hsr->name, ERR_PTR(err));
1501a68dc7b9SVladimir Oltean 	}
150218596f50SGeorge McCollister }
1503c64b9c05SVladimir Oltean 
1504724395f4SVladimir Oltean int dsa_port_tag_8021q_vlan_add(struct dsa_port *dp, u16 vid, bool broadcast)
1505c64b9c05SVladimir Oltean {
1506c64b9c05SVladimir Oltean 	struct dsa_notifier_tag_8021q_vlan_info info = {
1507c64b9c05SVladimir Oltean 		.tree_index = dp->ds->dst->index,
1508c64b9c05SVladimir Oltean 		.sw_index = dp->ds->index,
1509c64b9c05SVladimir Oltean 		.port = dp->index,
1510c64b9c05SVladimir Oltean 		.vid = vid,
1511c64b9c05SVladimir Oltean 	};
1512c64b9c05SVladimir Oltean 
1513724395f4SVladimir Oltean 	if (broadcast)
1514c64b9c05SVladimir Oltean 		return dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info);
1515724395f4SVladimir Oltean 
1516724395f4SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_ADD, &info);
1517c64b9c05SVladimir Oltean }
1518c64b9c05SVladimir Oltean 
1519724395f4SVladimir Oltean void dsa_port_tag_8021q_vlan_del(struct dsa_port *dp, u16 vid, bool broadcast)
1520c64b9c05SVladimir Oltean {
1521c64b9c05SVladimir Oltean 	struct dsa_notifier_tag_8021q_vlan_info info = {
1522c64b9c05SVladimir Oltean 		.tree_index = dp->ds->dst->index,
1523c64b9c05SVladimir Oltean 		.sw_index = dp->ds->index,
1524c64b9c05SVladimir Oltean 		.port = dp->index,
1525c64b9c05SVladimir Oltean 		.vid = vid,
1526c64b9c05SVladimir Oltean 	};
1527c64b9c05SVladimir Oltean 	int err;
1528c64b9c05SVladimir Oltean 
1529724395f4SVladimir Oltean 	if (broadcast)
1530c64b9c05SVladimir Oltean 		err = dsa_broadcast(DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info);
1531724395f4SVladimir Oltean 	else
1532724395f4SVladimir Oltean 		err = dsa_port_notify(dp, DSA_NOTIFIER_TAG_8021Q_VLAN_DEL, &info);
1533c64b9c05SVladimir Oltean 	if (err)
1534ab97462bSVladimir Oltean 		dev_err(dp->ds->dev,
1535ab97462bSVladimir Oltean 			"port %d failed to notify tag_8021q VLAN %d deletion: %pe\n",
1536ab97462bSVladimir Oltean 			dp->index, vid, ERR_PTR(err));
1537c64b9c05SVladimir Oltean }
1538