xref: /openbmc/linux/net/dsa/port.c (revision 18596f50)
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 
125cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
126cfbed329SVivien Didelot {
127cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
128f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
129cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
130cfbed329SVivien Didelot 		.port = dp->index,
131cfbed329SVivien Didelot 		.br = br,
132cfbed329SVivien Didelot 	};
133cfbed329SVivien Didelot 	int err;
134cfbed329SVivien Didelot 
135c1388063SRussell King 	/* Set the flooding mode before joining the port in the switch */
136bae33f2bSVladimir Oltean 	err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD);
137c1388063SRussell King 	if (err)
138c1388063SRussell King 		return err;
139c1388063SRussell King 
140c1388063SRussell King 	/* Here the interface is already bridged. Reflect the current
141c1388063SRussell King 	 * configuration so that drivers can program their chips accordingly.
142cfbed329SVivien Didelot 	 */
143cfbed329SVivien Didelot 	dp->bridge_dev = br;
144cfbed329SVivien Didelot 
145f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
146cfbed329SVivien Didelot 
147cfbed329SVivien Didelot 	/* The bridging is rolled back on error */
148c1388063SRussell King 	if (err) {
149bae33f2bSVladimir Oltean 		dsa_port_bridge_flags(dp, 0);
150cfbed329SVivien Didelot 		dp->bridge_dev = NULL;
151c1388063SRussell King 	}
152cfbed329SVivien Didelot 
153cfbed329SVivien Didelot 	return err;
154cfbed329SVivien Didelot }
155cfbed329SVivien Didelot 
156cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
157cfbed329SVivien Didelot {
158cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
159f66a6a69SVladimir Oltean 		.tree_index = dp->ds->dst->index,
160cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
161cfbed329SVivien Didelot 		.port = dp->index,
162cfbed329SVivien Didelot 		.br = br,
163cfbed329SVivien Didelot 	};
164cfbed329SVivien Didelot 	int err;
165cfbed329SVivien Didelot 
166cfbed329SVivien Didelot 	/* Here the port is already unbridged. Reflect the current configuration
167cfbed329SVivien Didelot 	 * so that drivers can program their chips accordingly.
168cfbed329SVivien Didelot 	 */
169cfbed329SVivien Didelot 	dp->bridge_dev = NULL;
170cfbed329SVivien Didelot 
171f66a6a69SVladimir Oltean 	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
172cfbed329SVivien Didelot 	if (err)
173cfbed329SVivien Didelot 		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
174cfbed329SVivien Didelot 
175c1388063SRussell King 	/* Port is leaving the bridge, disable flooding */
176bae33f2bSVladimir Oltean 	dsa_port_bridge_flags(dp, 0);
177c1388063SRussell King 
178cfbed329SVivien Didelot 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
179cfbed329SVivien Didelot 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
180cfbed329SVivien Didelot 	 */
181cfbed329SVivien Didelot 	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
182cfbed329SVivien Didelot }
1834d61d304SVivien Didelot 
184058102a6STobias Waldekranz int dsa_port_lag_change(struct dsa_port *dp,
185058102a6STobias Waldekranz 			struct netdev_lag_lower_state_info *linfo)
186058102a6STobias Waldekranz {
187058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
188058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
189058102a6STobias Waldekranz 		.port = dp->index,
190058102a6STobias Waldekranz 	};
191058102a6STobias Waldekranz 	bool tx_enabled;
192058102a6STobias Waldekranz 
193058102a6STobias Waldekranz 	if (!dp->lag_dev)
194058102a6STobias Waldekranz 		return 0;
195058102a6STobias Waldekranz 
196058102a6STobias Waldekranz 	/* On statically configured aggregates (e.g. loadbalance
197058102a6STobias Waldekranz 	 * without LACP) ports will always be tx_enabled, even if the
198058102a6STobias Waldekranz 	 * link is down. Thus we require both link_up and tx_enabled
199058102a6STobias Waldekranz 	 * in order to include it in the tx set.
200058102a6STobias Waldekranz 	 */
201058102a6STobias Waldekranz 	tx_enabled = linfo->link_up && linfo->tx_enabled;
202058102a6STobias Waldekranz 
203058102a6STobias Waldekranz 	if (tx_enabled == dp->lag_tx_enabled)
204058102a6STobias Waldekranz 		return 0;
205058102a6STobias Waldekranz 
206058102a6STobias Waldekranz 	dp->lag_tx_enabled = tx_enabled;
207058102a6STobias Waldekranz 
208058102a6STobias Waldekranz 	return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
209058102a6STobias Waldekranz }
210058102a6STobias Waldekranz 
211058102a6STobias Waldekranz int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag,
212058102a6STobias Waldekranz 		      struct netdev_lag_upper_info *uinfo)
213058102a6STobias Waldekranz {
214058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
215058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
216058102a6STobias Waldekranz 		.port = dp->index,
217058102a6STobias Waldekranz 		.lag = lag,
218058102a6STobias Waldekranz 		.info = uinfo,
219058102a6STobias Waldekranz 	};
220058102a6STobias Waldekranz 	int err;
221058102a6STobias Waldekranz 
222058102a6STobias Waldekranz 	dsa_lag_map(dp->ds->dst, lag);
223058102a6STobias Waldekranz 	dp->lag_dev = lag;
224058102a6STobias Waldekranz 
225058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
226058102a6STobias Waldekranz 	if (err) {
227058102a6STobias Waldekranz 		dp->lag_dev = NULL;
228058102a6STobias Waldekranz 		dsa_lag_unmap(dp->ds->dst, lag);
229058102a6STobias Waldekranz 	}
230058102a6STobias Waldekranz 
231058102a6STobias Waldekranz 	return err;
232058102a6STobias Waldekranz }
233058102a6STobias Waldekranz 
234058102a6STobias Waldekranz void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
235058102a6STobias Waldekranz {
236058102a6STobias Waldekranz 	struct dsa_notifier_lag_info info = {
237058102a6STobias Waldekranz 		.sw_index = dp->ds->index,
238058102a6STobias Waldekranz 		.port = dp->index,
239058102a6STobias Waldekranz 		.lag = lag,
240058102a6STobias Waldekranz 	};
241058102a6STobias Waldekranz 	int err;
242058102a6STobias Waldekranz 
243058102a6STobias Waldekranz 	if (!dp->lag_dev)
244058102a6STobias Waldekranz 		return;
245058102a6STobias Waldekranz 
246058102a6STobias Waldekranz 	/* Port might have been part of a LAG that in turn was
247058102a6STobias Waldekranz 	 * attached to a bridge.
248058102a6STobias Waldekranz 	 */
249058102a6STobias Waldekranz 	if (dp->bridge_dev)
250058102a6STobias Waldekranz 		dsa_port_bridge_leave(dp, dp->bridge_dev);
251058102a6STobias Waldekranz 
252058102a6STobias Waldekranz 	dp->lag_tx_enabled = false;
253058102a6STobias Waldekranz 	dp->lag_dev = NULL;
254058102a6STobias Waldekranz 
255058102a6STobias Waldekranz 	err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
256058102a6STobias Waldekranz 	if (err)
257058102a6STobias Waldekranz 		pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n",
258058102a6STobias Waldekranz 		       err);
259058102a6STobias Waldekranz 
260058102a6STobias Waldekranz 	dsa_lag_unmap(dp->ds->dst, lag);
261058102a6STobias Waldekranz }
262058102a6STobias Waldekranz 
263adb256ebSVladimir Oltean /* Must be called under rcu_read_lock() */
2648f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
2658f5d16f6SVladimir Oltean 					      bool vlan_filtering)
2668f5d16f6SVladimir Oltean {
2678f5d16f6SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
268adb256ebSVladimir Oltean 	int err, i;
269adb256ebSVladimir Oltean 
270adb256ebSVladimir Oltean 	/* VLAN awareness was off, so the question is "can we turn it on".
271adb256ebSVladimir Oltean 	 * We may have had 8021q uppers, those need to go. Make sure we don't
272adb256ebSVladimir Oltean 	 * enter an inconsistent state: deny changing the VLAN awareness state
273adb256ebSVladimir Oltean 	 * as long as we have 8021q uppers.
274adb256ebSVladimir Oltean 	 */
275adb256ebSVladimir Oltean 	if (vlan_filtering && dsa_is_user_port(ds, dp->index)) {
276adb256ebSVladimir Oltean 		struct net_device *upper_dev, *slave = dp->slave;
277adb256ebSVladimir Oltean 		struct net_device *br = dp->bridge_dev;
278adb256ebSVladimir Oltean 		struct list_head *iter;
279adb256ebSVladimir Oltean 
280adb256ebSVladimir Oltean 		netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
281adb256ebSVladimir Oltean 			struct bridge_vlan_info br_info;
282adb256ebSVladimir Oltean 			u16 vid;
283adb256ebSVladimir Oltean 
284adb256ebSVladimir Oltean 			if (!is_vlan_dev(upper_dev))
285adb256ebSVladimir Oltean 				continue;
286adb256ebSVladimir Oltean 
287adb256ebSVladimir Oltean 			vid = vlan_dev_vlan_id(upper_dev);
288adb256ebSVladimir Oltean 
289adb256ebSVladimir Oltean 			/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
290adb256ebSVladimir Oltean 			 * device, respectively the VID is not found, returning
291adb256ebSVladimir Oltean 			 * 0 means success, which is a failure for us here.
292adb256ebSVladimir Oltean 			 */
293adb256ebSVladimir Oltean 			err = br_vlan_get_info(br, vid, &br_info);
294adb256ebSVladimir Oltean 			if (err == 0) {
295adb256ebSVladimir Oltean 				dev_err(ds->dev, "Must remove upper %s first\n",
296adb256ebSVladimir Oltean 					upper_dev->name);
297adb256ebSVladimir Oltean 				return false;
298adb256ebSVladimir Oltean 			}
299adb256ebSVladimir Oltean 		}
300adb256ebSVladimir Oltean 	}
3018f5d16f6SVladimir Oltean 
3028f5d16f6SVladimir Oltean 	if (!ds->vlan_filtering_is_global)
3038f5d16f6SVladimir Oltean 		return true;
3048f5d16f6SVladimir Oltean 
3058f5d16f6SVladimir Oltean 	/* For cases where enabling/disabling VLAN awareness is global to the
3068f5d16f6SVladimir Oltean 	 * switch, we need to handle the case where multiple bridges span
3078f5d16f6SVladimir Oltean 	 * different ports of the same switch device and one of them has a
3088f5d16f6SVladimir Oltean 	 * different setting than what is being requested.
3098f5d16f6SVladimir Oltean 	 */
3108f5d16f6SVladimir Oltean 	for (i = 0; i < ds->num_ports; i++) {
3118f5d16f6SVladimir Oltean 		struct net_device *other_bridge;
3128f5d16f6SVladimir Oltean 
3138f5d16f6SVladimir Oltean 		other_bridge = dsa_to_port(ds, i)->bridge_dev;
3148f5d16f6SVladimir Oltean 		if (!other_bridge)
3158f5d16f6SVladimir Oltean 			continue;
3168f5d16f6SVladimir Oltean 		/* If it's the same bridge, it also has same
3178f5d16f6SVladimir Oltean 		 * vlan_filtering setting => no need to check
3188f5d16f6SVladimir Oltean 		 */
3198f5d16f6SVladimir Oltean 		if (other_bridge == dp->bridge_dev)
3208f5d16f6SVladimir Oltean 			continue;
3218f5d16f6SVladimir Oltean 		if (br_vlan_enabled(other_bridge) != vlan_filtering) {
3228f5d16f6SVladimir Oltean 			dev_err(ds->dev, "VLAN filtering is a global setting\n");
3238f5d16f6SVladimir Oltean 			return false;
3248f5d16f6SVladimir Oltean 		}
3258f5d16f6SVladimir Oltean 	}
3268f5d16f6SVladimir Oltean 	return true;
3278f5d16f6SVladimir Oltean }
3288f5d16f6SVladimir Oltean 
329bae33f2bSVladimir Oltean int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering)
3304d61d304SVivien Didelot {
3314d61d304SVivien Didelot 	struct dsa_switch *ds = dp->ds;
332adb256ebSVladimir Oltean 	bool apply;
333bae33f2bSVladimir Oltean 	int err;
334adb256ebSVladimir Oltean 
3358f5d16f6SVladimir Oltean 	if (!ds->ops->port_vlan_filtering)
336707ec383SVladimir Oltean 		return -EOPNOTSUPP;
3378f5d16f6SVladimir Oltean 
338adb256ebSVladimir Oltean 	/* We are called from dsa_slave_switchdev_blocking_event(),
339adb256ebSVladimir Oltean 	 * which is not under rcu_read_lock(), unlike
340adb256ebSVladimir Oltean 	 * dsa_slave_switchdev_event().
341adb256ebSVladimir Oltean 	 */
342adb256ebSVladimir Oltean 	rcu_read_lock();
343adb256ebSVladimir Oltean 	apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering);
344adb256ebSVladimir Oltean 	rcu_read_unlock();
345adb256ebSVladimir Oltean 	if (!apply)
3468f5d16f6SVladimir Oltean 		return -EINVAL;
347707ec383SVladimir Oltean 
348ec9121e7SVladimir Oltean 	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
349ec9121e7SVladimir Oltean 		return 0;
350ec9121e7SVladimir Oltean 
351bae33f2bSVladimir Oltean 	err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering);
35233162e9aSVladimir Oltean 	if (err)
35333162e9aSVladimir Oltean 		return err;
3548f5d16f6SVladimir Oltean 
35514574676SVladimir Oltean 	if (ds->vlan_filtering_is_global)
35614574676SVladimir Oltean 		ds->vlan_filtering = vlan_filtering;
35714574676SVladimir Oltean 	else
35833162e9aSVladimir Oltean 		dp->vlan_filtering = vlan_filtering;
3592e554a7aSVladimir Oltean 
3604d61d304SVivien Didelot 	return 0;
3614d61d304SVivien Didelot }
362d87bd94eSVivien Didelot 
36354a0ed0dSRussell King /* This enforces legacy behavior for switch drivers which assume they can't
36454a0ed0dSRussell King  * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0
36554a0ed0dSRussell King  */
36654a0ed0dSRussell King bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
36754a0ed0dSRussell King {
36854a0ed0dSRussell King 	struct dsa_switch *ds = dp->ds;
36954a0ed0dSRussell King 
37054a0ed0dSRussell King 	if (!dp->bridge_dev)
37154a0ed0dSRussell King 		return false;
37254a0ed0dSRussell King 
37354a0ed0dSRussell King 	return (!ds->configure_vlan_while_not_filtering &&
37454a0ed0dSRussell King 		!br_vlan_enabled(dp->bridge_dev));
37554a0ed0dSRussell King }
37654a0ed0dSRussell King 
377bae33f2bSVladimir Oltean int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
378d87bd94eSVivien Didelot {
379d87bd94eSVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
380d87bd94eSVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
381bae33f2bSVladimir Oltean 	struct dsa_notifier_ageing_time_info info;
382bae33f2bSVladimir Oltean 	int err;
383d87bd94eSVivien Didelot 
384bae33f2bSVladimir Oltean 	info.ageing_time = ageing_time;
385bae33f2bSVladimir Oltean 
386bae33f2bSVladimir Oltean 	err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
387bae33f2bSVladimir Oltean 	if (err)
388bae33f2bSVladimir Oltean 		return err;
389d87bd94eSVivien Didelot 
390d87bd94eSVivien Didelot 	dp->ageing_time = ageing_time;
391d87bd94eSVivien Didelot 
39277b61365SVladimir Oltean 	return 0;
393d87bd94eSVivien Didelot }
394d1cffff0SVivien Didelot 
395bae33f2bSVladimir Oltean int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags)
396ea87005aSFlorian Fainelli {
397ea87005aSFlorian Fainelli 	struct dsa_switch *ds = dp->ds;
398ea87005aSFlorian Fainelli 
399ea87005aSFlorian Fainelli 	if (!ds->ops->port_egress_floods ||
400ea87005aSFlorian Fainelli 	    (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
401ea87005aSFlorian Fainelli 		return -EINVAL;
402ea87005aSFlorian Fainelli 
403ea87005aSFlorian Fainelli 	return 0;
404ea87005aSFlorian Fainelli }
405ea87005aSFlorian Fainelli 
406bae33f2bSVladimir Oltean int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags)
40757652796SRussell King {
40857652796SRussell King 	struct dsa_switch *ds = dp->ds;
40957652796SRussell King 	int port = dp->index;
41057652796SRussell King 	int err = 0;
41157652796SRussell King 
41257652796SRussell King 	if (ds->ops->port_egress_floods)
41357652796SRussell King 		err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
41457652796SRussell King 						  flags & BR_MCAST_FLOOD);
41557652796SRussell King 
41657652796SRussell King 	return err;
41757652796SRussell King }
41857652796SRussell King 
419bae33f2bSVladimir Oltean int dsa_port_mrouter(struct dsa_port *dp, bool mrouter)
42008cc83ccSVivien Didelot {
42108cc83ccSVivien Didelot 	struct dsa_switch *ds = dp->ds;
42208cc83ccSVivien Didelot 	int port = dp->index;
42308cc83ccSVivien Didelot 
424bae33f2bSVladimir Oltean 	if (!ds->ops->port_egress_floods)
425bae33f2bSVladimir Oltean 		return -EOPNOTSUPP;
42608cc83ccSVivien Didelot 
42708cc83ccSVivien Didelot 	return ds->ops->port_egress_floods(ds, port, true, mrouter);
42808cc83ccSVivien Didelot }
42908cc83ccSVivien Didelot 
430bfcb8132SVladimir Oltean int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
431bfcb8132SVladimir Oltean 			bool propagate_upstream)
432bfcb8132SVladimir Oltean {
433bfcb8132SVladimir Oltean 	struct dsa_notifier_mtu_info info = {
434bfcb8132SVladimir Oltean 		.sw_index = dp->ds->index,
435bfcb8132SVladimir Oltean 		.propagate_upstream = propagate_upstream,
436bfcb8132SVladimir Oltean 		.port = dp->index,
437bfcb8132SVladimir Oltean 		.mtu = new_mtu,
438bfcb8132SVladimir Oltean 	};
439bfcb8132SVladimir Oltean 
440bfcb8132SVladimir Oltean 	return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info);
441bfcb8132SVladimir Oltean }
442bfcb8132SVladimir Oltean 
4432acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
4442acf4e6aSArkadi Sharshevsky 		     u16 vid)
445d1cffff0SVivien Didelot {
446685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
447685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
448685fb6a4SVivien Didelot 		.port = dp->index,
4492acf4e6aSArkadi Sharshevsky 		.addr = addr,
4502acf4e6aSArkadi Sharshevsky 		.vid = vid,
451685fb6a4SVivien Didelot 	};
452d1cffff0SVivien Didelot 
453685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
454d1cffff0SVivien Didelot }
455d1cffff0SVivien Didelot 
4562acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
4572acf4e6aSArkadi Sharshevsky 		     u16 vid)
458d1cffff0SVivien Didelot {
459685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
460685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
461685fb6a4SVivien Didelot 		.port = dp->index,
4622acf4e6aSArkadi Sharshevsky 		.addr = addr,
4632acf4e6aSArkadi Sharshevsky 		.vid = vid,
4642acf4e6aSArkadi Sharshevsky 
465685fb6a4SVivien Didelot 	};
466d1cffff0SVivien Didelot 
467685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
468d1cffff0SVivien Didelot }
469d1cffff0SVivien Didelot 
470de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
471de40fc5dSVivien Didelot {
472de40fc5dSVivien Didelot 	struct dsa_switch *ds = dp->ds;
473de40fc5dSVivien Didelot 	int port = dp->index;
474de40fc5dSVivien Didelot 
475de40fc5dSVivien Didelot 	if (!ds->ops->port_fdb_dump)
476de40fc5dSVivien Didelot 		return -EOPNOTSUPP;
477de40fc5dSVivien Didelot 
478de40fc5dSVivien Didelot 	return ds->ops->port_fdb_dump(ds, port, cb, data);
479de40fc5dSVivien Didelot }
480de40fc5dSVivien Didelot 
481bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp,
482ffb68fc5SVladimir Oltean 		     const struct switchdev_obj_port_mdb *mdb)
4833a9afea3SVivien Didelot {
4848ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
4858ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
4868ae5bcdcSVivien Didelot 		.port = dp->index,
4878ae5bcdcSVivien Didelot 		.mdb = mdb,
4888ae5bcdcSVivien Didelot 	};
4893a9afea3SVivien Didelot 
4908ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
4913a9afea3SVivien Didelot }
4923a9afea3SVivien Didelot 
493bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp,
4943a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb)
4953a9afea3SVivien Didelot {
4968ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
4978ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
4988ae5bcdcSVivien Didelot 		.port = dp->index,
4998ae5bcdcSVivien Didelot 		.mdb = mdb,
5008ae5bcdcSVivien Didelot 	};
5013a9afea3SVivien Didelot 
5028ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
5033a9afea3SVivien Didelot }
5043a9afea3SVivien Didelot 
505076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp,
506ffb68fc5SVladimir Oltean 		      const struct switchdev_obj_port_vlan *vlan)
507076e7133SVivien Didelot {
508d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
509d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
510d0c627b8SVivien Didelot 		.port = dp->index,
511d0c627b8SVivien Didelot 		.vlan = vlan,
512d0c627b8SVivien Didelot 	};
513076e7133SVivien Didelot 
514d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
515076e7133SVivien Didelot }
516076e7133SVivien Didelot 
517076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp,
518076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan)
519076e7133SVivien Didelot {
520d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
521d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
522d0c627b8SVivien Didelot 		.port = dp->index,
523d0c627b8SVivien Didelot 		.vlan = vlan,
524d0c627b8SVivien Didelot 	};
525076e7133SVivien Didelot 
526d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
527076e7133SVivien Didelot }
52857ab1ca2SVivien Didelot 
52953da0ebaSVladimir Oltean void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
53053da0ebaSVladimir Oltean 			       const struct dsa_device_ops *tag_ops)
53153da0ebaSVladimir Oltean {
53253da0ebaSVladimir Oltean 	cpu_dp->filter = tag_ops->filter;
53353da0ebaSVladimir Oltean 	cpu_dp->rcv = tag_ops->rcv;
53453da0ebaSVladimir Oltean 	cpu_dp->tag_ops = tag_ops;
53553da0ebaSVladimir Oltean }
53653da0ebaSVladimir Oltean 
5376207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
5386207a78cSFlorian Fainelli {
5396207a78cSFlorian Fainelli 	struct device_node *phy_dn;
5406207a78cSFlorian Fainelli 	struct phy_device *phydev;
5416207a78cSFlorian Fainelli 
5426207a78cSFlorian Fainelli 	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
5436207a78cSFlorian Fainelli 	if (!phy_dn)
5446207a78cSFlorian Fainelli 		return NULL;
5456207a78cSFlorian Fainelli 
5466207a78cSFlorian Fainelli 	phydev = of_phy_find_device(phy_dn);
5476207a78cSFlorian Fainelli 	if (!phydev) {
5486207a78cSFlorian Fainelli 		of_node_put(phy_dn);
5496207a78cSFlorian Fainelli 		return ERR_PTR(-EPROBE_DEFER);
5506207a78cSFlorian Fainelli 	}
5516207a78cSFlorian Fainelli 
5529919a363SWen Yang 	of_node_put(phy_dn);
5536207a78cSFlorian Fainelli 	return phydev;
5546207a78cSFlorian Fainelli }
5556207a78cSFlorian Fainelli 
5568ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config,
55777373d49SIoana Ciornei 				      unsigned long *supported,
55877373d49SIoana Ciornei 				      struct phylink_link_state *state)
55977373d49SIoana Ciornei {
56077373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
56177373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
56277373d49SIoana Ciornei 
56377373d49SIoana Ciornei 	if (!ds->ops->phylink_validate)
56477373d49SIoana Ciornei 		return;
56577373d49SIoana Ciornei 
56677373d49SIoana Ciornei 	ds->ops->phylink_validate(ds, dp->index, supported, state);
56777373d49SIoana Ciornei }
56877373d49SIoana Ciornei 
5698ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
57077373d49SIoana Ciornei 					       struct phylink_link_state *state)
57177373d49SIoana Ciornei {
57277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
57377373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
57487615c96SRussell King 	int err;
57577373d49SIoana Ciornei 
576d46b7e4fSRussell King 	/* Only called for inband modes */
577d46b7e4fSRussell King 	if (!ds->ops->phylink_mac_link_state) {
578d46b7e4fSRussell King 		state->link = 0;
579d46b7e4fSRussell King 		return;
58077373d49SIoana Ciornei 	}
581d46b7e4fSRussell King 
58287615c96SRussell King 	err = ds->ops->phylink_mac_link_state(ds, dp->index, state);
58387615c96SRussell King 	if (err < 0) {
58487615c96SRussell King 		dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d\n",
58587615c96SRussell King 			dp->index, err);
586d46b7e4fSRussell King 		state->link = 0;
587d46b7e4fSRussell King 	}
58887615c96SRussell King }
58977373d49SIoana Ciornei 
5908ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config,
59177373d49SIoana Ciornei 					unsigned int mode,
59277373d49SIoana Ciornei 					const struct phylink_link_state *state)
59377373d49SIoana Ciornei {
59477373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
59577373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
59677373d49SIoana Ciornei 
59777373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_config)
59877373d49SIoana Ciornei 		return;
59977373d49SIoana Ciornei 
60077373d49SIoana Ciornei 	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
60177373d49SIoana Ciornei }
60277373d49SIoana Ciornei 
6038ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
60477373d49SIoana Ciornei {
60577373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
60677373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
60777373d49SIoana Ciornei 
60877373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_an_restart)
60977373d49SIoana Ciornei 		return;
61077373d49SIoana Ciornei 
61177373d49SIoana Ciornei 	ds->ops->phylink_mac_an_restart(ds, dp->index);
61277373d49SIoana Ciornei }
61377373d49SIoana Ciornei 
6148ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
61577373d49SIoana Ciornei 					   unsigned int mode,
61677373d49SIoana Ciornei 					   phy_interface_t interface)
61777373d49SIoana Ciornei {
61877373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
6190e279218SIoana Ciornei 	struct phy_device *phydev = NULL;
62077373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
62177373d49SIoana Ciornei 
6220e279218SIoana Ciornei 	if (dsa_is_user_port(ds, dp->index))
6230e279218SIoana Ciornei 		phydev = dp->slave->phydev;
6240e279218SIoana Ciornei 
62577373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_down) {
6260e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
6270e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
62877373d49SIoana Ciornei 		return;
62977373d49SIoana Ciornei 	}
63077373d49SIoana Ciornei 
63177373d49SIoana Ciornei 	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
63277373d49SIoana Ciornei }
63377373d49SIoana Ciornei 
6348ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
63591a208f2SRussell King 					 struct phy_device *phydev,
63677373d49SIoana Ciornei 					 unsigned int mode,
63777373d49SIoana Ciornei 					 phy_interface_t interface,
63891a208f2SRussell King 					 int speed, int duplex,
63991a208f2SRussell King 					 bool tx_pause, bool rx_pause)
64077373d49SIoana Ciornei {
64177373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
64277373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
64377373d49SIoana Ciornei 
64477373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_up) {
6450e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
6460e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
64777373d49SIoana Ciornei 		return;
64877373d49SIoana Ciornei 	}
64977373d49SIoana Ciornei 
6505b502a7bSRussell King 	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
6515b502a7bSRussell King 				     speed, duplex, tx_pause, rx_pause);
65277373d49SIoana Ciornei }
65377373d49SIoana Ciornei 
65477373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
65577373d49SIoana Ciornei 	.validate = dsa_port_phylink_validate,
656d46b7e4fSRussell King 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
65777373d49SIoana Ciornei 	.mac_config = dsa_port_phylink_mac_config,
65877373d49SIoana Ciornei 	.mac_an_restart = dsa_port_phylink_mac_an_restart,
65977373d49SIoana Ciornei 	.mac_link_down = dsa_port_phylink_mac_link_down,
66077373d49SIoana Ciornei 	.mac_link_up = dsa_port_phylink_mac_link_up,
66177373d49SIoana Ciornei };
66277373d49SIoana Ciornei 
66333615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
66433615367SSebastian Reichel {
66533615367SSebastian Reichel 	struct dsa_switch *ds = dp->ds;
66633615367SSebastian Reichel 	struct phy_device *phydev;
66733615367SSebastian Reichel 	int port = dp->index;
66833615367SSebastian Reichel 	int err = 0;
66933615367SSebastian Reichel 
6706207a78cSFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
6716207a78cSFlorian Fainelli 	if (!phydev)
67233615367SSebastian Reichel 		return 0;
67333615367SSebastian Reichel 
6746207a78cSFlorian Fainelli 	if (IS_ERR(phydev))
6756207a78cSFlorian Fainelli 		return PTR_ERR(phydev);
67633615367SSebastian Reichel 
67733615367SSebastian Reichel 	if (enable) {
67833615367SSebastian Reichel 		err = genphy_resume(phydev);
67933615367SSebastian Reichel 		if (err < 0)
68033615367SSebastian Reichel 			goto err_put_dev;
68133615367SSebastian Reichel 
68233615367SSebastian Reichel 		err = genphy_read_status(phydev);
68333615367SSebastian Reichel 		if (err < 0)
68433615367SSebastian Reichel 			goto err_put_dev;
68533615367SSebastian Reichel 	} else {
68633615367SSebastian Reichel 		err = genphy_suspend(phydev);
68733615367SSebastian Reichel 		if (err < 0)
68833615367SSebastian Reichel 			goto err_put_dev;
68933615367SSebastian Reichel 	}
69033615367SSebastian Reichel 
69133615367SSebastian Reichel 	if (ds->ops->adjust_link)
69233615367SSebastian Reichel 		ds->ops->adjust_link(ds, port, phydev);
69333615367SSebastian Reichel 
69433615367SSebastian Reichel 	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
69533615367SSebastian Reichel 
69633615367SSebastian Reichel err_put_dev:
69733615367SSebastian Reichel 	put_device(&phydev->mdio.dev);
69833615367SSebastian Reichel 	return err;
69933615367SSebastian Reichel }
70033615367SSebastian Reichel 
70133615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
70257ab1ca2SVivien Didelot {
70357ab1ca2SVivien Didelot 	struct device_node *dn = dp->dn;
70457ab1ca2SVivien Didelot 	struct dsa_switch *ds = dp->ds;
70557ab1ca2SVivien Didelot 	struct phy_device *phydev;
70657ab1ca2SVivien Didelot 	int port = dp->index;
7070c65b2b9SAndrew Lunn 	phy_interface_t mode;
70857ab1ca2SVivien Didelot 	int err;
70957ab1ca2SVivien Didelot 
71057ab1ca2SVivien Didelot 	err = of_phy_register_fixed_link(dn);
71157ab1ca2SVivien Didelot 	if (err) {
71257ab1ca2SVivien Didelot 		dev_err(ds->dev,
71357ab1ca2SVivien Didelot 			"failed to register the fixed PHY of port %d\n",
71457ab1ca2SVivien Didelot 			port);
71557ab1ca2SVivien Didelot 		return err;
71657ab1ca2SVivien Didelot 	}
71757ab1ca2SVivien Didelot 
71857ab1ca2SVivien Didelot 	phydev = of_phy_find_device(dn);
71957ab1ca2SVivien Didelot 
7200c65b2b9SAndrew Lunn 	err = of_get_phy_mode(dn, &mode);
7210c65b2b9SAndrew Lunn 	if (err)
72257ab1ca2SVivien Didelot 		mode = PHY_INTERFACE_MODE_NA;
72357ab1ca2SVivien Didelot 	phydev->interface = mode;
72457ab1ca2SVivien Didelot 
72557ab1ca2SVivien Didelot 	genphy_read_status(phydev);
72657ab1ca2SVivien Didelot 
72757ab1ca2SVivien Didelot 	if (ds->ops->adjust_link)
72857ab1ca2SVivien Didelot 		ds->ops->adjust_link(ds, port, phydev);
72957ab1ca2SVivien Didelot 
73057ab1ca2SVivien Didelot 	put_device(&phydev->mdio.dev);
73157ab1ca2SVivien Didelot 
73257ab1ca2SVivien Didelot 	return 0;
73357ab1ca2SVivien Didelot }
73457ab1ca2SVivien Didelot 
7350e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp)
7360e279218SIoana Ciornei {
7370e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
7380e279218SIoana Ciornei 	struct device_node *port_dn = dp->dn;
7390c65b2b9SAndrew Lunn 	phy_interface_t mode;
7400c65b2b9SAndrew Lunn 	int err;
7410e279218SIoana Ciornei 
7420c65b2b9SAndrew Lunn 	err = of_get_phy_mode(port_dn, &mode);
7430c65b2b9SAndrew Lunn 	if (err)
7440e279218SIoana Ciornei 		mode = PHY_INTERFACE_MODE_NA;
7450e279218SIoana Ciornei 
7460e279218SIoana Ciornei 	dp->pl_config.dev = ds->dev;
7470e279218SIoana Ciornei 	dp->pl_config.type = PHYLINK_DEV;
748787cac3fSVladimir Oltean 	dp->pl_config.pcs_poll = ds->pcs_poll;
7490e279218SIoana Ciornei 
7500e279218SIoana Ciornei 	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
7510e279218SIoana Ciornei 				mode, &dsa_port_phylink_mac_ops);
7520e279218SIoana Ciornei 	if (IS_ERR(dp->pl)) {
7530e279218SIoana Ciornei 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
7540e279218SIoana Ciornei 		return PTR_ERR(dp->pl);
7550e279218SIoana Ciornei 	}
7560e279218SIoana Ciornei 
7570e279218SIoana Ciornei 	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
7582131fba5SFlorian Fainelli 	if (err && err != -ENODEV) {
7590e279218SIoana Ciornei 		pr_err("could not attach to PHY: %d\n", err);
7600e279218SIoana Ciornei 		goto err_phy_connect;
7610e279218SIoana Ciornei 	}
7620e279218SIoana Ciornei 
7630e279218SIoana Ciornei 	return 0;
7640e279218SIoana Ciornei 
7650e279218SIoana Ciornei err_phy_connect:
7660e279218SIoana Ciornei 	phylink_destroy(dp->pl);
7670e279218SIoana Ciornei 	return err;
7680e279218SIoana Ciornei }
7690e279218SIoana Ciornei 
77033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp)
77157ab1ca2SVivien Didelot {
7720e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
773a20f9970SAndrew Lunn 	struct device_node *phy_np;
7743be98b2dSAndrew Lunn 	int port = dp->index;
7750e279218SIoana Ciornei 
776a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link) {
777a20f9970SAndrew Lunn 		phy_np = of_parse_phandle(dp->dn, "phy-handle", 0);
7783be98b2dSAndrew Lunn 		if (of_phy_is_fixed_link(dp->dn) || phy_np) {
7793be98b2dSAndrew Lunn 			if (ds->ops->phylink_mac_link_down)
7803be98b2dSAndrew Lunn 				ds->ops->phylink_mac_link_down(ds, port,
7813be98b2dSAndrew Lunn 					MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
7820e279218SIoana Ciornei 			return dsa_port_phylink_register(dp);
7833be98b2dSAndrew Lunn 		}
784a20f9970SAndrew Lunn 		return 0;
785a20f9970SAndrew Lunn 	}
7860e279218SIoana Ciornei 
7870e279218SIoana Ciornei 	dev_warn(ds->dev,
7880e279218SIoana Ciornei 		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
7890e279218SIoana Ciornei 
79033615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
79133615367SSebastian Reichel 		return dsa_port_fixed_link_register_of(dp);
79233615367SSebastian Reichel 	else
79333615367SSebastian Reichel 		return dsa_port_setup_phy_of(dp, true);
79433615367SSebastian Reichel }
79557ab1ca2SVivien Didelot 
79633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp)
79733615367SSebastian Reichel {
7980e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
7990e279218SIoana Ciornei 
800a20f9970SAndrew Lunn 	if (!ds->ops->adjust_link && dp->pl) {
8010e279218SIoana Ciornei 		rtnl_lock();
8020e279218SIoana Ciornei 		phylink_disconnect_phy(dp->pl);
8030e279218SIoana Ciornei 		rtnl_unlock();
8040e279218SIoana Ciornei 		phylink_destroy(dp->pl);
805a20f9970SAndrew Lunn 		dp->pl = NULL;
8060e279218SIoana Ciornei 		return;
8070e279218SIoana Ciornei 	}
8080e279218SIoana Ciornei 
80933615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
81033615367SSebastian Reichel 		of_phy_deregister_fixed_link(dp->dn);
81133615367SSebastian Reichel 	else
81233615367SSebastian Reichel 		dsa_port_setup_phy_of(dp, false);
81357ab1ca2SVivien Didelot }
814cf963573SFlorian Fainelli 
815cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
816cf963573SFlorian Fainelli {
817cf963573SFlorian Fainelli 	struct phy_device *phydev;
818cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
819cf963573SFlorian Fainelli 
820cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
821cf963573SFlorian Fainelli 		return ret;
822cf963573SFlorian Fainelli 
823cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
824cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
825cf963573SFlorian Fainelli 		return ret;
826cf963573SFlorian Fainelli 
827cf963573SFlorian Fainelli 	ret = phy_ethtool_get_strings(phydev, data);
828cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
829cf963573SFlorian Fainelli 
830cf963573SFlorian Fainelli 	return ret;
831cf963573SFlorian Fainelli }
832cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
833cf963573SFlorian Fainelli 
834cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
835cf963573SFlorian Fainelli {
836cf963573SFlorian Fainelli 	struct phy_device *phydev;
837cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
838cf963573SFlorian Fainelli 
839cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
840cf963573SFlorian Fainelli 		return ret;
841cf963573SFlorian Fainelli 
842cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
843cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
844cf963573SFlorian Fainelli 		return ret;
845cf963573SFlorian Fainelli 
846cf963573SFlorian Fainelli 	ret = phy_ethtool_get_stats(phydev, NULL, data);
847cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
848cf963573SFlorian Fainelli 
849cf963573SFlorian Fainelli 	return ret;
850cf963573SFlorian Fainelli }
851cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
852cf963573SFlorian Fainelli 
853cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp)
854cf963573SFlorian Fainelli {
855cf963573SFlorian Fainelli 	struct phy_device *phydev;
856cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
857cf963573SFlorian Fainelli 
858cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
859cf963573SFlorian Fainelli 		return ret;
860cf963573SFlorian Fainelli 
861cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
862cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
863cf963573SFlorian Fainelli 		return ret;
864cf963573SFlorian Fainelli 
865cf963573SFlorian Fainelli 	ret = phy_ethtool_get_sset_count(phydev);
866cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
867cf963573SFlorian Fainelli 
868cf963573SFlorian Fainelli 	return ret;
869cf963573SFlorian Fainelli }
870cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
871*18596f50SGeorge McCollister 
872*18596f50SGeorge McCollister int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr)
873*18596f50SGeorge McCollister {
874*18596f50SGeorge McCollister 	struct dsa_notifier_hsr_info info = {
875*18596f50SGeorge McCollister 		.sw_index = dp->ds->index,
876*18596f50SGeorge McCollister 		.port = dp->index,
877*18596f50SGeorge McCollister 		.hsr = hsr,
878*18596f50SGeorge McCollister 	};
879*18596f50SGeorge McCollister 	int err;
880*18596f50SGeorge McCollister 
881*18596f50SGeorge McCollister 	dp->hsr_dev = hsr;
882*18596f50SGeorge McCollister 
883*18596f50SGeorge McCollister 	err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_JOIN, &info);
884*18596f50SGeorge McCollister 	if (err)
885*18596f50SGeorge McCollister 		dp->hsr_dev = NULL;
886*18596f50SGeorge McCollister 
887*18596f50SGeorge McCollister 	return err;
888*18596f50SGeorge McCollister }
889*18596f50SGeorge McCollister 
890*18596f50SGeorge McCollister void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr)
891*18596f50SGeorge McCollister {
892*18596f50SGeorge McCollister 	struct dsa_notifier_hsr_info info = {
893*18596f50SGeorge McCollister 		.sw_index = dp->ds->index,
894*18596f50SGeorge McCollister 		.port = dp->index,
895*18596f50SGeorge McCollister 		.hsr = hsr,
896*18596f50SGeorge McCollister 	};
897*18596f50SGeorge McCollister 	int err;
898*18596f50SGeorge McCollister 
899*18596f50SGeorge McCollister 	dp->hsr_dev = NULL;
900*18596f50SGeorge McCollister 
901*18596f50SGeorge McCollister 	err = dsa_port_notify(dp, DSA_NOTIFIER_HSR_LEAVE, &info);
902*18596f50SGeorge McCollister 	if (err)
903*18596f50SGeorge McCollister 		pr_err("DSA: failed to notify DSA_NOTIFIER_HSR_LEAVE\n");
904*18596f50SGeorge McCollister }
905