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