Blame view

net/dsa/port.c 16.6 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"
bb9f60317   Andrew Lunn   net: dsa: add mor...
15
  static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
cfbed329b   Vivien Didelot   net: dsa: move br...
16
17
18
19
20
21
22
23
  {
  	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...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  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...
54
  static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
a40c175b4   Vivien Didelot   net: dsa: move po...
55
56
57
58
59
60
61
62
  {
  	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...
63

fb8a6a2b8   Vivien Didelot   net: dsa: add por...
64
65
  int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy)
  {
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
66
67
68
69
70
71
72
73
74
  	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...
75
76
  	if (!dp->bridge_dev)
  		dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
77
78
79
  
  	return 0;
  }
75104db0c   Andrew Lunn   dsa: Remove phyde...
80
  void dsa_port_disable(struct dsa_port *dp)
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
81
82
83
  {
  	struct dsa_switch *ds = dp->ds;
  	int port = dp->index;
9c2054a5c   Russell King   net: dsa: fix uni...
84
85
  	if (!dp->bridge_dev)
  		dsa_port_set_state_now(dp, BR_STATE_DISABLED);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
86
87
  
  	if (ds->ops->port_disable)
75104db0c   Andrew Lunn   dsa: Remove phyde...
88
  		ds->ops->port_disable(ds, port);
fb8a6a2b8   Vivien Didelot   net: dsa: add por...
89
  }
cfbed329b   Vivien Didelot   net: dsa: move br...
90
91
92
93
94
95
96
97
  int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
  {
  	struct dsa_notifier_bridge_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.br = br,
  	};
  	int err;
c13880634   Russell King   net: dsa: enable ...
98
99
100
101
102
103
104
  	/* 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...
105
106
107
108
109
110
  	 */
  	dp->bridge_dev = br;
  
  	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info);
  
  	/* The bridging is rolled back on error */
c13880634   Russell King   net: dsa: enable ...
111
112
  	if (err) {
  		dsa_port_bridge_flags(dp, 0, NULL);
cfbed329b   Vivien Didelot   net: dsa: move br...
113
  		dp->bridge_dev = NULL;
c13880634   Russell King   net: dsa: enable ...
114
  	}
cfbed329b   Vivien Didelot   net: dsa: move br...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  
  	return err;
  }
  
  void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
  {
  	struct dsa_notifier_bridge_info info = {
  		.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;
  
  	err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info);
  	if (err)
  		pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE
  ");
c13880634   Russell King   net: dsa: enable ...
137
138
  	/* Port is leaving the bridge, disable flooding */
  	dsa_port_bridge_flags(dp, 0, NULL);
cfbed329b   Vivien Didelot   net: dsa: move br...
139
140
141
142
143
  	/* 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...
144

8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
  					      bool vlan_filtering)
  {
  	struct dsa_switch *ds = dp->ds;
  	int i;
  
  	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...
178
179
180
181
  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...
182
  	int err;
4d61d3043   Vivien Didelot   net: dsa: move VL...
183
184
185
186
  
  	/* bridge skips -EOPNOTSUPP, so skip the prepare phase */
  	if (switchdev_trans_ph_prepare(trans))
  		return 0;
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
187
188
189
190
191
  	if (!ds->ops->port_vlan_filtering)
  		return 0;
  
  	if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering))
  		return -EINVAL;
ec9121e7d   Vladimir Oltean   net: dsa: Skip ca...
192
193
  	if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
  		return 0;
8f5d16f63   Vladimir Oltean   net: dsa: Be awar...
194
195
196
197
  	err = ds->ops->port_vlan_filtering(ds, dp->index,
  					   vlan_filtering);
  	if (err)
  		return err;
145746765   Vladimir Oltean   net: dsa: Keep th...
198
199
200
201
  	if (ds->vlan_filtering_is_global)
  		ds->vlan_filtering = vlan_filtering;
  	else
  		dp->vlan_filtering = vlan_filtering;
4d61d3043   Vivien Didelot   net: dsa: move VL...
202
203
  	return 0;
  }
d87bd94e1   Vivien Didelot   net: dsa: move ag...
204

d87bd94e1   Vivien Didelot   net: dsa: move ag...
205
206
207
208
209
  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...
210
211
  	struct dsa_notifier_ageing_time_info info = {
  		.ageing_time = ageing_time,
1faabf744   Vivien Didelot   net: dsa: add not...
212
213
  		.trans = trans,
  	};
d87bd94e1   Vivien Didelot   net: dsa: move ag...
214

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

d87bd94e1   Vivien Didelot   net: dsa: move ag...
218
  	dp->ageing_time = ageing_time;
d87bd94e1   Vivien Didelot   net: dsa: move ag...
219

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

ea87005a0   Florian Fainelli   net: dsa: Add set...
223
224
225
226
227
228
229
230
231
232
233
  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...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  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...
250
251
252
253
254
255
256
257
258
259
260
  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);
  }
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
261
262
  int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
  		     u16 vid)
d1cffff00   Vivien Didelot   net: dsa: move FD...
263
  {
685fb6a40   Vivien Didelot   net: dsa: add FDB...
264
265
266
  	struct dsa_notifier_fdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
267
268
  		.addr = addr,
  		.vid = vid,
685fb6a40   Vivien Didelot   net: dsa: add FDB...
269
  	};
d1cffff00   Vivien Didelot   net: dsa: move FD...
270

685fb6a40   Vivien Didelot   net: dsa: add FDB...
271
  	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info);
d1cffff00   Vivien Didelot   net: dsa: move FD...
272
  }
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
273
274
  int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
  		     u16 vid)
d1cffff00   Vivien Didelot   net: dsa: move FD...
275
  {
685fb6a40   Vivien Didelot   net: dsa: add FDB...
276
277
278
  	struct dsa_notifier_fdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
2acf4e6a8   Arkadi Sharshevsky   net: dsa: Remove ...
279
280
  		.addr = addr,
  		.vid = vid,
685fb6a40   Vivien Didelot   net: dsa: add FDB...
281
  	};
d1cffff00   Vivien Didelot   net: dsa: move FD...
282

685fb6a40   Vivien Didelot   net: dsa: add FDB...
283
  	return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
d1cffff00   Vivien Didelot   net: dsa: move FD...
284
  }
de40fc5d2   Vivien Didelot   net: dsa: add por...
285
286
287
288
289
290
291
292
293
294
  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...
295
  int dsa_port_mdb_add(const struct dsa_port *dp,
3a9afea37   Vivien Didelot   net: dsa: move MD...
296
297
298
  		     const struct switchdev_obj_port_mdb *mdb,
  		     struct switchdev_trans *trans)
  {
8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
299
300
301
302
303
304
  	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...
305

8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
306
  	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info);
3a9afea37   Vivien Didelot   net: dsa: move MD...
307
  }
bb9f60317   Andrew Lunn   net: dsa: add mor...
308
  int dsa_port_mdb_del(const struct dsa_port *dp,
3a9afea37   Vivien Didelot   net: dsa: move MD...
309
310
  		     const struct switchdev_obj_port_mdb *mdb)
  {
8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
311
312
313
314
315
  	struct dsa_notifier_mdb_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.mdb = mdb,
  	};
3a9afea37   Vivien Didelot   net: dsa: move MD...
316

8ae5bcdc5   Vivien Didelot   net: dsa: add MDB...
317
  	return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
3a9afea37   Vivien Didelot   net: dsa: move MD...
318
  }
076e71336   Vivien Didelot   net: dsa: move VL...
319
320
321
322
  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...
323
324
325
326
327
328
  	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...
329

c5335d737   Vivien Didelot   net: dsa: check b...
330
  	return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info);
076e71336   Vivien Didelot   net: dsa: move VL...
331
332
333
334
335
  }
  
  int dsa_port_vlan_del(struct dsa_port *dp,
  		      const struct switchdev_obj_port_vlan *vlan)
  {
d0c627b87   Vivien Didelot   net: dsa: add VLA...
336
337
338
339
340
  	struct dsa_notifier_vlan_info info = {
  		.sw_index = dp->ds->index,
  		.port = dp->index,
  		.vlan = vlan,
  	};
076e71336   Vivien Didelot   net: dsa: move VL...
341

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

314f76d7a   Vladimir Oltean   net: dsa: Add mor...
345
346
347
348
349
350
351
352
353
354
355
356
357
  int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags)
  {
  	struct switchdev_obj_port_vlan vlan = {
  		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
  		.flags = flags,
  		.vid_begin = vid,
  		.vid_end = vid,
  	};
  	struct switchdev_trans trans;
  	int err;
  
  	trans.ph_prepare = true;
  	err = dsa_port_vlan_add(dp, &vlan, &trans);
cf360866b   Vivien Didelot   net: dsa: do not ...
358
359
  	if (err)
  		return err;
314f76d7a   Vladimir Oltean   net: dsa: Add mor...
360
361
362
363
  
  	trans.ph_prepare = false;
  	return dsa_port_vlan_add(dp, &vlan, &trans);
  }
146c1bed4   Vladimir Oltean   net: dsa: Export ...
364
  EXPORT_SYMBOL(dsa_port_vid_add);
314f76d7a   Vladimir Oltean   net: dsa: Add mor...
365
366
367
368
369
370
371
372
373
374
375
  
  int dsa_port_vid_del(struct dsa_port *dp, u16 vid)
  {
  	struct switchdev_obj_port_vlan vlan = {
  		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
  		.vid_begin = vid,
  		.vid_end = vid,
  	};
  
  	return dsa_port_vlan_del(dp, &vlan);
  }
146c1bed4   Vladimir Oltean   net: dsa: Export ...
376
  EXPORT_SYMBOL(dsa_port_vid_del);
314f76d7a   Vladimir Oltean   net: dsa: Add mor...
377

6207a78c0   Florian Fainelli   net: dsa: Add hel...
378
  static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp)
33615367f   Sebastian Reichel   net: dsa: Support...
379
  {
33615367f   Sebastian Reichel   net: dsa: Support...
380
  	struct device_node *phy_dn;
33615367f   Sebastian Reichel   net: dsa: Support...
381
  	struct phy_device *phydev;
33615367f   Sebastian Reichel   net: dsa: Support...
382

6207a78c0   Florian Fainelli   net: dsa: Add hel...
383
  	phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0);
33615367f   Sebastian Reichel   net: dsa: Support...
384
  	if (!phy_dn)
6207a78c0   Florian Fainelli   net: dsa: Add hel...
385
  		return NULL;
33615367f   Sebastian Reichel   net: dsa: Support...
386
387
388
  
  	phydev = of_phy_find_device(phy_dn);
  	if (!phydev) {
6207a78c0   Florian Fainelli   net: dsa: Add hel...
389
390
  		of_node_put(phy_dn);
  		return ERR_PTR(-EPROBE_DEFER);
33615367f   Sebastian Reichel   net: dsa: Support...
391
  	}
9919a363a   Wen Yang   net: dsa: fix a l...
392
  	of_node_put(phy_dn);
6207a78c0   Florian Fainelli   net: dsa: Add hel...
393
394
  	return phydev;
  }
77373d49d   Ioana Ciornei   net: dsa: Move th...
395
396
397
398
399
400
401
402
403
404
405
406
407
  void dsa_port_phylink_validate(struct phylink_config *config,
  			       unsigned long *supported,
  			       struct phylink_link_state *state)
  {
  	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);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_validate);
d46b7e4fb   Russell King   net: phylink: ren...
408
409
  void dsa_port_phylink_mac_pcs_get_state(struct phylink_config *config,
  					struct phylink_link_state *state)
77373d49d   Ioana Ciornei   net: dsa: Move th...
410
411
412
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
  	struct dsa_switch *ds = dp->ds;
d46b7e4fb   Russell King   net: phylink: ren...
413
414
415
416
417
  	/* Only called for inband modes */
  	if (!ds->ops->phylink_mac_link_state) {
  		state->link = 0;
  		return;
  	}
77373d49d   Ioana Ciornei   net: dsa: Move th...
418

d46b7e4fb   Russell King   net: phylink: ren...
419
420
  	if (ds->ops->phylink_mac_link_state(ds, dp->index, state) < 0)
  		state->link = 0;
77373d49d   Ioana Ciornei   net: dsa: Move th...
421
  }
d46b7e4fb   Russell King   net: phylink: ren...
422
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_pcs_get_state);
77373d49d   Ioana Ciornei   net: dsa: Move th...
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
  
  void dsa_port_phylink_mac_config(struct phylink_config *config,
  				 unsigned int mode,
  				 const struct phylink_link_state *state)
  {
  	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);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_config);
  
  void dsa_port_phylink_mac_an_restart(struct phylink_config *config)
  {
  	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);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_an_restart);
  
  void dsa_port_phylink_mac_link_down(struct phylink_config *config,
  				    unsigned int mode,
  				    phy_interface_t interface)
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
455
  	struct phy_device *phydev = NULL;
77373d49d   Ioana Ciornei   net: dsa: Move th...
456
  	struct dsa_switch *ds = dp->ds;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
457
458
  	if (dsa_is_user_port(ds, dp->index))
  		phydev = dp->slave->phydev;
77373d49d   Ioana Ciornei   net: dsa: Move th...
459
  	if (!ds->ops->phylink_mac_link_down) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
460
461
  		if (ds->ops->adjust_link && phydev)
  			ds->ops->adjust_link(ds, dp->index, phydev);
77373d49d   Ioana Ciornei   net: dsa: Move th...
462
463
464
465
466
467
468
469
470
471
472
473
474
  		return;
  	}
  
  	ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_down);
  
  void dsa_port_phylink_mac_link_up(struct phylink_config *config,
  				  unsigned int mode,
  				  phy_interface_t interface,
  				  struct phy_device *phydev)
  {
  	struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
77373d49d   Ioana Ciornei   net: dsa: Move th...
475
476
477
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->phylink_mac_link_up) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
478
479
  		if (ds->ops->adjust_link && phydev)
  			ds->ops->adjust_link(ds, dp->index, phydev);
77373d49d   Ioana Ciornei   net: dsa: Move th...
480
481
482
483
484
485
486
487
488
  		return;
  	}
  
  	ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev);
  }
  EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_up);
  
  const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
  	.validate = dsa_port_phylink_validate,
d46b7e4fb   Russell King   net: phylink: ren...
489
  	.mac_pcs_get_state = dsa_port_phylink_mac_pcs_get_state,
77373d49d   Ioana Ciornei   net: dsa: Move th...
490
491
492
493
494
  	.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...
495
496
497
498
499
500
501
502
503
504
505
506
507
  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...
508
  	if (enable) {
33615367f   Sebastian Reichel   net: dsa: Support...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
  		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...
529
530
531
532
  	return err;
  }
  
  static int dsa_port_fixed_link_register_of(struct dsa_port *dp)
57ab1ca21   Vivien Didelot   net: dsa: move fi...
533
534
535
536
537
  {
  	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...
538
  	phy_interface_t mode;
57ab1ca21   Vivien Didelot   net: dsa: move fi...
539
  	int err;
33615367f   Sebastian Reichel   net: dsa: Support...
540
541
542
543
544
545
546
547
  	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...
548

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

0c65b2b90   Andrew Lunn   net: of_get_phy_m...
551
552
  	err = of_get_phy_mode(dn, &mode);
  	if (err)
33615367f   Sebastian Reichel   net: dsa: Support...
553
554
  		mode = PHY_INTERFACE_MODE_NA;
  	phydev->interface = mode;
57ab1ca21   Vivien Didelot   net: dsa: move fi...
555

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

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

33615367f   Sebastian Reichel   net: dsa: Support...
561
  	put_device(&phydev->mdio.dev);
57ab1ca21   Vivien Didelot   net: dsa: move fi...
562
563
564
  
  	return 0;
  }
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
565
566
567
568
  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...
569
570
  	phy_interface_t mode;
  	int err;
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
571

0c65b2b90   Andrew Lunn   net: of_get_phy_m...
572
573
  	err = of_get_phy_mode(port_dn, &mode);
  	if (err)
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
574
575
576
577
578
579
580
581
582
583
584
585
586
587
  		mode = PHY_INTERFACE_MODE_NA;
  
  	dp->pl_config.dev = ds->dev;
  	dp->pl_config.type = PHYLINK_DEV;
  
  	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...
588
  	if (err && err != -ENODEV) {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  		pr_err("could not attach to PHY: %d
  ", err);
  		goto err_phy_connect;
  	}
  
  	rtnl_lock();
  	phylink_start(dp->pl);
  	rtnl_unlock();
  
  	return 0;
  
  err_phy_connect:
  	phylink_destroy(dp->pl);
  	return err;
  }
33615367f   Sebastian Reichel   net: dsa: Support...
604
  int dsa_port_link_register_of(struct dsa_port *dp)
57ab1ca21   Vivien Didelot   net: dsa: move fi...
605
  {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
606
607
608
609
610
611
612
613
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->adjust_link)
  		return dsa_port_phylink_register(dp);
  
  	dev_warn(ds->dev,
  		 "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!
  ");
33615367f   Sebastian Reichel   net: dsa: Support...
614
615
616
617
618
  	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...
619

33615367f   Sebastian Reichel   net: dsa: Support...
620
621
  void dsa_port_link_unregister_of(struct dsa_port *dp)
  {
0e2792181   Ioana Ciornei   net: dsa: Use PHY...
622
623
624
625
626
627
628
629
630
  	struct dsa_switch *ds = dp->ds;
  
  	if (!ds->ops->adjust_link) {
  		rtnl_lock();
  		phylink_disconnect_phy(dp->pl);
  		rtnl_unlock();
  		phylink_destroy(dp->pl);
  		return;
  	}
33615367f   Sebastian Reichel   net: dsa: Support...
631
632
633
634
  	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...
635
  }
cf9635730   Florian Fainelli   net: dsa: Allow p...
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
  
  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);