Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
a40c175b VD |
2 | /* |
3 | * Handling of a single switch port | |
4 | * | |
5 | * Copyright (c) 2017 Savoir-faire Linux Inc. | |
6 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> | |
a40c175b VD |
7 | */ |
8 | ||
9 | #include <linux/if_bridge.h> | |
cfbed329 | 10 | #include <linux/notifier.h> |
57ab1ca2 VD |
11 | #include <linux/of_mdio.h> |
12 | #include <linux/of_net.h> | |
a40c175b VD |
13 | |
14 | #include "dsa_priv.h" | |
15 | ||
bb9f6031 | 16 | static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) |
cfbed329 VD |
17 | { |
18 | struct raw_notifier_head *nh = &dp->ds->dst->nh; | |
19 | int err; | |
20 | ||
21 | err = raw_notifier_call_chain(nh, e, v); | |
22 | ||
23 | return notifier_to_errno(err); | |
24 | } | |
25 | ||
a40c175b VD |
26 | int dsa_port_set_state(struct dsa_port *dp, u8 state, |
27 | struct switchdev_trans *trans) | |
28 | { | |
29 | struct dsa_switch *ds = dp->ds; | |
30 | int port = dp->index; | |
31 | ||
32 | if (switchdev_trans_ph_prepare(trans)) | |
33 | return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP; | |
34 | ||
35 | if (ds->ops->port_stp_state_set) | |
36 | ds->ops->port_stp_state_set(ds, port, state); | |
37 | ||
38 | if (ds->ops->port_fast_age) { | |
39 | /* Fast age FDB entries or flush appropriate forwarding database | |
40 | * for the given port, if we are moving it from Learning or | |
41 | * Forwarding state, to Disabled or Blocking or Listening state. | |
42 | */ | |
43 | ||
44 | if ((dp->stp_state == BR_STATE_LEARNING || | |
45 | dp->stp_state == BR_STATE_FORWARDING) && | |
46 | (state == BR_STATE_DISABLED || | |
47 | state == BR_STATE_BLOCKING || | |
48 | state == BR_STATE_LISTENING)) | |
49 | ds->ops->port_fast_age(ds, port); | |
50 | } | |
51 | ||
52 | dp->stp_state = state; | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
fb8a6a2b | 57 | static void dsa_port_set_state_now(struct dsa_port *dp, u8 state) |
a40c175b VD |
58 | { |
59 | int err; | |
60 | ||
61 | err = dsa_port_set_state(dp, state, NULL); | |
62 | if (err) | |
63 | pr_err("DSA: failed to set STP state %u (%d)\n", state, err); | |
64 | } | |
cfbed329 | 65 | |
fb8a6a2b VD |
66 | int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy) |
67 | { | |
fb8a6a2b VD |
68 | struct dsa_switch *ds = dp->ds; |
69 | int port = dp->index; | |
70 | int err; | |
71 | ||
72 | if (ds->ops->port_enable) { | |
73 | err = ds->ops->port_enable(ds, port, phy); | |
74 | if (err) | |
75 | return err; | |
76 | } | |
77 | ||
9c2054a5 RK |
78 | if (!dp->bridge_dev) |
79 | dsa_port_set_state_now(dp, BR_STATE_FORWARDING); | |
fb8a6a2b VD |
80 | |
81 | return 0; | |
82 | } | |
83 | ||
75104db0 | 84 | void dsa_port_disable(struct dsa_port *dp) |
fb8a6a2b VD |
85 | { |
86 | struct dsa_switch *ds = dp->ds; | |
87 | int port = dp->index; | |
88 | ||
9c2054a5 RK |
89 | if (!dp->bridge_dev) |
90 | dsa_port_set_state_now(dp, BR_STATE_DISABLED); | |
fb8a6a2b VD |
91 | |
92 | if (ds->ops->port_disable) | |
75104db0 | 93 | ds->ops->port_disable(ds, port); |
fb8a6a2b VD |
94 | } |
95 | ||
cfbed329 VD |
96 | int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br) |
97 | { | |
98 | struct dsa_notifier_bridge_info info = { | |
99 | .sw_index = dp->ds->index, | |
100 | .port = dp->index, | |
101 | .br = br, | |
102 | }; | |
103 | int err; | |
104 | ||
c1388063 RK |
105 | /* Set the flooding mode before joining the port in the switch */ |
106 | err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL); | |
107 | if (err) | |
108 | return err; | |
109 | ||
110 | /* Here the interface is already bridged. Reflect the current | |
111 | * configuration so that drivers can program their chips accordingly. | |
cfbed329 VD |
112 | */ |
113 | dp->bridge_dev = br; | |
114 | ||
115 | err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_JOIN, &info); | |
116 | ||
117 | /* The bridging is rolled back on error */ | |
c1388063 RK |
118 | if (err) { |
119 | dsa_port_bridge_flags(dp, 0, NULL); | |
cfbed329 | 120 | dp->bridge_dev = NULL; |
c1388063 | 121 | } |
cfbed329 VD |
122 | |
123 | return err; | |
124 | } | |
125 | ||
126 | void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br) | |
127 | { | |
128 | struct dsa_notifier_bridge_info info = { | |
129 | .sw_index = dp->ds->index, | |
130 | .port = dp->index, | |
131 | .br = br, | |
132 | }; | |
133 | int err; | |
134 | ||
135 | /* Here the port is already unbridged. Reflect the current configuration | |
136 | * so that drivers can program their chips accordingly. | |
137 | */ | |
138 | dp->bridge_dev = NULL; | |
139 | ||
140 | err = dsa_port_notify(dp, DSA_NOTIFIER_BRIDGE_LEAVE, &info); | |
141 | if (err) | |
142 | pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n"); | |
143 | ||
c1388063 RK |
144 | /* Port is leaving the bridge, disable flooding */ |
145 | dsa_port_bridge_flags(dp, 0, NULL); | |
146 | ||
cfbed329 VD |
147 | /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer, |
148 | * so allow it to be in BR_STATE_FORWARDING to be kept functional | |
149 | */ | |
150 | dsa_port_set_state_now(dp, BR_STATE_FORWARDING); | |
151 | } | |
4d61d304 | 152 | |
8f5d16f6 VO |
153 | static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, |
154 | bool vlan_filtering) | |
155 | { | |
156 | struct dsa_switch *ds = dp->ds; | |
157 | int i; | |
158 | ||
159 | if (!ds->vlan_filtering_is_global) | |
160 | return true; | |
161 | ||
162 | /* For cases where enabling/disabling VLAN awareness is global to the | |
163 | * switch, we need to handle the case where multiple bridges span | |
164 | * different ports of the same switch device and one of them has a | |
165 | * different setting than what is being requested. | |
166 | */ | |
167 | for (i = 0; i < ds->num_ports; i++) { | |
168 | struct net_device *other_bridge; | |
169 | ||
170 | other_bridge = dsa_to_port(ds, i)->bridge_dev; | |
171 | if (!other_bridge) | |
172 | continue; | |
173 | /* If it's the same bridge, it also has same | |
174 | * vlan_filtering setting => no need to check | |
175 | */ | |
176 | if (other_bridge == dp->bridge_dev) | |
177 | continue; | |
178 | if (br_vlan_enabled(other_bridge) != vlan_filtering) { | |
179 | dev_err(ds->dev, "VLAN filtering is a global setting\n"); | |
180 | return false; | |
181 | } | |
182 | } | |
183 | return true; | |
184 | } | |
185 | ||
4d61d304 VD |
186 | int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, |
187 | struct switchdev_trans *trans) | |
188 | { | |
189 | struct dsa_switch *ds = dp->ds; | |
33162e9a | 190 | int err; |
4d61d304 VD |
191 | |
192 | /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ | |
193 | if (switchdev_trans_ph_prepare(trans)) | |
194 | return 0; | |
195 | ||
8f5d16f6 VO |
196 | if (!ds->ops->port_vlan_filtering) |
197 | return 0; | |
198 | ||
199 | if (!dsa_port_can_apply_vlan_filtering(dp, vlan_filtering)) | |
200 | return -EINVAL; | |
201 | ||
ec9121e7 VO |
202 | if (dsa_port_is_vlan_filtering(dp) == vlan_filtering) |
203 | return 0; | |
204 | ||
8f5d16f6 VO |
205 | err = ds->ops->port_vlan_filtering(ds, dp->index, |
206 | vlan_filtering); | |
207 | if (err) | |
208 | return err; | |
209 | ||
14574676 VO |
210 | if (ds->vlan_filtering_is_global) |
211 | ds->vlan_filtering = vlan_filtering; | |
212 | else | |
213 | dp->vlan_filtering = vlan_filtering; | |
4d61d304 VD |
214 | return 0; |
215 | } | |
d87bd94e | 216 | |
d87bd94e VD |
217 | int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, |
218 | struct switchdev_trans *trans) | |
219 | { | |
220 | unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock); | |
221 | unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); | |
1faabf74 VD |
222 | struct dsa_notifier_ageing_time_info info = { |
223 | .ageing_time = ageing_time, | |
1faabf74 VD |
224 | .trans = trans, |
225 | }; | |
d87bd94e | 226 | |
1faabf74 VD |
227 | if (switchdev_trans_ph_prepare(trans)) |
228 | return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); | |
d87bd94e | 229 | |
d87bd94e | 230 | dp->ageing_time = ageing_time; |
d87bd94e | 231 | |
1faabf74 | 232 | return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); |
d87bd94e | 233 | } |
d1cffff0 | 234 | |
ea87005a FF |
235 | int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags, |
236 | struct switchdev_trans *trans) | |
237 | { | |
238 | struct dsa_switch *ds = dp->ds; | |
239 | ||
240 | if (!ds->ops->port_egress_floods || | |
241 | (flags & ~(BR_FLOOD | BR_MCAST_FLOOD))) | |
242 | return -EINVAL; | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
57652796 RK |
247 | int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags, |
248 | struct switchdev_trans *trans) | |
249 | { | |
250 | struct dsa_switch *ds = dp->ds; | |
251 | int port = dp->index; | |
252 | int err = 0; | |
253 | ||
254 | if (switchdev_trans_ph_prepare(trans)) | |
255 | return 0; | |
256 | ||
257 | if (ds->ops->port_egress_floods) | |
258 | err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD, | |
259 | flags & BR_MCAST_FLOOD); | |
260 | ||
261 | return err; | |
262 | } | |
263 | ||
2acf4e6a AS |
264 | int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, |
265 | u16 vid) | |
d1cffff0 | 266 | { |
685fb6a4 VD |
267 | struct dsa_notifier_fdb_info info = { |
268 | .sw_index = dp->ds->index, | |
269 | .port = dp->index, | |
2acf4e6a AS |
270 | .addr = addr, |
271 | .vid = vid, | |
685fb6a4 | 272 | }; |
d1cffff0 | 273 | |
685fb6a4 | 274 | return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); |
d1cffff0 VD |
275 | } |
276 | ||
2acf4e6a AS |
277 | int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, |
278 | u16 vid) | |
d1cffff0 | 279 | { |
685fb6a4 VD |
280 | struct dsa_notifier_fdb_info info = { |
281 | .sw_index = dp->ds->index, | |
282 | .port = dp->index, | |
2acf4e6a AS |
283 | .addr = addr, |
284 | .vid = vid, | |
285 | ||
685fb6a4 | 286 | }; |
d1cffff0 | 287 | |
685fb6a4 | 288 | return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); |
d1cffff0 VD |
289 | } |
290 | ||
de40fc5d VD |
291 | int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data) |
292 | { | |
293 | struct dsa_switch *ds = dp->ds; | |
294 | int port = dp->index; | |
295 | ||
296 | if (!ds->ops->port_fdb_dump) | |
297 | return -EOPNOTSUPP; | |
298 | ||
299 | return ds->ops->port_fdb_dump(ds, port, cb, data); | |
300 | } | |
301 | ||
bb9f6031 | 302 | int dsa_port_mdb_add(const struct dsa_port *dp, |
3a9afea3 VD |
303 | const struct switchdev_obj_port_mdb *mdb, |
304 | struct switchdev_trans *trans) | |
305 | { | |
8ae5bcdc VD |
306 | struct dsa_notifier_mdb_info info = { |
307 | .sw_index = dp->ds->index, | |
308 | .port = dp->index, | |
309 | .trans = trans, | |
310 | .mdb = mdb, | |
311 | }; | |
3a9afea3 | 312 | |
8ae5bcdc | 313 | return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ADD, &info); |
3a9afea3 VD |
314 | } |
315 | ||
bb9f6031 | 316 | int dsa_port_mdb_del(const struct dsa_port *dp, |
3a9afea3 VD |
317 | const struct switchdev_obj_port_mdb *mdb) |
318 | { | |
8ae5bcdc VD |
319 | struct dsa_notifier_mdb_info info = { |
320 | .sw_index = dp->ds->index, | |
321 | .port = dp->index, | |
322 | .mdb = mdb, | |
323 | }; | |
3a9afea3 | 324 | |
8ae5bcdc | 325 | return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); |
3a9afea3 VD |
326 | } |
327 | ||
076e7133 VD |
328 | int dsa_port_vlan_add(struct dsa_port *dp, |
329 | const struct switchdev_obj_port_vlan *vlan, | |
330 | struct switchdev_trans *trans) | |
331 | { | |
d0c627b8 VD |
332 | struct dsa_notifier_vlan_info info = { |
333 | .sw_index = dp->ds->index, | |
334 | .port = dp->index, | |
335 | .trans = trans, | |
336 | .vlan = vlan, | |
337 | }; | |
076e7133 | 338 | |
061f6a50 FF |
339 | /* Can be called from dsa_slave_port_obj_add() or |
340 | * dsa_slave_vlan_rx_add_vid() | |
341 | */ | |
342 | if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev)) | |
2ea7a679 AL |
343 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); |
344 | ||
345 | return 0; | |
076e7133 VD |
346 | } |
347 | ||
348 | int dsa_port_vlan_del(struct dsa_port *dp, | |
349 | const struct switchdev_obj_port_vlan *vlan) | |
350 | { | |
d0c627b8 VD |
351 | struct dsa_notifier_vlan_info info = { |
352 | .sw_index = dp->ds->index, | |
353 | .port = dp->index, | |
354 | .vlan = vlan, | |
355 | }; | |
076e7133 | 356 | |
061f6a50 | 357 | if (vlan->obj.orig_dev && netif_is_bridge_master(vlan->obj.orig_dev)) |
da0efa88 PM |
358 | return -EOPNOTSUPP; |
359 | ||
061f6a50 FF |
360 | /* Can be called from dsa_slave_port_obj_del() or |
361 | * dsa_slave_vlan_rx_kill_vid() | |
362 | */ | |
363 | if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev)) | |
2ea7a679 AL |
364 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); |
365 | ||
366 | return 0; | |
076e7133 | 367 | } |
57ab1ca2 | 368 | |
314f76d7 VO |
369 | int dsa_port_vid_add(struct dsa_port *dp, u16 vid, u16 flags) |
370 | { | |
371 | struct switchdev_obj_port_vlan vlan = { | |
372 | .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, | |
373 | .flags = flags, | |
374 | .vid_begin = vid, | |
375 | .vid_end = vid, | |
376 | }; | |
377 | struct switchdev_trans trans; | |
378 | int err; | |
379 | ||
380 | trans.ph_prepare = true; | |
381 | err = dsa_port_vlan_add(dp, &vlan, &trans); | |
382 | if (err == -EOPNOTSUPP) | |
383 | return 0; | |
384 | ||
385 | trans.ph_prepare = false; | |
386 | return dsa_port_vlan_add(dp, &vlan, &trans); | |
387 | } | |
146c1bed | 388 | EXPORT_SYMBOL(dsa_port_vid_add); |
314f76d7 VO |
389 | |
390 | int dsa_port_vid_del(struct dsa_port *dp, u16 vid) | |
391 | { | |
392 | struct switchdev_obj_port_vlan vlan = { | |
393 | .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, | |
394 | .vid_begin = vid, | |
395 | .vid_end = vid, | |
396 | }; | |
397 | ||
398 | return dsa_port_vlan_del(dp, &vlan); | |
399 | } | |
146c1bed | 400 | EXPORT_SYMBOL(dsa_port_vid_del); |
314f76d7 | 401 | |
6207a78c | 402 | static struct phy_device *dsa_port_get_phy_device(struct dsa_port *dp) |
33615367 | 403 | { |
33615367 | 404 | struct device_node *phy_dn; |
33615367 | 405 | struct phy_device *phydev; |
33615367 | 406 | |
6207a78c | 407 | phy_dn = of_parse_phandle(dp->dn, "phy-handle", 0); |
33615367 | 408 | if (!phy_dn) |
6207a78c | 409 | return NULL; |
33615367 SR |
410 | |
411 | phydev = of_phy_find_device(phy_dn); | |
412 | if (!phydev) { | |
6207a78c FF |
413 | of_node_put(phy_dn); |
414 | return ERR_PTR(-EPROBE_DEFER); | |
33615367 SR |
415 | } |
416 | ||
9919a363 | 417 | of_node_put(phy_dn); |
6207a78c FF |
418 | return phydev; |
419 | } | |
420 | ||
77373d49 IC |
421 | void dsa_port_phylink_validate(struct phylink_config *config, |
422 | unsigned long *supported, | |
423 | struct phylink_link_state *state) | |
424 | { | |
425 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
426 | struct dsa_switch *ds = dp->ds; | |
427 | ||
428 | if (!ds->ops->phylink_validate) | |
429 | return; | |
430 | ||
431 | ds->ops->phylink_validate(ds, dp->index, supported, state); | |
432 | } | |
433 | EXPORT_SYMBOL_GPL(dsa_port_phylink_validate); | |
434 | ||
435 | int dsa_port_phylink_mac_link_state(struct phylink_config *config, | |
436 | struct phylink_link_state *state) | |
437 | { | |
438 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
439 | struct dsa_switch *ds = dp->ds; | |
440 | ||
441 | /* Only called for SGMII and 802.3z */ | |
442 | if (!ds->ops->phylink_mac_link_state) | |
443 | return -EOPNOTSUPP; | |
444 | ||
445 | return ds->ops->phylink_mac_link_state(ds, dp->index, state); | |
446 | } | |
447 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_state); | |
448 | ||
449 | void dsa_port_phylink_mac_config(struct phylink_config *config, | |
450 | unsigned int mode, | |
451 | const struct phylink_link_state *state) | |
452 | { | |
453 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
454 | struct dsa_switch *ds = dp->ds; | |
455 | ||
456 | if (!ds->ops->phylink_mac_config) | |
457 | return; | |
458 | ||
459 | ds->ops->phylink_mac_config(ds, dp->index, mode, state); | |
460 | } | |
461 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_config); | |
462 | ||
463 | void dsa_port_phylink_mac_an_restart(struct phylink_config *config) | |
464 | { | |
465 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
466 | struct dsa_switch *ds = dp->ds; | |
467 | ||
468 | if (!ds->ops->phylink_mac_an_restart) | |
469 | return; | |
470 | ||
471 | ds->ops->phylink_mac_an_restart(ds, dp->index); | |
472 | } | |
473 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_an_restart); | |
474 | ||
475 | void dsa_port_phylink_mac_link_down(struct phylink_config *config, | |
476 | unsigned int mode, | |
477 | phy_interface_t interface) | |
478 | { | |
479 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
0e279218 | 480 | struct phy_device *phydev = NULL; |
77373d49 IC |
481 | struct dsa_switch *ds = dp->ds; |
482 | ||
0e279218 IC |
483 | if (dsa_is_user_port(ds, dp->index)) |
484 | phydev = dp->slave->phydev; | |
485 | ||
77373d49 | 486 | if (!ds->ops->phylink_mac_link_down) { |
0e279218 IC |
487 | if (ds->ops->adjust_link && phydev) |
488 | ds->ops->adjust_link(ds, dp->index, phydev); | |
77373d49 IC |
489 | return; |
490 | } | |
491 | ||
492 | ds->ops->phylink_mac_link_down(ds, dp->index, mode, interface); | |
493 | } | |
494 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_down); | |
495 | ||
496 | void dsa_port_phylink_mac_link_up(struct phylink_config *config, | |
497 | unsigned int mode, | |
498 | phy_interface_t interface, | |
499 | struct phy_device *phydev) | |
500 | { | |
501 | struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); | |
77373d49 IC |
502 | struct dsa_switch *ds = dp->ds; |
503 | ||
504 | if (!ds->ops->phylink_mac_link_up) { | |
0e279218 IC |
505 | if (ds->ops->adjust_link && phydev) |
506 | ds->ops->adjust_link(ds, dp->index, phydev); | |
77373d49 IC |
507 | return; |
508 | } | |
509 | ||
510 | ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); | |
511 | } | |
512 | EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_link_up); | |
513 | ||
514 | const struct phylink_mac_ops dsa_port_phylink_mac_ops = { | |
515 | .validate = dsa_port_phylink_validate, | |
516 | .mac_link_state = dsa_port_phylink_mac_link_state, | |
517 | .mac_config = dsa_port_phylink_mac_config, | |
518 | .mac_an_restart = dsa_port_phylink_mac_an_restart, | |
519 | .mac_link_down = dsa_port_phylink_mac_link_down, | |
520 | .mac_link_up = dsa_port_phylink_mac_link_up, | |
521 | }; | |
522 | ||
6207a78c FF |
523 | static int dsa_port_setup_phy_of(struct dsa_port *dp, bool enable) |
524 | { | |
525 | struct dsa_switch *ds = dp->ds; | |
526 | struct phy_device *phydev; | |
527 | int port = dp->index; | |
528 | int err = 0; | |
529 | ||
530 | phydev = dsa_port_get_phy_device(dp); | |
531 | if (!phydev) | |
532 | return 0; | |
533 | ||
534 | if (IS_ERR(phydev)) | |
535 | return PTR_ERR(phydev); | |
536 | ||
33615367 SR |
537 | if (enable) { |
538 | err = genphy_config_init(phydev); | |
539 | if (err < 0) | |
540 | goto err_put_dev; | |
541 | ||
542 | err = genphy_resume(phydev); | |
543 | if (err < 0) | |
544 | goto err_put_dev; | |
545 | ||
546 | err = genphy_read_status(phydev); | |
547 | if (err < 0) | |
548 | goto err_put_dev; | |
549 | } else { | |
550 | err = genphy_suspend(phydev); | |
551 | if (err < 0) | |
552 | goto err_put_dev; | |
553 | } | |
554 | ||
555 | if (ds->ops->adjust_link) | |
556 | ds->ops->adjust_link(ds, port, phydev); | |
557 | ||
558 | dev_dbg(ds->dev, "enabled port's phy: %s", phydev_name(phydev)); | |
559 | ||
560 | err_put_dev: | |
561 | put_device(&phydev->mdio.dev); | |
33615367 SR |
562 | return err; |
563 | } | |
564 | ||
565 | static int dsa_port_fixed_link_register_of(struct dsa_port *dp) | |
57ab1ca2 VD |
566 | { |
567 | struct device_node *dn = dp->dn; | |
568 | struct dsa_switch *ds = dp->ds; | |
569 | struct phy_device *phydev; | |
570 | int port = dp->index; | |
571 | int mode; | |
572 | int err; | |
573 | ||
33615367 SR |
574 | err = of_phy_register_fixed_link(dn); |
575 | if (err) { | |
576 | dev_err(ds->dev, | |
577 | "failed to register the fixed PHY of port %d\n", | |
578 | port); | |
579 | return err; | |
580 | } | |
57ab1ca2 | 581 | |
33615367 | 582 | phydev = of_phy_find_device(dn); |
57ab1ca2 | 583 | |
33615367 SR |
584 | mode = of_get_phy_mode(dn); |
585 | if (mode < 0) | |
586 | mode = PHY_INTERFACE_MODE_NA; | |
587 | phydev->interface = mode; | |
57ab1ca2 | 588 | |
33615367 SR |
589 | genphy_config_init(phydev); |
590 | genphy_read_status(phydev); | |
57ab1ca2 | 591 | |
33615367 SR |
592 | if (ds->ops->adjust_link) |
593 | ds->ops->adjust_link(ds, port, phydev); | |
57ab1ca2 | 594 | |
33615367 | 595 | put_device(&phydev->mdio.dev); |
57ab1ca2 VD |
596 | |
597 | return 0; | |
598 | } | |
599 | ||
0e279218 IC |
600 | static int dsa_port_phylink_register(struct dsa_port *dp) |
601 | { | |
602 | struct dsa_switch *ds = dp->ds; | |
603 | struct device_node *port_dn = dp->dn; | |
604 | int mode, err; | |
605 | ||
606 | mode = of_get_phy_mode(port_dn); | |
607 | if (mode < 0) | |
608 | mode = PHY_INTERFACE_MODE_NA; | |
609 | ||
610 | dp->pl_config.dev = ds->dev; | |
611 | dp->pl_config.type = PHYLINK_DEV; | |
612 | ||
613 | dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), | |
614 | mode, &dsa_port_phylink_mac_ops); | |
615 | if (IS_ERR(dp->pl)) { | |
616 | pr_err("error creating PHYLINK: %ld\n", PTR_ERR(dp->pl)); | |
617 | return PTR_ERR(dp->pl); | |
618 | } | |
619 | ||
620 | err = phylink_of_phy_connect(dp->pl, port_dn, 0); | |
621 | if (err) { | |
622 | pr_err("could not attach to PHY: %d\n", err); | |
623 | goto err_phy_connect; | |
624 | } | |
625 | ||
626 | rtnl_lock(); | |
627 | phylink_start(dp->pl); | |
628 | rtnl_unlock(); | |
629 | ||
630 | return 0; | |
631 | ||
632 | err_phy_connect: | |
633 | phylink_destroy(dp->pl); | |
634 | return err; | |
635 | } | |
636 | ||
33615367 | 637 | int dsa_port_link_register_of(struct dsa_port *dp) |
57ab1ca2 | 638 | { |
0e279218 IC |
639 | struct dsa_switch *ds = dp->ds; |
640 | ||
641 | if (!ds->ops->adjust_link) | |
642 | return dsa_port_phylink_register(dp); | |
643 | ||
644 | dev_warn(ds->dev, | |
645 | "Using legacy PHYLIB callbacks. Please migrate to PHYLINK!\n"); | |
646 | ||
33615367 SR |
647 | if (of_phy_is_fixed_link(dp->dn)) |
648 | return dsa_port_fixed_link_register_of(dp); | |
649 | else | |
650 | return dsa_port_setup_phy_of(dp, true); | |
651 | } | |
57ab1ca2 | 652 | |
33615367 SR |
653 | void dsa_port_link_unregister_of(struct dsa_port *dp) |
654 | { | |
0e279218 IC |
655 | struct dsa_switch *ds = dp->ds; |
656 | ||
657 | if (!ds->ops->adjust_link) { | |
658 | rtnl_lock(); | |
659 | phylink_disconnect_phy(dp->pl); | |
660 | rtnl_unlock(); | |
661 | phylink_destroy(dp->pl); | |
662 | return; | |
663 | } | |
664 | ||
33615367 SR |
665 | if (of_phy_is_fixed_link(dp->dn)) |
666 | of_phy_deregister_fixed_link(dp->dn); | |
667 | else | |
668 | dsa_port_setup_phy_of(dp, false); | |
57ab1ca2 | 669 | } |
cf963573 FF |
670 | |
671 | int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data) | |
672 | { | |
673 | struct phy_device *phydev; | |
674 | int ret = -EOPNOTSUPP; | |
675 | ||
676 | if (of_phy_is_fixed_link(dp->dn)) | |
677 | return ret; | |
678 | ||
679 | phydev = dsa_port_get_phy_device(dp); | |
680 | if (IS_ERR_OR_NULL(phydev)) | |
681 | return ret; | |
682 | ||
683 | ret = phy_ethtool_get_strings(phydev, data); | |
684 | put_device(&phydev->mdio.dev); | |
685 | ||
686 | return ret; | |
687 | } | |
688 | EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings); | |
689 | ||
690 | int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data) | |
691 | { | |
692 | struct phy_device *phydev; | |
693 | int ret = -EOPNOTSUPP; | |
694 | ||
695 | if (of_phy_is_fixed_link(dp->dn)) | |
696 | return ret; | |
697 | ||
698 | phydev = dsa_port_get_phy_device(dp); | |
699 | if (IS_ERR_OR_NULL(phydev)) | |
700 | return ret; | |
701 | ||
702 | ret = phy_ethtool_get_stats(phydev, NULL, data); | |
703 | put_device(&phydev->mdio.dev); | |
704 | ||
705 | return ret; | |
706 | } | |
707 | EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats); | |
708 | ||
709 | int dsa_port_get_phy_sset_count(struct dsa_port *dp) | |
710 | { | |
711 | struct phy_device *phydev; | |
712 | int ret = -EOPNOTSUPP; | |
713 | ||
714 | if (of_phy_is_fixed_link(dp->dn)) | |
715 | return ret; | |
716 | ||
717 | phydev = dsa_port_get_phy_device(dp); | |
718 | if (IS_ERR_OR_NULL(phydev)) | |
719 | return ret; | |
720 | ||
721 | ret = phy_ethtool_get_sset_count(phydev); | |
722 | put_device(&phydev->mdio.dev); | |
723 | ||
724 | return ret; | |
725 | } | |
726 | EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count); |