xref: /openbmc/linux/net/dsa/port.c (revision 185c9a76)
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 
33bae33f2bSVladimir Oltean int dsa_port_set_state(struct dsa_port *dp, u8 state)
34a40c175bSVivien Didelot {
35a40c175bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
36a40c175bSVivien Didelot 	int port = dp->index;
37a40c175bSVivien Didelot 
38bae33f2bSVladimir Oltean 	if (!ds->ops->port_stp_state_set)
39bae33f2bSVladimir Oltean 		return -EOPNOTSUPP;
40a40c175bSVivien Didelot 
41a40c175bSVivien Didelot 	ds->ops->port_stp_state_set(ds, port, state);
42a40c175bSVivien Didelot 
43a40c175bSVivien Didelot 	if (ds->ops->port_fast_age) {
44a40c175bSVivien Didelot 		/* Fast age FDB entries or flush appropriate forwarding database
45a40c175bSVivien Didelot 		 * for the given port, if we are moving it from Learning or
46a40c175bSVivien Didelot 		 * Forwarding state, to Disabled or Blocking or Listening state.
47a40c175bSVivien Didelot 		 */
48a40c175bSVivien Didelot 
49a40c175bSVivien Didelot 		if ((dp->stp_state == BR_STATE_LEARNING ||
50a40c175bSVivien Didelot 		     dp->stp_state == BR_STATE_FORWARDING) &&
51a40c175bSVivien Didelot 		    (state == BR_STATE_DISABLED ||
52a40c175bSVivien Didelot 		     state == BR_STATE_BLOCKING ||
53a40c175bSVivien Didelot 		     state == BR_STATE_LISTENING))
54a40c175bSVivien Didelot 			ds->ops->port_fast_age(ds, port);
55a40c175bSVivien Didelot 	}
56a40c175bSVivien Didelot 
57a40c175bSVivien Didelot 	dp->stp_state = state;
58a40c175bSVivien Didelot 
59a40c175bSVivien Didelot 	return 0;
60a40c175bSVivien Didelot }
61a40c175bSVivien Didelot 
62fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
63a40c175bSVivien Didelot {
64a40c175bSVivien Didelot 	int err;
65a40c175bSVivien Didelot 
66bae33f2bSVladimir Oltean 	err = dsa_port_set_state(dp, state);
67a40c175bSVivien Didelot 	if (err)
68a40c175bSVivien Didelot 		pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
69a40c175bSVivien Didelot }
70cfbed329SVivien Didelot 
718640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
72fb8a6a2bSVivien Didelot {
73fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
74fb8a6a2bSVivien Didelot 	int port = dp->index;
75fb8a6a2bSVivien Didelot 	int err;
76fb8a6a2bSVivien Didelot 
77fb8a6a2bSVivien Didelot 	if (ds->ops->port_enable) {
78fb8a6a2bSVivien Didelot 		err = ds->ops->port_enable(ds, port, phy);
79fb8a6a2bSVivien Didelot 		if (err)
80fb8a6a2bSVivien Didelot 			return err;
81fb8a6a2bSVivien Didelot 	}
82fb8a6a2bSVivien Didelot 
839c2054a5SRussell King 	if (!dp->bridge_dev)
849c2054a5SRussell King 		dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
85fb8a6a2bSVivien Didelot 
868640f8dcSRussell King 	if (dp->pl)
878640f8dcSRussell King 		phylink_start(dp->pl);
888640f8dcSRussell King 
89fb8a6a2bSVivien Didelot 	return 0;
90fb8a6a2bSVivien Didelot }
91fb8a6a2bSVivien Didelot 
928640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
938640f8dcSRussell King {
948640f8dcSRussell King 	int err;
958640f8dcSRussell King 
968640f8dcSRussell King 	rtnl_lock();
978640f8dcSRussell King 	err = dsa_port_enable_rt(dp, phy);
988640f8dcSRussell King 	rtnl_unlock();
998640f8dcSRussell King 
1008640f8dcSRussell King 	return err;
1018640f8dcSRussell King }
1028640f8dcSRussell King 
1038640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp)
104fb8a6a2bSVivien Didelot {
105fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
106fb8a6a2bSVivien Didelot 	int port = dp->index;
107fb8a6a2bSVivien Didelot 
1088640f8dcSRussell King 	if (dp->pl)
1098640f8dcSRussell King 		phylink_stop(dp->pl);
1108640f8dcSRussell King 
1119c2054a5SRussell King 	if (!dp->bridge_dev)
112fb8a6a2bSVivien Didelot 		dsa_port_set_state_now(dp, BR_STATE_DISABLED);
113fb8a6a2bSVivien Didelot 
114fb8a6a2bSVivien Didelot 	if (ds->ops->port_disable)
11575104db0SAndrew Lunn 		ds->ops->port_disable(ds, port);
116fb8a6a2bSVivien Didelot }
117fb8a6a2bSVivien Didelot 
1188640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp)
1198640f8dcSRussell King {
1208640f8dcSRussell King 	rtnl_lock();
1218640f8dcSRussell King 	dsa_port_disable_rt(dp);
1228640f8dcSRussell King 	rtnl_unlock();
1238640f8dcSRussell King }
1248640f8dcSRussell King 
1255e38c158SVladimir Oltean static void dsa_port_change_brport_flags(struct dsa_port *dp,
1265e38c158SVladimir Oltean 					 bool bridge_offload)
1275e38c158SVladimir Oltean {
128e18f4c18SVladimir Oltean 	struct switchdev_brport_flags flags;
129e18f4c18SVladimir Oltean 	int flag;
1305e38c158SVladimir Oltean 
131e18f4c18SVladimir Oltean 	flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
1325e38c158SVladimir Oltean 	if (bridge_offload)
133e18f4c18SVladimir Oltean 		flags.val = flags.mask;
1345e38c158SVladimir Oltean 	else
135e18f4c18SVladimir Oltean 		flags.val = flags.mask & ~BR_LEARNING;
1365e38c158SVladimir Oltean 
137e18f4c18SVladimir Oltean 	for_each_set_bit(flag, &flags.mask, 32) {
138e18f4c18SVladimir Oltean 		struct switchdev_brport_flags tmp;
1395e38c158SVladimir Oltean 
140e18f4c18SVladimir Oltean 		tmp.val = flags.val & BIT(flag);
141e18f4c18SVladimir Oltean 		tmp.mask = BIT(flag);
142e18f4c18SVladimir Oltean 
143a8b659e7SVladimir Oltean 		dsa_port_bridge_flags(dp, tmp, NULL);
1445e38c158SVladimir Oltean 	}
1455e38c158SVladimir Oltean }
1465e38c158SVladimir Oltean 
147cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
148cfbed329SVivien Didelot {
149cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
150f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
151cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
152cfbed329SVivien Didelot 		.port = dp->index,
153cfbed329SVivien Didelot 		.br = br,
154cfbed329SVivien Didelot 	};
155cfbed329SVivien Didelot 	int err;
156cfbed329SVivien Didelot 
1575e38c158SVladimir Oltean 	/* Notify the port driver to set its configurable flags in a way that
1585e38c158SVladimir Oltean 	 * matches the initial settings of a bridge port.
1595e38c158SVladimir Oltean 	 */
1605e38c158SVladimir Oltean 	dsa_port_change_brport_flags(dp, true);
161c1388063SRussell King 
162c1388063SRussell King 	/* Here the interface is already bridged. Reflect the current
163c1388063SRussell King 	 * configuration so that drivers can program their chips accordingly.
164cfbed329SVivien Didelot 	 */
165cfbed329SVivien Didelot 	dp->bridge_dev = br;
166cfbed329SVivien Didelot 
167f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
168cfbed329SVivien Didelot 
169cfbed329SVivien Didelot 	/* The bridging is rolled back on error */
170c1388063SRussell King 	if (err) {
1715e38c158SVladimir Oltean 		dsa_port_change_brport_flags(dp, false);
172cfbed329SVivien Didelot 		dp->bridge_dev = NULL;
173c1388063SRussell King 	}
174cfbed329SVivien Didelot 
175cfbed329SVivien Didelot 	return err;
176cfbed329SVivien Didelot }
177cfbed329SVivien Didelot 
178cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
179cfbed329SVivien Didelot {
180cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
181f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
182cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
183cfbed329SVivien Didelot 		.port = dp->index,
184cfbed329SVivien Didelot 		.br = br,
185cfbed329SVivien Didelot 	};
186cfbed329SVivien Didelot 	int err;
187cfbed329SVivien Didelot 
188cfbed329SVivien Didelot 	/* Here the port is already unbridged. Reflect the current configuration
189cfbed329SVivien Didelot 	 * so that drivers can program their chips accordingly.
190cfbed329SVivien Didelot 	 */
191cfbed329SVivien Didelot 	dp->bridge_dev = NULL;
192cfbed329SVivien Didelot 
193f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
194cfbed329SVivien Didelot 	if (err)
195cfbed329SVivien Didelot 		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
196cfbed329SVivien Didelot 
1975e38c158SVladimir Oltean 	/* Configure the port for standalone mode (no address learning,
1985e38c158SVladimir Oltean 	 * flood everything).
1995e38c158SVladimir Oltean 	 * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events
2005e38c158SVladimir Oltean 	 * when the user requests it through netlink or sysfs, but not
2015e38c158SVladimir Oltean 	 * automatically at port join or leave, so we need to handle resetting
2025e38c158SVladimir Oltean 	 * the brport flags ourselves. But we even prefer it that way, because
2035e38c158SVladimir Oltean 	 * otherwise, some setups might never get the notification they need,
2045e38c158SVladimir Oltean 	 * for example, when a port leaves a LAG that offloads the bridge,
2055e38c158SVladimir Oltean 	 * it becomes standalone, but as far as the bridge is concerned, no
2065e38c158SVladimir Oltean 	 * port ever left.
2075e38c158SVladimir Oltean 	 */
2085e38c158SVladimir Oltean 	dsa_port_change_brport_flags(dp, false);
209c1388063SRussell King 
210cfbed329SVivien Didelot 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
211cfbed329SVivien Didelot 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
212cfbed329SVivien Didelot 	 */
213cfbed329SVivien Didelot 	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
214cfbed329SVivien Didelot }
2154d61d304SVivien Didelot 
216058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp,
217058102a6STobias Waldekranz 			struct netdev_lag_lower_state_info *linfo)
218058102a6STobias Waldekranz {
219058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
220058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
221058102a6STobias Waldekranz 		.port = dp->index,
222058102a6STobias Waldekranz 	};
223058102a6STobias Waldekranz 	bool tx_enabled;
224058102a6STobias Waldekranz 
225058102a6STobias Waldekranz 	if (!dp->lag_dev)
226058102a6STobias Waldekranz 		return 0;
227058102a6STobias Waldekranz 
228058102a6STobias Waldekranz 	/* On statically configured aggregates (e.g. loadbalance
229058102a6STobias Waldekranz 	 * without LACP) ports will always be tx_enabled, even if the
230058102a6STobias Waldekranz 	 * link is down. Thus we require both link_up and tx_enabled
231058102a6STobias Waldekranz 	 * in order to include it in the tx set.
232058102a6STobias Waldekranz 	 */
233058102a6STobias Waldekranz 	tx_enabled = linfo->link_up && linfo->tx_enabled;
234058102a6STobias Waldekranz 
235058102a6STobias Waldekranz 	if (tx_enabled == dp->lag_tx_enabled)
236058102a6STobias Waldekranz 		return 0;
237058102a6STobias Waldekranz 
238058102a6STobias Waldekranz 	dp->lag_tx_enabled = tx_enabled;
239058102a6STobias Waldekranz 
240058102a6STobias Waldekranz 	return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
241058102a6STobias Waldekranz }
242058102a6STobias Waldekranz 
243058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag,
244058102a6STobias Waldekranz 		      struct netdev_lag_upper_info *uinfo)
245058102a6STobias Waldekranz {
246058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
247058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
248058102a6STobias Waldekranz 		.port = dp->index,
249058102a6STobias Waldekranz 		.lag = lag,
250058102a6STobias Waldekranz 		.info = uinfo,
251058102a6STobias Waldekranz 	};
252*185c9a76SVladimir Oltean 	struct net_device *bridge_dev;
253058102a6STobias Waldekranz 	int err;
254058102a6STobias Waldekranz 
255058102a6STobias Waldekranz 	dsa_lag_map(dp->ds->dst, lag);
256058102a6STobias Waldekranz 	dp->lag_dev = lag;
257058102a6STobias Waldekranz 
258058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
259*185c9a76SVladimir Oltean 	if (err)
260*185c9a76SVladimir Oltean 		goto err_lag_join;
261*185c9a76SVladimir Oltean 
262*185c9a76SVladimir Oltean 	bridge_dev = netdev_master_upper_dev_get(lag);
263*185c9a76SVladimir Oltean 	if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
264*185c9a76SVladimir Oltean 		return 0;
265*185c9a76SVladimir Oltean 
266*185c9a76SVladimir Oltean 	err = dsa_port_bridge_join(dp, bridge_dev);
267*185c9a76SVladimir Oltean 	if (err)
268*185c9a76SVladimir Oltean 		goto err_bridge_join;
269*185c9a76SVladimir Oltean 
270*185c9a76SVladimir Oltean 	return 0;
271*185c9a76SVladimir Oltean 
272*185c9a76SVladimir Oltean err_bridge_join:
273*185c9a76SVladimir Oltean 	dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
274*185c9a76SVladimir Oltean err_lag_join:
275058102a6STobias Waldekranz 	dp->lag_dev = NULL;
276058102a6STobias Waldekranz 	dsa_lag_unmap(dp->ds->dst, lag);
277058102a6STobias Waldekranz 	return err;
278058102a6STobias Waldekranz }
279058102a6STobias Waldekranz 
280058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
281058102a6STobias Waldekranz {
282058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
283058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
284058102a6STobias Waldekranz 		.port = dp->index,
285058102a6STobias Waldekranz 		.lag = lag,
286058102a6STobias Waldekranz 	};
287058102a6STobias Waldekranz 	int err;
288058102a6STobias Waldekranz 
289058102a6STobias Waldekranz 	if (!dp->lag_dev)
290058102a6STobias Waldekranz 		return;
291058102a6STobias Waldekranz 
292058102a6STobias Waldekranz 	/* Port might have been part of a LAG that in turn was
293058102a6STobias Waldekranz 	 * attached to a bridge.
294058102a6STobias Waldekranz 	 */
295058102a6STobias Waldekranz 	if (dp->bridge_dev)
296058102a6STobias Waldekranz 		dsa_port_bridge_leave(dp, dp->bridge_dev);
297058102a6STobias Waldekranz 
298058102a6STobias Waldekranz 	dp->lag_tx_enabled = false;
299058102a6STobias Waldekranz 	dp->lag_dev = NULL;
300058102a6STobias Waldekranz 
301058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
302058102a6STobias Waldekranz 	if (err)
303058102a6STobias Waldekranz 		pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n",
304058102a6STobias Waldekranz 		       err);
305058102a6STobias Waldekranz 
306058102a6STobias Waldekranz 	dsa_lag_unmap(dp->ds->dst, lag);
307058102a6STobias Waldekranz }
308058102a6STobias Waldekranz 
309adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */
3108f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
31189153ed6SVladimir Oltean 					      bool vlan_filtering,
31289153ed6SVladimir Oltean 					      struct netlink_ext_ack *extack)
3138f5d16f6SVladimir Oltean {
3148f5d16f6SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
315adb256ebSVladimir Oltean 	int err, i;
316adb256ebSVladimir Oltean 
317adb256ebSVladimir Oltean 	/* VLAN awareness was off, so the question is "can we turn it on".
318adb256ebSVladimir Oltean 	 * We may have had 8021q uppers, those need to go. Make sure we don't
319adb256ebSVladimir Oltean 	 * enter an inconsistent state: deny changing the VLAN awareness state
320adb256ebSVladimir Oltean 	 * as long as we have 8021q uppers.
321adb256ebSVladimir Oltean 	 */
322adb256ebSVladimir Oltean 	if (vlan_filtering && dsa_is_user_port(ds, dp->index)) {
323adb256ebSVladimir Oltean 		struct net_device *upper_dev, *slave = dp->slave;
324adb256ebSVladimir Oltean 		struct net_device *br = dp->bridge_dev;
325adb256ebSVladimir Oltean 		struct list_head *iter;
326adb256ebSVladimir Oltean 
327adb256ebSVladimir Oltean 		netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
328adb256ebSVladimir Oltean 			struct bridge_vlan_info br_info;
329adb256ebSVladimir Oltean 			u16 vid;
330adb256ebSVladimir Oltean 
331adb256ebSVladimir Oltean 			if (!is_vlan_dev(upper_dev))
332adb256ebSVladimir Oltean 				continue;
333adb256ebSVladimir Oltean 
334adb256ebSVladimir Oltean 			vid = vlan_dev_vlan_id(upper_dev);
335adb256ebSVladimir Oltean 
336adb256ebSVladimir Oltean 			/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
337adb256ebSVladimir Oltean 			 * device, respectively the VID is not found, returning
338adb256ebSVladimir Oltean 			 * 0 means success, which is a failure for us here.
339adb256ebSVladimir Oltean 			 */
340adb256ebSVladimir Oltean 			err = br_vlan_get_info(br, vid, &br_info);
341adb256ebSVladimir Oltean 			if (err == 0) {
34289153ed6SVladimir Oltean 				NL_SET_ERR_MSG_MOD(extack,
34389153ed6SVladimir Oltean 						   "Must first remove VLAN uppers having VIDs also present in bridge");
344adb256ebSVladimir Oltean 				return false;
345adb256ebSVladimir Oltean 			}
346adb256ebSVladimir Oltean 		}
347adb256ebSVladimir Oltean 	}
3488f5d16f6SVladimir Oltean 
3498f5d16f6SVladimir Oltean 	if (!ds->vlan_filtering_is_global)
3508f5d16f6SVladimir Oltean 		return true;
3518f5d16f6SVladimir Oltean 
3528f5d16f6SVladimir Oltean 	/* For cases where enabling/disabling VLAN awareness is global to the
3538f5d16f6SVladimir Oltean 	 * switch, we need to handle the case where multiple bridges span
3548f5d16f6SVladimir Oltean 	 * different ports of the same switch device and one of them has a
3558f5d16f6SVladimir Oltean 	 * different setting than what is being requested.
3568f5d16f6SVladimir Oltean 	 */
3578f5d16f6SVladimir Oltean 	for (i = 0; i < ds->num_ports; i++) {
3588f5d16f6SVladimir Oltean 		struct net_device *other_bridge;
3598f5d16f6SVladimir Oltean 
3608f5d16f6SVladimir Oltean 		other_bridge = dsa_to_port(ds, i)->bridge_dev;
3618f5d16f6SVladimir Oltean 		if (!other_bridge)
3628f5d16f6SVladimir Oltean 			continue;
3638f5d16f6SVladimir Oltean 		/* If it's the same bridge, it also has same
3648f5d16f6SVladimir Oltean 		 * vlan_filtering setting => no need to check
3658f5d16f6SVladimir Oltean 		 */
3668f5d16f6SVladimir Oltean 		if (other_bridge == dp->bridge_dev)
3678f5d16f6SVladimir Oltean 			continue;
3688f5d16f6SVladimir Oltean 		if (br_vlan_enabled(other_bridge) != vlan_filtering) {
36989153ed6SVladimir Oltean 			NL_SET_ERR_MSG_MOD(extack,
37089153ed6SVladimir Oltean 					   "VLAN filtering is a global setting");
3718f5d16f6SVladimir Oltean 			return false;
3728f5d16f6SVladimir Oltean 		}
3738f5d16f6SVladimir Oltean 	}
3748f5d16f6SVladimir Oltean 	return true;
3758f5d16f6SVladimir Oltean }
3768f5d16f6SVladimir Oltean 
37789153ed6SVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
37889153ed6SVladimir Oltean 			    struct netlink_ext_ack *extack)
3794d61d304SVivien Didelot {
3804d61d304SVivien Didelot 	struct dsa_switch *ds = dp->ds;
381adb256ebSVladimir Oltean 	bool apply;
382bae33f2bSVladimir Oltean 	int err;
383adb256ebSVladimir Oltean 
3848f5d16f6SVladimir Oltean 	if (!ds->ops->port_vlan_filtering)
385707ec383SVladimir Oltean 		return -EOPNOTSUPP;
3868f5d16f6SVladimir Oltean 
387adb256ebSVladimir Oltean 	/* We are called from dsa_slave_switchdev_blocking_event(),
388adb256ebSVladimir Oltean 	 * which is not under rcu_read_lock(), unlike
389adb256ebSVladimir Oltean 	 * dsa_slave_switchdev_event().
390adb256ebSVladimir Oltean 	 */
391adb256ebSVladimir Oltean 	rcu_read_lock();
39289153ed6SVladimir Oltean 	apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering, extack);
393adb256ebSVladimir Oltean 	rcu_read_unlock();
394adb256ebSVladimir Oltean 	if (!apply)
3958f5d16f6SVladimir Oltean 		return -EINVAL;
396707ec383SVladimir Oltean 
397ec9121e7SVladimir Oltean 	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
398ec9121e7SVladimir Oltean 		return 0;
399ec9121e7SVladimir Oltean 
40089153ed6SVladimir Oltean 	err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
40189153ed6SVladimir Oltean 					   extack);
40233162e9aSVladimir Oltean 	if (err)
40333162e9aSVladimir Oltean 		return err;
4048f5d16f6SVladimir Oltean 
40514574676SVladimir Oltean 	if (ds->vlan_filtering_is_global)
40614574676SVladimir Oltean 		ds->vlan_filtering = vlan_filtering;
40714574676SVladimir Oltean 	else
40833162e9aSVladimir Oltean 		dp->vlan_filtering = vlan_filtering;
4092e554a7aSVladimir Oltean 
4104d61d304SVivien Didelot 	return 0;
4114d61d304SVivien Didelot }
412d87bd94eSVivien Didelot 
41354a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't
41454a0ed0dSRussell King  * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0
41554a0ed0dSRussell King  */
41654a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
41754a0ed0dSRussell King {
41854a0ed0dSRussell King 	struct dsa_switch *ds = dp->ds;
41954a0ed0dSRussell King 
42054a0ed0dSRussell King 	if (!dp->bridge_dev)
42154a0ed0dSRussell King 		return false;
42254a0ed0dSRussell King 
42354a0ed0dSRussell King 	return (!ds->configure_vlan_while_not_filtering &&
42454a0ed0dSRussell King 		!br_vlan_enabled(dp->bridge_dev));
42554a0ed0dSRussell King }
42654a0ed0dSRussell King 
427bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
428d87bd94eSVivien Didelot {
429d87bd94eSVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
430d87bd94eSVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
431bae33f2bSVladimir Oltean 	struct dsa_notifier_ageing_time_info info;
432bae33f2bSVladimir Oltean 	int err;
433d87bd94eSVivien Didelot 
434bae33f2bSVladimir Oltean 	info.ageing_time = ageing_time;
435bae33f2bSVladimir Oltean 
436bae33f2bSVladimir Oltean 	err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
437bae33f2bSVladimir Oltean 	if (err)
438bae33f2bSVladimir Oltean 		return err;
439d87bd94eSVivien Didelot 
440d87bd94eSVivien Didelot 	dp->ageing_time = ageing_time;
441d87bd94eSVivien Didelot 
44277b61365SVladimir Oltean 	return 0;
443d87bd94eSVivien Didelot }
444d1cffff0SVivien Didelot 
445e18f4c18SVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
446a8b659e7SVladimir Oltean 			      struct switchdev_brport_flags flags,
447a8b659e7SVladimir Oltean 			      struct netlink_ext_ack *extack)
448ea87005aSFlorian Fainelli {
449ea87005aSFlorian Fainelli 	struct dsa_switch *ds = dp->ds;
450ea87005aSFlorian Fainelli 
451a8b659e7SVladimir Oltean 	if (!ds->ops->port_pre_bridge_flags)
452ea87005aSFlorian Fainelli 		return -EINVAL;
453ea87005aSFlorian Fainelli 
454a8b659e7SVladimir Oltean 	return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack);
455ea87005aSFlorian Fainelli }
456ea87005aSFlorian Fainelli 
457e18f4c18SVladimir Oltean int dsa_port_bridge_flags(const struct dsa_port *dp,
458a8b659e7SVladimir Oltean 			  struct switchdev_brport_flags flags,
459a8b659e7SVladimir Oltean 			  struct netlink_ext_ack *extack)
46057652796SRussell King {
46157652796SRussell King 	struct dsa_switch *ds = dp->ds;
46257652796SRussell King 
463a8b659e7SVladimir Oltean 	if (!ds->ops->port_bridge_flags)
464a8b659e7SVladimir Oltean 		return -EINVAL;
46557652796SRussell King 
466a8b659e7SVladimir Oltean 	return ds->ops->port_bridge_flags(ds, dp->index, flags, extack);
46757652796SRussell King }
46857652796SRussell King 
469a8b659e7SVladimir Oltean int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
470a8b659e7SVladimir Oltean 		     struct netlink_ext_ack *extack)
47108cc83ccSVivien Didelot {
47208cc83ccSVivien Didelot 	struct dsa_switch *ds = dp->ds;
47308cc83ccSVivien Didelot 
474a8b659e7SVladimir Oltean 	if (!ds->ops->port_set_mrouter)
475bae33f2bSVladimir Oltean 		return -EOPNOTSUPP;
47608cc83ccSVivien Didelot 
477a8b659e7SVladimir Oltean 	return ds->ops->port_set_mrouter(ds, dp->index, mrouter, extack);
47808cc83ccSVivien Didelot }
47908cc83ccSVivien Didelot 
480bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
481bfcb8132SVladimir Oltean 			bool propagate_upstream)
482bfcb8132SVladimir Oltean {
483bfcb8132SVladimir Oltean 	struct dsa_notifier_mtu_info info = {
484bfcb8132SVladimir Oltean 		.sw_index = dp->ds->index,
485bfcb8132SVladimir Oltean 		.propagate_upstream = propagate_upstream,
486bfcb8132SVladimir Oltean 		.port = dp->index,
487bfcb8132SVladimir Oltean 		.mtu = new_mtu,
488bfcb8132SVladimir Oltean 	};
489bfcb8132SVladimir Oltean 
490bfcb8132SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info);
491bfcb8132SVladimir Oltean }
492bfcb8132SVladimir Oltean 
4932acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
4942acf4e6aSArkadi Sharshevsky 		     u16 vid)
495d1cffff0SVivien Didelot {
496685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
497685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
498685fb6a4SVivien Didelot 		.port = dp->index,
4992acf4e6aSArkadi Sharshevsky 		.addr = addr,
5002acf4e6aSArkadi Sharshevsky 		.vid = vid,
501685fb6a4SVivien Didelot 	};
502d1cffff0SVivien Didelot 
503685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
504d1cffff0SVivien Didelot }
505d1cffff0SVivien Didelot 
5062acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
5072acf4e6aSArkadi Sharshevsky 		     u16 vid)
508d1cffff0SVivien Didelot {
509685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
510685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
511685fb6a4SVivien Didelot 		.port = dp->index,
5122acf4e6aSArkadi Sharshevsky 		.addr = addr,
5132acf4e6aSArkadi Sharshevsky 		.vid = vid,
5142acf4e6aSArkadi Sharshevsky 
515685fb6a4SVivien Didelot 	};
516d1cffff0SVivien Didelot 
517685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
518d1cffff0SVivien Didelot }
519d1cffff0SVivien Didelot 
520de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
521de40fc5dSVivien Didelot {
522de40fc5dSVivien Didelot 	struct dsa_switch *ds = dp->ds;
523de40fc5dSVivien Didelot 	int port = dp->index;
524de40fc5dSVivien Didelot 
525de40fc5dSVivien Didelot 	if (!ds->ops->port_fdb_dump)
526de40fc5dSVivien Didelot 		return -EOPNOTSUPP;
527de40fc5dSVivien Didelot 
528de40fc5dSVivien Didelot 	return ds->ops->port_fdb_dump(ds, port, cb, data);
529de40fc5dSVivien Didelot }
530de40fc5dSVivien Didelot 
531bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp,
532ffb68fc5SVladimir Oltean 		     const struct switchdev_obj_port_mdb *mdb)
5333a9afea3SVivien Didelot {
5348ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
5358ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
5368ae5bcdcSVivien Didelot 		.port = dp->index,
5378ae5bcdcSVivien Didelot 		.mdb = mdb,
5388ae5bcdcSVivien Didelot 	};
5393a9afea3SVivien Didelot 
5408ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
5413a9afea3SVivien Didelot }
5423a9afea3SVivien Didelot 
543bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp,
5443a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb)
5453a9afea3SVivien Didelot {
5468ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
5478ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
5488ae5bcdcSVivien Didelot 		.port = dp->index,
5498ae5bcdcSVivien Didelot 		.mdb = mdb,
5508ae5bcdcSVivien Didelot 	};
5513a9afea3SVivien Didelot 
5528ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
5533a9afea3SVivien Didelot }
5543a9afea3SVivien Didelot 
555076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp,
55631046a5fSVladimir Oltean 		      const struct switchdev_obj_port_vlan *vlan,
55731046a5fSVladimir Oltean 		      struct netlink_ext_ack *extack)
558076e7133SVivien Didelot {
559d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
560d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
561d0c627b8SVivien Didelot 		.port = dp->index,
562d0c627b8SVivien Didelot 		.vlan = vlan,
56331046a5fSVladimir Oltean 		.extack = extack,
564d0c627b8SVivien Didelot 	};
565076e7133SVivien Didelot 
566d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
567076e7133SVivien Didelot }
568076e7133SVivien Didelot 
569076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp,
570076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan)
571076e7133SVivien Didelot {
572d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
573d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
574d0c627b8SVivien Didelot 		.port = dp->index,
575d0c627b8SVivien Didelot 		.vlan = vlan,
576d0c627b8SVivien Didelot 	};
577076e7133SVivien Didelot 
578d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
579076e7133SVivien Didelot }
58057ab1ca2SVivien Didelot 
581c595c433SHoratiu Vultur int dsa_port_mrp_add(const struct dsa_port *dp,
582c595c433SHoratiu Vultur 		     const struct switchdev_obj_mrp *mrp)
583c595c433SHoratiu Vultur {
584c595c433SHoratiu Vultur 	struct dsa_notifier_mrp_info info = {
585c595c433SHoratiu Vultur 		.sw_index = dp->ds->index,
586c595c433SHoratiu Vultur 		.port = dp->index,
587c595c433SHoratiu Vultur 		.mrp = mrp,
588c595c433SHoratiu Vultur 	};
589c595c433SHoratiu Vultur 
590c595c433SHoratiu Vultur 	return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD, &info);
591c595c433SHoratiu Vultur }
592c595c433SHoratiu Vultur 
593c595c433SHoratiu Vultur int dsa_port_mrp_del(const struct dsa_port *dp,
594c595c433SHoratiu Vultur 		     const struct switchdev_obj_mrp *mrp)
595c595c433SHoratiu Vultur {
596c595c433SHoratiu Vultur 	struct dsa_notifier_mrp_info info = {
597c595c433SHoratiu Vultur 		.sw_index = dp->ds->index,
598c595c433SHoratiu Vultur 		.port = dp->index,
599c595c433SHoratiu Vultur 		.mrp = mrp,
600c595c433SHoratiu Vultur 	};
601c595c433SHoratiu Vultur 
602c595c433SHoratiu Vultur 	return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL, &info);
603c595c433SHoratiu Vultur }
604c595c433SHoratiu Vultur 
605c595c433SHoratiu Vultur int dsa_port_mrp_add_ring_role(const struct dsa_port *dp,
606c595c433SHoratiu Vultur 			       const struct switchdev_obj_ring_role_mrp *mrp)
607c595c433SHoratiu Vultur {
608c595c433SHoratiu Vultur 	struct dsa_notifier_mrp_ring_role_info info = {
609c595c433SHoratiu Vultur 		.sw_index = dp->ds->index,
610c595c433SHoratiu Vultur 		.port = dp->index,
611c595c433SHoratiu Vultur 		.mrp = mrp,
612c595c433SHoratiu Vultur 	};
613c595c433SHoratiu Vultur 
614c595c433SHoratiu Vultur 	return dsa_port_notify(dp, DSA_NOTIFIER_MRP_ADD_RING_ROLE, &info);
615c595c433SHoratiu Vultur }
616c595c433SHoratiu Vultur 
617c595c433SHoratiu Vultur int dsa_port_mrp_del_ring_role(const struct dsa_port *dp,
618c595c433SHoratiu Vultur 			       const struct switchdev_obj_ring_role_mrp *mrp)
619c595c433SHoratiu Vultur {
620c595c433SHoratiu Vultur 	struct dsa_notifier_mrp_ring_role_info info = {
621c595c433SHoratiu Vultur 		.sw_index = dp->ds->index,
622c595c433SHoratiu Vultur 		.port = dp->index,
623c595c433SHoratiu Vultur 		.mrp = mrp,
624c595c433SHoratiu Vultur 	};
625c595c433SHoratiu Vultur 
626c595c433SHoratiu Vultur 	return dsa_port_notify(dp, DSA_NOTIFIER_MRP_DEL_RING_ROLE, &info);
627c595c433SHoratiu Vultur }
628c595c433SHoratiu Vultur 
62953da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
63053da0ebaSVladimir Oltean 			       const struct dsa_device_ops *tag_ops)
63153da0ebaSVladimir Oltean {
63253da0ebaSVladimir Oltean 	cpu_dp->filter = tag_ops->filter;
63353da0ebaSVladimir Oltean 	cpu_dp->rcv = tag_ops->rcv;
63453da0ebaSVladimir Oltean 	cpu_dp->tag_ops = tag_ops;
63553da0ebaSVladimir Oltean }
63653da0ebaSVladimir Oltean 
6376207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
6386207a78cSFlorian Fainelli {
6396207a78cSFlorian Fainelli 	struct device_node *phy_dn;
6406207a78cSFlorian Fainelli 	struct phy_device *phydev;
6416207a78cSFlorian Fainelli 
6426207a78cSFlorian Fainelli 	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
6436207a78cSFlorian Fainelli 	if (!phy_dn)
6446207a78cSFlorian Fainelli 		return NULL;
6456207a78cSFlorian Fainelli 
6466207a78cSFlorian Fainelli 	phydev = of_phy_find_device(phy_dn);
6476207a78cSFlorian Fainelli 	if (!phydev) {
6486207a78cSFlorian Fainelli 		of_node_put(phy_dn);
6496207a78cSFlorian Fainelli 		return ERR_PTR(-EPROBE_DEFER);
6506207a78cSFlorian Fainelli 	}
6516207a78cSFlorian Fainelli 
6529919a363SWen Yang 	of_node_put(phy_dn);
6536207a78cSFlorian Fainelli 	return phydev;
6546207a78cSFlorian Fainelli }
6556207a78cSFlorian Fainelli 
6568ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config,
65777373d49SIoana Ciornei 				      unsigned long *supported,
65877373d49SIoana Ciornei 				      struct phylink_link_state *state)
65977373d49SIoana Ciornei {
66077373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
66177373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
66277373d49SIoana Ciornei 
66377373d49SIoana Ciornei 	if (!ds->ops->phylink_validate)
66477373d49SIoana Ciornei 		return;
66577373d49SIoana Ciornei 
66677373d49SIoana Ciornei 	ds->ops->phylink_validate(ds, dp->index, supported, state);
66777373d49SIoana Ciornei }
66877373d49SIoana Ciornei 
6698ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
67077373d49SIoana Ciornei 					       struct phylink_link_state *state)
67177373d49SIoana Ciornei {
67277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
67377373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
67487615c96SRussell King 	int err;
67577373d49SIoana Ciornei 
676d46b7e4fSRussell King 	/* Only called for inband modes */
677d46b7e4fSRussell King 	if (!ds->ops->phylink_mac_link_state) {
678d46b7e4fSRussell King 		state->link = 0;
679d46b7e4fSRussell King 		return;
68077373d49SIoana Ciornei 	}
681d46b7e4fSRussell King 
68287615c96SRussell King 	err = ds->ops->phylink_mac_link_state(ds, dp->index, state);
68387615c96SRussell King 	if (err < 0) {
68487615c96SRussell King 		dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n",
68587615c96SRussell King 			dp->index, err);
686d46b7e4fSRussell King 		state->link = 0;
687d46b7e4fSRussell King 	}
68887615c96SRussell King }
68977373d49SIoana Ciornei 
6908ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config,
69177373d49SIoana Ciornei 					unsigned int mode,
69277373d49SIoana Ciornei 					const struct phylink_link_state *state)
69377373d49SIoana Ciornei {
69477373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
69577373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
69677373d49SIoana Ciornei 
69777373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_config)
69877373d49SIoana Ciornei 		return;
69977373d49SIoana Ciornei 
70077373d49SIoana Ciornei 	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
70177373d49SIoana Ciornei }
70277373d49SIoana Ciornei 
7038ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
70477373d49SIoana Ciornei {
70577373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
70677373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
70777373d49SIoana Ciornei 
70877373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_an_restart)
70977373d49SIoana Ciornei 		return;
71077373d49SIoana Ciornei 
71177373d49SIoana Ciornei 	ds->ops->phylink_mac_an_restart(ds, dp->index);
71277373d49SIoana Ciornei }
71377373d49SIoana Ciornei 
7148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
71577373d49SIoana Ciornei 					   unsigned int mode,
71677373d49SIoana Ciornei 					   phy_interface_t interface)
71777373d49SIoana Ciornei {
71877373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
7190e279218SIoana Ciornei 	struct phy_device *phydev = NULL;
72077373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
72177373d49SIoana Ciornei 
7220e279218SIoana Ciornei 	if (dsa_is_user_port(ds, dp->index))
7230e279218SIoana Ciornei 		phydev = dp->slave->phydev;
7240e279218SIoana Ciornei 
72577373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_down) {
7260e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
7270e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
72877373d49SIoana Ciornei 		return;
72977373d49SIoana Ciornei 	}
73077373d49SIoana Ciornei 
73177373d49SIoana Ciornei 	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
73277373d49SIoana Ciornei }
73377373d49SIoana Ciornei 
7348ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
73591a208f2SRussell King 					 struct phy_device *phydev,
73677373d49SIoana Ciornei 					 unsigned int mode,
73777373d49SIoana Ciornei 					 phy_interface_t interface,
73891a208f2SRussell King 					 int speed, int duplex,
73991a208f2SRussell King 					 bool tx_pause, bool rx_pause)
74077373d49SIoana Ciornei {
74177373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
74277373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
74377373d49SIoana Ciornei 
74477373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_up) {
7450e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
7460e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
74777373d49SIoana Ciornei 		return;
74877373d49SIoana Ciornei 	}
74977373d49SIoana Ciornei 
7505b502a7bSRussell King 	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
7515b502a7bSRussell King 				     speed, duplex, tx_pause, rx_pause);
75277373d49SIoana Ciornei }
75377373d49SIoana Ciornei 
75477373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
75577373d49SIoana Ciornei 	.validate = dsa_port_phylink_validate,
756d46b7e4fSRussell King 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
75777373d49SIoana Ciornei 	.mac_config = dsa_port_phylink_mac_config,
75877373d49SIoana Ciornei 	.mac_an_restart = dsa_port_phylink_mac_an_restart,
75977373d49SIoana Ciornei 	.mac_link_down = dsa_port_phylink_mac_link_down,
76077373d49SIoana Ciornei 	.mac_link_up = dsa_port_phylink_mac_link_up,
76177373d49SIoana Ciornei };
76277373d49SIoana Ciornei 
76333615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
76433615367SSebastian Reichel {
76533615367SSebastian Reichel 	struct dsa_switch *ds = dp->ds;
76633615367SSebastian Reichel 	struct phy_device *phydev;
76733615367SSebastian Reichel 	int port = dp->index;
76833615367SSebastian Reichel 	int err = 0;
76933615367SSebastian Reichel 
7706207a78cSFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
7716207a78cSFlorian Fainelli 	if (!phydev)
77233615367SSebastian Reichel 		return 0;
77333615367SSebastian Reichel 
7746207a78cSFlorian Fainelli 	if (IS_ERR(phydev))
7756207a78cSFlorian Fainelli 		return PTR_ERR(phydev);
77633615367SSebastian Reichel 
77733615367SSebastian Reichel 	if (enable) {
77833615367SSebastian Reichel 		err = genphy_resume(phydev);
77933615367SSebastian Reichel 		if (err < 0)
78033615367SSebastian Reichel 			goto err_put_dev;
78133615367SSebastian Reichel 
78233615367SSebastian Reichel 		err = genphy_read_status(phydev);
78333615367SSebastian Reichel 		if (err < 0)
78433615367SSebastian Reichel 			goto err_put_dev;
78533615367SSebastian Reichel 	} else {
78633615367SSebastian Reichel 		err = genphy_suspend(phydev);
78733615367SSebastian Reichel 		if (err < 0)
78833615367SSebastian Reichel 			goto err_put_dev;
78933615367SSebastian Reichel 	}
79033615367SSebastian Reichel 
79133615367SSebastian Reichel 	if (ds->ops->adjust_link)
79233615367SSebastian Reichel 		ds->ops->adjust_link(ds, port, phydev);
79333615367SSebastian Reichel 
79433615367SSebastian Reichel 	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
79533615367SSebastian Reichel 
79633615367SSebastian Reichel err_put_dev:
79733615367SSebastian Reichel 	put_device(&phydev->mdio.dev);
79833615367SSebastian Reichel 	return err;
79933615367SSebastian Reichel }
80033615367SSebastian Reichel 
80133615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
80257ab1ca2SVivien Didelot {
80357ab1ca2SVivien Didelot 	struct device_node *dn = dp->dn;
80457ab1ca2SVivien Didelot 	struct dsa_switch *ds = dp->ds;
80557ab1ca2SVivien Didelot 	struct phy_device *phydev;
80657ab1ca2SVivien Didelot 	int port = dp->index;
8070c65b2b9SAndrew Lunn 	phy_interface_t mode;
80857ab1ca2SVivien Didelot 	int err;
80957ab1ca2SVivien Didelot 
81057ab1ca2SVivien Didelot 	err = of_phy_register_fixed_link(dn);
81157ab1ca2SVivien Didelot 	if (err) {
81257ab1ca2SVivien Didelot 		dev_err(ds->dev,
81357ab1ca2SVivien Didelot 			"failed to register the fixed PHY of port %d\n",
81457ab1ca2SVivien Didelot 			port);
81557ab1ca2SVivien Didelot 		return err;
81657ab1ca2SVivien Didelot 	}
81757ab1ca2SVivien Didelot 
81857ab1ca2SVivien Didelot 	phydev = of_phy_find_device(dn);
81957ab1ca2SVivien Didelot 
8200c65b2b9SAndrew Lunn 	err = of_get_phy_mode(dn, &mode);
8210c65b2b9SAndrew Lunn 	if (err)
82257ab1ca2SVivien Didelot 		mode = PHY_INTERFACE_MODE_NA;
82357ab1ca2SVivien Didelot 	phydev->interface = mode;
82457ab1ca2SVivien Didelot 
82557ab1ca2SVivien Didelot 	genphy_read_status(phydev);
82657ab1ca2SVivien Didelot 
82757ab1ca2SVivien Didelot 	if (ds->ops->adjust_link)
82857ab1ca2SVivien Didelot 		ds->ops->adjust_link(ds, port, phydev);
82957ab1ca2SVivien Didelot 
83057ab1ca2SVivien Didelot 	put_device(&phydev->mdio.dev);
83157ab1ca2SVivien Didelot 
83257ab1ca2SVivien Didelot 	return 0;
83357ab1ca2SVivien Didelot }
83457ab1ca2SVivien Didelot 
8350e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp)
8360e279218SIoana Ciornei {
8370e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
8380e279218SIoana Ciornei 	struct device_node *port_dn = dp->dn;
8390c65b2b9SAndrew Lunn 	phy_interface_t mode;
8400c65b2b9SAndrew Lunn 	int err;
8410e279218SIoana Ciornei 
8420c65b2b9SAndrew Lunn 	err = of_get_phy_mode(port_dn, &mode);
8430c65b2b9SAndrew Lunn 	if (err)
8440e279218SIoana Ciornei 		mode = PHY_INTERFACE_MODE_NA;
8450e279218SIoana Ciornei 
8460e279218SIoana Ciornei 	dp->pl_config.dev = ds->dev;
8470e279218SIoana Ciornei 	dp->pl_config.type = PHYLINK_DEV;
848787cac3fSVladimir Oltean 	dp->pl_config.pcs_poll = ds->pcs_poll;
8490e279218SIoana Ciornei 
8500e279218SIoana Ciornei 	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
8510e279218SIoana Ciornei 				mode, &dsa_port_phylink_mac_ops);
8520e279218SIoana Ciornei 	if (IS_ERR(dp->pl)) {
8530e279218SIoana Ciornei 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
8540e279218SIoana Ciornei 		return PTR_ERR(dp->pl);
8550e279218SIoana Ciornei 	}
8560e279218SIoana Ciornei 
8570e279218SIoana Ciornei 	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
8582131fba5SFlorian Fainelli 	if (err && err != -ENODEV) {
8590e279218SIoana Ciornei 		pr_err("could not attach to PHY: %d\n", err);
8600e279218SIoana Ciornei 		goto err_phy_connect;
8610e279218SIoana Ciornei 	}
8620e279218SIoana Ciornei 
8630e279218SIoana Ciornei 	return 0;
8640e279218SIoana Ciornei 
8650e279218SIoana Ciornei err_phy_connect:
8660e279218SIoana Ciornei 	phylink_destroy(dp->pl);
8670e279218SIoana Ciornei 	return err;
8680e279218SIoana Ciornei }
8690e279218SIoana Ciornei 
87033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp)
87157ab1ca2SVivien Didelot {
8720e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
873a20f9970SAndrew Lunn 	struct device_node *phy_np;
8743be98b2dSAndrew Lunn 	int port = dp->index;
8750e279218SIoana Ciornei 
876a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link) {
877a20f9970SAndrew Lunn 		phy_np = of_parse_phandle(dp->dn, "phy-handle", 0);
8783be98b2dSAndrew Lunn 		if (of_phy_is_fixed_link(dp->dn) || phy_np) {
8793be98b2dSAndrew Lunn 			if (ds->ops->phylink_mac_link_down)
8803be98b2dSAndrew Lunn 				ds->ops->phylink_mac_link_down(ds, port,
8813be98b2dSAndrew Lunn 					MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
8820e279218SIoana Ciornei 			return dsa_port_phylink_register(dp);
8833be98b2dSAndrew Lunn 		}
884a20f9970SAndrew Lunn 		return 0;
885a20f9970SAndrew Lunn 	}
8860e279218SIoana Ciornei 
8870e279218SIoana Ciornei 	dev_warn(ds->dev,
8880e279218SIoana Ciornei 		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
8890e279218SIoana Ciornei 
89033615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
89133615367SSebastian Reichel 		return dsa_port_fixed_link_register_of(dp);
89233615367SSebastian Reichel 	else
89333615367SSebastian Reichel 		return dsa_port_setup_phy_of(dp, true);
89433615367SSebastian Reichel }
89557ab1ca2SVivien Didelot 
89633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp)
89733615367SSebastian Reichel {
8980e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
8990e279218SIoana Ciornei 
900a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link && dp->pl) {
9010e279218SIoana Ciornei 		rtnl_lock();
9020e279218SIoana Ciornei 		phylink_disconnect_phy(dp->pl);
9030e279218SIoana Ciornei 		rtnl_unlock();
9040e279218SIoana Ciornei 		phylink_destroy(dp->pl);
905a20f9970SAndrew Lunn 		dp->pl = NULL;
9060e279218SIoana Ciornei 		return;
9070e279218SIoana Ciornei 	}
9080e279218SIoana Ciornei 
90933615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
91033615367SSebastian Reichel 		of_phy_deregister_fixed_link(dp->dn);
91133615367SSebastian Reichel 	else
91233615367SSebastian Reichel 		dsa_port_setup_phy_of(dp, false);
91357ab1ca2SVivien Didelot }
914cf963573SFlorian Fainelli 
915cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
916cf963573SFlorian Fainelli {
917cf963573SFlorian Fainelli 	struct phy_device *phydev;
918cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
919cf963573SFlorian Fainelli 
920cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
921cf963573SFlorian Fainelli 		return ret;
922cf963573SFlorian Fainelli 
923cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
924cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
925cf963573SFlorian Fainelli 		return ret;
926cf963573SFlorian Fainelli 
927cf963573SFlorian Fainelli 	ret = phy_ethtool_get_strings(phydev, data);
928cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
929cf963573SFlorian Fainelli 
930cf963573SFlorian Fainelli 	return ret;
931cf963573SFlorian Fainelli }
932cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
933cf963573SFlorian Fainelli 
934cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
935cf963573SFlorian Fainelli {
936cf963573SFlorian Fainelli 	struct phy_device *phydev;
937cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
938cf963573SFlorian Fainelli 
939cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
940cf963573SFlorian Fainelli 		return ret;
941cf963573SFlorian Fainelli 
942cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
943cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
944cf963573SFlorian Fainelli 		return ret;
945cf963573SFlorian Fainelli 
946cf963573SFlorian Fainelli 	ret = phy_ethtool_get_stats(phydev, NULL, data);
947cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
948cf963573SFlorian Fainelli 
949cf963573SFlorian Fainelli 	return ret;
950cf963573SFlorian Fainelli }
951cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
952cf963573SFlorian Fainelli 
953cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp)
954cf963573SFlorian Fainelli {
955cf963573SFlorian Fainelli 	struct phy_device *phydev;
956cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
957cf963573SFlorian Fainelli 
958cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
959cf963573SFlorian Fainelli 		return ret;
960cf963573SFlorian Fainelli 
961cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
962cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
963cf963573SFlorian Fainelli 		return ret;
964cf963573SFlorian Fainelli 
965cf963573SFlorian Fainelli 	ret = phy_ethtool_get_sset_count(phydev);
966cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
967cf963573SFlorian Fainelli 
968cf963573SFlorian Fainelli 	return ret;
969cf963573SFlorian Fainelli }
970cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
97118596f50SGeorge McCollister 
97218596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr)
97318596f50SGeorge McCollister {
97418596f50SGeorge McCollister 	struct dsa_notifier_hsr_info info = {
97518596f50SGeorge McCollister 		.sw_index = dp->ds->index,
97618596f50SGeorge McCollister 		.port = dp->index,
97718596f50SGeorge McCollister 		.hsr = hsr,
97818596f50SGeorge McCollister 	};
97918596f50SGeorge McCollister 	int err;
98018596f50SGeorge McCollister 
98118596f50SGeorge McCollister 	dp->hsr_dev = hsr;
98218596f50SGeorge McCollister 
98318596f50SGeorge McCollister 	err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info);
98418596f50SGeorge McCollister 	if (err)
98518596f50SGeorge McCollister 		dp->hsr_dev = NULL;
98618596f50SGeorge McCollister 
98718596f50SGeorge McCollister 	return err;
98818596f50SGeorge McCollister }
98918596f50SGeorge McCollister 
99018596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr)
99118596f50SGeorge McCollister {
99218596f50SGeorge McCollister 	struct dsa_notifier_hsr_info info = {
99318596f50SGeorge McCollister 		.sw_index = dp->ds->index,
99418596f50SGeorge McCollister 		.port = dp->index,
99518596f50SGeorge McCollister 		.hsr = hsr,
99618596f50SGeorge McCollister 	};
99718596f50SGeorge McCollister 	int err;
99818596f50SGeorge McCollister 
99918596f50SGeorge McCollister 	dp->hsr_dev = NULL;
100018596f50SGeorge McCollister 
100118596f50SGeorge McCollister 	err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info);
100218596f50SGeorge McCollister 	if (err)
100318596f50SGeorge McCollister 		pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n");
100418596f50SGeorge McCollister }
1005