xref: /openbmc/linux/net/dsa/port.c (revision 8640f8dc)
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 
16bb9f6031SAndrew Lunn static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
17cfbed329SVivien Didelot {
18cfbed329SVivien Didelot 	struct raw_notifier_head *nh = &dp->ds->dst->nh;
19cfbed329SVivien Didelot 	int err;
20cfbed329SVivien Didelot 
21cfbed329SVivien Didelot 	err = raw_notifier_call_chain(nh, e, v);
22cfbed329SVivien Didelot 
23cfbed329SVivien Didelot 	return notifier_to_errno(err);
24cfbed329SVivien Didelot }
25cfbed329SVivien Didelot 
26a40c175bSVivien Didelot int dsa_port_set_state(struct dsa_port *dp, u8 state,
27a40c175bSVivien Didelot 		       struct switchdev_trans *trans)
28a40c175bSVivien Didelot {
29a40c175bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
30a40c175bSVivien Didelot 	int port = dp->index;
31a40c175bSVivien Didelot 
32a40c175bSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
33a40c175bSVivien Didelot 		return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
34a40c175bSVivien Didelot 
35a40c175bSVivien Didelot 	if (ds->ops->port_stp_state_set)
36a40c175bSVivien Didelot 		ds->ops->port_stp_state_set(ds, port, state);
37a40c175bSVivien Didelot 
38a40c175bSVivien Didelot 	if (ds->ops->port_fast_age) {
39a40c175bSVivien Didelot 		/* Fast age FDB entries or flush appropriate forwarding database
40a40c175bSVivien Didelot 		 * for the given port, if we are moving it from Learning or
41a40c175bSVivien Didelot 		 * Forwarding state, to Disabled or Blocking or Listening state.
42a40c175bSVivien Didelot 		 */
43a40c175bSVivien Didelot 
44a40c175bSVivien Didelot 		if ((dp->stp_state == BR_STATE_LEARNING ||
45a40c175bSVivien Didelot 		     dp->stp_state == BR_STATE_FORWARDING) &&
46a40c175bSVivien Didelot 		    (state == BR_STATE_DISABLED ||
47a40c175bSVivien Didelot 		     state == BR_STATE_BLOCKING ||
48a40c175bSVivien Didelot 		     state == BR_STATE_LISTENING))
49a40c175bSVivien Didelot 			ds->ops->port_fast_age(ds, port);
50a40c175bSVivien Didelot 	}
51a40c175bSVivien Didelot 
52a40c175bSVivien Didelot 	dp->stp_state = state;
53a40c175bSVivien Didelot 
54a40c175bSVivien Didelot 	return 0;
55a40c175bSVivien Didelot }
56a40c175bSVivien Didelot 
57fb8a6a2bSVivien Didelot static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
58a40c175bSVivien Didelot {
59a40c175bSVivien Didelot 	int err;
60a40c175bSVivien Didelot 
61a40c175bSVivien Didelot 	err = dsa_port_set_state(dp, state, NULL);
62a40c175bSVivien Didelot 	if (err)
63a40c175bSVivien Didelot 		pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
64a40c175bSVivien Didelot }
65cfbed329SVivien Didelot 
668640f8dcSRussell King int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
67fb8a6a2bSVivien Didelot {
68fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
69fb8a6a2bSVivien Didelot 	int port = dp->index;
70fb8a6a2bSVivien Didelot 	int err;
71fb8a6a2bSVivien Didelot 
72fb8a6a2bSVivien Didelot 	if (ds->ops->port_enable) {
73fb8a6a2bSVivien Didelot 		err = ds->ops->port_enable(ds, port, phy);
74fb8a6a2bSVivien Didelot 		if (err)
75fb8a6a2bSVivien Didelot 			return err;
76fb8a6a2bSVivien Didelot 	}
77fb8a6a2bSVivien Didelot 
789c2054a5SRussell King 	if (!dp->bridge_dev)
799c2054a5SRussell King 		dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
80fb8a6a2bSVivien Didelot 
818640f8dcSRussell King 	if (dp->pl)
828640f8dcSRussell King 		phylink_start(dp->pl);
838640f8dcSRussell King 
84fb8a6a2bSVivien Didelot 	return 0;
85fb8a6a2bSVivien Didelot }
86fb8a6a2bSVivien Didelot 
878640f8dcSRussell King int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
888640f8dcSRussell King {
898640f8dcSRussell King 	int err;
908640f8dcSRussell King 
918640f8dcSRussell King 	rtnl_lock();
928640f8dcSRussell King 	err = dsa_port_enable_rt(dp, phy);
938640f8dcSRussell King 	rtnl_unlock();
948640f8dcSRussell King 
958640f8dcSRussell King 	return err;
968640f8dcSRussell King }
978640f8dcSRussell King 
988640f8dcSRussell King void dsa_port_disable_rt(struct dsa_port *dp)
99fb8a6a2bSVivien Didelot {
100fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
101fb8a6a2bSVivien Didelot 	int port = dp->index;
102fb8a6a2bSVivien Didelot 
1038640f8dcSRussell King 	if (dp->pl)
1048640f8dcSRussell King 		phylink_stop(dp->pl);
1058640f8dcSRussell King 
1069c2054a5SRussell King 	if (!dp->bridge_dev)
107fb8a6a2bSVivien Didelot 		dsa_port_set_state_now(dp, BR_STATE_DISABLED);
108fb8a6a2bSVivien Didelot 
109fb8a6a2bSVivien Didelot 	if (ds->ops->port_disable)
11075104db0SAndrew Lunn 		ds->ops->port_disable(ds, port);
111fb8a6a2bSVivien Didelot }
112fb8a6a2bSVivien Didelot 
1138640f8dcSRussell King void dsa_port_disable(struct dsa_port *dp)
1148640f8dcSRussell King {
1158640f8dcSRussell King 	rtnl_lock();
1168640f8dcSRussell King 	dsa_port_disable_rt(dp);
1178640f8dcSRussell King 	rtnl_unlock();
1188640f8dcSRussell King }
1198640f8dcSRussell King 
120cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
121cfbed329SVivien Didelot {
122cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
123cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
124cfbed329SVivien Didelot 		.port = dp->index,
125cfbed329SVivien Didelot 		.br = br,
126cfbed329SVivien Didelot 	};
127cfbed329SVivien Didelot 	int err;
128cfbed329SVivien Didelot 
129c1388063SRussell King 	/* Set the flooding mode before joining the port in the switch */
130c1388063SRussell King 	err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
131c1388063SRussell King 	if (err)
132c1388063SRussell King 		return err;
133c1388063SRussell King 
134c1388063SRussell King 	/* Here the interface is already bridged. Reflect the current
135c1388063SRussell King 	 * configuration so that drivers can program their chips accordingly.
136cfbed329SVivien Didelot 	 */
137cfbed329SVivien Didelot 	dp->bridge_dev = br;
138cfbed329SVivien Didelot 
139cfbed329SVivien Didelot 	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
140cfbed329SVivien Didelot 
141cfbed329SVivien Didelot 	/* The bridging is rolled back on error */
142c1388063SRussell King 	if (err) {
143c1388063SRussell King 		dsa_port_bridge_flags(dp, 0, NULL);
144cfbed329SVivien Didelot 		dp->bridge_dev = NULL;
145c1388063SRussell King 	}
146cfbed329SVivien Didelot 
147cfbed329SVivien Didelot 	return err;
148cfbed329SVivien Didelot }
149cfbed329SVivien Didelot 
150cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
151cfbed329SVivien Didelot {
152cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
153cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
154cfbed329SVivien Didelot 		.port = dp->index,
155cfbed329SVivien Didelot 		.br = br,
156cfbed329SVivien Didelot 	};
157cfbed329SVivien Didelot 	int err;
158cfbed329SVivien Didelot 
159cfbed329SVivien Didelot 	/* Here the port is already unbridged. Reflect the current configuration
160cfbed329SVivien Didelot 	 * so that drivers can program their chips accordingly.
161cfbed329SVivien Didelot 	 */
162cfbed329SVivien Didelot 	dp->bridge_dev = NULL;
163cfbed329SVivien Didelot 
164cfbed329SVivien Didelot 	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
165cfbed329SVivien Didelot 	if (err)
166cfbed329SVivien Didelot 		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
167cfbed329SVivien Didelot 
168c1388063SRussell King 	/* Port is leaving the bridge, disable flooding */
169c1388063SRussell King 	dsa_port_bridge_flags(dp, 0, NULL);
170c1388063SRussell King 
171cfbed329SVivien Didelot 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
172cfbed329SVivien Didelot 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
173cfbed329SVivien Didelot 	 */
174cfbed329SVivien Didelot 	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
175cfbed329SVivien Didelot }
1764d61d304SVivien Didelot 
1778f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
1788f5d16f6SVladimir Oltean 					      bool vlan_filtering)
1798f5d16f6SVladimir Oltean {
1808f5d16f6SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1818f5d16f6SVladimir Oltean 	int i;
1828f5d16f6SVladimir Oltean 
1838f5d16f6SVladimir Oltean 	if (!ds->vlan_filtering_is_global)
1848f5d16f6SVladimir Oltean 		return true;
1858f5d16f6SVladimir Oltean 
1868f5d16f6SVladimir Oltean 	/* For cases where enabling/disabling VLAN awareness is global to the
1878f5d16f6SVladimir Oltean 	 * switch, we need to handle the case where multiple bridges span
1888f5d16f6SVladimir Oltean 	 * different ports of the same switch device and one of them has a
1898f5d16f6SVladimir Oltean 	 * different setting than what is being requested.
1908f5d16f6SVladimir Oltean 	 */
1918f5d16f6SVladimir Oltean 	for (i = 0; i < ds->num_ports; i++) {
1928f5d16f6SVladimir Oltean 		struct net_device *other_bridge;
1938f5d16f6SVladimir Oltean 
1948f5d16f6SVladimir Oltean 		other_bridge = dsa_to_port(ds, i)->bridge_dev;
1958f5d16f6SVladimir Oltean 		if (!other_bridge)
1968f5d16f6SVladimir Oltean 			continue;
1978f5d16f6SVladimir Oltean 		/* If it's the same bridge, it also has same
1988f5d16f6SVladimir Oltean 		 * vlan_filtering setting => no need to check
1998f5d16f6SVladimir Oltean 		 */
2008f5d16f6SVladimir Oltean 		if (other_bridge == dp->bridge_dev)
2018f5d16f6SVladimir Oltean 			continue;
2028f5d16f6SVladimir Oltean 		if (br_vlan_enabled(other_bridge) != vlan_filtering) {
2038f5d16f6SVladimir Oltean 			dev_err(ds->dev, "VLAN filtering is a global setting\n");
2048f5d16f6SVladimir Oltean 			return false;
2058f5d16f6SVladimir Oltean 		}
2068f5d16f6SVladimir Oltean 	}
2078f5d16f6SVladimir Oltean 	return true;
2088f5d16f6SVladimir Oltean }
2098f5d16f6SVladimir Oltean 
2104d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
2114d61d304SVivien Didelot 			    struct switchdev_trans *trans)
2124d61d304SVivien Didelot {
2134d61d304SVivien Didelot 	struct dsa_switch *ds = dp->ds;
21433162e9aSVladimir Oltean 	int err;
2154d61d304SVivien Didelot 
2164d61d304SVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
2174d61d304SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
2184d61d304SVivien Didelot 		return 0;
2194d61d304SVivien Didelot 
2208f5d16f6SVladimir Oltean 	if (!ds->ops->port_vlan_filtering)
2218f5d16f6SVladimir Oltean 		return 0;
2228f5d16f6SVladimir Oltean 
2238f5d16f6SVladimir Oltean 	if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering))
2248f5d16f6SVladimir Oltean 		return -EINVAL;
2258f5d16f6SVladimir Oltean 
226ec9121e7SVladimir Oltean 	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
227ec9121e7SVladimir Oltean 		return 0;
228ec9121e7SVladimir Oltean 
22933162e9aSVladimir Oltean 	err = ds->ops->port_vlan_filtering(ds, dp->index,
2304d61d304SVivien Didelot 					   vlan_filtering);
23133162e9aSVladimir Oltean 	if (err)
23233162e9aSVladimir Oltean 		return err;
2338f5d16f6SVladimir Oltean 
23414574676SVladimir Oltean 	if (ds->vlan_filtering_is_global)
23514574676SVladimir Oltean 		ds->vlan_filtering = vlan_filtering;
23614574676SVladimir Oltean 	else
23733162e9aSVladimir Oltean 		dp->vlan_filtering = vlan_filtering;
2384d61d304SVivien Didelot 	return 0;
2394d61d304SVivien Didelot }
240d87bd94eSVivien Didelot 
241d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
242d87bd94eSVivien Didelot 			 struct switchdev_trans *trans)
243d87bd94eSVivien Didelot {
244d87bd94eSVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
245d87bd94eSVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
2461faabf74SVivien Didelot 	struct dsa_notifier_ageing_time_info info = {
2471faabf74SVivien Didelot 		.ageing_time = ageing_time,
2481faabf74SVivien Didelot 		.trans = trans,
2491faabf74SVivien Didelot 	};
250d87bd94eSVivien Didelot 
2511faabf74SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
2521faabf74SVivien Didelot 		return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
253d87bd94eSVivien Didelot 
254d87bd94eSVivien Didelot 	dp->ageing_time = ageing_time;
255d87bd94eSVivien Didelot 
2561faabf74SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
257d87bd94eSVivien Didelot }
258d1cffff0SVivien Didelot 
259ea87005aSFlorian Fainelli int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
260ea87005aSFlorian Fainelli 			      struct switchdev_trans *trans)
261ea87005aSFlorian Fainelli {
262ea87005aSFlorian Fainelli 	struct dsa_switch *ds = dp->ds;
263ea87005aSFlorian Fainelli 
264ea87005aSFlorian Fainelli 	if (!ds->ops->port_egress_floods ||
265ea87005aSFlorian Fainelli 	    (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
266ea87005aSFlorian Fainelli 		return -EINVAL;
267ea87005aSFlorian Fainelli 
268ea87005aSFlorian Fainelli 	return 0;
269ea87005aSFlorian Fainelli }
270ea87005aSFlorian Fainelli 
27157652796SRussell King int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
27257652796SRussell King 			  struct switchdev_trans *trans)
27357652796SRussell King {
27457652796SRussell King 	struct dsa_switch *ds = dp->ds;
27557652796SRussell King 	int port = dp->index;
27657652796SRussell King 	int err = 0;
27757652796SRussell King 
27857652796SRussell King 	if (switchdev_trans_ph_prepare(trans))
27957652796SRussell King 		return 0;
28057652796SRussell King 
28157652796SRussell King 	if (ds->ops->port_egress_floods)
28257652796SRussell King 		err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
28357652796SRussell King 						  flags & BR_MCAST_FLOOD);
28457652796SRussell King 
28557652796SRussell King 	return err;
28657652796SRussell King }
28757652796SRussell King 
28808cc83ccSVivien Didelot int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
28908cc83ccSVivien Didelot 		     struct switchdev_trans *trans)
29008cc83ccSVivien Didelot {
29108cc83ccSVivien Didelot 	struct dsa_switch *ds = dp->ds;
29208cc83ccSVivien Didelot 	int port = dp->index;
29308cc83ccSVivien Didelot 
29408cc83ccSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
29508cc83ccSVivien Didelot 		return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP;
29608cc83ccSVivien Didelot 
29708cc83ccSVivien Didelot 	return ds->ops->port_egress_floods(ds, port, true, mrouter);
29808cc83ccSVivien Didelot }
29908cc83ccSVivien Didelot 
3002acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
3012acf4e6aSArkadi Sharshevsky 		     u16 vid)
302d1cffff0SVivien Didelot {
303685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
304685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
305685fb6a4SVivien Didelot 		.port = dp->index,
3062acf4e6aSArkadi Sharshevsky 		.addr = addr,
3072acf4e6aSArkadi Sharshevsky 		.vid = vid,
308685fb6a4SVivien Didelot 	};
309d1cffff0SVivien Didelot 
310685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
311d1cffff0SVivien Didelot }
312d1cffff0SVivien Didelot 
3132acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
3142acf4e6aSArkadi Sharshevsky 		     u16 vid)
315d1cffff0SVivien Didelot {
316685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
317685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
318685fb6a4SVivien Didelot 		.port = dp->index,
3192acf4e6aSArkadi Sharshevsky 		.addr = addr,
3202acf4e6aSArkadi Sharshevsky 		.vid = vid,
3212acf4e6aSArkadi Sharshevsky 
322685fb6a4SVivien Didelot 	};
323d1cffff0SVivien Didelot 
324685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
325d1cffff0SVivien Didelot }
326d1cffff0SVivien Didelot 
327de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
328de40fc5dSVivien Didelot {
329de40fc5dSVivien Didelot 	struct dsa_switch *ds = dp->ds;
330de40fc5dSVivien Didelot 	int port = dp->index;
331de40fc5dSVivien Didelot 
332de40fc5dSVivien Didelot 	if (!ds->ops->port_fdb_dump)
333de40fc5dSVivien Didelot 		return -EOPNOTSUPP;
334de40fc5dSVivien Didelot 
335de40fc5dSVivien Didelot 	return ds->ops->port_fdb_dump(ds, port, cb, data);
336de40fc5dSVivien Didelot }
337de40fc5dSVivien Didelot 
338bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp,
3393a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb,
3403a9afea3SVivien Didelot 		     struct switchdev_trans *trans)
3413a9afea3SVivien Didelot {
3428ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
3438ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
3448ae5bcdcSVivien Didelot 		.port = dp->index,
3458ae5bcdcSVivien Didelot 		.trans = trans,
3468ae5bcdcSVivien Didelot 		.mdb = mdb,
3478ae5bcdcSVivien Didelot 	};
3483a9afea3SVivien Didelot 
3498ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
3503a9afea3SVivien Didelot }
3513a9afea3SVivien Didelot 
352bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp,
3533a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb)
3543a9afea3SVivien Didelot {
3558ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
3568ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
3578ae5bcdcSVivien Didelot 		.port = dp->index,
3588ae5bcdcSVivien Didelot 		.mdb = mdb,
3598ae5bcdcSVivien Didelot 	};
3603a9afea3SVivien Didelot 
3618ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
3623a9afea3SVivien Didelot }
3633a9afea3SVivien Didelot 
364076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp,
365076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan,
366076e7133SVivien Didelot 		      struct switchdev_trans *trans)
367076e7133SVivien Didelot {
368d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
369d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
370d0c627b8SVivien Didelot 		.port = dp->index,
371d0c627b8SVivien Didelot 		.trans = trans,
372d0c627b8SVivien Didelot 		.vlan = vlan,
373d0c627b8SVivien Didelot 	};
374076e7133SVivien Didelot 
375d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
376076e7133SVivien Didelot }
377076e7133SVivien Didelot 
378076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp,
379076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan)
380076e7133SVivien Didelot {
381d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
382d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
383d0c627b8SVivien Didelot 		.port = dp->index,
384d0c627b8SVivien Didelot 		.vlan = vlan,
385d0c627b8SVivien Didelot 	};
386076e7133SVivien Didelot 
387d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
388076e7133SVivien Didelot }
38957ab1ca2SVivien Didelot 
390314f76d7SVladimir Oltean int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags)
391314f76d7SVladimir Oltean {
392314f76d7SVladimir Oltean 	struct switchdev_obj_port_vlan vlan = {
393314f76d7SVladimir Oltean 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
394314f76d7SVladimir Oltean 		.flags = flags,
395314f76d7SVladimir Oltean 		.vid_begin = vid,
396314f76d7SVladimir Oltean 		.vid_end = vid,
397314f76d7SVladimir Oltean 	};
398314f76d7SVladimir Oltean 	struct switchdev_trans trans;
399314f76d7SVladimir Oltean 	int err;
400314f76d7SVladimir Oltean 
401314f76d7SVladimir Oltean 	trans.ph_prepare = true;
402314f76d7SVladimir Oltean 	err = dsa_port_vlan_add(dp, &vlan, &trans);
403cf360866SVivien Didelot 	if (err)
404cf360866SVivien Didelot 		return err;
405314f76d7SVladimir Oltean 
406314f76d7SVladimir Oltean 	trans.ph_prepare = false;
407314f76d7SVladimir Oltean 	return dsa_port_vlan_add(dp, &vlan, &trans);
408314f76d7SVladimir Oltean }
409146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_add);
410314f76d7SVladimir Oltean 
411314f76d7SVladimir Oltean int dsa_port_vid_del(struct dsa_port *dp, u16 vid)
412314f76d7SVladimir Oltean {
413314f76d7SVladimir Oltean 	struct switchdev_obj_port_vlan vlan = {
414314f76d7SVladimir Oltean 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
415314f76d7SVladimir Oltean 		.vid_begin = vid,
416314f76d7SVladimir Oltean 		.vid_end = vid,
417314f76d7SVladimir Oltean 	};
418314f76d7SVladimir Oltean 
419314f76d7SVladimir Oltean 	return dsa_port_vlan_del(dp, &vlan);
420314f76d7SVladimir Oltean }
421146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_del);
422314f76d7SVladimir Oltean 
4236207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
4246207a78cSFlorian Fainelli {
4256207a78cSFlorian Fainelli 	struct device_node *phy_dn;
4266207a78cSFlorian Fainelli 	struct phy_device *phydev;
4276207a78cSFlorian Fainelli 
4286207a78cSFlorian Fainelli 	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
4296207a78cSFlorian Fainelli 	if (!phy_dn)
4306207a78cSFlorian Fainelli 		return NULL;
4316207a78cSFlorian Fainelli 
4326207a78cSFlorian Fainelli 	phydev = of_phy_find_device(phy_dn);
4336207a78cSFlorian Fainelli 	if (!phydev) {
4346207a78cSFlorian Fainelli 		of_node_put(phy_dn);
4356207a78cSFlorian Fainelli 		return ERR_PTR(-EPROBE_DEFER);
4366207a78cSFlorian Fainelli 	}
4376207a78cSFlorian Fainelli 
4389919a363SWen Yang 	of_node_put(phy_dn);
4396207a78cSFlorian Fainelli 	return phydev;
4406207a78cSFlorian Fainelli }
4416207a78cSFlorian Fainelli 
4428ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config,
44377373d49SIoana Ciornei 				      unsigned long *supported,
44477373d49SIoana Ciornei 				      struct phylink_link_state *state)
44577373d49SIoana Ciornei {
44677373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
44777373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
44877373d49SIoana Ciornei 
44977373d49SIoana Ciornei 	if (!ds->ops->phylink_validate)
45077373d49SIoana Ciornei 		return;
45177373d49SIoana Ciornei 
45277373d49SIoana Ciornei 	ds->ops->phylink_validate(ds, dp->index, supported, state);
45377373d49SIoana Ciornei }
45477373d49SIoana Ciornei 
4558ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
45677373d49SIoana Ciornei 					       struct phylink_link_state *state)
45777373d49SIoana Ciornei {
45877373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
45977373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
46077373d49SIoana Ciornei 
461d46b7e4fSRussell King 	/* Only called for inband modes */
462d46b7e4fSRussell King 	if (!ds->ops->phylink_mac_link_state) {
463d46b7e4fSRussell King 		state->link = 0;
464d46b7e4fSRussell King 		return;
46577373d49SIoana Ciornei 	}
466d46b7e4fSRussell King 
467d46b7e4fSRussell King 	if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0)
468d46b7e4fSRussell King 		state->link = 0;
469d46b7e4fSRussell King }
47077373d49SIoana Ciornei 
4718ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config,
47277373d49SIoana Ciornei 					unsigned int mode,
47377373d49SIoana Ciornei 					const struct phylink_link_state *state)
47477373d49SIoana Ciornei {
47577373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
47677373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
47777373d49SIoana Ciornei 
47877373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_config)
47977373d49SIoana Ciornei 		return;
48077373d49SIoana Ciornei 
48177373d49SIoana Ciornei 	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
48277373d49SIoana Ciornei }
48377373d49SIoana Ciornei 
4848ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
48577373d49SIoana Ciornei {
48677373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
48777373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
48877373d49SIoana Ciornei 
48977373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_an_restart)
49077373d49SIoana Ciornei 		return;
49177373d49SIoana Ciornei 
49277373d49SIoana Ciornei 	ds->ops->phylink_mac_an_restart(ds, dp->index);
49377373d49SIoana Ciornei }
49477373d49SIoana Ciornei 
4958ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
49677373d49SIoana Ciornei 					   unsigned int mode,
49777373d49SIoana Ciornei 					   phy_interface_t interface)
49877373d49SIoana Ciornei {
49977373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
5000e279218SIoana Ciornei 	struct phy_device *phydev = NULL;
50177373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
50277373d49SIoana Ciornei 
5030e279218SIoana Ciornei 	if (dsa_is_user_port(ds, dp->index))
5040e279218SIoana Ciornei 		phydev = dp->slave->phydev;
5050e279218SIoana Ciornei 
50677373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_down) {
5070e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
5080e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
50977373d49SIoana Ciornei 		return;
51077373d49SIoana Ciornei 	}
51177373d49SIoana Ciornei 
51277373d49SIoana Ciornei 	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
51377373d49SIoana Ciornei }
51477373d49SIoana Ciornei 
5158ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
51677373d49SIoana Ciornei 					 unsigned int mode,
51777373d49SIoana Ciornei 					 phy_interface_t interface,
51877373d49SIoana Ciornei 					 struct phy_device *phydev)
51977373d49SIoana Ciornei {
52077373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
52177373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
52277373d49SIoana Ciornei 
52377373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_up) {
5240e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
5250e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
52677373d49SIoana Ciornei 		return;
52777373d49SIoana Ciornei 	}
52877373d49SIoana Ciornei 
52977373d49SIoana Ciornei 	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
53077373d49SIoana Ciornei }
53177373d49SIoana Ciornei 
53277373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
53377373d49SIoana Ciornei 	.validate = dsa_port_phylink_validate,
534d46b7e4fSRussell King 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
53577373d49SIoana Ciornei 	.mac_config = dsa_port_phylink_mac_config,
53677373d49SIoana Ciornei 	.mac_an_restart = dsa_port_phylink_mac_an_restart,
53777373d49SIoana Ciornei 	.mac_link_down = dsa_port_phylink_mac_link_down,
53877373d49SIoana Ciornei 	.mac_link_up = dsa_port_phylink_mac_link_up,
53977373d49SIoana Ciornei };
54077373d49SIoana Ciornei 
54133615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
54233615367SSebastian Reichel {
54333615367SSebastian Reichel 	struct dsa_switch *ds = dp->ds;
54433615367SSebastian Reichel 	struct phy_device *phydev;
54533615367SSebastian Reichel 	int port = dp->index;
54633615367SSebastian Reichel 	int err = 0;
54733615367SSebastian Reichel 
5486207a78cSFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
5496207a78cSFlorian Fainelli 	if (!phydev)
55033615367SSebastian Reichel 		return 0;
55133615367SSebastian Reichel 
5526207a78cSFlorian Fainelli 	if (IS_ERR(phydev))
5536207a78cSFlorian Fainelli 		return PTR_ERR(phydev);
55433615367SSebastian Reichel 
55533615367SSebastian Reichel 	if (enable) {
55633615367SSebastian Reichel 		err = genphy_resume(phydev);
55733615367SSebastian Reichel 		if (err < 0)
55833615367SSebastian Reichel 			goto err_put_dev;
55933615367SSebastian Reichel 
56033615367SSebastian Reichel 		err = genphy_read_status(phydev);
56133615367SSebastian Reichel 		if (err < 0)
56233615367SSebastian Reichel 			goto err_put_dev;
56333615367SSebastian Reichel 	} else {
56433615367SSebastian Reichel 		err = genphy_suspend(phydev);
56533615367SSebastian Reichel 		if (err < 0)
56633615367SSebastian Reichel 			goto err_put_dev;
56733615367SSebastian Reichel 	}
56833615367SSebastian Reichel 
56933615367SSebastian Reichel 	if (ds->ops->adjust_link)
57033615367SSebastian Reichel 		ds->ops->adjust_link(ds, port, phydev);
57133615367SSebastian Reichel 
57233615367SSebastian Reichel 	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
57333615367SSebastian Reichel 
57433615367SSebastian Reichel err_put_dev:
57533615367SSebastian Reichel 	put_device(&phydev->mdio.dev);
57633615367SSebastian Reichel 	return err;
57733615367SSebastian Reichel }
57833615367SSebastian Reichel 
57933615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
58057ab1ca2SVivien Didelot {
58157ab1ca2SVivien Didelot 	struct device_node *dn = dp->dn;
58257ab1ca2SVivien Didelot 	struct dsa_switch *ds = dp->ds;
58357ab1ca2SVivien Didelot 	struct phy_device *phydev;
58457ab1ca2SVivien Didelot 	int port = dp->index;
5850c65b2b9SAndrew Lunn 	phy_interface_t mode;
58657ab1ca2SVivien Didelot 	int err;
58757ab1ca2SVivien Didelot 
58857ab1ca2SVivien Didelot 	err = of_phy_register_fixed_link(dn);
58957ab1ca2SVivien Didelot 	if (err) {
59057ab1ca2SVivien Didelot 		dev_err(ds->dev,
59157ab1ca2SVivien Didelot 			"failed to register the fixed PHY of port %d\n",
59257ab1ca2SVivien Didelot 			port);
59357ab1ca2SVivien Didelot 		return err;
59457ab1ca2SVivien Didelot 	}
59557ab1ca2SVivien Didelot 
59657ab1ca2SVivien Didelot 	phydev = of_phy_find_device(dn);
59757ab1ca2SVivien Didelot 
5980c65b2b9SAndrew Lunn 	err = of_get_phy_mode(dn, &mode);
5990c65b2b9SAndrew Lunn 	if (err)
60057ab1ca2SVivien Didelot 		mode = PHY_INTERFACE_MODE_NA;
60157ab1ca2SVivien Didelot 	phydev->interface = mode;
60257ab1ca2SVivien Didelot 
60357ab1ca2SVivien Didelot 	genphy_read_status(phydev);
60457ab1ca2SVivien Didelot 
60557ab1ca2SVivien Didelot 	if (ds->ops->adjust_link)
60657ab1ca2SVivien Didelot 		ds->ops->adjust_link(ds, port, phydev);
60757ab1ca2SVivien Didelot 
60857ab1ca2SVivien Didelot 	put_device(&phydev->mdio.dev);
60957ab1ca2SVivien Didelot 
61057ab1ca2SVivien Didelot 	return 0;
61157ab1ca2SVivien Didelot }
61257ab1ca2SVivien Didelot 
6130e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp)
6140e279218SIoana Ciornei {
6150e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
6160e279218SIoana Ciornei 	struct device_node *port_dn = dp->dn;
6170c65b2b9SAndrew Lunn 	phy_interface_t mode;
6180c65b2b9SAndrew Lunn 	int err;
6190e279218SIoana Ciornei 
6200c65b2b9SAndrew Lunn 	err = of_get_phy_mode(port_dn, &mode);
6210c65b2b9SAndrew Lunn 	if (err)
6220e279218SIoana Ciornei 		mode = PHY_INTERFACE_MODE_NA;
6230e279218SIoana Ciornei 
6240e279218SIoana Ciornei 	dp->pl_config.dev = ds->dev;
6250e279218SIoana Ciornei 	dp->pl_config.type = PHYLINK_DEV;
626787cac3fSVladimir Oltean 	dp->pl_config.pcs_poll = ds->pcs_poll;
6270e279218SIoana Ciornei 
6280e279218SIoana Ciornei 	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
6290e279218SIoana Ciornei 				mode, &dsa_port_phylink_mac_ops);
6300e279218SIoana Ciornei 	if (IS_ERR(dp->pl)) {
6310e279218SIoana Ciornei 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
6320e279218SIoana Ciornei 		return PTR_ERR(dp->pl);
6330e279218SIoana Ciornei 	}
6340e279218SIoana Ciornei 
6350e279218SIoana Ciornei 	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
6362131fba5SFlorian Fainelli 	if (err && err != -ENODEV) {
6370e279218SIoana Ciornei 		pr_err("could not attach to PHY: %d\n", err);
6380e279218SIoana Ciornei 		goto err_phy_connect;
6390e279218SIoana Ciornei 	}
6400e279218SIoana Ciornei 
6410e279218SIoana Ciornei 	return 0;
6420e279218SIoana Ciornei 
6430e279218SIoana Ciornei err_phy_connect:
6440e279218SIoana Ciornei 	phylink_destroy(dp->pl);
6450e279218SIoana Ciornei 	return err;
6460e279218SIoana Ciornei }
6470e279218SIoana Ciornei 
64833615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp)
64957ab1ca2SVivien Didelot {
6500e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
6510e279218SIoana Ciornei 
6520e279218SIoana Ciornei 	if (!ds->ops->adjust_link)
6530e279218SIoana Ciornei 		return dsa_port_phylink_register(dp);
6540e279218SIoana Ciornei 
6550e279218SIoana Ciornei 	dev_warn(ds->dev,
6560e279218SIoana Ciornei 		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
6570e279218SIoana Ciornei 
65833615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
65933615367SSebastian Reichel 		return dsa_port_fixed_link_register_of(dp);
66033615367SSebastian Reichel 	else
66133615367SSebastian Reichel 		return dsa_port_setup_phy_of(dp, true);
66233615367SSebastian Reichel }
66357ab1ca2SVivien Didelot 
66433615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp)
66533615367SSebastian Reichel {
6660e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
6670e279218SIoana Ciornei 
6680e279218SIoana Ciornei 	if (!ds->ops->adjust_link) {
6690e279218SIoana Ciornei 		rtnl_lock();
6700e279218SIoana Ciornei 		phylink_disconnect_phy(dp->pl);
6710e279218SIoana Ciornei 		rtnl_unlock();
6720e279218SIoana Ciornei 		phylink_destroy(dp->pl);
6730e279218SIoana Ciornei 		return;
6740e279218SIoana Ciornei 	}
6750e279218SIoana Ciornei 
67633615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
67733615367SSebastian Reichel 		of_phy_deregister_fixed_link(dp->dn);
67833615367SSebastian Reichel 	else
67933615367SSebastian Reichel 		dsa_port_setup_phy_of(dp, false);
68057ab1ca2SVivien Didelot }
681cf963573SFlorian Fainelli 
682cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
683cf963573SFlorian Fainelli {
684cf963573SFlorian Fainelli 	struct phy_device *phydev;
685cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
686cf963573SFlorian Fainelli 
687cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
688cf963573SFlorian Fainelli 		return ret;
689cf963573SFlorian Fainelli 
690cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
691cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
692cf963573SFlorian Fainelli 		return ret;
693cf963573SFlorian Fainelli 
694cf963573SFlorian Fainelli 	ret = phy_ethtool_get_strings(phydev, data);
695cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
696cf963573SFlorian Fainelli 
697cf963573SFlorian Fainelli 	return ret;
698cf963573SFlorian Fainelli }
699cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
700cf963573SFlorian Fainelli 
701cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
702cf963573SFlorian Fainelli {
703cf963573SFlorian Fainelli 	struct phy_device *phydev;
704cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
705cf963573SFlorian Fainelli 
706cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
707cf963573SFlorian Fainelli 		return ret;
708cf963573SFlorian Fainelli 
709cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
710cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
711cf963573SFlorian Fainelli 		return ret;
712cf963573SFlorian Fainelli 
713cf963573SFlorian Fainelli 	ret = phy_ethtool_get_stats(phydev, NULL, data);
714cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
715cf963573SFlorian Fainelli 
716cf963573SFlorian Fainelli 	return ret;
717cf963573SFlorian Fainelli }
718cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
719cf963573SFlorian Fainelli 
720cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp)
721cf963573SFlorian Fainelli {
722cf963573SFlorian Fainelli 	struct phy_device *phydev;
723cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
724cf963573SFlorian Fainelli 
725cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
726cf963573SFlorian Fainelli 		return ret;
727cf963573SFlorian Fainelli 
728cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
729cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
730cf963573SFlorian Fainelli 		return ret;
731cf963573SFlorian Fainelli 
732cf963573SFlorian Fainelli 	ret = phy_ethtool_get_sset_count(phydev);
733cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
734cf963573SFlorian Fainelli 
735cf963573SFlorian Fainelli 	return ret;
736cf963573SFlorian Fainelli }
737cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
738