1 | /* |
2 | |
3 | mii.c: MII interface library |
4 | |
5 | Maintained by Jeff Garzik <jgarzik@pobox.com> |
6 | Copyright 2001,2002 Jeff Garzik |
7 | |
8 | Various code came from myson803.c and other files by |
9 | Donald Becker. Copyright: |
10 | |
11 | Written 1998-2002 by Donald Becker. |
12 | |
13 | This software may be used and distributed according |
14 | to the terms of the GNU General Public License (GPL), |
15 | incorporated herein by reference. Drivers based on |
16 | or derived from this code fall under the GPL and must |
17 | retain the authorship, copyright and license notice. |
18 | This file is not a complete program and may only be |
19 | used when the entire operating system is licensed |
20 | under the GPL. |
21 | |
22 | The author may be reached as becker@scyld.com, or C/O |
23 | Scyld Computing Corporation |
24 | 410 Severn Ave., Suite 210 |
25 | Annapolis MD 21403 |
26 | |
27 | |
28 | */ |
29 | |
30 | #include <linux/kernel.h> |
31 | #include <linux/module.h> |
32 | #include <linux/netdevice.h> |
33 | #include <linux/ethtool.h> |
34 | #include <linux/mii.h> |
35 | |
36 | static u32 mii_get_an(struct mii_if_info *mii, u16 addr) |
37 | { |
38 | int advert; |
39 | |
40 | advert = mii->mdio_read(mii->dev, mii->phy_id, addr); |
41 | |
42 | return mii_lpa_to_ethtool_lpa_t(lpa: advert); |
43 | } |
44 | |
45 | /** |
46 | * mii_ethtool_gset - get settings that are specified in @ecmd |
47 | * @mii: MII interface |
48 | * @ecmd: requested ethtool_cmd |
49 | * |
50 | * The @ecmd parameter is expected to have been cleared before calling |
51 | * mii_ethtool_gset(). |
52 | */ |
53 | void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) |
54 | { |
55 | struct net_device *dev = mii->dev; |
56 | u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0; |
57 | u32 nego; |
58 | |
59 | ecmd->supported = |
60 | (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | |
61 | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | |
62 | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); |
63 | if (mii->supports_gmii) |
64 | ecmd->supported |= SUPPORTED_1000baseT_Half | |
65 | SUPPORTED_1000baseT_Full; |
66 | |
67 | /* only supports twisted-pair */ |
68 | ecmd->port = PORT_MII; |
69 | |
70 | /* only supports internal transceiver */ |
71 | ecmd->transceiver = XCVR_INTERNAL; |
72 | |
73 | /* this isn't fully supported at higher layers */ |
74 | ecmd->phy_address = mii->phy_id; |
75 | ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22; |
76 | |
77 | ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII; |
78 | |
79 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
80 | bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR); |
81 | if (mii->supports_gmii) { |
82 | ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); |
83 | stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000); |
84 | } |
85 | |
86 | ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE); |
87 | if (mii->supports_gmii) |
88 | ecmd->advertising |= |
89 | mii_ctrl1000_to_ethtool_adv_t(adv: ctrl1000); |
90 | |
91 | if (bmcr & BMCR_ANENABLE) { |
92 | ecmd->advertising |= ADVERTISED_Autoneg; |
93 | ecmd->autoneg = AUTONEG_ENABLE; |
94 | |
95 | if (bmsr & BMSR_ANEGCOMPLETE) { |
96 | ecmd->lp_advertising = mii_get_an(mii, MII_LPA); |
97 | ecmd->lp_advertising |= |
98 | mii_stat1000_to_ethtool_lpa_t(lpa: stat1000); |
99 | } else { |
100 | ecmd->lp_advertising = 0; |
101 | } |
102 | |
103 | nego = ecmd->advertising & ecmd->lp_advertising; |
104 | |
105 | if (nego & (ADVERTISED_1000baseT_Full | |
106 | ADVERTISED_1000baseT_Half)) { |
107 | ethtool_cmd_speed_set(ep: ecmd, SPEED_1000); |
108 | ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full); |
109 | } else if (nego & (ADVERTISED_100baseT_Full | |
110 | ADVERTISED_100baseT_Half)) { |
111 | ethtool_cmd_speed_set(ep: ecmd, SPEED_100); |
112 | ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full); |
113 | } else { |
114 | ethtool_cmd_speed_set(ep: ecmd, SPEED_10); |
115 | ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full); |
116 | } |
117 | } else { |
118 | ecmd->autoneg = AUTONEG_DISABLE; |
119 | |
120 | ethtool_cmd_speed_set(ep: ecmd, |
121 | speed: ((bmcr & BMCR_SPEED1000 && |
122 | (bmcr & BMCR_SPEED100) == 0) ? |
123 | SPEED_1000 : |
124 | ((bmcr & BMCR_SPEED100) ? |
125 | SPEED_100 : SPEED_10))); |
126 | ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; |
127 | } |
128 | |
129 | mii->full_duplex = ecmd->duplex; |
130 | |
131 | /* ignore maxtxpkt, maxrxpkt for now */ |
132 | } |
133 | |
134 | /** |
135 | * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd |
136 | * @mii: MII interface |
137 | * @cmd: requested ethtool_link_ksettings |
138 | * |
139 | * The @cmd parameter is expected to have been cleared before calling |
140 | * mii_ethtool_get_link_ksettings(). |
141 | */ |
142 | void mii_ethtool_get_link_ksettings(struct mii_if_info *mii, |
143 | struct ethtool_link_ksettings *cmd) |
144 | { |
145 | struct net_device *dev = mii->dev; |
146 | u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0; |
147 | u32 nego, supported, advertising, lp_advertising; |
148 | |
149 | supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | |
150 | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | |
151 | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); |
152 | if (mii->supports_gmii) |
153 | supported |= SUPPORTED_1000baseT_Half | |
154 | SUPPORTED_1000baseT_Full; |
155 | |
156 | /* only supports twisted-pair */ |
157 | cmd->base.port = PORT_MII; |
158 | |
159 | /* this isn't fully supported at higher layers */ |
160 | cmd->base.phy_address = mii->phy_id; |
161 | cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; |
162 | |
163 | advertising = ADVERTISED_TP | ADVERTISED_MII; |
164 | |
165 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
166 | bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR); |
167 | if (mii->supports_gmii) { |
168 | ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); |
169 | stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000); |
170 | } |
171 | |
172 | advertising |= mii_get_an(mii, MII_ADVERTISE); |
173 | if (mii->supports_gmii) |
174 | advertising |= mii_ctrl1000_to_ethtool_adv_t(adv: ctrl1000); |
175 | |
176 | if (bmcr & BMCR_ANENABLE) { |
177 | advertising |= ADVERTISED_Autoneg; |
178 | cmd->base.autoneg = AUTONEG_ENABLE; |
179 | |
180 | if (bmsr & BMSR_ANEGCOMPLETE) { |
181 | lp_advertising = mii_get_an(mii, MII_LPA); |
182 | lp_advertising |= |
183 | mii_stat1000_to_ethtool_lpa_t(lpa: stat1000); |
184 | } else { |
185 | lp_advertising = 0; |
186 | } |
187 | |
188 | nego = advertising & lp_advertising; |
189 | |
190 | if (nego & (ADVERTISED_1000baseT_Full | |
191 | ADVERTISED_1000baseT_Half)) { |
192 | cmd->base.speed = SPEED_1000; |
193 | cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full); |
194 | } else if (nego & (ADVERTISED_100baseT_Full | |
195 | ADVERTISED_100baseT_Half)) { |
196 | cmd->base.speed = SPEED_100; |
197 | cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full); |
198 | } else { |
199 | cmd->base.speed = SPEED_10; |
200 | cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full); |
201 | } |
202 | } else { |
203 | cmd->base.autoneg = AUTONEG_DISABLE; |
204 | |
205 | cmd->base.speed = ((bmcr & BMCR_SPEED1000 && |
206 | (bmcr & BMCR_SPEED100) == 0) ? |
207 | SPEED_1000 : |
208 | ((bmcr & BMCR_SPEED100) ? |
209 | SPEED_100 : SPEED_10)); |
210 | cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? |
211 | DUPLEX_FULL : DUPLEX_HALF; |
212 | |
213 | lp_advertising = 0; |
214 | } |
215 | |
216 | mii->full_duplex = cmd->base.duplex; |
217 | |
218 | ethtool_convert_legacy_u32_to_link_mode(dst: cmd->link_modes.supported, |
219 | legacy_u32: supported); |
220 | ethtool_convert_legacy_u32_to_link_mode(dst: cmd->link_modes.advertising, |
221 | legacy_u32: advertising); |
222 | ethtool_convert_legacy_u32_to_link_mode(dst: cmd->link_modes.lp_advertising, |
223 | legacy_u32: lp_advertising); |
224 | |
225 | /* ignore maxtxpkt, maxrxpkt for now */ |
226 | } |
227 | |
228 | /** |
229 | * mii_ethtool_sset - set settings that are specified in @ecmd |
230 | * @mii: MII interface |
231 | * @ecmd: requested ethtool_cmd |
232 | * |
233 | * Returns 0 for success, negative on error. |
234 | */ |
235 | int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) |
236 | { |
237 | struct net_device *dev = mii->dev; |
238 | u32 speed = ethtool_cmd_speed(ep: ecmd); |
239 | |
240 | if (speed != SPEED_10 && |
241 | speed != SPEED_100 && |
242 | speed != SPEED_1000) |
243 | return -EINVAL; |
244 | if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) |
245 | return -EINVAL; |
246 | if (ecmd->port != PORT_MII) |
247 | return -EINVAL; |
248 | if (ecmd->transceiver != XCVR_INTERNAL) |
249 | return -EINVAL; |
250 | if (ecmd->phy_address != mii->phy_id) |
251 | return -EINVAL; |
252 | if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE) |
253 | return -EINVAL; |
254 | if ((speed == SPEED_1000) && (!mii->supports_gmii)) |
255 | return -EINVAL; |
256 | |
257 | /* ignore supported, maxtxpkt, maxrxpkt */ |
258 | |
259 | if (ecmd->autoneg == AUTONEG_ENABLE) { |
260 | u32 bmcr, advert, tmp; |
261 | u32 advert2 = 0, tmp2 = 0; |
262 | |
263 | if ((ecmd->advertising & (ADVERTISED_10baseT_Half | |
264 | ADVERTISED_10baseT_Full | |
265 | ADVERTISED_100baseT_Half | |
266 | ADVERTISED_100baseT_Full | |
267 | ADVERTISED_1000baseT_Half | |
268 | ADVERTISED_1000baseT_Full)) == 0) |
269 | return -EINVAL; |
270 | |
271 | /* advertise only what has been requested */ |
272 | advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE); |
273 | tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); |
274 | if (mii->supports_gmii) { |
275 | advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000); |
276 | tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); |
277 | } |
278 | tmp |= ethtool_adv_to_mii_adv_t(ethadv: ecmd->advertising); |
279 | |
280 | if (mii->supports_gmii) |
281 | tmp2 |= |
282 | ethtool_adv_to_mii_ctrl1000_t(ethadv: ecmd->advertising); |
283 | if (advert != tmp) { |
284 | mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp); |
285 | mii->advertising = tmp; |
286 | } |
287 | if ((mii->supports_gmii) && (advert2 != tmp2)) |
288 | mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2); |
289 | |
290 | /* turn on autonegotiation, and force a renegotiate */ |
291 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
292 | bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); |
293 | mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr); |
294 | |
295 | mii->force_media = 0; |
296 | } else { |
297 | u32 bmcr, tmp; |
298 | |
299 | /* turn off auto negotiation, set speed and duplexity */ |
300 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
301 | tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | |
302 | BMCR_SPEED1000 | BMCR_FULLDPLX); |
303 | if (speed == SPEED_1000) |
304 | tmp |= BMCR_SPEED1000; |
305 | else if (speed == SPEED_100) |
306 | tmp |= BMCR_SPEED100; |
307 | if (ecmd->duplex == DUPLEX_FULL) { |
308 | tmp |= BMCR_FULLDPLX; |
309 | mii->full_duplex = 1; |
310 | } else |
311 | mii->full_duplex = 0; |
312 | if (bmcr != tmp) |
313 | mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp); |
314 | |
315 | mii->force_media = 1; |
316 | } |
317 | return 0; |
318 | } |
319 | |
320 | /** |
321 | * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd |
322 | * @mii: MII interfaces |
323 | * @cmd: requested ethtool_link_ksettings |
324 | * |
325 | * Returns 0 for success, negative on error. |
326 | */ |
327 | int mii_ethtool_set_link_ksettings(struct mii_if_info *mii, |
328 | const struct ethtool_link_ksettings *cmd) |
329 | { |
330 | struct net_device *dev = mii->dev; |
331 | u32 speed = cmd->base.speed; |
332 | |
333 | if (speed != SPEED_10 && |
334 | speed != SPEED_100 && |
335 | speed != SPEED_1000) |
336 | return -EINVAL; |
337 | if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL) |
338 | return -EINVAL; |
339 | if (cmd->base.port != PORT_MII) |
340 | return -EINVAL; |
341 | if (cmd->base.phy_address != mii->phy_id) |
342 | return -EINVAL; |
343 | if (cmd->base.autoneg != AUTONEG_DISABLE && |
344 | cmd->base.autoneg != AUTONEG_ENABLE) |
345 | return -EINVAL; |
346 | if ((speed == SPEED_1000) && (!mii->supports_gmii)) |
347 | return -EINVAL; |
348 | |
349 | /* ignore supported, maxtxpkt, maxrxpkt */ |
350 | |
351 | if (cmd->base.autoneg == AUTONEG_ENABLE) { |
352 | u32 bmcr, advert, tmp; |
353 | u32 advert2 = 0, tmp2 = 0; |
354 | u32 advertising; |
355 | |
356 | ethtool_convert_link_mode_to_legacy_u32( |
357 | legacy_u32: &advertising, src: cmd->link_modes.advertising); |
358 | |
359 | if ((advertising & (ADVERTISED_10baseT_Half | |
360 | ADVERTISED_10baseT_Full | |
361 | ADVERTISED_100baseT_Half | |
362 | ADVERTISED_100baseT_Full | |
363 | ADVERTISED_1000baseT_Half | |
364 | ADVERTISED_1000baseT_Full)) == 0) |
365 | return -EINVAL; |
366 | |
367 | /* advertise only what has been requested */ |
368 | advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE); |
369 | tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4); |
370 | if (mii->supports_gmii) { |
371 | advert2 = mii->mdio_read(dev, mii->phy_id, |
372 | MII_CTRL1000); |
373 | tmp2 = advert2 & |
374 | ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); |
375 | } |
376 | tmp |= ethtool_adv_to_mii_adv_t(ethadv: advertising); |
377 | |
378 | if (mii->supports_gmii) |
379 | tmp2 |= ethtool_adv_to_mii_ctrl1000_t(ethadv: advertising); |
380 | if (advert != tmp) { |
381 | mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp); |
382 | mii->advertising = tmp; |
383 | } |
384 | if ((mii->supports_gmii) && (advert2 != tmp2)) |
385 | mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2); |
386 | |
387 | /* turn on autonegotiation, and force a renegotiate */ |
388 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
389 | bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); |
390 | mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr); |
391 | |
392 | mii->force_media = 0; |
393 | } else { |
394 | u32 bmcr, tmp; |
395 | |
396 | /* turn off auto negotiation, set speed and duplexity */ |
397 | bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR); |
398 | tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | |
399 | BMCR_SPEED1000 | BMCR_FULLDPLX); |
400 | if (speed == SPEED_1000) |
401 | tmp |= BMCR_SPEED1000; |
402 | else if (speed == SPEED_100) |
403 | tmp |= BMCR_SPEED100; |
404 | if (cmd->base.duplex == DUPLEX_FULL) { |
405 | tmp |= BMCR_FULLDPLX; |
406 | mii->full_duplex = 1; |
407 | } else { |
408 | mii->full_duplex = 0; |
409 | } |
410 | if (bmcr != tmp) |
411 | mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp); |
412 | |
413 | mii->force_media = 1; |
414 | } |
415 | return 0; |
416 | } |
417 | |
418 | /** |
419 | * mii_check_gmii_support - check if the MII supports Gb interfaces |
420 | * @mii: the MII interface |
421 | */ |
422 | int mii_check_gmii_support(struct mii_if_info *mii) |
423 | { |
424 | int reg; |
425 | |
426 | reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); |
427 | if (reg & BMSR_ESTATEN) { |
428 | reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS); |
429 | if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) |
430 | return 1; |
431 | } |
432 | |
433 | return 0; |
434 | } |
435 | |
436 | /** |
437 | * mii_link_ok - is link status up/ok |
438 | * @mii: the MII interface |
439 | * |
440 | * Returns 1 if the MII reports link status up/ok, 0 otherwise. |
441 | */ |
442 | int mii_link_ok (struct mii_if_info *mii) |
443 | { |
444 | /* first, a dummy read, needed to latch some MII phys */ |
445 | mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR); |
446 | if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS) |
447 | return 1; |
448 | return 0; |
449 | } |
450 | |
451 | /** |
452 | * mii_nway_restart - restart NWay (autonegotiation) for this interface |
453 | * @mii: the MII interface |
454 | * |
455 | * Returns 0 on success, negative on error. |
456 | */ |
457 | int mii_nway_restart (struct mii_if_info *mii) |
458 | { |
459 | int bmcr; |
460 | int r = -EINVAL; |
461 | |
462 | /* if autoneg is off, it's an error */ |
463 | bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR); |
464 | |
465 | if (bmcr & BMCR_ANENABLE) { |
466 | bmcr |= BMCR_ANRESTART; |
467 | mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr); |
468 | r = 0; |
469 | } |
470 | |
471 | return r; |
472 | } |
473 | |
474 | /** |
475 | * mii_check_link - check MII link status |
476 | * @mii: MII interface |
477 | * |
478 | * If the link status changed (previous != current), call |
479 | * netif_carrier_on() if current link status is Up or call |
480 | * netif_carrier_off() if current link status is Down. |
481 | */ |
482 | void mii_check_link (struct mii_if_info *mii) |
483 | { |
484 | int cur_link = mii_link_ok(mii); |
485 | int prev_link = netif_carrier_ok(dev: mii->dev); |
486 | |
487 | if (cur_link && !prev_link) |
488 | netif_carrier_on(dev: mii->dev); |
489 | else if (prev_link && !cur_link) |
490 | netif_carrier_off(dev: mii->dev); |
491 | } |
492 | |
493 | /** |
494 | * mii_check_media - check the MII interface for a carrier/speed/duplex change |
495 | * @mii: the MII interface |
496 | * @ok_to_print: OK to print link up/down messages |
497 | * @init_media: OK to save duplex mode in @mii |
498 | * |
499 | * Returns 1 if the duplex mode changed, 0 if not. |
500 | * If the media type is forced, always returns 0. |
501 | */ |
502 | unsigned int mii_check_media (struct mii_if_info *mii, |
503 | unsigned int ok_to_print, |
504 | unsigned int init_media) |
505 | { |
506 | unsigned int old_carrier, new_carrier; |
507 | int advertise, lpa, media, duplex; |
508 | int lpa2 = 0; |
509 | |
510 | /* check current and old link status */ |
511 | old_carrier = netif_carrier_ok(dev: mii->dev) ? 1 : 0; |
512 | new_carrier = (unsigned int) mii_link_ok(mii); |
513 | |
514 | /* if carrier state did not change, this is a "bounce", |
515 | * just exit as everything is already set correctly |
516 | */ |
517 | if ((!init_media) && (old_carrier == new_carrier)) |
518 | return 0; /* duplex did not change */ |
519 | |
520 | /* no carrier, nothing much to do */ |
521 | if (!new_carrier) { |
522 | netif_carrier_off(dev: mii->dev); |
523 | if (ok_to_print) |
524 | netdev_info(dev: mii->dev, format: "link down\n" ); |
525 | return 0; /* duplex did not change */ |
526 | } |
527 | |
528 | /* |
529 | * we have carrier, see who's on the other end |
530 | */ |
531 | netif_carrier_on(dev: mii->dev); |
532 | |
533 | if (mii->force_media) { |
534 | if (ok_to_print) |
535 | netdev_info(dev: mii->dev, format: "link up\n" ); |
536 | return 0; /* duplex did not change */ |
537 | } |
538 | |
539 | /* get MII advertise and LPA values */ |
540 | if ((!init_media) && (mii->advertising)) |
541 | advertise = mii->advertising; |
542 | else { |
543 | advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE); |
544 | mii->advertising = advertise; |
545 | } |
546 | lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA); |
547 | if (mii->supports_gmii) |
548 | lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000); |
549 | |
550 | /* figure out media and duplex from advertise and LPA values */ |
551 | media = mii_nway_result(negotiated: lpa & advertise); |
552 | duplex = (media & ADVERTISE_FULL) ? 1 : 0; |
553 | if (lpa2 & LPA_1000FULL) |
554 | duplex = 1; |
555 | |
556 | if (ok_to_print) |
557 | netdev_info(dev: mii->dev, format: "link up, %uMbps, %s-duplex, lpa 0x%04X\n" , |
558 | lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 : |
559 | media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? |
560 | 100 : 10, |
561 | duplex ? "full" : "half" , |
562 | lpa); |
563 | |
564 | if ((init_media) || (mii->full_duplex != duplex)) { |
565 | mii->full_duplex = duplex; |
566 | return 1; /* duplex changed */ |
567 | } |
568 | |
569 | return 0; /* duplex did not change */ |
570 | } |
571 | |
572 | /** |
573 | * generic_mii_ioctl - main MII ioctl interface |
574 | * @mii_if: the MII interface |
575 | * @mii_data: MII ioctl data structure |
576 | * @cmd: MII ioctl command |
577 | * @duplex_chg_out: pointer to @duplex_changed status if there was no |
578 | * ioctl error |
579 | * |
580 | * Returns 0 on success, negative on error. |
581 | */ |
582 | int generic_mii_ioctl(struct mii_if_info *mii_if, |
583 | struct mii_ioctl_data *mii_data, int cmd, |
584 | unsigned int *duplex_chg_out) |
585 | { |
586 | int rc = 0; |
587 | unsigned int duplex_changed = 0; |
588 | |
589 | if (duplex_chg_out) |
590 | *duplex_chg_out = 0; |
591 | |
592 | mii_data->phy_id &= mii_if->phy_id_mask; |
593 | mii_data->reg_num &= mii_if->reg_num_mask; |
594 | |
595 | switch(cmd) { |
596 | case SIOCGMIIPHY: |
597 | mii_data->phy_id = mii_if->phy_id; |
598 | fallthrough; |
599 | |
600 | case SIOCGMIIREG: |
601 | mii_data->val_out = |
602 | mii_if->mdio_read(mii_if->dev, mii_data->phy_id, |
603 | mii_data->reg_num); |
604 | break; |
605 | |
606 | case SIOCSMIIREG: { |
607 | u16 val = mii_data->val_in; |
608 | |
609 | if (mii_data->phy_id == mii_if->phy_id) { |
610 | switch(mii_data->reg_num) { |
611 | case MII_BMCR: { |
612 | unsigned int new_duplex = 0; |
613 | if (val & (BMCR_RESET|BMCR_ANENABLE)) |
614 | mii_if->force_media = 0; |
615 | else |
616 | mii_if->force_media = 1; |
617 | if (mii_if->force_media && |
618 | (val & BMCR_FULLDPLX)) |
619 | new_duplex = 1; |
620 | if (mii_if->full_duplex != new_duplex) { |
621 | duplex_changed = 1; |
622 | mii_if->full_duplex = new_duplex; |
623 | } |
624 | break; |
625 | } |
626 | case MII_ADVERTISE: |
627 | mii_if->advertising = val; |
628 | break; |
629 | default: |
630 | /* do nothing */ |
631 | break; |
632 | } |
633 | } |
634 | |
635 | mii_if->mdio_write(mii_if->dev, mii_data->phy_id, |
636 | mii_data->reg_num, val); |
637 | break; |
638 | } |
639 | |
640 | default: |
641 | rc = -EOPNOTSUPP; |
642 | break; |
643 | } |
644 | |
645 | if ((rc == 0) && (duplex_chg_out) && (duplex_changed)) |
646 | *duplex_chg_out = 1; |
647 | |
648 | return rc; |
649 | } |
650 | |
651 | MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>" ); |
652 | MODULE_DESCRIPTION ("MII hardware support library" ); |
653 | MODULE_LICENSE("GPL" ); |
654 | |
655 | EXPORT_SYMBOL(mii_link_ok); |
656 | EXPORT_SYMBOL(mii_nway_restart); |
657 | EXPORT_SYMBOL(mii_ethtool_gset); |
658 | EXPORT_SYMBOL(mii_ethtool_get_link_ksettings); |
659 | EXPORT_SYMBOL(mii_ethtool_sset); |
660 | EXPORT_SYMBOL(mii_ethtool_set_link_ksettings); |
661 | EXPORT_SYMBOL(mii_check_link); |
662 | EXPORT_SYMBOL(mii_check_media); |
663 | EXPORT_SYMBOL(mii_check_gmii_support); |
664 | EXPORT_SYMBOL(generic_mii_ioctl); |
665 | |
666 | |