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