net/mlx5e: Change VF representors' RQ type
[sfrench/cifs-2.6.git] / drivers / net / 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/kernel.h>
31 #include <linux/module.h>
32 #include <linux/netdevice.h>
33 #include <linux/ethtool.h>
34 #include <linux/mii.h>
35
36 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 {
38         int advert;
39
40         advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
41
42         return mii_lpa_to_ethtool_lpa_t(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  */
55 int 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  */
145 void 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  */
237 int 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  */
329 int 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  */
424 int 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  */
444 int 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  */
459 int 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  */
484 void 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  */
504 unsigned 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  */
584 int 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
653 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
654 MODULE_DESCRIPTION ("MII hardware support library");
655 MODULE_LICENSE("GPL");
656
657 EXPORT_SYMBOL(mii_link_ok);
658 EXPORT_SYMBOL(mii_nway_restart);
659 EXPORT_SYMBOL(mii_ethtool_gset);
660 EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
661 EXPORT_SYMBOL(mii_ethtool_sset);
662 EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
663 EXPORT_SYMBOL(mii_check_link);
664 EXPORT_SYMBOL(mii_check_media);
665 EXPORT_SYMBOL(mii_check_gmii_support);
666 EXPORT_SYMBOL(generic_mii_ioctl);
667