9ns: Support rename
[akaros.git] / kern / drivers / net / linux_mii.c
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_mii.h"
31
32 static uint32_t mii_get_an(struct mii_if_info *mii, uint16_t addr)
33 {
34         int advert;
35
36         advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
37
38         return mii_lpa_to_ethtool_lpa_t(advert);
39 }
40
41 #if 0 // AKAROS_PORT
42 /**
43  * mii_ethtool_gset - get settings that are specified in @ecmd
44  * @mii: MII interface
45  * @ecmd: requested ethtool_cmd
46  *
47  * The @ecmd parameter is expected to have been cleared before calling
48  * mii_ethtool_gset().
49  *
50  * Returns 0 for success, negative on error.
51  */
52 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
53 {
54         struct ether *dev = mii->dev;
55         uint16_t bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
56         uint32_t nego;
57
58         ecmd->supported =
59             (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
60              SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
61              SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
62         if (mii->supports_gmii)
63                 ecmd->supported |= SUPPORTED_1000baseT_Half |
64                         SUPPORTED_1000baseT_Full;
65
66         /* only supports twisted-pair */
67         ecmd->port = PORT_MII;
68
69         /* only supports internal transceiver */
70         ecmd->transceiver = XCVR_INTERNAL;
71
72         /* this isn't fully supported at higher layers */
73         ecmd->phy_address = mii->phy_id;
74         ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
75
76         ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
77
78         bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
79         bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
80         if (mii->supports_gmii) {
81                 ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
82                 stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
83         }
84         if (bmcr & BMCR_ANENABLE) {
85                 ecmd->advertising |= ADVERTISED_Autoneg;
86                 ecmd->autoneg = AUTONEG_ENABLE;
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 (bmsr & BMSR_ANEGCOMPLETE) {
94                         ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
95                         ecmd->lp_advertising |=
96                                         mii_stat1000_to_ethtool_lpa_t(stat1000);
97                 } else {
98                         ecmd->lp_advertising = 0;
99                 }
100
101                 nego = ecmd->advertising & ecmd->lp_advertising;
102
103                 if (nego & (ADVERTISED_1000baseT_Full |
104                             ADVERTISED_1000baseT_Half)) {
105                         ethtool_cmd_speed_set(ecmd, SPEED_1000);
106                         ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
107                 } else if (nego & (ADVERTISED_100baseT_Full |
108                                    ADVERTISED_100baseT_Half)) {
109                         ethtool_cmd_speed_set(ecmd, SPEED_100);
110                         ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
111                 } else {
112                         ethtool_cmd_speed_set(ecmd, SPEED_10);
113                         ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
114                 }
115         } else {
116                 ecmd->autoneg = AUTONEG_DISABLE;
117
118                 ethtool_cmd_speed_set(ecmd,
119                                       ((bmcr & BMCR_SPEED1000 &&
120                                         (bmcr & BMCR_SPEED100) == 0) ?
121                                        SPEED_1000 :
122                                        ((bmcr & BMCR_SPEED100) ?
123                                         SPEED_100 : SPEED_10)));
124                 ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
125         }
126
127         mii->full_duplex = ecmd->duplex;
128
129         /* ignore maxtxpkt, maxrxpkt for now */
130
131         return 0;
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 ether *dev = mii->dev;
146         uint16_t bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
147         uint32_t 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         if (bmcr & BMCR_ANENABLE) {
172                 advertising |= ADVERTISED_Autoneg;
173                 cmd->base.autoneg = AUTONEG_ENABLE;
174
175                 advertising |= mii_get_an(mii, MII_ADVERTISE);
176                 if (mii->supports_gmii)
177                         advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
178
179                 if (bmsr & BMSR_ANEGCOMPLETE) {
180                         lp_advertising = mii_get_an(mii, MII_LPA);
181                         lp_advertising |=
182                                         mii_stat1000_to_ethtool_lpa_t(stat1000);
183                 } else {
184                         lp_advertising = 0;
185                 }
186
187                 nego = advertising & lp_advertising;
188
189                 if (nego & (ADVERTISED_1000baseT_Full |
190                             ADVERTISED_1000baseT_Half)) {
191                         cmd->base.speed = SPEED_1000;
192                         cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
193                 } else if (nego & (ADVERTISED_100baseT_Full |
194                                    ADVERTISED_100baseT_Half)) {
195                         cmd->base.speed = SPEED_100;
196                         cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
197                 } else {
198                         cmd->base.speed = SPEED_10;
199                         cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
200                 }
201         } else {
202                 cmd->base.autoneg = AUTONEG_DISABLE;
203
204                 cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
205                                     (bmcr & BMCR_SPEED100) == 0) ?
206                                    SPEED_1000 :
207                                    ((bmcr & BMCR_SPEED100) ?
208                                     SPEED_100 : SPEED_10));
209                 cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
210                         DUPLEX_FULL : DUPLEX_HALF;
211
212                 lp_advertising = 0;
213         }
214
215         mii->full_duplex = cmd->base.duplex;
216
217         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
218                                                 supported);
219         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
220                                                 advertising);
221         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
222                                                 lp_advertising);
223
224         /* ignore maxtxpkt, maxrxpkt for now */
225 }
226
227 /**
228  * mii_ethtool_sset - set settings that are specified in @ecmd
229  * @mii: MII interface
230  * @ecmd: requested ethtool_cmd
231  *
232  * Returns 0 for success, negative on error.
233  */
234 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
235 {
236         struct ether *dev = mii->dev;
237         uint32_t speed = ethtool_cmd_speed(ecmd);
238
239         if (speed != SPEED_10 &&
240             speed != SPEED_100 &&
241             speed != SPEED_1000)
242                 return -EINVAL;
243         if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
244                 return -EINVAL;
245         if (ecmd->port != PORT_MII)
246                 return -EINVAL;
247         if (ecmd->transceiver != XCVR_INTERNAL)
248                 return -EINVAL;
249         if (ecmd->phy_address != mii->phy_id)
250                 return -EINVAL;
251         if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
252                 return -EINVAL;
253         if ((speed == SPEED_1000) && (!mii->supports_gmii))
254                 return -EINVAL;
255
256         /* ignore supported, maxtxpkt, maxrxpkt */
257
258         if (ecmd->autoneg == AUTONEG_ENABLE) {
259                 uint32_t bmcr, advert, tmp;
260                 uint32_t advert2 = 0, tmp2 = 0;
261
262                 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
263                                           ADVERTISED_10baseT_Full |
264                                           ADVERTISED_100baseT_Half |
265                                           ADVERTISED_100baseT_Full |
266                                           ADVERTISED_1000baseT_Half |
267                                           ADVERTISED_1000baseT_Full)) == 0)
268                         return -EINVAL;
269
270                 /* advertise only what has been requested */
271                 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
272                 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
273                 if (mii->supports_gmii) {
274                         advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
275                         tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
276                 }
277                 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
278
279                 if (mii->supports_gmii)
280                         tmp2 |=
281                               ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
282                 if (advert != tmp) {
283                         mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
284                         mii->advertising = tmp;
285                 }
286                 if ((mii->supports_gmii) && (advert2 != tmp2))
287                         mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
288
289                 /* turn on autonegotiation, and force a renegotiate */
290                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
291                 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
292                 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
293
294                 mii->force_media = 0;
295         } else {
296                 uint32_t bmcr, tmp;
297
298                 /* turn off auto negotiation, set speed and duplexity */
299                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
300                 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
301                                BMCR_SPEED1000 | BMCR_FULLDPLX);
302                 if (speed == SPEED_1000)
303                         tmp |= BMCR_SPEED1000;
304                 else if (speed == SPEED_100)
305                         tmp |= BMCR_SPEED100;
306                 if (ecmd->duplex == DUPLEX_FULL) {
307                         tmp |= BMCR_FULLDPLX;
308                         mii->full_duplex = 1;
309                 } else
310                         mii->full_duplex = 0;
311                 if (bmcr != tmp)
312                         mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
313
314                 mii->force_media = 1;
315         }
316         return 0;
317 }
318
319 /**
320  * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
321  * @mii: MII interfaces
322  * @cmd: requested ethtool_link_ksettings
323  *
324  * Returns 0 for success, negative on error.
325  */
326 int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
327                                    const struct ethtool_link_ksettings *cmd)
328 {
329         struct ether *dev = mii->dev;
330         uint32_t speed = cmd->base.speed;
331
332         if (speed != SPEED_10 &&
333             speed != SPEED_100 &&
334             speed != SPEED_1000)
335                 return -EINVAL;
336         if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
337                 return -EINVAL;
338         if (cmd->base.port != PORT_MII)
339                 return -EINVAL;
340         if (cmd->base.phy_address != mii->phy_id)
341                 return -EINVAL;
342         if (cmd->base.autoneg != AUTONEG_DISABLE &&
343             cmd->base.autoneg != AUTONEG_ENABLE)
344                 return -EINVAL;
345         if ((speed == SPEED_1000) && (!mii->supports_gmii))
346                 return -EINVAL;
347
348         /* ignore supported, maxtxpkt, maxrxpkt */
349
350         if (cmd->base.autoneg == AUTONEG_ENABLE) {
351                 uint32_t bmcr, advert, tmp;
352                 uint32_t advert2 = 0, tmp2 = 0;
353                 uint32_t advertising;
354
355                 ethtool_convert_link_mode_to_legacy_u32(
356                         &advertising, cmd->link_modes.advertising);
357
358                 if ((advertising & (ADVERTISED_10baseT_Half |
359                                     ADVERTISED_10baseT_Full |
360                                     ADVERTISED_100baseT_Half |
361                                     ADVERTISED_100baseT_Full |
362                                     ADVERTISED_1000baseT_Half |
363                                     ADVERTISED_1000baseT_Full)) == 0)
364                         return -EINVAL;
365
366                 /* advertise only what has been requested */
367                 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
368                 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
369                 if (mii->supports_gmii) {
370                         advert2 = mii->mdio_read(dev, mii->phy_id,
371                                                  MII_CTRL1000);
372                         tmp2 = advert2 &
373                                 ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
374                 }
375                 tmp |= ethtool_adv_to_mii_adv_t(advertising);
376
377                 if (mii->supports_gmii)
378                         tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
379                 if (advert != tmp) {
380                         mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
381                         mii->advertising = tmp;
382                 }
383                 if ((mii->supports_gmii) && (advert2 != tmp2))
384                         mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
385
386                 /* turn on autonegotiation, and force a renegotiate */
387                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
388                 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
389                 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
390
391                 mii->force_media = 0;
392         } else {
393                 uint32_t bmcr, tmp;
394
395                 /* turn off auto negotiation, set speed and duplexity */
396                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
397                 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
398                                BMCR_SPEED1000 | BMCR_FULLDPLX);
399                 if (speed == SPEED_1000)
400                         tmp |= BMCR_SPEED1000;
401                 else if (speed == SPEED_100)
402                         tmp |= BMCR_SPEED100;
403                 if (cmd->base.duplex == DUPLEX_FULL) {
404                         tmp |= BMCR_FULLDPLX;
405                         mii->full_duplex = 1;
406                 } else {
407                         mii->full_duplex = 0;
408                 }
409                 if (bmcr != tmp)
410                         mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
411
412                 mii->force_media = 1;
413         }
414         return 0;
415 }
416 #endif
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(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  */
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(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 #if 0 // AKAROS_PORT
573 /**
574  * generic_mii_ioctl - main MII ioctl interface
575  * @mii_if: the MII interface
576  * @mii_data: MII ioctl data structure
577  * @cmd: MII ioctl command
578  * @duplex_chg_out: pointer to @duplex_changed status if there was no
579  *      ioctl error
580  *
581  * Returns 0 on success, negative on error.
582  */
583 int generic_mii_ioctl(struct mii_if_info *mii_if,
584                       struct mii_ioctl_data *mii_data, int cmd,
585                       unsigned int *duplex_chg_out)
586 {
587         int rc = 0;
588         unsigned int duplex_changed = 0;
589
590         if (duplex_chg_out)
591                 *duplex_chg_out = 0;
592
593         mii_data->phy_id &= mii_if->phy_id_mask;
594         mii_data->reg_num &= mii_if->reg_num_mask;
595
596         switch(cmd) {
597         case SIOCGMIIPHY:
598                 mii_data->phy_id = mii_if->phy_id;
599                 /* fall through */
600
601         case SIOCGMIIREG:
602                 mii_data->val_out =
603                         mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
604                                           mii_data->reg_num);
605                 break;
606
607         case SIOCSMIIREG: {
608                 uint16_t val = mii_data->val_in;
609
610                 if (mii_data->phy_id == mii_if->phy_id) {
611                         switch(mii_data->reg_num) {
612                         case MII_BMCR: {
613                                 unsigned int new_duplex = 0;
614                                 if (val & (BMCR_RESET|BMCR_ANENABLE))
615                                         mii_if->force_media = 0;
616                                 else
617                                         mii_if->force_media = 1;
618                                 if (mii_if->force_media &&
619                                     (val & BMCR_FULLDPLX))
620                                         new_duplex = 1;
621                                 if (mii_if->full_duplex != new_duplex) {
622                                         duplex_changed = 1;
623                                         mii_if->full_duplex = new_duplex;
624                                 }
625                                 break;
626                         }
627                         case MII_ADVERTISE:
628                                 mii_if->advertising = val;
629                                 break;
630                         default:
631                                 /* do nothing */
632                                 break;
633                         }
634                 }
635
636                 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
637                                    mii_data->reg_num, val);
638                 break;
639         }
640
641         default:
642                 rc = -EOPNOTSUPP;
643                 break;
644         }
645
646         if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
647                 *duplex_chg_out = 1;
648
649         return rc;
650 }
651 #endif