xref: /openbmc/linux/net/dsa/port.c (revision 91a208f2)
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 
66fb8a6a2bSVivien Didelot int dsa_port_enable(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 
81fb8a6a2bSVivien Didelot 	return 0;
82fb8a6a2bSVivien Didelot }
83fb8a6a2bSVivien Didelot 
8475104db0SAndrew Lunn void dsa_port_disable(struct dsa_port *dp)
85fb8a6a2bSVivien Didelot {
86fb8a6a2bSVivien Didelot 	struct dsa_switch *ds = dp->ds;
87fb8a6a2bSVivien Didelot 	int port = dp->index;
88fb8a6a2bSVivien Didelot 
899c2054a5SRussell King 	if (!dp->bridge_dev)
90fb8a6a2bSVivien Didelot 		dsa_port_set_state_now(dp, BR_STATE_DISABLED);
91fb8a6a2bSVivien Didelot 
92fb8a6a2bSVivien Didelot 	if (ds->ops->port_disable)
9375104db0SAndrew Lunn 		ds->ops->port_disable(ds, port);
94fb8a6a2bSVivien Didelot }
95fb8a6a2bSVivien Didelot 
96cfbed329SVivien Didelot int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
97cfbed329SVivien Didelot {
98cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
99cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
100cfbed329SVivien Didelot 		.port = dp->index,
101cfbed329SVivien Didelot 		.br = br,
102cfbed329SVivien Didelot 	};
103cfbed329SVivien Didelot 	int err;
104cfbed329SVivien Didelot 
105c1388063SRussell King 	/* Set the flooding mode before joining the port in the switch */
106c1388063SRussell King 	err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
107c1388063SRussell King 	if (err)
108c1388063SRussell King 		return err;
109c1388063SRussell King 
110c1388063SRussell King 	/* Here the interface is already bridged. Reflect the current
111c1388063SRussell King 	 * configuration so that drivers can program their chips accordingly.
112cfbed329SVivien Didelot 	 */
113cfbed329SVivien Didelot 	dp->bridge_dev = br;
114cfbed329SVivien Didelot 
115cfbed329SVivien Didelot 	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
116cfbed329SVivien Didelot 
117cfbed329SVivien Didelot 	/* The bridging is rolled back on error */
118c1388063SRussell King 	if (err) {
119c1388063SRussell King 		dsa_port_bridge_flags(dp, 0, NULL);
120cfbed329SVivien Didelot 		dp->bridge_dev = NULL;
121c1388063SRussell King 	}
122cfbed329SVivien Didelot 
123cfbed329SVivien Didelot 	return err;
124cfbed329SVivien Didelot }
125cfbed329SVivien Didelot 
126cfbed329SVivien Didelot void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
127cfbed329SVivien Didelot {
128cfbed329SVivien Didelot 	struct dsa_notifier_bridge_info info = {
129cfbed329SVivien Didelot 		.sw_index = dp->ds->index,
130cfbed329SVivien Didelot 		.port = dp->index,
131cfbed329SVivien Didelot 		.br = br,
132cfbed329SVivien Didelot 	};
133cfbed329SVivien Didelot 	int err;
134cfbed329SVivien Didelot 
135cfbed329SVivien Didelot 	/* Here the port is already unbridged. Reflect the current configuration
136cfbed329SVivien Didelot 	 * so that drivers can program their chips accordingly.
137cfbed329SVivien Didelot 	 */
138cfbed329SVivien Didelot 	dp->bridge_dev = NULL;
139cfbed329SVivien Didelot 
140cfbed329SVivien Didelot 	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
141cfbed329SVivien Didelot 	if (err)
142cfbed329SVivien Didelot 		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
143cfbed329SVivien Didelot 
144c1388063SRussell King 	/* Port is leaving the bridge, disable flooding */
145c1388063SRussell King 	dsa_port_bridge_flags(dp, 0, NULL);
146c1388063SRussell King 
147cfbed329SVivien Didelot 	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
148cfbed329SVivien Didelot 	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
149cfbed329SVivien Didelot 	 */
150cfbed329SVivien Didelot 	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
151cfbed329SVivien Didelot }
1524d61d304SVivien Didelot 
1538f5d16f6SVladimir Oltean static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
1548f5d16f6SVladimir Oltean 					      bool vlan_filtering)
1558f5d16f6SVladimir Oltean {
1568f5d16f6SVladimir Oltean 	struct dsa_switch *ds = dp->ds;
1578f5d16f6SVladimir Oltean 	int i;
1588f5d16f6SVladimir Oltean 
1598f5d16f6SVladimir Oltean 	if (!ds->vlan_filtering_is_global)
1608f5d16f6SVladimir Oltean 		return true;
1618f5d16f6SVladimir Oltean 
1628f5d16f6SVladimir Oltean 	/* For cases where enabling/disabling VLAN awareness is global to the
1638f5d16f6SVladimir Oltean 	 * switch, we need to handle the case where multiple bridges span
1648f5d16f6SVladimir Oltean 	 * different ports of the same switch device and one of them has a
1658f5d16f6SVladimir Oltean 	 * different setting than what is being requested.
1668f5d16f6SVladimir Oltean 	 */
1678f5d16f6SVladimir Oltean 	for (i = 0; i < ds->num_ports; i++) {
1688f5d16f6SVladimir Oltean 		struct net_device *other_bridge;
1698f5d16f6SVladimir Oltean 
1708f5d16f6SVladimir Oltean 		other_bridge = dsa_to_port(ds, i)->bridge_dev;
1718f5d16f6SVladimir Oltean 		if (!other_bridge)
1728f5d16f6SVladimir Oltean 			continue;
1738f5d16f6SVladimir Oltean 		/* If it's the same bridge, it also has same
1748f5d16f6SVladimir Oltean 		 * vlan_filtering setting => no need to check
1758f5d16f6SVladimir Oltean 		 */
1768f5d16f6SVladimir Oltean 		if (other_bridge == dp->bridge_dev)
1778f5d16f6SVladimir Oltean 			continue;
1788f5d16f6SVladimir Oltean 		if (br_vlan_enabled(other_bridge) != vlan_filtering) {
1798f5d16f6SVladimir Oltean 			dev_err(ds->dev, "VLAN filtering is a global setting\n");
1808f5d16f6SVladimir Oltean 			return false;
1818f5d16f6SVladimir Oltean 		}
1828f5d16f6SVladimir Oltean 	}
1838f5d16f6SVladimir Oltean 	return true;
1848f5d16f6SVladimir Oltean }
1858f5d16f6SVladimir Oltean 
1864d61d304SVivien Didelot int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
1874d61d304SVivien Didelot 			    struct switchdev_trans *trans)
1884d61d304SVivien Didelot {
1894d61d304SVivien Didelot 	struct dsa_switch *ds = dp->ds;
19033162e9aSVladimir Oltean 	int err;
1914d61d304SVivien Didelot 
1924d61d304SVivien Didelot 	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
1934d61d304SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
1944d61d304SVivien Didelot 		return 0;
1954d61d304SVivien Didelot 
1968f5d16f6SVladimir Oltean 	if (!ds->ops->port_vlan_filtering)
1978f5d16f6SVladimir Oltean 		return 0;
1988f5d16f6SVladimir Oltean 
1998f5d16f6SVladimir Oltean 	if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering))
2008f5d16f6SVladimir Oltean 		return -EINVAL;
2018f5d16f6SVladimir Oltean 
202ec9121e7SVladimir Oltean 	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
203ec9121e7SVladimir Oltean 		return 0;
204ec9121e7SVladimir Oltean 
20533162e9aSVladimir Oltean 	err = ds->ops->port_vlan_filtering(ds, dp->index,
2064d61d304SVivien Didelot 					   vlan_filtering);
20733162e9aSVladimir Oltean 	if (err)
20833162e9aSVladimir Oltean 		return err;
2098f5d16f6SVladimir Oltean 
21014574676SVladimir Oltean 	if (ds->vlan_filtering_is_global)
21114574676SVladimir Oltean 		ds->vlan_filtering = vlan_filtering;
21214574676SVladimir Oltean 	else
21333162e9aSVladimir Oltean 		dp->vlan_filtering = vlan_filtering;
2144d61d304SVivien Didelot 	return 0;
2154d61d304SVivien Didelot }
216d87bd94eSVivien Didelot 
217d87bd94eSVivien Didelot int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
218d87bd94eSVivien Didelot 			 struct switchdev_trans *trans)
219d87bd94eSVivien Didelot {
220d87bd94eSVivien Didelot 	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
221d87bd94eSVivien Didelot 	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
2221faabf74SVivien Didelot 	struct dsa_notifier_ageing_time_info info = {
2231faabf74SVivien Didelot 		.ageing_time = ageing_time,
2241faabf74SVivien Didelot 		.trans = trans,
2251faabf74SVivien Didelot 	};
226d87bd94eSVivien Didelot 
2271faabf74SVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
2281faabf74SVivien Didelot 		return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
229d87bd94eSVivien Didelot 
230d87bd94eSVivien Didelot 	dp->ageing_time = ageing_time;
231d87bd94eSVivien Didelot 
2321faabf74SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
233d87bd94eSVivien Didelot }
234d1cffff0SVivien Didelot 
235ea87005aSFlorian Fainelli int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
236ea87005aSFlorian Fainelli 			      struct switchdev_trans *trans)
237ea87005aSFlorian Fainelli {
238ea87005aSFlorian Fainelli 	struct dsa_switch *ds = dp->ds;
239ea87005aSFlorian Fainelli 
240ea87005aSFlorian Fainelli 	if (!ds->ops->port_egress_floods ||
241ea87005aSFlorian Fainelli 	    (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
242ea87005aSFlorian Fainelli 		return -EINVAL;
243ea87005aSFlorian Fainelli 
244ea87005aSFlorian Fainelli 	return 0;
245ea87005aSFlorian Fainelli }
246ea87005aSFlorian Fainelli 
24757652796SRussell King int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
24857652796SRussell King 			  struct switchdev_trans *trans)
24957652796SRussell King {
25057652796SRussell King 	struct dsa_switch *ds = dp->ds;
25157652796SRussell King 	int port = dp->index;
25257652796SRussell King 	int err = 0;
25357652796SRussell King 
25457652796SRussell King 	if (switchdev_trans_ph_prepare(trans))
25557652796SRussell King 		return 0;
25657652796SRussell King 
25757652796SRussell King 	if (ds->ops->port_egress_floods)
25857652796SRussell King 		err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
25957652796SRussell King 						  flags & BR_MCAST_FLOOD);
26057652796SRussell King 
26157652796SRussell King 	return err;
26257652796SRussell King }
26357652796SRussell King 
26408cc83ccSVivien Didelot int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
26508cc83ccSVivien Didelot 		     struct switchdev_trans *trans)
26608cc83ccSVivien Didelot {
26708cc83ccSVivien Didelot 	struct dsa_switch *ds = dp->ds;
26808cc83ccSVivien Didelot 	int port = dp->index;
26908cc83ccSVivien Didelot 
27008cc83ccSVivien Didelot 	if (switchdev_trans_ph_prepare(trans))
27108cc83ccSVivien Didelot 		return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP;
27208cc83ccSVivien Didelot 
27308cc83ccSVivien Didelot 	return ds->ops->port_egress_floods(ds, port, true, mrouter);
27408cc83ccSVivien Didelot }
27508cc83ccSVivien Didelot 
2762acf4e6aSArkadi Sharshevsky int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
2772acf4e6aSArkadi Sharshevsky 		     u16 vid)
278d1cffff0SVivien Didelot {
279685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
280685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
281685fb6a4SVivien Didelot 		.port = dp->index,
2822acf4e6aSArkadi Sharshevsky 		.addr = addr,
2832acf4e6aSArkadi Sharshevsky 		.vid = vid,
284685fb6a4SVivien Didelot 	};
285d1cffff0SVivien Didelot 
286685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
287d1cffff0SVivien Didelot }
288d1cffff0SVivien Didelot 
2892acf4e6aSArkadi Sharshevsky int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
2902acf4e6aSArkadi Sharshevsky 		     u16 vid)
291d1cffff0SVivien Didelot {
292685fb6a4SVivien Didelot 	struct dsa_notifier_fdb_info info = {
293685fb6a4SVivien Didelot 		.sw_index = dp->ds->index,
294685fb6a4SVivien Didelot 		.port = dp->index,
2952acf4e6aSArkadi Sharshevsky 		.addr = addr,
2962acf4e6aSArkadi Sharshevsky 		.vid = vid,
2972acf4e6aSArkadi Sharshevsky 
298685fb6a4SVivien Didelot 	};
299d1cffff0SVivien Didelot 
300685fb6a4SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
301d1cffff0SVivien Didelot }
302d1cffff0SVivien Didelot 
303de40fc5dSVivien Didelot int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
304de40fc5dSVivien Didelot {
305de40fc5dSVivien Didelot 	struct dsa_switch *ds = dp->ds;
306de40fc5dSVivien Didelot 	int port = dp->index;
307de40fc5dSVivien Didelot 
308de40fc5dSVivien Didelot 	if (!ds->ops->port_fdb_dump)
309de40fc5dSVivien Didelot 		return -EOPNOTSUPP;
310de40fc5dSVivien Didelot 
311de40fc5dSVivien Didelot 	return ds->ops->port_fdb_dump(ds, port, cb, data);
312de40fc5dSVivien Didelot }
313de40fc5dSVivien Didelot 
314bb9f6031SAndrew Lunn int dsa_port_mdb_add(const struct dsa_port *dp,
3153a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb,
3163a9afea3SVivien Didelot 		     struct switchdev_trans *trans)
3173a9afea3SVivien Didelot {
3188ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
3198ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
3208ae5bcdcSVivien Didelot 		.port = dp->index,
3218ae5bcdcSVivien Didelot 		.trans = trans,
3228ae5bcdcSVivien Didelot 		.mdb = mdb,
3238ae5bcdcSVivien Didelot 	};
3243a9afea3SVivien Didelot 
3258ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
3263a9afea3SVivien Didelot }
3273a9afea3SVivien Didelot 
328bb9f6031SAndrew Lunn int dsa_port_mdb_del(const struct dsa_port *dp,
3293a9afea3SVivien Didelot 		     const struct switchdev_obj_port_mdb *mdb)
3303a9afea3SVivien Didelot {
3318ae5bcdcSVivien Didelot 	struct dsa_notifier_mdb_info info = {
3328ae5bcdcSVivien Didelot 		.sw_index = dp->ds->index,
3338ae5bcdcSVivien Didelot 		.port = dp->index,
3348ae5bcdcSVivien Didelot 		.mdb = mdb,
3358ae5bcdcSVivien Didelot 	};
3363a9afea3SVivien Didelot 
3378ae5bcdcSVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
3383a9afea3SVivien Didelot }
3393a9afea3SVivien Didelot 
340076e7133SVivien Didelot int dsa_port_vlan_add(struct dsa_port *dp,
341076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan,
342076e7133SVivien Didelot 		      struct switchdev_trans *trans)
343076e7133SVivien Didelot {
344d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
345d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
346d0c627b8SVivien Didelot 		.port = dp->index,
347d0c627b8SVivien Didelot 		.trans = trans,
348d0c627b8SVivien Didelot 		.vlan = vlan,
349d0c627b8SVivien Didelot 	};
350076e7133SVivien Didelot 
351d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
352076e7133SVivien Didelot }
353076e7133SVivien Didelot 
354076e7133SVivien Didelot int dsa_port_vlan_del(struct dsa_port *dp,
355076e7133SVivien Didelot 		      const struct switchdev_obj_port_vlan *vlan)
356076e7133SVivien Didelot {
357d0c627b8SVivien Didelot 	struct dsa_notifier_vlan_info info = {
358d0c627b8SVivien Didelot 		.sw_index = dp->ds->index,
359d0c627b8SVivien Didelot 		.port = dp->index,
360d0c627b8SVivien Didelot 		.vlan = vlan,
361d0c627b8SVivien Didelot 	};
362076e7133SVivien Didelot 
363d0c627b8SVivien Didelot 	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
364076e7133SVivien Didelot }
36557ab1ca2SVivien Didelot 
366314f76d7SVladimir Oltean int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags)
367314f76d7SVladimir Oltean {
368314f76d7SVladimir Oltean 	struct switchdev_obj_port_vlan vlan = {
369314f76d7SVladimir Oltean 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
370314f76d7SVladimir Oltean 		.flags = flags,
371314f76d7SVladimir Oltean 		.vid_begin = vid,
372314f76d7SVladimir Oltean 		.vid_end = vid,
373314f76d7SVladimir Oltean 	};
374314f76d7SVladimir Oltean 	struct switchdev_trans trans;
375314f76d7SVladimir Oltean 	int err;
376314f76d7SVladimir Oltean 
377314f76d7SVladimir Oltean 	trans.ph_prepare = true;
378314f76d7SVladimir Oltean 	err = dsa_port_vlan_add(dp, &vlan, &trans);
379cf360866SVivien Didelot 	if (err)
380cf360866SVivien Didelot 		return err;
381314f76d7SVladimir Oltean 
382314f76d7SVladimir Oltean 	trans.ph_prepare = false;
383314f76d7SVladimir Oltean 	return dsa_port_vlan_add(dp, &vlan, &trans);
384314f76d7SVladimir Oltean }
385146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_add);
386314f76d7SVladimir Oltean 
387314f76d7SVladimir Oltean int dsa_port_vid_del(struct dsa_port *dp, u16 vid)
388314f76d7SVladimir Oltean {
389314f76d7SVladimir Oltean 	struct switchdev_obj_port_vlan vlan = {
390314f76d7SVladimir Oltean 		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
391314f76d7SVladimir Oltean 		.vid_begin = vid,
392314f76d7SVladimir Oltean 		.vid_end = vid,
393314f76d7SVladimir Oltean 	};
394314f76d7SVladimir Oltean 
395314f76d7SVladimir Oltean 	return dsa_port_vlan_del(dp, &vlan);
396314f76d7SVladimir Oltean }
397146c1bedSVladimir Oltean EXPORT_SYMBOL(dsa_port_vid_del);
398314f76d7SVladimir Oltean 
3996207a78cSFlorian Fainelli static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
4006207a78cSFlorian Fainelli {
4016207a78cSFlorian Fainelli 	struct device_node *phy_dn;
4026207a78cSFlorian Fainelli 	struct phy_device *phydev;
4036207a78cSFlorian Fainelli 
4046207a78cSFlorian Fainelli 	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
4056207a78cSFlorian Fainelli 	if (!phy_dn)
4066207a78cSFlorian Fainelli 		return NULL;
4076207a78cSFlorian Fainelli 
4086207a78cSFlorian Fainelli 	phydev = of_phy_find_device(phy_dn);
4096207a78cSFlorian Fainelli 	if (!phydev) {
4106207a78cSFlorian Fainelli 		of_node_put(phy_dn);
4116207a78cSFlorian Fainelli 		return ERR_PTR(-EPROBE_DEFER);
4126207a78cSFlorian Fainelli 	}
4136207a78cSFlorian Fainelli 
4149919a363SWen Yang 	of_node_put(phy_dn);
4156207a78cSFlorian Fainelli 	return phydev;
4166207a78cSFlorian Fainelli }
4176207a78cSFlorian Fainelli 
4188ae67496SFlorian Fainelli static void dsa_port_phylink_validate(struct phylink_config *config,
41977373d49SIoana Ciornei 				      unsigned long *supported,
42077373d49SIoana Ciornei 				      struct phylink_link_state *state)
42177373d49SIoana Ciornei {
42277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
42377373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
42477373d49SIoana Ciornei 
42577373d49SIoana Ciornei 	if (!ds->ops->phylink_validate)
42677373d49SIoana Ciornei 		return;
42777373d49SIoana Ciornei 
42877373d49SIoana Ciornei 	ds->ops->phylink_validate(ds, dp->index, supported, state);
42977373d49SIoana Ciornei }
43077373d49SIoana Ciornei 
4318ae67496SFlorian Fainelli static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
43277373d49SIoana Ciornei 					       struct phylink_link_state *state)
43377373d49SIoana Ciornei {
43477373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
43577373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
43677373d49SIoana Ciornei 
437d46b7e4fSRussell King 	/* Only called for inband modes */
438d46b7e4fSRussell King 	if (!ds->ops->phylink_mac_link_state) {
439d46b7e4fSRussell King 		state->link = 0;
440d46b7e4fSRussell King 		return;
44177373d49SIoana Ciornei 	}
442d46b7e4fSRussell King 
443d46b7e4fSRussell King 	if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0)
444d46b7e4fSRussell King 		state->link = 0;
445d46b7e4fSRussell King }
44677373d49SIoana Ciornei 
4478ae67496SFlorian Fainelli static void dsa_port_phylink_mac_config(struct phylink_config *config,
44877373d49SIoana Ciornei 					unsigned int mode,
44977373d49SIoana Ciornei 					const struct phylink_link_state *state)
45077373d49SIoana Ciornei {
45177373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
45277373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
45377373d49SIoana Ciornei 
45477373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_config)
45577373d49SIoana Ciornei 		return;
45677373d49SIoana Ciornei 
45777373d49SIoana Ciornei 	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
45877373d49SIoana Ciornei }
45977373d49SIoana Ciornei 
4608ae67496SFlorian Fainelli static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
46177373d49SIoana Ciornei {
46277373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
46377373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
46477373d49SIoana Ciornei 
46577373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_an_restart)
46677373d49SIoana Ciornei 		return;
46777373d49SIoana Ciornei 
46877373d49SIoana Ciornei 	ds->ops->phylink_mac_an_restart(ds, dp->index);
46977373d49SIoana Ciornei }
47077373d49SIoana Ciornei 
4718ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
47277373d49SIoana Ciornei 					   unsigned int mode,
47377373d49SIoana Ciornei 					   phy_interface_t interface)
47477373d49SIoana Ciornei {
47577373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
4760e279218SIoana Ciornei 	struct phy_device *phydev = NULL;
47777373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
47877373d49SIoana Ciornei 
4790e279218SIoana Ciornei 	if (dsa_is_user_port(ds, dp->index))
4800e279218SIoana Ciornei 		phydev = dp->slave->phydev;
4810e279218SIoana Ciornei 
48277373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_down) {
4830e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
4840e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
48577373d49SIoana Ciornei 		return;
48677373d49SIoana Ciornei 	}
48777373d49SIoana Ciornei 
48877373d49SIoana Ciornei 	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
48977373d49SIoana Ciornei }
49077373d49SIoana Ciornei 
4918ae67496SFlorian Fainelli static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
49291a208f2SRussell King 					 struct phy_device *phydev,
49377373d49SIoana Ciornei 					 unsigned int mode,
49477373d49SIoana Ciornei 					 phy_interface_t interface,
49591a208f2SRussell King 					 int speed, int duplex,
49691a208f2SRussell King 					 bool tx_pause, bool rx_pause)
49777373d49SIoana Ciornei {
49877373d49SIoana Ciornei 	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
49977373d49SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
50077373d49SIoana Ciornei 
50177373d49SIoana Ciornei 	if (!ds->ops->phylink_mac_link_up) {
5020e279218SIoana Ciornei 		if (ds->ops->adjust_link && phydev)
5030e279218SIoana Ciornei 			ds->ops->adjust_link(ds, dp->index, phydev);
50477373d49SIoana Ciornei 		return;
50577373d49SIoana Ciornei 	}
50677373d49SIoana Ciornei 
50777373d49SIoana Ciornei 	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
50877373d49SIoana Ciornei }
50977373d49SIoana Ciornei 
51077373d49SIoana Ciornei const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
51177373d49SIoana Ciornei 	.validate = dsa_port_phylink_validate,
512d46b7e4fSRussell King 	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
51377373d49SIoana Ciornei 	.mac_config = dsa_port_phylink_mac_config,
51477373d49SIoana Ciornei 	.mac_an_restart = dsa_port_phylink_mac_an_restart,
51577373d49SIoana Ciornei 	.mac_link_down = dsa_port_phylink_mac_link_down,
51677373d49SIoana Ciornei 	.mac_link_up = dsa_port_phylink_mac_link_up,
51777373d49SIoana Ciornei };
51877373d49SIoana Ciornei 
51933615367SSebastian Reichel static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
52033615367SSebastian Reichel {
52133615367SSebastian Reichel 	struct dsa_switch *ds = dp->ds;
52233615367SSebastian Reichel 	struct phy_device *phydev;
52333615367SSebastian Reichel 	int port = dp->index;
52433615367SSebastian Reichel 	int err = 0;
52533615367SSebastian Reichel 
5266207a78cSFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
5276207a78cSFlorian Fainelli 	if (!phydev)
52833615367SSebastian Reichel 		return 0;
52933615367SSebastian Reichel 
5306207a78cSFlorian Fainelli 	if (IS_ERR(phydev))
5316207a78cSFlorian Fainelli 		return PTR_ERR(phydev);
53233615367SSebastian Reichel 
53333615367SSebastian Reichel 	if (enable) {
53433615367SSebastian Reichel 		err = genphy_resume(phydev);
53533615367SSebastian Reichel 		if (err < 0)
53633615367SSebastian Reichel 			goto err_put_dev;
53733615367SSebastian Reichel 
53833615367SSebastian Reichel 		err = genphy_read_status(phydev);
53933615367SSebastian Reichel 		if (err < 0)
54033615367SSebastian Reichel 			goto err_put_dev;
54133615367SSebastian Reichel 	} else {
54233615367SSebastian Reichel 		err = genphy_suspend(phydev);
54333615367SSebastian Reichel 		if (err < 0)
54433615367SSebastian Reichel 			goto err_put_dev;
54533615367SSebastian Reichel 	}
54633615367SSebastian Reichel 
54733615367SSebastian Reichel 	if (ds->ops->adjust_link)
54833615367SSebastian Reichel 		ds->ops->adjust_link(ds, port, phydev);
54933615367SSebastian Reichel 
55033615367SSebastian Reichel 	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
55133615367SSebastian Reichel 
55233615367SSebastian Reichel err_put_dev:
55333615367SSebastian Reichel 	put_device(&phydev->mdio.dev);
55433615367SSebastian Reichel 	return err;
55533615367SSebastian Reichel }
55633615367SSebastian Reichel 
55733615367SSebastian Reichel static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
55857ab1ca2SVivien Didelot {
55957ab1ca2SVivien Didelot 	struct device_node *dn = dp->dn;
56057ab1ca2SVivien Didelot 	struct dsa_switch *ds = dp->ds;
56157ab1ca2SVivien Didelot 	struct phy_device *phydev;
56257ab1ca2SVivien Didelot 	int port = dp->index;
5630c65b2b9SAndrew Lunn 	phy_interface_t mode;
56457ab1ca2SVivien Didelot 	int err;
56557ab1ca2SVivien Didelot 
56657ab1ca2SVivien Didelot 	err = of_phy_register_fixed_link(dn);
56757ab1ca2SVivien Didelot 	if (err) {
56857ab1ca2SVivien Didelot 		dev_err(ds->dev,
56957ab1ca2SVivien Didelot 			"failed to register the fixed PHY of port %d\n",
57057ab1ca2SVivien Didelot 			port);
57157ab1ca2SVivien Didelot 		return err;
57257ab1ca2SVivien Didelot 	}
57357ab1ca2SVivien Didelot 
57457ab1ca2SVivien Didelot 	phydev = of_phy_find_device(dn);
57557ab1ca2SVivien Didelot 
5760c65b2b9SAndrew Lunn 	err = of_get_phy_mode(dn, &mode);
5770c65b2b9SAndrew Lunn 	if (err)
57857ab1ca2SVivien Didelot 		mode = PHY_INTERFACE_MODE_NA;
57957ab1ca2SVivien Didelot 	phydev->interface = mode;
58057ab1ca2SVivien Didelot 
58157ab1ca2SVivien Didelot 	genphy_read_status(phydev);
58257ab1ca2SVivien Didelot 
58357ab1ca2SVivien Didelot 	if (ds->ops->adjust_link)
58457ab1ca2SVivien Didelot 		ds->ops->adjust_link(ds, port, phydev);
58557ab1ca2SVivien Didelot 
58657ab1ca2SVivien Didelot 	put_device(&phydev->mdio.dev);
58757ab1ca2SVivien Didelot 
58857ab1ca2SVivien Didelot 	return 0;
58957ab1ca2SVivien Didelot }
59057ab1ca2SVivien Didelot 
5910e279218SIoana Ciornei static int dsa_port_phylink_register(struct dsa_port *dp)
5920e279218SIoana Ciornei {
5930e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
5940e279218SIoana Ciornei 	struct device_node *port_dn = dp->dn;
5950c65b2b9SAndrew Lunn 	phy_interface_t mode;
5960c65b2b9SAndrew Lunn 	int err;
5970e279218SIoana Ciornei 
5980c65b2b9SAndrew Lunn 	err = of_get_phy_mode(port_dn, &mode);
5990c65b2b9SAndrew Lunn 	if (err)
6000e279218SIoana Ciornei 		mode = PHY_INTERFACE_MODE_NA;
6010e279218SIoana Ciornei 
6020e279218SIoana Ciornei 	dp->pl_config.dev = ds->dev;
6030e279218SIoana Ciornei 	dp->pl_config.type = PHYLINK_DEV;
604787cac3fSVladimir Oltean 	dp->pl_config.pcs_poll = ds->pcs_poll;
6050e279218SIoana Ciornei 
6060e279218SIoana Ciornei 	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
6070e279218SIoana Ciornei 				mode, &dsa_port_phylink_mac_ops);
6080e279218SIoana Ciornei 	if (IS_ERR(dp->pl)) {
6090e279218SIoana Ciornei 		pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl));
6100e279218SIoana Ciornei 		return PTR_ERR(dp->pl);
6110e279218SIoana Ciornei 	}
6120e279218SIoana Ciornei 
6130e279218SIoana Ciornei 	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
6142131fba5SFlorian Fainelli 	if (err && err != -ENODEV) {
6150e279218SIoana Ciornei 		pr_err("could not attach to PHY: %d\n", err);
6160e279218SIoana Ciornei 		goto err_phy_connect;
6170e279218SIoana Ciornei 	}
6180e279218SIoana Ciornei 
6190e279218SIoana Ciornei 	rtnl_lock();
6200e279218SIoana Ciornei 	phylink_start(dp->pl);
6210e279218SIoana Ciornei 	rtnl_unlock();
6220e279218SIoana Ciornei 
6230e279218SIoana Ciornei 	return 0;
6240e279218SIoana Ciornei 
6250e279218SIoana Ciornei err_phy_connect:
6260e279218SIoana Ciornei 	phylink_destroy(dp->pl);
6270e279218SIoana Ciornei 	return err;
6280e279218SIoana Ciornei }
6290e279218SIoana Ciornei 
63033615367SSebastian Reichel int dsa_port_link_register_of(struct dsa_port *dp)
63157ab1ca2SVivien Didelot {
6320e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
6330e279218SIoana Ciornei 
6340e279218SIoana Ciornei 	if (!ds->ops->adjust_link)
6350e279218SIoana Ciornei 		return dsa_port_phylink_register(dp);
6360e279218SIoana Ciornei 
6370e279218SIoana Ciornei 	dev_warn(ds->dev,
6380e279218SIoana Ciornei 		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n");
6390e279218SIoana Ciornei 
64033615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
64133615367SSebastian Reichel 		return dsa_port_fixed_link_register_of(dp);
64233615367SSebastian Reichel 	else
64333615367SSebastian Reichel 		return dsa_port_setup_phy_of(dp, true);
64433615367SSebastian Reichel }
64557ab1ca2SVivien Didelot 
64633615367SSebastian Reichel void dsa_port_link_unregister_of(struct dsa_port *dp)
64733615367SSebastian Reichel {
6480e279218SIoana Ciornei 	struct dsa_switch *ds = dp->ds;
6490e279218SIoana Ciornei 
6500e279218SIoana Ciornei 	if (!ds->ops->adjust_link) {
6510e279218SIoana Ciornei 		rtnl_lock();
6520e279218SIoana Ciornei 		phylink_disconnect_phy(dp->pl);
6530e279218SIoana Ciornei 		rtnl_unlock();
6540e279218SIoana Ciornei 		phylink_destroy(dp->pl);
6550e279218SIoana Ciornei 		return;
6560e279218SIoana Ciornei 	}
6570e279218SIoana Ciornei 
65833615367SSebastian Reichel 	if (of_phy_is_fixed_link(dp->dn))
65933615367SSebastian Reichel 		of_phy_deregister_fixed_link(dp->dn);
66033615367SSebastian Reichel 	else
66133615367SSebastian Reichel 		dsa_port_setup_phy_of(dp, false);
66257ab1ca2SVivien Didelot }
663cf963573SFlorian Fainelli 
664cf963573SFlorian Fainelli int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
665cf963573SFlorian Fainelli {
666cf963573SFlorian Fainelli 	struct phy_device *phydev;
667cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
668cf963573SFlorian Fainelli 
669cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
670cf963573SFlorian Fainelli 		return ret;
671cf963573SFlorian Fainelli 
672cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
673cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
674cf963573SFlorian Fainelli 		return ret;
675cf963573SFlorian Fainelli 
676cf963573SFlorian Fainelli 	ret = phy_ethtool_get_strings(phydev, data);
677cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
678cf963573SFlorian Fainelli 
679cf963573SFlorian Fainelli 	return ret;
680cf963573SFlorian Fainelli }
681cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
682cf963573SFlorian Fainelli 
683cf963573SFlorian Fainelli int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
684cf963573SFlorian Fainelli {
685cf963573SFlorian Fainelli 	struct phy_device *phydev;
686cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
687cf963573SFlorian Fainelli 
688cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
689cf963573SFlorian Fainelli 		return ret;
690cf963573SFlorian Fainelli 
691cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
692cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
693cf963573SFlorian Fainelli 		return ret;
694cf963573SFlorian Fainelli 
695cf963573SFlorian Fainelli 	ret = phy_ethtool_get_stats(phydev, NULL, data);
696cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
697cf963573SFlorian Fainelli 
698cf963573SFlorian Fainelli 	return ret;
699cf963573SFlorian Fainelli }
700cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
701cf963573SFlorian Fainelli 
702cf963573SFlorian Fainelli int dsa_port_get_phy_sset_count(struct dsa_port *dp)
703cf963573SFlorian Fainelli {
704cf963573SFlorian Fainelli 	struct phy_device *phydev;
705cf963573SFlorian Fainelli 	int ret = -EOPNOTSUPP;
706cf963573SFlorian Fainelli 
707cf963573SFlorian Fainelli 	if (of_phy_is_fixed_link(dp->dn))
708cf963573SFlorian Fainelli 		return ret;
709cf963573SFlorian Fainelli 
710cf963573SFlorian Fainelli 	phydev = dsa_port_get_phy_device(dp);
711cf963573SFlorian Fainelli 	if (IS_ERR_OR_NULL(phydev))
712cf963573SFlorian Fainelli 		return ret;
713cf963573SFlorian Fainelli 
714cf963573SFlorian Fainelli 	ret = phy_ethtool_get_sset_count(phydev);
715cf963573SFlorian Fainelli 	put_device(&phydev->mdio.dev);
716cf963573SFlorian Fainelli 
717cf963573SFlorian Fainelli 	return ret;
718cf963573SFlorian Fainelli }
719cf963573SFlorian Fainelli EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
720