1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
47
48
49
50
51
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
68 ecmd->port = PORT_MII;
69
70
71 ecmd->transceiver = XCVR_INTERNAL;
72
73
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
132}
133
134
135
136
137
138
139
140
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
157 cmd->base.port = PORT_MII;
158
159
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
226}
227
228
229
230
231
232
233
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
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
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
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
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
322
323
324
325
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
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
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
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
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
420
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
438
439
440
441
442int mii_link_ok (struct mii_if_info *mii)
443{
444
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
453
454
455
456
457int mii_nway_restart (struct mii_if_info *mii)
458{
459 int bmcr;
460 int r = -EINVAL;
461
462
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
476
477
478
479
480
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
495
496
497
498
499
500
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
511 old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
512 new_carrier = (unsigned int) mii_link_ok(mii);
513
514
515
516
517 if ((!init_media) && (old_carrier == new_carrier))
518 return 0;
519
520
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;
526 }
527
528
529
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;
537 }
538
539
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
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;
567 }
568
569 return 0;
570}
571
572
573
574
575
576
577
578
579
580
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
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
666