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