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