]>
Commit | Line | Data |
---|---|---|
9525ae83 RK |
1 | /* |
2 | * phylink models the MAC to optional PHY connection, supporting | |
3 | * technologies such as SFP cages where the PHY is hot-pluggable. | |
4 | * | |
5 | * Copyright (C) 2015 Russell King | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include <linux/ethtool.h> | |
12 | #include <linux/export.h> | |
13 | #include <linux/gpio/consumer.h> | |
14 | #include <linux/netdevice.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/of_mdio.h> | |
17 | #include <linux/phy.h> | |
18 | #include <linux/phy_fixed.h> | |
19 | #include <linux/phylink.h> | |
20 | #include <linux/rtnetlink.h> | |
21 | #include <linux/spinlock.h> | |
22 | #include <linux/workqueue.h> | |
23 | ||
ce0aa27f | 24 | #include "sfp.h" |
9525ae83 RK |
25 | #include "swphy.h" |
26 | ||
27 | #define SUPPORTED_INTERFACES \ | |
28 | (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \ | |
29 | SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane) | |
30 | #define ADVERTISED_INTERFACES \ | |
31 | (ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \ | |
32 | ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane) | |
33 | ||
34 | enum { | |
35 | PHYLINK_DISABLE_STOPPED, | |
ce0aa27f | 36 | PHYLINK_DISABLE_LINK, |
9525ae83 RK |
37 | }; |
38 | ||
8796c892 RK |
39 | /** |
40 | * struct phylink - internal data type for phylink | |
41 | */ | |
9525ae83 | 42 | struct phylink { |
8796c892 | 43 | /* private: */ |
9525ae83 RK |
44 | struct net_device *netdev; |
45 | const struct phylink_mac_ops *ops; | |
46 | ||
47 | unsigned long phylink_disable_state; /* bitmask of disables */ | |
48 | struct phy_device *phydev; | |
49 | phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ | |
50 | u8 link_an_mode; /* MLO_AN_xxx */ | |
51 | u8 link_port; /* The current non-phy ethtool port */ | |
52 | __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); | |
53 | ||
54 | /* The link configuration settings */ | |
55 | struct phylink_link_state link_config; | |
56 | struct gpio_desc *link_gpio; | |
1ac63e39 FF |
57 | void (*get_fixed_state)(struct net_device *dev, |
58 | struct phylink_link_state *s); | |
9525ae83 RK |
59 | |
60 | struct mutex state_mutex; | |
61 | struct phylink_link_state phy_state; | |
62 | struct work_struct resolve; | |
63 | ||
64 | bool mac_link_dropped; | |
ce0aa27f RK |
65 | |
66 | struct sfp_bus *sfp_bus; | |
9525ae83 RK |
67 | }; |
68 | ||
69 | static inline void linkmode_zero(unsigned long *dst) | |
70 | { | |
71 | bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
72 | } | |
73 | ||
74 | static inline void linkmode_copy(unsigned long *dst, const unsigned long *src) | |
75 | { | |
76 | bitmap_copy(dst, src, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
77 | } | |
78 | ||
79 | static inline void linkmode_and(unsigned long *dst, const unsigned long *a, | |
80 | const unsigned long *b) | |
81 | { | |
82 | bitmap_and(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
83 | } | |
84 | ||
85 | static inline void linkmode_or(unsigned long *dst, const unsigned long *a, | |
86 | const unsigned long *b) | |
87 | { | |
88 | bitmap_or(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
89 | } | |
90 | ||
91 | static inline bool linkmode_empty(const unsigned long *src) | |
92 | { | |
93 | return bitmap_empty(src, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
94 | } | |
95 | ||
8796c892 RK |
96 | /** |
97 | * phylink_set_port_modes() - set the port type modes in the ethtool mask | |
98 | * @mask: ethtool link mode mask | |
99 | * | |
100 | * Sets all the port type modes in the ethtool mask. MAC drivers should | |
101 | * use this in their 'validate' callback. | |
102 | */ | |
9525ae83 RK |
103 | void phylink_set_port_modes(unsigned long *mask) |
104 | { | |
105 | phylink_set(mask, TP); | |
106 | phylink_set(mask, AUI); | |
107 | phylink_set(mask, MII); | |
108 | phylink_set(mask, FIBRE); | |
109 | phylink_set(mask, BNC); | |
110 | phylink_set(mask, Backplane); | |
111 | } | |
112 | EXPORT_SYMBOL_GPL(phylink_set_port_modes); | |
113 | ||
114 | static int phylink_is_empty_linkmode(const unsigned long *linkmode) | |
115 | { | |
116 | __ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, }; | |
117 | ||
118 | phylink_set_port_modes(tmp); | |
119 | phylink_set(tmp, Autoneg); | |
120 | phylink_set(tmp, Pause); | |
121 | phylink_set(tmp, Asym_Pause); | |
122 | ||
123 | bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
124 | ||
125 | return linkmode_empty(tmp); | |
126 | } | |
127 | ||
128 | static const char *phylink_an_mode_str(unsigned int mode) | |
129 | { | |
130 | static const char *modestr[] = { | |
131 | [MLO_AN_PHY] = "phy", | |
132 | [MLO_AN_FIXED] = "fixed", | |
86a362c4 | 133 | [MLO_AN_INBAND] = "inband", |
9525ae83 RK |
134 | }; |
135 | ||
136 | return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; | |
137 | } | |
138 | ||
139 | static int phylink_validate(struct phylink *pl, unsigned long *supported, | |
140 | struct phylink_link_state *state) | |
141 | { | |
142 | pl->ops->validate(pl->netdev, supported, state); | |
143 | ||
144 | return phylink_is_empty_linkmode(supported) ? -EINVAL : 0; | |
145 | } | |
146 | ||
8fa7b9b6 RK |
147 | static int phylink_parse_fixedlink(struct phylink *pl, |
148 | struct fwnode_handle *fwnode) | |
9525ae83 | 149 | { |
8fa7b9b6 | 150 | struct fwnode_handle *fixed_node; |
9525ae83 RK |
151 | const struct phy_setting *s; |
152 | struct gpio_desc *desc; | |
9525ae83 | 153 | u32 speed; |
8fa7b9b6 | 154 | int ret; |
9525ae83 | 155 | |
8fa7b9b6 | 156 | fixed_node = fwnode_get_named_child_node(fwnode, "fixed-link"); |
9525ae83 | 157 | if (fixed_node) { |
8fa7b9b6 | 158 | ret = fwnode_property_read_u32(fixed_node, "speed", &speed); |
9525ae83 RK |
159 | |
160 | pl->link_config.speed = speed; | |
161 | pl->link_config.duplex = DUPLEX_HALF; | |
162 | ||
8fa7b9b6 | 163 | if (fwnode_property_read_bool(fixed_node, "full-duplex")) |
9525ae83 RK |
164 | pl->link_config.duplex = DUPLEX_FULL; |
165 | ||
166 | /* We treat the "pause" and "asym-pause" terminology as | |
167 | * defining the link partner's ability. */ | |
8fa7b9b6 | 168 | if (fwnode_property_read_bool(fixed_node, "pause")) |
9525ae83 | 169 | pl->link_config.pause |= MLO_PAUSE_SYM; |
8fa7b9b6 | 170 | if (fwnode_property_read_bool(fixed_node, "asym-pause")) |
9525ae83 RK |
171 | pl->link_config.pause |= MLO_PAUSE_ASYM; |
172 | ||
173 | if (ret == 0) { | |
8fa7b9b6 RK |
174 | desc = fwnode_get_named_gpiod(fixed_node, "link-gpios", |
175 | 0, GPIOD_IN, "?"); | |
9525ae83 RK |
176 | |
177 | if (!IS_ERR(desc)) | |
178 | pl->link_gpio = desc; | |
179 | else if (desc == ERR_PTR(-EPROBE_DEFER)) | |
180 | ret = -EPROBE_DEFER; | |
181 | } | |
8fa7b9b6 | 182 | fwnode_handle_put(fixed_node); |
9525ae83 RK |
183 | |
184 | if (ret) | |
185 | return ret; | |
186 | } else { | |
8fa7b9b6 RK |
187 | u32 prop[5]; |
188 | ||
189 | ret = fwnode_property_read_u32_array(fwnode, "fixed-link", | |
190 | NULL, 0); | |
191 | if (ret != ARRAY_SIZE(prop)) { | |
9525ae83 RK |
192 | netdev_err(pl->netdev, "broken fixed-link?\n"); |
193 | return -EINVAL; | |
194 | } | |
8fa7b9b6 RK |
195 | |
196 | ret = fwnode_property_read_u32_array(fwnode, "fixed-link", | |
197 | prop, ARRAY_SIZE(prop)); | |
198 | if (!ret) { | |
199 | pl->link_config.duplex = prop[1] ? | |
9525ae83 | 200 | DUPLEX_FULL : DUPLEX_HALF; |
8fa7b9b6 RK |
201 | pl->link_config.speed = prop[2]; |
202 | if (prop[3]) | |
9525ae83 | 203 | pl->link_config.pause |= MLO_PAUSE_SYM; |
8fa7b9b6 | 204 | if (prop[4]) |
9525ae83 RK |
205 | pl->link_config.pause |= MLO_PAUSE_ASYM; |
206 | } | |
207 | } | |
208 | ||
209 | if (pl->link_config.speed > SPEED_1000 && | |
210 | pl->link_config.duplex != DUPLEX_FULL) | |
211 | netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n", | |
212 | pl->link_config.speed); | |
213 | ||
214 | bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
215 | linkmode_copy(pl->link_config.advertising, pl->supported); | |
216 | phylink_validate(pl, pl->supported, &pl->link_config); | |
217 | ||
218 | s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex, | |
219 | pl->supported, | |
220 | __ETHTOOL_LINK_MODE_MASK_NBITS, true); | |
221 | linkmode_zero(pl->supported); | |
222 | phylink_set(pl->supported, MII); | |
223 | if (s) { | |
224 | __set_bit(s->bit, pl->supported); | |
225 | } else { | |
226 | netdev_warn(pl->netdev, "fixed link %s duplex %dMbps not recognised\n", | |
227 | pl->link_config.duplex == DUPLEX_FULL ? "full" : "half", | |
228 | pl->link_config.speed); | |
229 | } | |
230 | ||
231 | linkmode_and(pl->link_config.advertising, pl->link_config.advertising, | |
232 | pl->supported); | |
233 | ||
234 | pl->link_config.link = 1; | |
235 | pl->link_config.an_complete = 1; | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
8fa7b9b6 | 240 | static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) |
9525ae83 | 241 | { |
8fa7b9b6 | 242 | struct fwnode_handle *dn; |
9525ae83 RK |
243 | const char *managed; |
244 | ||
8fa7b9b6 RK |
245 | dn = fwnode_get_named_child_node(fwnode, "fixed-link"); |
246 | if (dn || fwnode_property_present(fwnode, "fixed-link")) | |
9525ae83 | 247 | pl->link_an_mode = MLO_AN_FIXED; |
8fa7b9b6 | 248 | fwnode_handle_put(dn); |
9525ae83 | 249 | |
8fa7b9b6 | 250 | if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 && |
9525ae83 RK |
251 | strcmp(managed, "in-band-status") == 0) { |
252 | if (pl->link_an_mode == MLO_AN_FIXED) { | |
253 | netdev_err(pl->netdev, | |
254 | "can't use both fixed-link and in-band-status\n"); | |
255 | return -EINVAL; | |
256 | } | |
257 | ||
258 | linkmode_zero(pl->supported); | |
259 | phylink_set(pl->supported, MII); | |
260 | phylink_set(pl->supported, Autoneg); | |
261 | phylink_set(pl->supported, Asym_Pause); | |
262 | phylink_set(pl->supported, Pause); | |
263 | pl->link_config.an_enabled = true; | |
86a362c4 | 264 | pl->link_an_mode = MLO_AN_INBAND; |
9525ae83 RK |
265 | |
266 | switch (pl->link_config.interface) { | |
267 | case PHY_INTERFACE_MODE_SGMII: | |
268 | phylink_set(pl->supported, 10baseT_Half); | |
269 | phylink_set(pl->supported, 10baseT_Full); | |
270 | phylink_set(pl->supported, 100baseT_Half); | |
271 | phylink_set(pl->supported, 100baseT_Full); | |
272 | phylink_set(pl->supported, 1000baseT_Half); | |
273 | phylink_set(pl->supported, 1000baseT_Full); | |
9525ae83 RK |
274 | break; |
275 | ||
276 | case PHY_INTERFACE_MODE_1000BASEX: | |
277 | phylink_set(pl->supported, 1000baseX_Full); | |
9525ae83 RK |
278 | break; |
279 | ||
280 | case PHY_INTERFACE_MODE_2500BASEX: | |
281 | phylink_set(pl->supported, 2500baseX_Full); | |
9525ae83 RK |
282 | break; |
283 | ||
da7c1862 RK |
284 | case PHY_INTERFACE_MODE_10GKR: |
285 | phylink_set(pl->supported, 10baseT_Half); | |
286 | phylink_set(pl->supported, 10baseT_Full); | |
287 | phylink_set(pl->supported, 100baseT_Half); | |
288 | phylink_set(pl->supported, 100baseT_Full); | |
289 | phylink_set(pl->supported, 1000baseT_Half); | |
290 | phylink_set(pl->supported, 1000baseT_Full); | |
291 | phylink_set(pl->supported, 1000baseX_Full); | |
292 | phylink_set(pl->supported, 10000baseKR_Full); | |
293 | phylink_set(pl->supported, 10000baseCR_Full); | |
294 | phylink_set(pl->supported, 10000baseSR_Full); | |
295 | phylink_set(pl->supported, 10000baseLR_Full); | |
296 | phylink_set(pl->supported, 10000baseLRM_Full); | |
297 | phylink_set(pl->supported, 10000baseER_Full); | |
da7c1862 RK |
298 | break; |
299 | ||
9525ae83 RK |
300 | default: |
301 | netdev_err(pl->netdev, | |
302 | "incorrect link mode %s for in-band status\n", | |
303 | phy_modes(pl->link_config.interface)); | |
304 | return -EINVAL; | |
305 | } | |
306 | ||
307 | linkmode_copy(pl->link_config.advertising, pl->supported); | |
308 | ||
309 | if (phylink_validate(pl, pl->supported, &pl->link_config)) { | |
310 | netdev_err(pl->netdev, | |
311 | "failed to validate link configuration for in-band status\n"); | |
312 | return -EINVAL; | |
313 | } | |
314 | } | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static void phylink_mac_config(struct phylink *pl, | |
320 | const struct phylink_link_state *state) | |
321 | { | |
322 | netdev_dbg(pl->netdev, | |
323 | "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", | |
324 | __func__, phylink_an_mode_str(pl->link_an_mode), | |
325 | phy_modes(state->interface), | |
326 | phy_speed_to_str(state->speed), | |
327 | phy_duplex_to_str(state->duplex), | |
328 | __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, | |
329 | state->pause, state->link, state->an_enabled); | |
330 | ||
331 | pl->ops->mac_config(pl->netdev, pl->link_an_mode, state); | |
332 | } | |
333 | ||
334 | static void phylink_mac_an_restart(struct phylink *pl) | |
335 | { | |
336 | if (pl->link_config.an_enabled && | |
365c1e64 | 337 | phy_interface_mode_is_8023z(pl->link_config.interface)) |
9525ae83 RK |
338 | pl->ops->mac_an_restart(pl->netdev); |
339 | } | |
340 | ||
341 | static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state) | |
342 | { | |
343 | struct net_device *ndev = pl->netdev; | |
344 | ||
345 | linkmode_copy(state->advertising, pl->link_config.advertising); | |
346 | linkmode_zero(state->lp_advertising); | |
347 | state->interface = pl->link_config.interface; | |
348 | state->an_enabled = pl->link_config.an_enabled; | |
349 | state->link = 1; | |
350 | ||
351 | return pl->ops->mac_link_state(ndev, state); | |
352 | } | |
353 | ||
354 | /* The fixed state is... fixed except for the link state, | |
1ac63e39 | 355 | * which may be determined by a GPIO or a callback. |
9525ae83 RK |
356 | */ |
357 | static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) | |
358 | { | |
359 | *state = pl->link_config; | |
1ac63e39 FF |
360 | if (pl->get_fixed_state) |
361 | pl->get_fixed_state(pl->netdev, state); | |
362 | else if (pl->link_gpio) | |
9525ae83 RK |
363 | state->link = !!gpiod_get_value(pl->link_gpio); |
364 | } | |
365 | ||
366 | /* Flow control is resolved according to our and the link partners | |
367 | * advertisments using the following drawn from the 802.3 specs: | |
368 | * Local device Link partner | |
369 | * Pause AsymDir Pause AsymDir Result | |
370 | * 1 X 1 X TX+RX | |
371 | * 0 1 1 1 RX | |
372 | * 1 1 0 1 TX | |
373 | */ | |
374 | static void phylink_resolve_flow(struct phylink *pl, | |
516b29ed | 375 | struct phylink_link_state *state) |
9525ae83 RK |
376 | { |
377 | int new_pause = 0; | |
378 | ||
379 | if (pl->link_config.pause & MLO_PAUSE_AN) { | |
380 | int pause = 0; | |
381 | ||
382 | if (phylink_test(pl->link_config.advertising, Pause)) | |
383 | pause |= MLO_PAUSE_SYM; | |
384 | if (phylink_test(pl->link_config.advertising, Asym_Pause)) | |
385 | pause |= MLO_PAUSE_ASYM; | |
386 | ||
387 | pause &= state->pause; | |
388 | ||
389 | if (pause & MLO_PAUSE_SYM) | |
390 | new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; | |
391 | else if (pause & MLO_PAUSE_ASYM) | |
392 | new_pause = state->pause & MLO_PAUSE_SYM ? | |
393 | MLO_PAUSE_RX : MLO_PAUSE_TX; | |
394 | } else { | |
395 | new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK; | |
396 | } | |
397 | ||
398 | state->pause &= ~MLO_PAUSE_TXRX_MASK; | |
399 | state->pause |= new_pause; | |
400 | } | |
401 | ||
402 | static const char *phylink_pause_to_str(int pause) | |
403 | { | |
404 | switch (pause & MLO_PAUSE_TXRX_MASK) { | |
405 | case MLO_PAUSE_TX | MLO_PAUSE_RX: | |
406 | return "rx/tx"; | |
407 | case MLO_PAUSE_TX: | |
408 | return "tx"; | |
409 | case MLO_PAUSE_RX: | |
410 | return "rx"; | |
411 | default: | |
412 | return "off"; | |
413 | } | |
414 | } | |
415 | ||
416 | static void phylink_resolve(struct work_struct *w) | |
417 | { | |
418 | struct phylink *pl = container_of(w, struct phylink, resolve); | |
419 | struct phylink_link_state link_state; | |
420 | struct net_device *ndev = pl->netdev; | |
421 | ||
422 | mutex_lock(&pl->state_mutex); | |
423 | if (pl->phylink_disable_state) { | |
424 | pl->mac_link_dropped = false; | |
425 | link_state.link = false; | |
426 | } else if (pl->mac_link_dropped) { | |
427 | link_state.link = false; | |
428 | } else { | |
429 | switch (pl->link_an_mode) { | |
430 | case MLO_AN_PHY: | |
431 | link_state = pl->phy_state; | |
432 | phylink_resolve_flow(pl, &link_state); | |
433 | phylink_mac_config(pl, &link_state); | |
434 | break; | |
435 | ||
436 | case MLO_AN_FIXED: | |
437 | phylink_get_fixed_state(pl, &link_state); | |
438 | phylink_mac_config(pl, &link_state); | |
439 | break; | |
440 | ||
86a362c4 | 441 | case MLO_AN_INBAND: |
9525ae83 RK |
442 | phylink_get_mac_state(pl, &link_state); |
443 | if (pl->phydev) { | |
444 | bool changed = false; | |
445 | ||
446 | link_state.link = link_state.link && | |
447 | pl->phy_state.link; | |
448 | ||
449 | if (pl->phy_state.interface != | |
450 | link_state.interface) { | |
451 | link_state.interface = pl->phy_state.interface; | |
452 | changed = true; | |
453 | } | |
454 | ||
455 | /* Propagate the flow control from the PHY | |
456 | * to the MAC. Also propagate the interface | |
457 | * if changed. | |
458 | */ | |
459 | if (pl->phy_state.link || changed) { | |
460 | link_state.pause |= pl->phy_state.pause; | |
461 | phylink_resolve_flow(pl, &link_state); | |
462 | ||
463 | phylink_mac_config(pl, &link_state); | |
464 | } | |
465 | } | |
466 | break; | |
9525ae83 RK |
467 | } |
468 | } | |
469 | ||
470 | if (link_state.link != netif_carrier_ok(ndev)) { | |
471 | if (!link_state.link) { | |
472 | netif_carrier_off(ndev); | |
473 | pl->ops->mac_link_down(ndev, pl->link_an_mode); | |
474 | netdev_info(ndev, "Link is Down\n"); | |
475 | } else { | |
476 | pl->ops->mac_link_up(ndev, pl->link_an_mode, | |
477 | pl->phydev); | |
478 | ||
479 | netif_carrier_on(ndev); | |
480 | ||
481 | netdev_info(ndev, | |
482 | "Link is Up - %s/%s - flow control %s\n", | |
483 | phy_speed_to_str(link_state.speed), | |
484 | phy_duplex_to_str(link_state.duplex), | |
485 | phylink_pause_to_str(link_state.pause)); | |
486 | } | |
487 | } | |
488 | if (!link_state.link && pl->mac_link_dropped) { | |
489 | pl->mac_link_dropped = false; | |
490 | queue_work(system_power_efficient_wq, &pl->resolve); | |
491 | } | |
492 | mutex_unlock(&pl->state_mutex); | |
493 | } | |
494 | ||
495 | static void phylink_run_resolve(struct phylink *pl) | |
496 | { | |
497 | if (!pl->phylink_disable_state) | |
498 | queue_work(system_power_efficient_wq, &pl->resolve); | |
499 | } | |
500 | ||
ce0aa27f RK |
501 | static const struct sfp_upstream_ops sfp_phylink_ops; |
502 | ||
8fa7b9b6 RK |
503 | static int phylink_register_sfp(struct phylink *pl, |
504 | struct fwnode_handle *fwnode) | |
ce0aa27f | 505 | { |
8fa7b9b6 RK |
506 | struct fwnode_reference_args ref; |
507 | int ret; | |
ce0aa27f | 508 | |
02477809 FF |
509 | if (!fwnode) |
510 | return 0; | |
511 | ||
8fa7b9b6 RK |
512 | ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, |
513 | 0, 0, &ref); | |
514 | if (ret < 0) { | |
515 | if (ret == -ENOENT) | |
516 | return 0; | |
517 | ||
518 | netdev_err(pl->netdev, "unable to parse \"sfp\" node: %d\n", | |
519 | ret); | |
520 | return ret; | |
521 | } | |
ce0aa27f | 522 | |
8fa7b9b6 | 523 | pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl->netdev, pl, |
ce0aa27f RK |
524 | &sfp_phylink_ops); |
525 | if (!pl->sfp_bus) | |
526 | return -ENOMEM; | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
8796c892 RK |
531 | /** |
532 | * phylink_create() - create a phylink instance | |
533 | * @ndev: a pointer to the &struct net_device | |
8fa7b9b6 RK |
534 | * @fwnode: a pointer to a &struct fwnode_handle describing the network |
535 | * interface | |
8796c892 RK |
536 | * @iface: the desired link mode defined by &typedef phy_interface_t |
537 | * @ops: a pointer to a &struct phylink_mac_ops for the MAC. | |
538 | * | |
539 | * Create a new phylink instance, and parse the link parameters found in @np. | |
540 | * This will parse in-band modes, fixed-link or SFP configuration. | |
541 | * | |
542 | * Returns a pointer to a &struct phylink, or an error-pointer value. Users | |
543 | * must use IS_ERR() to check for errors from this function. | |
544 | */ | |
8fa7b9b6 RK |
545 | struct phylink *phylink_create(struct net_device *ndev, |
546 | struct fwnode_handle *fwnode, | |
516b29ed FF |
547 | phy_interface_t iface, |
548 | const struct phylink_mac_ops *ops) | |
9525ae83 RK |
549 | { |
550 | struct phylink *pl; | |
551 | int ret; | |
552 | ||
553 | pl = kzalloc(sizeof(*pl), GFP_KERNEL); | |
554 | if (!pl) | |
555 | return ERR_PTR(-ENOMEM); | |
556 | ||
557 | mutex_init(&pl->state_mutex); | |
558 | INIT_WORK(&pl->resolve, phylink_resolve); | |
559 | pl->netdev = ndev; | |
560 | pl->phy_state.interface = iface; | |
561 | pl->link_interface = iface; | |
4be11ef0 FF |
562 | if (iface == PHY_INTERFACE_MODE_MOCA) |
563 | pl->link_port = PORT_BNC; | |
564 | else | |
565 | pl->link_port = PORT_MII; | |
9525ae83 RK |
566 | pl->link_config.interface = iface; |
567 | pl->link_config.pause = MLO_PAUSE_AN; | |
568 | pl->link_config.speed = SPEED_UNKNOWN; | |
569 | pl->link_config.duplex = DUPLEX_UNKNOWN; | |
74ee0e8c | 570 | pl->link_config.an_enabled = true; |
9525ae83 RK |
571 | pl->ops = ops; |
572 | __set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); | |
573 | ||
574 | bitmap_fill(pl->supported, __ETHTOOL_LINK_MODE_MASK_NBITS); | |
575 | linkmode_copy(pl->link_config.advertising, pl->supported); | |
576 | phylink_validate(pl, pl->supported, &pl->link_config); | |
577 | ||
8fa7b9b6 | 578 | ret = phylink_parse_mode(pl, fwnode); |
9525ae83 RK |
579 | if (ret < 0) { |
580 | kfree(pl); | |
581 | return ERR_PTR(ret); | |
582 | } | |
583 | ||
584 | if (pl->link_an_mode == MLO_AN_FIXED) { | |
8fa7b9b6 | 585 | ret = phylink_parse_fixedlink(pl, fwnode); |
9525ae83 RK |
586 | if (ret < 0) { |
587 | kfree(pl); | |
588 | return ERR_PTR(ret); | |
589 | } | |
590 | } | |
591 | ||
8fa7b9b6 | 592 | ret = phylink_register_sfp(pl, fwnode); |
ce0aa27f RK |
593 | if (ret < 0) { |
594 | kfree(pl); | |
595 | return ERR_PTR(ret); | |
596 | } | |
597 | ||
9525ae83 RK |
598 | return pl; |
599 | } | |
600 | EXPORT_SYMBOL_GPL(phylink_create); | |
601 | ||
8796c892 RK |
602 | /** |
603 | * phylink_destroy() - cleanup and destroy the phylink instance | |
604 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
605 | * | |
606 | * Destroy a phylink instance. Any PHY that has been attached must have been | |
607 | * cleaned up via phylink_disconnect_phy() prior to calling this function. | |
608 | */ | |
9525ae83 RK |
609 | void phylink_destroy(struct phylink *pl) |
610 | { | |
ce0aa27f RK |
611 | if (pl->sfp_bus) |
612 | sfp_unregister_upstream(pl->sfp_bus); | |
613 | ||
9525ae83 RK |
614 | cancel_work_sync(&pl->resolve); |
615 | kfree(pl); | |
616 | } | |
617 | EXPORT_SYMBOL_GPL(phylink_destroy); | |
618 | ||
1ec6e530 WY |
619 | static void phylink_phy_change(struct phy_device *phydev, bool up, |
620 | bool do_carrier) | |
9525ae83 RK |
621 | { |
622 | struct phylink *pl = phydev->phylink; | |
623 | ||
624 | mutex_lock(&pl->state_mutex); | |
625 | pl->phy_state.speed = phydev->speed; | |
626 | pl->phy_state.duplex = phydev->duplex; | |
627 | pl->phy_state.pause = MLO_PAUSE_NONE; | |
628 | if (phydev->pause) | |
629 | pl->phy_state.pause |= MLO_PAUSE_SYM; | |
630 | if (phydev->asym_pause) | |
631 | pl->phy_state.pause |= MLO_PAUSE_ASYM; | |
632 | pl->phy_state.interface = phydev->interface; | |
633 | pl->phy_state.link = up; | |
634 | mutex_unlock(&pl->state_mutex); | |
635 | ||
636 | phylink_run_resolve(pl); | |
637 | ||
638 | netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down", | |
516b29ed | 639 | phy_modes(phydev->interface), |
9525ae83 RK |
640 | phy_speed_to_str(phydev->speed), |
641 | phy_duplex_to_str(phydev->duplex)); | |
642 | } | |
643 | ||
644 | static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) | |
645 | { | |
646 | struct phylink_link_state config; | |
647 | __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); | |
648 | u32 advertising; | |
649 | int ret; | |
650 | ||
651 | memset(&config, 0, sizeof(config)); | |
652 | ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported); | |
653 | ethtool_convert_legacy_u32_to_link_mode(config.advertising, | |
654 | phy->advertising); | |
655 | config.interface = pl->link_config.interface; | |
656 | ||
657 | /* | |
658 | * This is the new way of dealing with flow control for PHYs, | |
659 | * as described by Timur Tabi in commit 529ed1275263 ("net: phy: | |
660 | * phy drivers should not set SUPPORTED_[Asym_]Pause") except | |
661 | * using our validate call to the MAC, we rely upon the MAC | |
662 | * clearing the bits from both supported and advertising fields. | |
663 | */ | |
664 | if (phylink_test(supported, Pause)) | |
665 | phylink_set(config.advertising, Pause); | |
666 | if (phylink_test(supported, Asym_Pause)) | |
667 | phylink_set(config.advertising, Asym_Pause); | |
668 | ||
669 | ret = phylink_validate(pl, supported, &config); | |
670 | if (ret) | |
671 | return ret; | |
672 | ||
673 | phy->phylink = pl; | |
674 | phy->phy_link_change = phylink_phy_change; | |
675 | ||
676 | netdev_info(pl->netdev, | |
677 | "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev), | |
678 | phy->drv->name); | |
679 | ||
680 | mutex_lock(&phy->lock); | |
681 | mutex_lock(&pl->state_mutex); | |
9525ae83 RK |
682 | pl->phydev = phy; |
683 | linkmode_copy(pl->supported, supported); | |
684 | linkmode_copy(pl->link_config.advertising, config.advertising); | |
685 | ||
686 | /* Restrict the phy advertisment according to the MAC support. */ | |
687 | ethtool_convert_link_mode_to_legacy_u32(&advertising, config.advertising); | |
688 | phy->advertising = advertising; | |
689 | mutex_unlock(&pl->state_mutex); | |
690 | mutex_unlock(&phy->lock); | |
691 | ||
692 | netdev_dbg(pl->netdev, | |
693 | "phy: setting supported %*pb advertising 0x%08x\n", | |
694 | __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported, | |
695 | phy->advertising); | |
696 | ||
697 | phy_start_machine(phy); | |
698 | if (phy->irq > 0) | |
699 | phy_start_interrupts(phy); | |
700 | ||
701 | return 0; | |
702 | } | |
703 | ||
8796c892 RK |
704 | /** |
705 | * phylink_connect_phy() - connect a PHY to the phylink instance | |
706 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
707 | * @phy: a pointer to a &struct phy_device. | |
708 | * | |
709 | * Connect @phy to the phylink instance specified by @pl by calling | |
710 | * phy_attach_direct(). Configure the @phy according to the MAC driver's | |
711 | * capabilities, start the PHYLIB state machine and enable any interrupts | |
712 | * that the PHY supports. | |
713 | * | |
714 | * This updates the phylink's ethtool supported and advertising link mode | |
715 | * masks. | |
716 | * | |
717 | * Returns 0 on success or a negative errno. | |
718 | */ | |
9525ae83 RK |
719 | int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) |
720 | { | |
721 | int ret; | |
722 | ||
cf4f2675 | 723 | if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED || |
86a362c4 RK |
724 | (pl->link_an_mode == MLO_AN_INBAND && |
725 | phy_interface_mode_is_8023z(pl->link_interface)))) | |
cf4f2675 RK |
726 | return -EINVAL; |
727 | ||
fe1c3ef2 RK |
728 | if (pl->phydev) |
729 | return -EBUSY; | |
730 | ||
4904b6ea FF |
731 | /* Use PHY device/driver interface */ |
732 | if (pl->link_interface == PHY_INTERFACE_MODE_NA) { | |
733 | pl->link_interface = phy->interface; | |
734 | pl->link_config.interface = pl->link_interface; | |
735 | } | |
736 | ||
9525ae83 RK |
737 | ret = phy_attach_direct(pl->netdev, phy, 0, pl->link_interface); |
738 | if (ret) | |
739 | return ret; | |
740 | ||
741 | ret = phylink_bringup_phy(pl, phy); | |
742 | if (ret) | |
743 | phy_detach(phy); | |
744 | ||
745 | return ret; | |
746 | } | |
747 | EXPORT_SYMBOL_GPL(phylink_connect_phy); | |
748 | ||
8796c892 RK |
749 | /** |
750 | * phylink_of_phy_connect() - connect the PHY specified in the DT mode. | |
751 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
752 | * @dn: a pointer to a &struct device_node. | |
0a62964c | 753 | * @flags: PHY-specific flags to communicate to the PHY device driver |
8796c892 RK |
754 | * |
755 | * Connect the phy specified in the device node @dn to the phylink instance | |
756 | * specified by @pl. Actions specified in phylink_connect_phy() will be | |
757 | * performed. | |
758 | * | |
759 | * Returns 0 on success or a negative errno. | |
760 | */ | |
0a62964c FF |
761 | int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, |
762 | u32 flags) | |
9525ae83 RK |
763 | { |
764 | struct device_node *phy_node; | |
765 | struct phy_device *phy_dev; | |
766 | int ret; | |
767 | ||
cf4f2675 RK |
768 | /* Fixed links and 802.3z are handled without needing a PHY */ |
769 | if (pl->link_an_mode == MLO_AN_FIXED || | |
86a362c4 RK |
770 | (pl->link_an_mode == MLO_AN_INBAND && |
771 | phy_interface_mode_is_8023z(pl->link_interface))) | |
9525ae83 RK |
772 | return 0; |
773 | ||
774 | phy_node = of_parse_phandle(dn, "phy-handle", 0); | |
775 | if (!phy_node) | |
776 | phy_node = of_parse_phandle(dn, "phy", 0); | |
777 | if (!phy_node) | |
778 | phy_node = of_parse_phandle(dn, "phy-device", 0); | |
779 | ||
780 | if (!phy_node) { | |
d38b4afd | 781 | if (pl->link_an_mode == MLO_AN_PHY) |
9525ae83 | 782 | return -ENODEV; |
9525ae83 RK |
783 | return 0; |
784 | } | |
785 | ||
0a62964c FF |
786 | phy_dev = of_phy_attach(pl->netdev, phy_node, flags, |
787 | pl->link_interface); | |
9525ae83 RK |
788 | /* We're done with the phy_node handle */ |
789 | of_node_put(phy_node); | |
790 | ||
791 | if (!phy_dev) | |
792 | return -ENODEV; | |
793 | ||
794 | ret = phylink_bringup_phy(pl, phy_dev); | |
795 | if (ret) | |
796 | phy_detach(phy_dev); | |
797 | ||
798 | return ret; | |
799 | } | |
800 | EXPORT_SYMBOL_GPL(phylink_of_phy_connect); | |
801 | ||
8796c892 RK |
802 | /** |
803 | * phylink_disconnect_phy() - disconnect any PHY attached to the phylink | |
804 | * instance. | |
805 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
806 | * | |
807 | * Disconnect any current PHY from the phylink instance described by @pl. | |
808 | */ | |
9525ae83 RK |
809 | void phylink_disconnect_phy(struct phylink *pl) |
810 | { | |
811 | struct phy_device *phy; | |
812 | ||
8b874514 | 813 | ASSERT_RTNL(); |
9525ae83 RK |
814 | |
815 | phy = pl->phydev; | |
816 | if (phy) { | |
817 | mutex_lock(&phy->lock); | |
818 | mutex_lock(&pl->state_mutex); | |
9525ae83 RK |
819 | pl->phydev = NULL; |
820 | mutex_unlock(&pl->state_mutex); | |
821 | mutex_unlock(&phy->lock); | |
822 | flush_work(&pl->resolve); | |
823 | ||
824 | phy_disconnect(phy); | |
825 | } | |
826 | } | |
827 | EXPORT_SYMBOL_GPL(phylink_disconnect_phy); | |
828 | ||
1ac63e39 FF |
829 | /** |
830 | * phylink_fixed_state_cb() - allow setting a fixed link callback | |
831 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
832 | * @cb: callback to execute to determine the fixed link state. | |
833 | * | |
834 | * The MAC driver should call this driver when the state of its link | |
835 | * can be determined through e.g: an out of band MMIO register. | |
836 | */ | |
837 | int phylink_fixed_state_cb(struct phylink *pl, | |
838 | void (*cb)(struct net_device *dev, | |
839 | struct phylink_link_state *state)) | |
840 | { | |
841 | /* It does not make sense to let the link be overriden unless we use | |
842 | * MLO_AN_FIXED | |
843 | */ | |
844 | if (pl->link_an_mode != MLO_AN_FIXED) | |
845 | return -EINVAL; | |
846 | ||
847 | mutex_lock(&pl->state_mutex); | |
848 | pl->get_fixed_state = cb; | |
849 | mutex_unlock(&pl->state_mutex); | |
850 | ||
851 | return 0; | |
852 | } | |
853 | EXPORT_SYMBOL_GPL(phylink_fixed_state_cb); | |
854 | ||
8796c892 RK |
855 | /** |
856 | * phylink_mac_change() - notify phylink of a change in MAC state | |
857 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
858 | * @up: indicates whether the link is currently up. | |
859 | * | |
860 | * The MAC driver should call this driver when the state of its link | |
861 | * changes (eg, link failure, new negotiation results, etc.) | |
862 | */ | |
9525ae83 RK |
863 | void phylink_mac_change(struct phylink *pl, bool up) |
864 | { | |
865 | if (!up) | |
866 | pl->mac_link_dropped = true; | |
867 | phylink_run_resolve(pl); | |
868 | netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down"); | |
869 | } | |
870 | EXPORT_SYMBOL_GPL(phylink_mac_change); | |
871 | ||
8796c892 RK |
872 | /** |
873 | * phylink_start() - start a phylink instance | |
874 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
875 | * | |
876 | * Start the phylink instance specified by @pl, configuring the MAC for the | |
877 | * desired link mode(s) and negotiation style. This should be called from the | |
878 | * network device driver's &struct net_device_ops ndo_open() method. | |
879 | */ | |
9525ae83 RK |
880 | void phylink_start(struct phylink *pl) |
881 | { | |
8b874514 | 882 | ASSERT_RTNL(); |
9525ae83 RK |
883 | |
884 | netdev_info(pl->netdev, "configuring for %s/%s link mode\n", | |
885 | phylink_an_mode_str(pl->link_an_mode), | |
886 | phy_modes(pl->link_config.interface)); | |
887 | ||
888 | /* Apply the link configuration to the MAC when starting. This allows | |
889 | * a fixed-link to start with the correct parameters, and also | |
890 | * ensures that we set the appropriate advertisment for Serdes links. | |
891 | */ | |
892 | phylink_resolve_flow(pl, &pl->link_config); | |
893 | phylink_mac_config(pl, &pl->link_config); | |
894 | ||
85b43945 RK |
895 | /* Restart autonegotiation if using 802.3z to ensure that the link |
896 | * parameters are properly negotiated. This is necessary for DSA | |
897 | * switches using 802.3z negotiation to ensure they see our modes. | |
898 | */ | |
899 | phylink_mac_an_restart(pl); | |
900 | ||
9525ae83 RK |
901 | clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); |
902 | phylink_run_resolve(pl); | |
903 | ||
ce0aa27f RK |
904 | if (pl->sfp_bus) |
905 | sfp_upstream_start(pl->sfp_bus); | |
9525ae83 RK |
906 | if (pl->phydev) |
907 | phy_start(pl->phydev); | |
908 | } | |
909 | EXPORT_SYMBOL_GPL(phylink_start); | |
910 | ||
8796c892 RK |
911 | /** |
912 | * phylink_stop() - stop a phylink instance | |
913 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
914 | * | |
915 | * Stop the phylink instance specified by @pl. This should be called from the | |
916 | * network device driver's &struct net_device_ops ndo_stop() method. The | |
917 | * network device's carrier state should not be changed prior to calling this | |
918 | * function. | |
919 | */ | |
9525ae83 RK |
920 | void phylink_stop(struct phylink *pl) |
921 | { | |
8b874514 | 922 | ASSERT_RTNL(); |
9525ae83 RK |
923 | |
924 | if (pl->phydev) | |
925 | phy_stop(pl->phydev); | |
ce0aa27f RK |
926 | if (pl->sfp_bus) |
927 | sfp_upstream_stop(pl->sfp_bus); | |
9525ae83 RK |
928 | |
929 | set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); | |
2012b7d6 | 930 | queue_work(system_power_efficient_wq, &pl->resolve); |
9525ae83 RK |
931 | flush_work(&pl->resolve); |
932 | } | |
933 | EXPORT_SYMBOL_GPL(phylink_stop); | |
934 | ||
8796c892 RK |
935 | /** |
936 | * phylink_ethtool_get_wol() - get the wake on lan parameters for the PHY | |
937 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
938 | * @wol: a pointer to &struct ethtool_wolinfo to hold the read parameters | |
939 | * | |
940 | * Read the wake on lan parameters from the PHY attached to the phylink | |
941 | * instance specified by @pl. If no PHY is currently attached, report no | |
942 | * support for wake on lan. | |
943 | */ | |
9525ae83 RK |
944 | void phylink_ethtool_get_wol(struct phylink *pl, struct ethtool_wolinfo *wol) |
945 | { | |
8b874514 | 946 | ASSERT_RTNL(); |
9525ae83 RK |
947 | |
948 | wol->supported = 0; | |
949 | wol->wolopts = 0; | |
950 | ||
951 | if (pl->phydev) | |
952 | phy_ethtool_get_wol(pl->phydev, wol); | |
953 | } | |
954 | EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol); | |
955 | ||
8796c892 RK |
956 | /** |
957 | * phylink_ethtool_set_wol() - set wake on lan parameters | |
958 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
959 | * @wol: a pointer to &struct ethtool_wolinfo for the desired parameters | |
960 | * | |
961 | * Set the wake on lan parameters for the PHY attached to the phylink | |
962 | * instance specified by @pl. If no PHY is attached, returns %EOPNOTSUPP | |
963 | * error. | |
964 | * | |
965 | * Returns zero on success or negative errno code. | |
966 | */ | |
9525ae83 RK |
967 | int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol) |
968 | { | |
969 | int ret = -EOPNOTSUPP; | |
970 | ||
8b874514 | 971 | ASSERT_RTNL(); |
9525ae83 RK |
972 | |
973 | if (pl->phydev) | |
974 | ret = phy_ethtool_set_wol(pl->phydev, wol); | |
975 | ||
976 | return ret; | |
977 | } | |
978 | EXPORT_SYMBOL_GPL(phylink_ethtool_set_wol); | |
979 | ||
980 | static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b) | |
981 | { | |
982 | __ETHTOOL_DECLARE_LINK_MODE_MASK(mask); | |
983 | ||
984 | linkmode_zero(mask); | |
985 | phylink_set_port_modes(mask); | |
986 | ||
987 | linkmode_and(dst, dst, mask); | |
988 | linkmode_or(dst, dst, b); | |
989 | } | |
990 | ||
991 | static void phylink_get_ksettings(const struct phylink_link_state *state, | |
992 | struct ethtool_link_ksettings *kset) | |
993 | { | |
994 | phylink_merge_link_mode(kset->link_modes.advertising, state->advertising); | |
995 | linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising); | |
996 | kset->base.speed = state->speed; | |
997 | kset->base.duplex = state->duplex; | |
998 | kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE : | |
999 | AUTONEG_DISABLE; | |
1000 | } | |
1001 | ||
8796c892 RK |
1002 | /** |
1003 | * phylink_ethtool_ksettings_get() - get the current link settings | |
1004 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1005 | * @kset: a pointer to a &struct ethtool_link_ksettings to hold link settings | |
1006 | * | |
1007 | * Read the current link settings for the phylink instance specified by @pl. | |
1008 | * This will be the link settings read from the MAC, PHY or fixed link | |
1009 | * settings depending on the current negotiation mode. | |
1010 | */ | |
9525ae83 | 1011 | int phylink_ethtool_ksettings_get(struct phylink *pl, |
516b29ed | 1012 | struct ethtool_link_ksettings *kset) |
9525ae83 RK |
1013 | { |
1014 | struct phylink_link_state link_state; | |
1015 | ||
8b874514 | 1016 | ASSERT_RTNL(); |
9525ae83 RK |
1017 | |
1018 | if (pl->phydev) { | |
1019 | phy_ethtool_ksettings_get(pl->phydev, kset); | |
1020 | } else { | |
1021 | kset->base.port = pl->link_port; | |
1022 | } | |
1023 | ||
1024 | linkmode_copy(kset->link_modes.supported, pl->supported); | |
1025 | ||
1026 | switch (pl->link_an_mode) { | |
1027 | case MLO_AN_FIXED: | |
1028 | /* We are using fixed settings. Report these as the | |
1029 | * current link settings - and note that these also | |
1030 | * represent the supported speeds/duplex/pause modes. | |
1031 | */ | |
1032 | phylink_get_fixed_state(pl, &link_state); | |
1033 | phylink_get_ksettings(&link_state, kset); | |
1034 | break; | |
1035 | ||
86a362c4 | 1036 | case MLO_AN_INBAND: |
9525ae83 RK |
1037 | /* If there is a phy attached, then use the reported |
1038 | * settings from the phy with no modification. | |
1039 | */ | |
1040 | if (pl->phydev) | |
1041 | break; | |
1042 | ||
9525ae83 RK |
1043 | phylink_get_mac_state(pl, &link_state); |
1044 | ||
1045 | /* The MAC is reporting the link results from its own PCS | |
1046 | * layer via in-band status. Report these as the current | |
1047 | * link settings. | |
1048 | */ | |
1049 | phylink_get_ksettings(&link_state, kset); | |
1050 | break; | |
1051 | } | |
1052 | ||
1053 | return 0; | |
1054 | } | |
1055 | EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get); | |
1056 | ||
8796c892 RK |
1057 | /** |
1058 | * phylink_ethtool_ksettings_set() - set the link settings | |
1059 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1060 | * @kset: a pointer to a &struct ethtool_link_ksettings for the desired modes | |
1061 | */ | |
9525ae83 | 1062 | int phylink_ethtool_ksettings_set(struct phylink *pl, |
516b29ed | 1063 | const struct ethtool_link_ksettings *kset) |
9525ae83 RK |
1064 | { |
1065 | struct ethtool_link_ksettings our_kset; | |
1066 | struct phylink_link_state config; | |
1067 | int ret; | |
1068 | ||
8b874514 | 1069 | ASSERT_RTNL(); |
9525ae83 RK |
1070 | |
1071 | if (kset->base.autoneg != AUTONEG_DISABLE && | |
1072 | kset->base.autoneg != AUTONEG_ENABLE) | |
1073 | return -EINVAL; | |
1074 | ||
1075 | config = pl->link_config; | |
1076 | ||
1077 | /* Mask out unsupported advertisments */ | |
1078 | linkmode_and(config.advertising, kset->link_modes.advertising, | |
1079 | pl->supported); | |
1080 | ||
1081 | /* FIXME: should we reject autoneg if phy/mac does not support it? */ | |
1082 | if (kset->base.autoneg == AUTONEG_DISABLE) { | |
1083 | const struct phy_setting *s; | |
1084 | ||
1085 | /* Autonegotiation disabled, select a suitable speed and | |
1086 | * duplex. | |
1087 | */ | |
1088 | s = phy_lookup_setting(kset->base.speed, kset->base.duplex, | |
1089 | pl->supported, | |
1090 | __ETHTOOL_LINK_MODE_MASK_NBITS, false); | |
1091 | if (!s) | |
1092 | return -EINVAL; | |
1093 | ||
1094 | /* If we have a fixed link (as specified by firmware), refuse | |
1095 | * to change link parameters. | |
1096 | */ | |
1097 | if (pl->link_an_mode == MLO_AN_FIXED && | |
1098 | (s->speed != pl->link_config.speed || | |
1099 | s->duplex != pl->link_config.duplex)) | |
1100 | return -EINVAL; | |
1101 | ||
1102 | config.speed = s->speed; | |
1103 | config.duplex = s->duplex; | |
1104 | config.an_enabled = false; | |
1105 | ||
1106 | __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); | |
1107 | } else { | |
1108 | /* If we have a fixed link, refuse to enable autonegotiation */ | |
1109 | if (pl->link_an_mode == MLO_AN_FIXED) | |
1110 | return -EINVAL; | |
1111 | ||
1112 | config.speed = SPEED_UNKNOWN; | |
1113 | config.duplex = DUPLEX_UNKNOWN; | |
1114 | config.an_enabled = true; | |
1115 | ||
1116 | __set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); | |
1117 | } | |
1118 | ||
1119 | if (phylink_validate(pl, pl->supported, &config)) | |
1120 | return -EINVAL; | |
1121 | ||
1122 | /* If autonegotiation is enabled, we must have an advertisment */ | |
1123 | if (config.an_enabled && phylink_is_empty_linkmode(config.advertising)) | |
1124 | return -EINVAL; | |
1125 | ||
1126 | our_kset = *kset; | |
1127 | linkmode_copy(our_kset.link_modes.advertising, config.advertising); | |
1128 | our_kset.base.speed = config.speed; | |
1129 | our_kset.base.duplex = config.duplex; | |
1130 | ||
1131 | /* If we have a PHY, configure the phy */ | |
1132 | if (pl->phydev) { | |
1133 | ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset); | |
1134 | if (ret) | |
1135 | return ret; | |
1136 | } | |
1137 | ||
1138 | mutex_lock(&pl->state_mutex); | |
1139 | /* Configure the MAC to match the new settings */ | |
1140 | linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising); | |
182088aa | 1141 | pl->link_config.interface = config.interface; |
9525ae83 RK |
1142 | pl->link_config.speed = our_kset.base.speed; |
1143 | pl->link_config.duplex = our_kset.base.duplex; | |
1144 | pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE; | |
1145 | ||
1146 | if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { | |
1147 | phylink_mac_config(pl, &pl->link_config); | |
1148 | phylink_mac_an_restart(pl); | |
1149 | } | |
1150 | mutex_unlock(&pl->state_mutex); | |
1151 | ||
d18c2a1b | 1152 | return 0; |
9525ae83 RK |
1153 | } |
1154 | EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set); | |
1155 | ||
8796c892 RK |
1156 | /** |
1157 | * phylink_ethtool_nway_reset() - restart negotiation | |
1158 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1159 | * | |
1160 | * Restart negotiation for the phylink instance specified by @pl. This will | |
1161 | * cause any attached phy to restart negotiation with the link partner, and | |
1162 | * if the MAC is in a BaseX mode, the MAC will also be requested to restart | |
1163 | * negotiation. | |
1164 | * | |
1165 | * Returns zero on success, or negative error code. | |
1166 | */ | |
9525ae83 RK |
1167 | int phylink_ethtool_nway_reset(struct phylink *pl) |
1168 | { | |
1169 | int ret = 0; | |
1170 | ||
8b874514 | 1171 | ASSERT_RTNL(); |
9525ae83 RK |
1172 | |
1173 | if (pl->phydev) | |
1174 | ret = phy_restart_aneg(pl->phydev); | |
1175 | phylink_mac_an_restart(pl); | |
1176 | ||
1177 | return ret; | |
1178 | } | |
1179 | EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset); | |
1180 | ||
8796c892 RK |
1181 | /** |
1182 | * phylink_ethtool_get_pauseparam() - get the current pause parameters | |
1183 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1184 | * @pause: a pointer to a &struct ethtool_pauseparam | |
1185 | */ | |
9525ae83 RK |
1186 | void phylink_ethtool_get_pauseparam(struct phylink *pl, |
1187 | struct ethtool_pauseparam *pause) | |
1188 | { | |
8b874514 | 1189 | ASSERT_RTNL(); |
9525ae83 RK |
1190 | |
1191 | pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN); | |
1192 | pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX); | |
1193 | pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX); | |
1194 | } | |
1195 | EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam); | |
1196 | ||
8796c892 RK |
1197 | /** |
1198 | * phylink_ethtool_set_pauseparam() - set the current pause parameters | |
1199 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1200 | * @pause: a pointer to a &struct ethtool_pauseparam | |
1201 | */ | |
9525ae83 RK |
1202 | int phylink_ethtool_set_pauseparam(struct phylink *pl, |
1203 | struct ethtool_pauseparam *pause) | |
1204 | { | |
1205 | struct phylink_link_state *config = &pl->link_config; | |
1206 | ||
8b874514 | 1207 | ASSERT_RTNL(); |
9525ae83 RK |
1208 | |
1209 | if (!phylink_test(pl->supported, Pause) && | |
1210 | !phylink_test(pl->supported, Asym_Pause)) | |
1211 | return -EOPNOTSUPP; | |
1212 | ||
1213 | if (!phylink_test(pl->supported, Asym_Pause) && | |
1214 | !pause->autoneg && pause->rx_pause != pause->tx_pause) | |
1215 | return -EINVAL; | |
1216 | ||
1217 | config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); | |
1218 | ||
1219 | if (pause->autoneg) | |
1220 | config->pause |= MLO_PAUSE_AN; | |
1221 | if (pause->rx_pause) | |
1222 | config->pause |= MLO_PAUSE_RX; | |
1223 | if (pause->tx_pause) | |
1224 | config->pause |= MLO_PAUSE_TX; | |
1225 | ||
1226 | if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { | |
1227 | switch (pl->link_an_mode) { | |
1228 | case MLO_AN_PHY: | |
1229 | /* Silently mark the carrier down, and then trigger a resolve */ | |
1230 | netif_carrier_off(pl->netdev); | |
1231 | phylink_run_resolve(pl); | |
1232 | break; | |
1233 | ||
1234 | case MLO_AN_FIXED: | |
1235 | /* Should we allow fixed links to change against the config? */ | |
1236 | phylink_resolve_flow(pl, config); | |
1237 | phylink_mac_config(pl, config); | |
1238 | break; | |
1239 | ||
86a362c4 | 1240 | case MLO_AN_INBAND: |
9525ae83 RK |
1241 | phylink_mac_config(pl, config); |
1242 | phylink_mac_an_restart(pl); | |
1243 | break; | |
1244 | } | |
1245 | } | |
1246 | ||
1247 | return 0; | |
1248 | } | |
1249 | EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam); | |
1250 | ||
770a1ad5 RK |
1251 | int phylink_ethtool_get_module_info(struct phylink *pl, |
1252 | struct ethtool_modinfo *modinfo) | |
1253 | { | |
1254 | int ret = -EOPNOTSUPP; | |
1255 | ||
1256 | WARN_ON(!lockdep_rtnl_is_held()); | |
1257 | ||
1258 | if (pl->sfp_bus) | |
1259 | ret = sfp_get_module_info(pl->sfp_bus, modinfo); | |
1260 | ||
1261 | return ret; | |
1262 | } | |
1263 | EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info); | |
1264 | ||
1265 | int phylink_ethtool_get_module_eeprom(struct phylink *pl, | |
1266 | struct ethtool_eeprom *ee, u8 *buf) | |
1267 | { | |
1268 | int ret = -EOPNOTSUPP; | |
1269 | ||
1270 | WARN_ON(!lockdep_rtnl_is_held()); | |
1271 | ||
1272 | if (pl->sfp_bus) | |
1273 | ret = sfp_get_module_eeprom(pl->sfp_bus, ee, buf); | |
1274 | ||
1275 | return ret; | |
1276 | } | |
1277 | EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom); | |
1278 | ||
8796c892 RK |
1279 | /** |
1280 | * phylink_ethtool_get_eee_err() - read the energy efficient ethernet error | |
1281 | * counter | |
1282 | * @pl: a pointer to a &struct phylink returned from phylink_create(). | |
1283 | * | |
1284 | * Read the Energy Efficient Ethernet error counter from the PHY associated | |
1285 | * with the phylink instance specified by @pl. | |
1286 | * | |
1287 | * Returns positive error counter value, or negative error code. | |
1288 | */ | |
9525ae83 RK |
1289 | int phylink_get_eee_err(struct phylink *pl) |
1290 | { | |
1291 | int ret = 0; | |
1292 | ||
8b874514 | 1293 | ASSERT_RTNL(); |
9525ae83 RK |
1294 | |
1295 | if (pl->phydev) | |
1296 | ret = phy_get_eee_err(pl->phydev); | |
1297 | ||
1298 | return ret; | |
1299 | } | |
1300 | EXPORT_SYMBOL_GPL(phylink_get_eee_err); | |
1301 | ||
8796c892 RK |
1302 | /** |
1303 | * phylink_ethtool_get_eee() - read the energy efficient ethernet parameters | |
1304 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1305 | * @eee: a pointer to a &struct ethtool_eee for the read parameters | |
1306 | */ | |
9525ae83 RK |
1307 | int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee) |
1308 | { | |
1309 | int ret = -EOPNOTSUPP; | |
1310 | ||
8b874514 | 1311 | ASSERT_RTNL(); |
9525ae83 RK |
1312 | |
1313 | if (pl->phydev) | |
1314 | ret = phy_ethtool_get_eee(pl->phydev, eee); | |
1315 | ||
1316 | return ret; | |
1317 | } | |
1318 | EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee); | |
1319 | ||
8796c892 RK |
1320 | /** |
1321 | * phylink_ethtool_set_eee() - set the energy efficient ethernet parameters | |
1322 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1323 | * @eee: a pointer to a &struct ethtool_eee for the desired parameters | |
1324 | */ | |
9525ae83 RK |
1325 | int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee) |
1326 | { | |
1327 | int ret = -EOPNOTSUPP; | |
1328 | ||
8b874514 | 1329 | ASSERT_RTNL(); |
9525ae83 RK |
1330 | |
1331 | if (pl->phydev) | |
1332 | ret = phy_ethtool_set_eee(pl->phydev, eee); | |
1333 | ||
1334 | return ret; | |
1335 | } | |
1336 | EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); | |
1337 | ||
1338 | /* This emulates MII registers for a fixed-mode phy operating as per the | |
1339 | * passed in state. "aneg" defines if we report negotiation is possible. | |
1340 | * | |
1341 | * FIXME: should deal with negotiation state too. | |
1342 | */ | |
1343 | static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, | |
1344 | struct phylink_link_state *state, bool aneg) | |
1345 | { | |
1346 | struct fixed_phy_status fs; | |
1347 | int val; | |
1348 | ||
1349 | fs.link = state->link; | |
1350 | fs.speed = state->speed; | |
1351 | fs.duplex = state->duplex; | |
1352 | fs.pause = state->pause & MLO_PAUSE_SYM; | |
1353 | fs.asym_pause = state->pause & MLO_PAUSE_ASYM; | |
1354 | ||
1355 | val = swphy_read_reg(reg, &fs); | |
1356 | if (reg == MII_BMSR) { | |
1357 | if (!state->an_complete) | |
1358 | val &= ~BMSR_ANEGCOMPLETE; | |
1359 | if (!aneg) | |
1360 | val &= ~BMSR_ANEGCAPABLE; | |
1361 | } | |
1362 | return val; | |
1363 | } | |
1364 | ||
ecbd87b8 RK |
1365 | static int phylink_phy_read(struct phylink *pl, unsigned int phy_id, |
1366 | unsigned int reg) | |
1367 | { | |
1368 | struct phy_device *phydev = pl->phydev; | |
1369 | int prtad, devad; | |
1370 | ||
1371 | if (mdio_phy_id_is_c45(phy_id)) { | |
1372 | prtad = mdio_phy_id_prtad(phy_id); | |
1373 | devad = mdio_phy_id_devad(phy_id); | |
1374 | devad = MII_ADDR_C45 | devad << 16 | reg; | |
1375 | } else if (phydev->is_c45) { | |
1376 | switch (reg) { | |
1377 | case MII_BMCR: | |
1378 | case MII_BMSR: | |
1379 | case MII_PHYSID1: | |
1380 | case MII_PHYSID2: | |
1381 | devad = __ffs(phydev->c45_ids.devices_in_package); | |
1382 | break; | |
1383 | case MII_ADVERTISE: | |
1384 | case MII_LPA: | |
1385 | if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) | |
1386 | return -EINVAL; | |
1387 | devad = MDIO_MMD_AN; | |
1388 | if (reg == MII_ADVERTISE) | |
1389 | reg = MDIO_AN_ADVERTISE; | |
1390 | else | |
1391 | reg = MDIO_AN_LPA; | |
1392 | break; | |
1393 | default: | |
1394 | return -EINVAL; | |
1395 | } | |
1396 | prtad = phy_id; | |
1397 | devad = MII_ADDR_C45 | devad << 16 | reg; | |
1398 | } else { | |
1399 | prtad = phy_id; | |
1400 | devad = reg; | |
1401 | } | |
1402 | return mdiobus_read(pl->phydev->mdio.bus, prtad, devad); | |
1403 | } | |
1404 | ||
1405 | static int phylink_phy_write(struct phylink *pl, unsigned int phy_id, | |
1406 | unsigned int reg, unsigned int val) | |
1407 | { | |
1408 | struct phy_device *phydev = pl->phydev; | |
1409 | int prtad, devad; | |
1410 | ||
1411 | if (mdio_phy_id_is_c45(phy_id)) { | |
1412 | prtad = mdio_phy_id_prtad(phy_id); | |
1413 | devad = mdio_phy_id_devad(phy_id); | |
1414 | devad = MII_ADDR_C45 | devad << 16 | reg; | |
1415 | } else if (phydev->is_c45) { | |
1416 | switch (reg) { | |
1417 | case MII_BMCR: | |
1418 | case MII_BMSR: | |
1419 | case MII_PHYSID1: | |
1420 | case MII_PHYSID2: | |
1421 | devad = __ffs(phydev->c45_ids.devices_in_package); | |
1422 | break; | |
1423 | case MII_ADVERTISE: | |
1424 | case MII_LPA: | |
1425 | if (!(phydev->c45_ids.devices_in_package & MDIO_DEVS_AN)) | |
1426 | return -EINVAL; | |
1427 | devad = MDIO_MMD_AN; | |
1428 | if (reg == MII_ADVERTISE) | |
1429 | reg = MDIO_AN_ADVERTISE; | |
1430 | else | |
1431 | reg = MDIO_AN_LPA; | |
1432 | break; | |
1433 | default: | |
1434 | return -EINVAL; | |
1435 | } | |
1436 | prtad = phy_id; | |
1437 | devad = MII_ADDR_C45 | devad << 16 | reg; | |
1438 | } else { | |
1439 | prtad = phy_id; | |
1440 | devad = reg; | |
1441 | } | |
1442 | ||
1443 | return mdiobus_write(phydev->mdio.bus, prtad, devad, val); | |
1444 | } | |
1445 | ||
9525ae83 RK |
1446 | static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, |
1447 | unsigned int reg) | |
1448 | { | |
1449 | struct phylink_link_state state; | |
1450 | int val = 0xffff; | |
1451 | ||
9525ae83 RK |
1452 | switch (pl->link_an_mode) { |
1453 | case MLO_AN_FIXED: | |
1454 | if (phy_id == 0) { | |
1455 | phylink_get_fixed_state(pl, &state); | |
1456 | val = phylink_mii_emul_read(pl->netdev, reg, &state, | |
1457 | true); | |
1458 | } | |
1459 | break; | |
1460 | ||
1461 | case MLO_AN_PHY: | |
1462 | return -EOPNOTSUPP; | |
1463 | ||
86a362c4 | 1464 | case MLO_AN_INBAND: |
9525ae83 RK |
1465 | if (phy_id == 0) { |
1466 | val = phylink_get_mac_state(pl, &state); | |
1467 | if (val < 0) | |
1468 | return val; | |
1469 | ||
1470 | val = phylink_mii_emul_read(pl->netdev, reg, &state, | |
1471 | true); | |
1472 | } | |
1473 | break; | |
1474 | } | |
1475 | ||
1476 | return val & 0xffff; | |
1477 | } | |
1478 | ||
1479 | static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, | |
1480 | unsigned int reg, unsigned int val) | |
1481 | { | |
9525ae83 RK |
1482 | switch (pl->link_an_mode) { |
1483 | case MLO_AN_FIXED: | |
1484 | break; | |
1485 | ||
1486 | case MLO_AN_PHY: | |
1487 | return -EOPNOTSUPP; | |
1488 | ||
86a362c4 | 1489 | case MLO_AN_INBAND: |
9525ae83 RK |
1490 | break; |
1491 | } | |
1492 | ||
1493 | return 0; | |
1494 | } | |
1495 | ||
8796c892 RK |
1496 | /** |
1497 | * phylink_mii_ioctl() - generic mii ioctl interface | |
1498 | * @pl: a pointer to a &struct phylink returned from phylink_create() | |
1499 | * @ifr: a pointer to a &struct ifreq for socket ioctls | |
1500 | * @cmd: ioctl cmd to execute | |
1501 | * | |
1502 | * Perform the specified MII ioctl on the PHY attached to the phylink instance | |
1503 | * specified by @pl. If no PHY is attached, emulate the presence of the PHY. | |
1504 | * | |
1505 | * Returns: zero on success or negative error code. | |
1506 | * | |
1507 | * %SIOCGMIIPHY: | |
1508 | * read register from the current PHY. | |
1509 | * %SIOCGMIIREG: | |
1510 | * read register from the specified PHY. | |
1511 | * %SIOCSMIIREG: | |
1512 | * set a register on the specified PHY. | |
1513 | */ | |
9525ae83 RK |
1514 | int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) |
1515 | { | |
ecbd87b8 RK |
1516 | struct mii_ioctl_data *mii = if_mii(ifr); |
1517 | int ret; | |
9525ae83 | 1518 | |
8b874514 | 1519 | ASSERT_RTNL(); |
9525ae83 | 1520 | |
ecbd87b8 | 1521 | if (pl->phydev) { |
86a362c4 | 1522 | /* PHYs only exist for MLO_AN_PHY and SGMII */ |
ecbd87b8 RK |
1523 | switch (cmd) { |
1524 | case SIOCGMIIPHY: | |
1525 | mii->phy_id = pl->phydev->mdio.addr; | |
46cd7503 | 1526 | /* fall through */ |
ecbd87b8 RK |
1527 | |
1528 | case SIOCGMIIREG: | |
1529 | ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num); | |
1530 | if (ret >= 0) { | |
1531 | mii->val_out = ret; | |
1532 | ret = 0; | |
1533 | } | |
1534 | break; | |
9525ae83 | 1535 | |
ecbd87b8 RK |
1536 | case SIOCSMIIREG: |
1537 | ret = phylink_phy_write(pl, mii->phy_id, mii->reg_num, | |
1538 | mii->val_in); | |
1539 | break; | |
1540 | ||
1541 | default: | |
1542 | ret = phy_mii_ioctl(pl->phydev, ifr, cmd); | |
1543 | break; | |
9525ae83 | 1544 | } |
ecbd87b8 RK |
1545 | } else { |
1546 | switch (cmd) { | |
1547 | case SIOCGMIIPHY: | |
1548 | mii->phy_id = 0; | |
46cd7503 | 1549 | /* fall through */ |
ecbd87b8 RK |
1550 | |
1551 | case SIOCGMIIREG: | |
1552 | ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num); | |
1553 | if (ret >= 0) { | |
1554 | mii->val_out = ret; | |
1555 | ret = 0; | |
1556 | } | |
1557 | break; | |
9525ae83 | 1558 | |
ecbd87b8 RK |
1559 | case SIOCSMIIREG: |
1560 | ret = phylink_mii_write(pl, mii->phy_id, mii->reg_num, | |
1561 | mii->val_in); | |
1562 | break; | |
9525ae83 | 1563 | |
ecbd87b8 RK |
1564 | default: |
1565 | ret = -EOPNOTSUPP; | |
1566 | break; | |
1567 | } | |
9525ae83 RK |
1568 | } |
1569 | ||
1570 | return ret; | |
1571 | } | |
1572 | EXPORT_SYMBOL_GPL(phylink_mii_ioctl); | |
1573 | ||
ce0aa27f RK |
1574 | static int phylink_sfp_module_insert(void *upstream, |
1575 | const struct sfp_eeprom_id *id) | |
1576 | { | |
1577 | struct phylink *pl = upstream; | |
1578 | __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; | |
1579 | struct phylink_link_state config; | |
1580 | phy_interface_t iface; | |
444d3502 | 1581 | int ret = 0; |
ce0aa27f RK |
1582 | bool changed; |
1583 | u8 port; | |
1584 | ||
8b874514 | 1585 | ASSERT_RTNL(); |
ce0aa27f | 1586 | |
a9c79364 RK |
1587 | sfp_parse_support(pl->sfp_bus, id, support); |
1588 | port = sfp_parse_port(pl->sfp_bus, id, support); | |
ce0aa27f RK |
1589 | |
1590 | memset(&config, 0, sizeof(config)); | |
1591 | linkmode_copy(config.advertising, support); | |
a9c79364 | 1592 | config.interface = PHY_INTERFACE_MODE_NA; |
ce0aa27f RK |
1593 | config.speed = SPEED_UNKNOWN; |
1594 | config.duplex = DUPLEX_UNKNOWN; | |
1595 | config.pause = MLO_PAUSE_AN; | |
1596 | config.an_enabled = pl->link_config.an_enabled; | |
1597 | ||
1598 | /* Ignore errors if we're expecting a PHY to attach later */ | |
1599 | ret = phylink_validate(pl, support, &config); | |
a9c79364 RK |
1600 | if (ret) { |
1601 | netdev_err(pl->netdev, "validation with support %*pb failed: %d\n", | |
1602 | __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); | |
1603 | return ret; | |
1604 | } | |
1605 | ||
1606 | iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); | |
1607 | if (iface == PHY_INTERFACE_MODE_NA) { | |
1608 | netdev_err(pl->netdev, | |
1609 | "selection of interface failed, advertisment %*pb\n", | |
1610 | __ETHTOOL_LINK_MODE_MASK_NBITS, config.advertising); | |
1611 | return -EINVAL; | |
1612 | } | |
1613 | ||
1614 | config.interface = iface; | |
1615 | ret = phylink_validate(pl, support, &config); | |
ce0aa27f RK |
1616 | if (ret) { |
1617 | netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n", | |
444d3502 RK |
1618 | phylink_an_mode_str(MLO_AN_INBAND), |
1619 | phy_modes(config.interface), | |
ce0aa27f RK |
1620 | __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); |
1621 | return ret; | |
1622 | } | |
1623 | ||
1624 | netdev_dbg(pl->netdev, "requesting link mode %s/%s with support %*pb\n", | |
444d3502 RK |
1625 | phylink_an_mode_str(MLO_AN_INBAND), |
1626 | phy_modes(config.interface), | |
ce0aa27f RK |
1627 | __ETHTOOL_LINK_MODE_MASK_NBITS, support); |
1628 | ||
86a362c4 | 1629 | if (phy_interface_mode_is_8023z(iface) && pl->phydev) |
ce0aa27f RK |
1630 | return -EINVAL; |
1631 | ||
1632 | changed = !bitmap_equal(pl->supported, support, | |
1633 | __ETHTOOL_LINK_MODE_MASK_NBITS); | |
1634 | if (changed) { | |
1635 | linkmode_copy(pl->supported, support); | |
1636 | linkmode_copy(pl->link_config.advertising, config.advertising); | |
1637 | } | |
1638 | ||
444d3502 | 1639 | if (pl->link_an_mode != MLO_AN_INBAND || |
ce0aa27f RK |
1640 | pl->link_config.interface != config.interface) { |
1641 | pl->link_config.interface = config.interface; | |
444d3502 | 1642 | pl->link_an_mode = MLO_AN_INBAND; |
ce0aa27f RK |
1643 | |
1644 | changed = true; | |
1645 | ||
1646 | netdev_info(pl->netdev, "switched to %s/%s link mode\n", | |
444d3502 | 1647 | phylink_an_mode_str(MLO_AN_INBAND), |
ce0aa27f RK |
1648 | phy_modes(config.interface)); |
1649 | } | |
1650 | ||
1651 | pl->link_port = port; | |
1652 | ||
1653 | if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, | |
1654 | &pl->phylink_disable_state)) | |
1655 | phylink_mac_config(pl, &pl->link_config); | |
1656 | ||
1657 | return ret; | |
1658 | } | |
1659 | ||
1660 | static void phylink_sfp_link_down(void *upstream) | |
1661 | { | |
1662 | struct phylink *pl = upstream; | |
1663 | ||
8b874514 | 1664 | ASSERT_RTNL(); |
ce0aa27f RK |
1665 | |
1666 | set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); | |
ac817f5a | 1667 | queue_work(system_power_efficient_wq, &pl->resolve); |
ce0aa27f | 1668 | flush_work(&pl->resolve); |
ce0aa27f RK |
1669 | } |
1670 | ||
1671 | static void phylink_sfp_link_up(void *upstream) | |
1672 | { | |
1673 | struct phylink *pl = upstream; | |
1674 | ||
8b874514 | 1675 | ASSERT_RTNL(); |
ce0aa27f RK |
1676 | |
1677 | clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state); | |
1678 | phylink_run_resolve(pl); | |
1679 | } | |
1680 | ||
1681 | static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) | |
1682 | { | |
1683 | return phylink_connect_phy(upstream, phy); | |
1684 | } | |
1685 | ||
1686 | static void phylink_sfp_disconnect_phy(void *upstream) | |
1687 | { | |
1688 | phylink_disconnect_phy(upstream); | |
1689 | } | |
1690 | ||
1691 | static const struct sfp_upstream_ops sfp_phylink_ops = { | |
1692 | .module_insert = phylink_sfp_module_insert, | |
1693 | .link_up = phylink_sfp_link_up, | |
1694 | .link_down = phylink_sfp_link_down, | |
1695 | .connect_phy = phylink_sfp_connect_phy, | |
1696 | .disconnect_phy = phylink_sfp_disconnect_phy, | |
1697 | }; | |
1698 | ||
9525ae83 | 1699 | MODULE_LICENSE("GPL"); |