Blame view

net/dsa/port.c 18.8 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
a40c175b4   Vivien Didelot   net: dsa: move po...
2
3
4
5
6
  /*
   * Handling of a single switch port
   *
   * Copyright (c) 2017 Savoir-faire Linux Inc.
   *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
a40c175b4   Vivien Didelot   net: dsa: move po...
7
8
9
   */
  
  #include <linux/if_bridge.h>
cfbed329b   Vivien Didelot   net: dsa: move br...
10
  #include <linux/notifier.h>
57ab1ca21   Vivien Didelot   net: dsa: move fi...
11
12
  #include <linux/of_mdio.h>
  #include <linux/of_net.h>
a40c175b4   Vivien Didelot   net: dsa: move po...
13
14
  
  #include "dsa_priv.h"
f66a6a69f   Vladimir Oltean   net: dsa: permit ...
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  static int dsa_broadcast(unsigned long e, void *v)
  {
  	struct dsa_switch_tree *dst;
  	int err = 0;
  
  	list_for_each_entry(dst, &dsa_tree_list, list) {
  		struct raw_notifier_head *nh = &dst->nh;
  
  		err = raw_notifier_call_chain(nh, e, v);
  		err = notifier_to_errno(err);
  		if (err)
  			break;
  	}
  
  	return err;
  }
bb9f60317   Andrew Lunn   net: dsa: add mor...
31
  static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
cfbed329b   Vivien Didelot   net: dsa: move br...
32
33
34
35
36
37
38
39
  {
  	struct raw_notifier_head *nh = &dp->ds->dst->nh;
  	int err;
  
  	err = raw_notifier_call_chain(nh, e, v);
  
  	return notifier_to_errno(err);
  }
a40c175b4   Vivien Didelot   net: dsa: move po...
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  int dsa_port_set_state(struct dsa_port *dp, u8 state,
  		       struct switchdev_trans *trans)
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
  
  	if (switchdev_trans_ph_prepare(trans))
  		return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
  
  	if (ds->ops->port_stp_state_set)
  		ds->ops->port_stp_state_set(ds, port, state);
  
  	if (ds->ops->port_fast_age) {
  		/* Fast age FDB entries or flush appropriate forwarding database
  		 * for the given port, if we are moving it from Learning or
  		 * Forwarding state, to Disabled or Blocking or Listening state.
  		 */
  
  		if ((dp->stp_state == BR_STATE_LEARNING ||
  		     dp->stp_state == BR_STATE_FORWARDING) &&
  		    (state == BR_STATE_DISABLED ||
  		     state == BR_STATE_BLOCKING ||
  		     state == BR_STATE_LISTENING))
  			ds->ops->port_fast_age(ds, port);
  	}
  
  	dp->stp_state = state;
  
  	return 0;
  }
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
70
  static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
a40c175b4   Vivien Didelot   net: dsa: move po...
71
72
73
74
75
76
77
78
  {
  	int err;
  
  	err = dsa_port_set_state(dp, state, NULL);
  	if (err)
  		pr_err("DSA: failed to set STP state %u (%d)
  ", state, err);
  }
cfbed329b   Vivien Didelot   net: dsa: move br...
79

8640f8dc6   Russell King   net: dsa: fix phy...
80
  int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
81
  {
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
82
83
84
85
86
87
88
89
90
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
  	int err;
  
  	if (ds->ops->port_enable) {
  		err = ds->ops->port_enable(ds, port, phy);
  		if (err)
  			return err;
  	}
9c2054a5c   Russell King   net: dsa: fix uni...
91
92
  	if (!dp->bridge_dev)
  		dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
93

8640f8dc6   Russell King   net: dsa: fix phy...
94
95
  	if (dp->pl)
  		phylink_start(dp->pl);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
96
97
  	return 0;
  }
8640f8dc6   Russell King   net: dsa: fix phy...
98
99
100
101
102
103
104
105
106
107
108
109
  int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
  {
  	int err;
  
  	rtnl_lock();
  	err = dsa_port_enable_rt(dp, phy);
  	rtnl_unlock();
  
  	return err;
  }
  
  void dsa_port_disable_rt(struct dsa_port *dp)
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
110
111
112
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
8640f8dc6   Russell King   net: dsa: fix phy...
113
114
  	if (dp->pl)
  		phylink_stop(dp->pl);
9c2054a5c   Russell King   net: dsa: fix uni...
115
116
  	if (!dp->bridge_dev)
  		dsa_port_set_state_now(dp, BR_STATE_DISABLED);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
117
118
  
  	if (ds->ops->port_disable)
75104db0c   Andrew Lunn   dsa: Remove phyde...
119
  		ds->ops->port_disable(ds, port);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
120
  }
8640f8dc6   Russell King   net: dsa: fix phy...
121
122
123
124
125
126
  void dsa_port_disable(struct dsa_port *dp)
  {
  	rtnl_lock();
  	dsa_port_disable_rt(dp);
  	rtnl_unlock();
  }
cfbed329b   Vivien Didelot   net: dsa: move br...
127
128
129
  int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
  {
  	struct dsa_notifier_bridge_info info = {
f66a6a69f   Vladimir Oltean   net: dsa: permit ...
130
  		.tree_index = dp->ds->dst->index,
cfbed329b   Vivien Didelot   net: dsa: move br...
131
132
133
134
135
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.br = br,
  	};
  	int err;
c13880634   Russell King   net: dsa: enable ...
136
137
138
139
140
141
142
  	/* Set the flooding mode before joining the port in the switch */
  	err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
  	if (err)
  		return err;
  
  	/* Here the interface is already bridged. Reflect the current
  	 * configuration so that drivers can program their chips accordingly.
cfbed329b   Vivien Didelot   net: dsa: move br...
143
144
  	 */
  	dp->bridge_dev = br;
f66a6a69f   Vladimir Oltean   net: dsa: permit ...
145
  	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
cfbed329b   Vivien Didelot   net: dsa: move br...
146
147
  
  	/* The bridging is rolled back on error */
c13880634   Russell King   net: dsa: enable ...
148
149
  	if (err) {
  		dsa_port_bridge_flags(dp, 0, NULL);
cfbed329b   Vivien Didelot   net: dsa: move br...
150
  		dp->bridge_dev = NULL;
c13880634   Russell King   net: dsa: enable ...
151
  	}
cfbed329b   Vivien Didelot   net: dsa: move br...
152
153
154
155
156
157
158
  
  	return err;
  }
  
  void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
  {
  	struct dsa_notifier_bridge_info info = {
f66a6a69f   Vladimir Oltean   net: dsa: permit ...
159
  		.tree_index = dp->ds->dst->index,
cfbed329b   Vivien Didelot   net: dsa: move br...
160
161
162
163
164
165
166
167
168
169
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.br = br,
  	};
  	int err;
  
  	/* Here the port is already unbridged. Reflect the current configuration
  	 * so that drivers can program their chips accordingly.
  	 */
  	dp->bridge_dev = NULL;
f66a6a69f   Vladimir Oltean   net: dsa: permit ...
170
  	err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
cfbed329b   Vivien Didelot   net: dsa: move br...
171
172
173
  	if (err)
  		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE
  ");
c13880634   Russell King   net: dsa: enable ...
174
175
  	/* Port is leaving the bridge, disable flooding */
  	dsa_port_bridge_flags(dp, 0, NULL);
cfbed329b   Vivien Didelot   net: dsa: move br...
176
177
178
179
180
  	/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
  	 * so allow it to be in BR_STATE_FORWARDING to be kept functional
  	 */
  	dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
  }
4d61d3043   Vivien Didelot   net: dsa: move VL...
181

adb256eb1   Vladimir Oltean   net: dsa: allow 8...
182
  /* Must be called under rcu_read_lock() */
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
183
184
185
186
  static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
  					      bool vlan_filtering)
  {
  	struct dsa_switch *ds = dp->ds;
adb256eb1   Vladimir Oltean   net: dsa: allow 8...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
  	int err, i;
  
  	/* VLAN awareness was off, so the question is "can we turn it on".
  	 * We may have had 8021q uppers, those need to go. Make sure we don't
  	 * enter an inconsistent state: deny changing the VLAN awareness state
  	 * as long as we have 8021q uppers.
  	 */
  	if (vlan_filtering && dsa_is_user_port(ds, dp->index)) {
  		struct net_device *upper_dev, *slave = dp->slave;
  		struct net_device *br = dp->bridge_dev;
  		struct list_head *iter;
  
  		netdev_for_each_upper_dev_rcu(slave, upper_dev, iter) {
  			struct bridge_vlan_info br_info;
  			u16 vid;
  
  			if (!is_vlan_dev(upper_dev))
  				continue;
  
  			vid = vlan_dev_vlan_id(upper_dev);
  
  			/* br_vlan_get_info() returns -EINVAL or -ENOENT if the
  			 * device, respectively the VID is not found, returning
  			 * 0 means success, which is a failure for us here.
  			 */
  			err = br_vlan_get_info(br, vid, &br_info);
  			if (err == 0) {
  				dev_err(ds->dev, "Must remove upper %s first
  ",
  					upper_dev->name);
  				return false;
  			}
  		}
  	}
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  
  	if (!ds->vlan_filtering_is_global)
  		return true;
  
  	/* For cases where enabling/disabling VLAN awareness is global to the
  	 * switch, we need to handle the case where multiple bridges span
  	 * different ports of the same switch device and one of them has a
  	 * different setting than what is being requested.
  	 */
  	for (i = 0; i < ds->num_ports; i++) {
  		struct net_device *other_bridge;
  
  		other_bridge = dsa_to_port(ds, i)->bridge_dev;
  		if (!other_bridge)
  			continue;
  		/* If it's the same bridge, it also has same
  		 * vlan_filtering setting => no need to check
  		 */
  		if (other_bridge == dp->bridge_dev)
  			continue;
  		if (br_vlan_enabled(other_bridge) != vlan_filtering) {
  			dev_err(ds->dev, "VLAN filtering is a global setting
  ");
  			return false;
  		}
  	}
  	return true;
  }
4d61d3043   Vivien Didelot   net: dsa: move VL...
249
250
251
252
  int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
  			    struct switchdev_trans *trans)
  {
  	struct dsa_switch *ds = dp->ds;
33162e9a0   Vladimir Oltean   net: dsa: Store v...
253
  	int err;
4d61d3043   Vivien Didelot   net: dsa: move VL...
254

707ec383b   Vladimir Oltean   net: dsa: refuse ...
255
  	if (switchdev_trans_ph_prepare(trans)) {
adb256eb1   Vladimir Oltean   net: dsa: allow 8...
256
  		bool apply;
707ec383b   Vladimir Oltean   net: dsa: refuse ...
257
258
  		if (!ds->ops->port_vlan_filtering)
  			return -EOPNOTSUPP;
4d61d3043   Vivien Didelot   net: dsa: move VL...
259

adb256eb1   Vladimir Oltean   net: dsa: allow 8...
260
261
262
263
264
265
266
267
  		/* We are called from dsa_slave_switchdev_blocking_event(),
  		 * which is not under rcu_read_lock(), unlike
  		 * dsa_slave_switchdev_event().
  		 */
  		rcu_read_lock();
  		apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering);
  		rcu_read_unlock();
  		if (!apply)
707ec383b   Vladimir Oltean   net: dsa: refuse ...
268
  			return -EINVAL;
707ec383b   Vladimir Oltean   net: dsa: refuse ...
269
  	}
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
270

ec9121e7d   Vladimir Oltean   net: dsa: Skip ca...
271
272
  	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
  		return 0;
2e554a7a5   Vladimir Oltean   net: dsa: propaga...
273
274
  	err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
  					   trans);
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
275
276
  	if (err)
  		return err;
2e554a7a5   Vladimir Oltean   net: dsa: propaga...
277
278
279
280
281
282
  	if (switchdev_trans_ph_commit(trans)) {
  		if (ds->vlan_filtering_is_global)
  			ds->vlan_filtering = vlan_filtering;
  		else
  			dp->vlan_filtering = vlan_filtering;
  	}
4d61d3043   Vivien Didelot   net: dsa: move VL...
283
284
  	return 0;
  }
d87bd94e1   Vivien Didelot   net: dsa: move ag...
285

54a0ed0df   Russell King   net: dsa: provide...
286
287
288
289
290
291
292
293
294
295
296
297
298
  /* This enforces legacy behavior for switch drivers which assume they can't
   * receive VLAN configuration when enslaved to a bridge with vlan_filtering=0
   */
  bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
  {
  	struct dsa_switch *ds = dp->ds;
  
  	if (!dp->bridge_dev)
  		return false;
  
  	return (!ds->configure_vlan_while_not_filtering &&
  		!br_vlan_enabled(dp->bridge_dev));
  }
d87bd94e1   Vivien Didelot   net: dsa: move ag...
299
300
301
302
303
  int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
  			 struct switchdev_trans *trans)
  {
  	unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
  	unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
1faabf744   Vivien Didelot   net: dsa: add not...
304
305
  	struct dsa_notifier_ageing_time_info info = {
  		.ageing_time = ageing_time,
1faabf744   Vivien Didelot   net: dsa: add not...
306
307
  		.trans = trans,
  	};
d87bd94e1   Vivien Didelot   net: dsa: move ag...
308

1faabf744   Vivien Didelot   net: dsa: add not...
309
310
  	if (switchdev_trans_ph_prepare(trans))
  		return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
d87bd94e1   Vivien Didelot   net: dsa: move ag...
311

d87bd94e1   Vivien Didelot   net: dsa: move ag...
312
  	dp->ageing_time = ageing_time;
d87bd94e1   Vivien Didelot   net: dsa: move ag...
313

1faabf744   Vivien Didelot   net: dsa: add not...
314
  	return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
d87bd94e1   Vivien Didelot   net: dsa: move ag...
315
  }
d1cffff00   Vivien Didelot   net: dsa: move FD...
316

ea87005a0   Florian Fainelli   net: dsa: Add set...
317
318
319
320
321
322
323
324
325
326
327
  int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
  			      struct switchdev_trans *trans)
  {
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->port_egress_floods ||
  	    (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
  		return -EINVAL;
  
  	return 0;
  }
57652796a   Russell King   net: dsa: add sup...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
  int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
  			  struct switchdev_trans *trans)
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
  	int err = 0;
  
  	if (switchdev_trans_ph_prepare(trans))
  		return 0;
  
  	if (ds->ops->port_egress_floods)
  		err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
  						  flags & BR_MCAST_FLOOD);
  
  	return err;
  }
08cc83cc7   Vivien Didelot   net: dsa: add sup...
344
345
346
347
348
349
350
351
352
353
354
  int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
  		     struct switchdev_trans *trans)
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
  
  	if (switchdev_trans_ph_prepare(trans))
  		return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP;
  
  	return ds->ops->port_egress_floods(ds, port, true, mrouter);
  }
bfcb81320   Vladimir Oltean   net: dsa: configu...
355
356
357
358
359
360
361
362
363
364
365
366
  int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
  			bool propagate_upstream)
  {
  	struct dsa_notifier_mtu_info info = {
  		.sw_index = dp->ds->index,
  		.propagate_upstream = propagate_upstream,
  		.port = dp->index,
  		.mtu = new_mtu,
  	};
  
  	return dsa_port_notify(dp, DSA_NOTIFIER_MTU, &info);
  }
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
367
368
  int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
  		     u16 vid)
d1cffff00   Vivien Didelot   net: dsa: move FD...
369
  {
685fb6a40   Vivien Didelot   net: dsa: add FDB...
370
371
372
  	struct dsa_notifier_fdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
373
374
  		.addr = addr,
  		.vid = vid,
685fb6a40   Vivien Didelot   net: dsa: add FDB...
375
  	};
d1cffff00   Vivien Didelot   net: dsa: move FD...
376

685fb6a40   Vivien Didelot   net: dsa: add FDB...
377
  	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
d1cffff00   Vivien Didelot   net: dsa: move FD...
378
  }
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
379
380
  int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
  		     u16 vid)
d1cffff00   Vivien Didelot   net: dsa: move FD...
381
  {
685fb6a40   Vivien Didelot   net: dsa: add FDB...
382
383
384
  	struct dsa_notifier_fdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
385
386
  		.addr = addr,
  		.vid = vid,
685fb6a40   Vivien Didelot   net: dsa: add FDB...
387
  	};
d1cffff00   Vivien Didelot   net: dsa: move FD...
388

685fb6a40   Vivien Didelot   net: dsa: add FDB...
389
  	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
d1cffff00   Vivien Didelot   net: dsa: move FD...
390
  }
de40fc5d2   Vivien Didelot   net: dsa: add por...
391
392
393
394
395
396
397
398
399
400
  int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
  
  	if (!ds->ops->port_fdb_dump)
  		return -EOPNOTSUPP;
  
  	return ds->ops->port_fdb_dump(ds, port, cb, data);
  }
bb9f60317   Andrew Lunn   net: dsa: add mor...
401
  int dsa_port_mdb_add(const struct dsa_port *dp,
3a9afea37   Vivien Didelot   net: dsa: move MD...
402
403
404
  		     const struct switchdev_obj_port_mdb *mdb,
  		     struct switchdev_trans *trans)
  {
8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
405
406
407
408
409
410
  	struct dsa_notifier_mdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.trans = trans,
  		.mdb = mdb,
  	};
3a9afea37   Vivien Didelot   net: dsa: move MD...
411

8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
412
  	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
3a9afea37   Vivien Didelot   net: dsa: move MD...
413
  }
bb9f60317   Andrew Lunn   net: dsa: add mor...
414
  int dsa_port_mdb_del(const struct dsa_port *dp,
3a9afea37   Vivien Didelot   net: dsa: move MD...
415
416
  		     const struct switchdev_obj_port_mdb *mdb)
  {
8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
417
418
419
420
421
  	struct dsa_notifier_mdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.mdb = mdb,
  	};
3a9afea37   Vivien Didelot   net: dsa: move MD...
422

8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
423
  	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
3a9afea37   Vivien Didelot   net: dsa: move MD...
424
  }
076e71336   Vivien Didelot   net: dsa: move VL...
425
426
427
428
  int dsa_port_vlan_add(struct dsa_port *dp,
  		      const struct switchdev_obj_port_vlan *vlan,
  		      struct switchdev_trans *trans)
  {
d0c627b87   Vivien Didelot   net: dsa: add VLA...
429
430
431
432
433
434
  	struct dsa_notifier_vlan_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.trans = trans,
  		.vlan = vlan,
  	};
076e71336   Vivien Didelot   net: dsa: move VL...
435

c5335d737   Vivien Didelot   net: dsa: check b...
436
  	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
076e71336   Vivien Didelot   net: dsa: move VL...
437
438
439
440
441
  }
  
  int dsa_port_vlan_del(struct dsa_port *dp,
  		      const struct switchdev_obj_port_vlan *vlan)
  {
d0c627b87   Vivien Didelot   net: dsa: add VLA...
442
443
444
445
446
  	struct dsa_notifier_vlan_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.vlan = vlan,
  	};
076e71336   Vivien Didelot   net: dsa: move VL...
447

c5335d737   Vivien Didelot   net: dsa: check b...
448
  	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info);
076e71336   Vivien Didelot   net: dsa: move VL...
449
  }
57ab1ca21   Vivien Didelot   net: dsa: move fi...
450

6207a78c0   Florian Fainelli   net: dsa: Add hel...
451
  static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
33615367f   Sebastian Reichel   net: dsa: Support...
452
  {
33615367f   Sebastian Reichel   net: dsa: Support...
453
  	struct device_node *phy_dn;
33615367f   Sebastian Reichel   net: dsa: Support...
454
  	struct phy_device *phydev;
33615367f   Sebastian Reichel   net: dsa: Support...
455

6207a78c0   Florian Fainelli   net: dsa: Add hel...
456
  	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
33615367f   Sebastian Reichel   net: dsa: Support...
457
  	if (!phy_dn)
6207a78c0   Florian Fainelli   net: dsa: Add hel...
458
  		return NULL;
33615367f   Sebastian Reichel   net: dsa: Support...
459
460
461
  
  	phydev = of_phy_find_device(phy_dn);
  	if (!phydev) {
6207a78c0   Florian Fainelli   net: dsa: Add hel...
462
463
  		of_node_put(phy_dn);
  		return ERR_PTR(-EPROBE_DEFER);
33615367f   Sebastian Reichel   net: dsa: Support...
464
  	}
9919a363a   Wen Yang   net: dsa: fix a l...
465
  	of_node_put(phy_dn);
6207a78c0   Florian Fainelli   net: dsa: Add hel...
466
467
  	return phydev;
  }
8ae674964   Florian Fainelli   net: dsa: Make PH...
468
469
470
  static void dsa_port_phylink_validate(struct phylink_config *config,
  				      unsigned long *supported,
  				      struct phylink_link_state *state)
77373d49d   Ioana Ciornei   net: dsa: Move th...
471
472
473
474
475
476
477
478
479
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->phylink_validate)
  		return;
  
  	ds->ops->phylink_validate(ds, dp->index, supported, state);
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
480

8ae674964   Florian Fainelli   net: dsa: Make PH...
481
482
  static void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
  					       struct phylink_link_state *state)
77373d49d   Ioana Ciornei   net: dsa: Move th...
483
484
485
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
  	struct dsa_switch *ds = dp->ds;
87615c96e   Russell King   net: dsa: warn if...
486
  	int err;
77373d49d   Ioana Ciornei   net: dsa: Move th...
487

d46b7e4fb   Russell King   net: phylink: ren...
488
489
490
491
492
  	/* Only called for inband modes */
  	if (!ds->ops->phylink_mac_link_state) {
  		state->link = 0;
  		return;
  	}
77373d49d   Ioana Ciornei   net: dsa: Move th...
493

87615c96e   Russell King   net: dsa: warn if...
494
495
496
497
498
  	err = ds->ops->phylink_mac_link_state(ds, dp->index, state);
  	if (err < 0) {
  		dev_err(ds->dev, "p%d: phylink_mac_link_state() failed: %d
  ",
  			dp->index, err);
d46b7e4fb   Russell King   net: phylink: ren...
499
  		state->link = 0;
87615c96e   Russell King   net: dsa: warn if...
500
  	}
77373d49d   Ioana Ciornei   net: dsa: Move th...
501
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
502

8ae674964   Florian Fainelli   net: dsa: Make PH...
503
504
505
  static void dsa_port_phylink_mac_config(struct phylink_config *config,
  					unsigned int mode,
  					const struct phylink_link_state *state)
77373d49d   Ioana Ciornei   net: dsa: Move th...
506
507
508
509
510
511
512
513
514
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->phylink_mac_config)
  		return;
  
  	ds->ops->phylink_mac_config(ds, dp->index, mode, state);
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
515

8ae674964   Florian Fainelli   net: dsa: Make PH...
516
  static void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
77373d49d   Ioana Ciornei   net: dsa: Move th...
517
518
519
520
521
522
523
524
525
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->phylink_mac_an_restart)
  		return;
  
  	ds->ops->phylink_mac_an_restart(ds, dp->index);
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
526

8ae674964   Florian Fainelli   net: dsa: Make PH...
527
528
529
  static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
  					   unsigned int mode,
  					   phy_interface_t interface)
77373d49d   Ioana Ciornei   net: dsa: Move th...
530
531
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
532
  	struct phy_device *phydev = NULL;
77373d49d   Ioana Ciornei   net: dsa: Move th...
533
  	struct dsa_switch *ds = dp->ds;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
534
535
  	if (dsa_is_user_port(ds, dp->index))
  		phydev = dp->slave->phydev;
77373d49d   Ioana Ciornei   net: dsa: Move th...
536
  	if (!ds->ops->phylink_mac_link_down) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
537
538
  		if (ds->ops->adjust_link && phydev)
  			ds->ops->adjust_link(ds, dp->index, phydev);
77373d49d   Ioana Ciornei   net: dsa: Move th...
539
540
541
542
543
  		return;
  	}
  
  	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
544

8ae674964   Florian Fainelli   net: dsa: Make PH...
545
  static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
91a208f21   Russell King   net: phylink: pro...
546
  					 struct phy_device *phydev,
8ae674964   Florian Fainelli   net: dsa: Make PH...
547
548
  					 unsigned int mode,
  					 phy_interface_t interface,
91a208f21   Russell King   net: phylink: pro...
549
550
  					 int speed, int duplex,
  					 bool tx_pause, bool rx_pause)
77373d49d   Ioana Ciornei   net: dsa: Move th...
551
552
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
77373d49d   Ioana Ciornei   net: dsa: Move th...
553
554
555
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->phylink_mac_link_up) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
556
557
  		if (ds->ops->adjust_link && phydev)
  			ds->ops->adjust_link(ds, dp->index, phydev);
77373d49d   Ioana Ciornei   net: dsa: Move th...
558
559
  		return;
  	}
5b502a7b2   Russell King   net: dsa: propaga...
560
561
  	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev,
  				     speed, duplex, tx_pause, rx_pause);
77373d49d   Ioana Ciornei   net: dsa: Move th...
562
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
563
564
565
  
  const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
  	.validate = dsa_port_phylink_validate,
d46b7e4fb   Russell King   net: phylink: ren...
566
  	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
77373d49d   Ioana Ciornei   net: dsa: Move th...
567
568
569
570
571
  	.mac_config = dsa_port_phylink_mac_config,
  	.mac_an_restart = dsa_port_phylink_mac_an_restart,
  	.mac_link_down = dsa_port_phylink_mac_link_down,
  	.mac_link_up = dsa_port_phylink_mac_link_up,
  };
6207a78c0   Florian Fainelli   net: dsa: Add hel...
572
573
574
575
576
577
578
579
580
581
582
583
584
  static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable)
  {
  	struct dsa_switch *ds = dp->ds;
  	struct phy_device *phydev;
  	int port = dp->index;
  	int err = 0;
  
  	phydev = dsa_port_get_phy_device(dp);
  	if (!phydev)
  		return 0;
  
  	if (IS_ERR(phydev))
  		return PTR_ERR(phydev);
33615367f   Sebastian Reichel   net: dsa: Support...
585
  	if (enable) {
33615367f   Sebastian Reichel   net: dsa: Support...
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
  		err = genphy_resume(phydev);
  		if (err < 0)
  			goto err_put_dev;
  
  		err = genphy_read_status(phydev);
  		if (err < 0)
  			goto err_put_dev;
  	} else {
  		err = genphy_suspend(phydev);
  		if (err < 0)
  			goto err_put_dev;
  	}
  
  	if (ds->ops->adjust_link)
  		ds->ops->adjust_link(ds, port, phydev);
  
  	dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev));
  
  err_put_dev:
  	put_device(&phydev->mdio.dev);
33615367f   Sebastian Reichel   net: dsa: Support...
606
607
608
609
  	return err;
  }
  
  static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
57ab1ca21   Vivien Didelot   net: dsa: move fi...
610
611
612
613
614
  {
  	struct device_node *dn = dp->dn;
  	struct dsa_switch *ds = dp->ds;
  	struct phy_device *phydev;
  	int port = dp->index;
0c65b2b90   Andrew Lunn   net: of_get_phy_m...
615
  	phy_interface_t mode;
57ab1ca21   Vivien Didelot   net: dsa: move fi...
616
  	int err;
33615367f   Sebastian Reichel   net: dsa: Support...
617
618
619
620
621
622
623
624
  	err = of_phy_register_fixed_link(dn);
  	if (err) {
  		dev_err(ds->dev,
  			"failed to register the fixed PHY of port %d
  ",
  			port);
  		return err;
  	}
57ab1ca21   Vivien Didelot   net: dsa: move fi...
625

33615367f   Sebastian Reichel   net: dsa: Support...
626
  	phydev = of_phy_find_device(dn);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
627

0c65b2b90   Andrew Lunn   net: of_get_phy_m...
628
629
  	err = of_get_phy_mode(dn, &mode);
  	if (err)
33615367f   Sebastian Reichel   net: dsa: Support...
630
631
  		mode = PHY_INTERFACE_MODE_NA;
  	phydev->interface = mode;
57ab1ca21   Vivien Didelot   net: dsa: move fi...
632

33615367f   Sebastian Reichel   net: dsa: Support...
633
  	genphy_read_status(phydev);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
634

33615367f   Sebastian Reichel   net: dsa: Support...
635
636
  	if (ds->ops->adjust_link)
  		ds->ops->adjust_link(ds, port, phydev);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
637

33615367f   Sebastian Reichel   net: dsa: Support...
638
  	put_device(&phydev->mdio.dev);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
639
640
641
  
  	return 0;
  }
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
642
643
644
645
  static int dsa_port_phylink_register(struct dsa_port *dp)
  {
  	struct dsa_switch *ds = dp->ds;
  	struct device_node *port_dn = dp->dn;
0c65b2b90   Andrew Lunn   net: of_get_phy_m...
646
647
  	phy_interface_t mode;
  	int err;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
648

0c65b2b90   Andrew Lunn   net: of_get_phy_m...
649
650
  	err = of_get_phy_mode(port_dn, &mode);
  	if (err)
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
651
652
653
654
  		mode = PHY_INTERFACE_MODE_NA;
  
  	dp->pl_config.dev = ds->dev;
  	dp->pl_config.type = PHYLINK_DEV;
787cac3f5   Vladimir Oltean   net: dsa: Pass pc...
655
  	dp->pl_config.pcs_poll = ds->pcs_poll;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
656
657
658
659
660
661
662
663
664
665
  
  	dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn),
  				mode, &dsa_port_phylink_mac_ops);
  	if (IS_ERR(dp->pl)) {
  		pr_err("error creating PHYLINK: %ld
  ", PTR_ERR(dp->pl));
  		return PTR_ERR(dp->pl);
  	}
  
  	err = phylink_of_phy_connect(dp->pl, port_dn, 0);
2131fba53   Florian Fainelli   net: dsa: Deal wi...
666
  	if (err && err != -ENODEV) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
667
668
669
670
  		pr_err("could not attach to PHY: %d
  ", err);
  		goto err_phy_connect;
  	}
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
671
672
673
674
675
676
  	return 0;
  
  err_phy_connect:
  	phylink_destroy(dp->pl);
  	return err;
  }
33615367f   Sebastian Reichel   net: dsa: Support...
677
  int dsa_port_link_register_of(struct dsa_port *dp)
57ab1ca21   Vivien Didelot   net: dsa: move fi...
678
  {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
679
  	struct dsa_switch *ds = dp->ds;
a20f99701   Andrew Lunn   net: dsa: Don't i...
680
  	struct device_node *phy_np;
3be98b2d5   Andrew Lunn   net: dsa: Down cp...
681
  	int port = dp->index;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
682

a20f99701   Andrew Lunn   net: dsa: Don't i...
683
684
  	if (!ds->ops->adjust_link) {
  		phy_np = of_parse_phandle(dp->dn, "phy-handle", 0);
3be98b2d5   Andrew Lunn   net: dsa: Down cp...
685
686
687
688
  		if (of_phy_is_fixed_link(dp->dn) || phy_np) {
  			if (ds->ops->phylink_mac_link_down)
  				ds->ops->phylink_mac_link_down(ds, port,
  					MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
a20f99701   Andrew Lunn   net: dsa: Don't i...
689
  			return dsa_port_phylink_register(dp);
3be98b2d5   Andrew Lunn   net: dsa: Down cp...
690
  		}
a20f99701   Andrew Lunn   net: dsa: Don't i...
691
692
  		return 0;
  	}
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
693
694
695
696
  
  	dev_warn(ds->dev,
  		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!
  ");
33615367f   Sebastian Reichel   net: dsa: Support...
697
698
699
700
701
  	if (of_phy_is_fixed_link(dp->dn))
  		return dsa_port_fixed_link_register_of(dp);
  	else
  		return dsa_port_setup_phy_of(dp, true);
  }
57ab1ca21   Vivien Didelot   net: dsa: move fi...
702

33615367f   Sebastian Reichel   net: dsa: Support...
703
704
  void dsa_port_link_unregister_of(struct dsa_port *dp)
  {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
705
  	struct dsa_switch *ds = dp->ds;
a20f99701   Andrew Lunn   net: dsa: Don't i...
706
  	if (!ds->ops->adjust_link && dp->pl) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
707
708
709
710
  		rtnl_lock();
  		phylink_disconnect_phy(dp->pl);
  		rtnl_unlock();
  		phylink_destroy(dp->pl);
a20f99701   Andrew Lunn   net: dsa: Don't i...
711
  		dp->pl = NULL;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
712
713
  		return;
  	}
33615367f   Sebastian Reichel   net: dsa: Support...
714
715
716
717
  	if (of_phy_is_fixed_link(dp->dn))
  		of_phy_deregister_fixed_link(dp->dn);
  	else
  		dsa_port_setup_phy_of(dp, false);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
718
  }
cf9635730   Florian Fainelli   net: dsa: Allow p...
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
  
  int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
  {
  	struct phy_device *phydev;
  	int ret = -EOPNOTSUPP;
  
  	if (of_phy_is_fixed_link(dp->dn))
  		return ret;
  
  	phydev = dsa_port_get_phy_device(dp);
  	if (IS_ERR_OR_NULL(phydev))
  		return ret;
  
  	ret = phy_ethtool_get_strings(phydev, data);
  	put_device(&phydev->mdio.dev);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
  
  int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
  {
  	struct phy_device *phydev;
  	int ret = -EOPNOTSUPP;
  
  	if (of_phy_is_fixed_link(dp->dn))
  		return ret;
  
  	phydev = dsa_port_get_phy_device(dp);
  	if (IS_ERR_OR_NULL(phydev))
  		return ret;
  
  	ret = phy_ethtool_get_stats(phydev, NULL, data);
  	put_device(&phydev->mdio.dev);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
  
  int dsa_port_get_phy_sset_count(struct dsa_port *dp)
  {
  	struct phy_device *phydev;
  	int ret = -EOPNOTSUPP;
  
  	if (of_phy_is_fixed_link(dp->dn))
  		return ret;
  
  	phydev = dsa_port_get_phy_device(dp);
  	if (IS_ERR_OR_NULL(phydev))
  		return ret;
  
  	ret = phy_ethtool_get_sset_count(phydev);
  	put_device(&phydev->mdio.dev);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);