]> Git Repo - linux.git/blob - drivers/net/mii.c
Merge tag 'riscv-for-linus-6.14-mw1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / net / mii.c
1 /*
2
3         mii.c: MII interface library
4
5         Maintained by Jeff Garzik <[email protected]>
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 [email protected], 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         if (!(bmsr & BMSR_LSTATUS))
217                 cmd->base.speed = SPEED_UNKNOWN;
218
219         mii->full_duplex = cmd->base.duplex;
220
221         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
222                                                 supported);
223         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
224                                                 advertising);
225         ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
226                                                 lp_advertising);
227
228         /* ignore maxtxpkt, maxrxpkt for now */
229 }
230
231 /**
232  * mii_ethtool_sset - set settings that are specified in @ecmd
233  * @mii: MII interface
234  * @ecmd: requested ethtool_cmd
235  *
236  * Returns 0 for success, negative on error.
237  */
238 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
239 {
240         struct net_device *dev = mii->dev;
241         u32 speed = ethtool_cmd_speed(ecmd);
242
243         if (speed != SPEED_10 &&
244             speed != SPEED_100 &&
245             speed != SPEED_1000)
246                 return -EINVAL;
247         if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
248                 return -EINVAL;
249         if (ecmd->port != PORT_MII)
250                 return -EINVAL;
251         if (ecmd->transceiver != XCVR_INTERNAL)
252                 return -EINVAL;
253         if (ecmd->phy_address != mii->phy_id)
254                 return -EINVAL;
255         if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
256                 return -EINVAL;
257         if ((speed == SPEED_1000) && (!mii->supports_gmii))
258                 return -EINVAL;
259
260         /* ignore supported, maxtxpkt, maxrxpkt */
261
262         if (ecmd->autoneg == AUTONEG_ENABLE) {
263                 u32 bmcr, advert, tmp;
264                 u32 advert2 = 0, tmp2 = 0;
265
266                 if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
267                                           ADVERTISED_10baseT_Full |
268                                           ADVERTISED_100baseT_Half |
269                                           ADVERTISED_100baseT_Full |
270                                           ADVERTISED_1000baseT_Half |
271                                           ADVERTISED_1000baseT_Full)) == 0)
272                         return -EINVAL;
273
274                 /* advertise only what has been requested */
275                 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
276                 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
277                 if (mii->supports_gmii) {
278                         advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
279                         tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
280                 }
281                 tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
282
283                 if (mii->supports_gmii)
284                         tmp2 |=
285                               ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
286                 if (advert != tmp) {
287                         mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
288                         mii->advertising = tmp;
289                 }
290                 if ((mii->supports_gmii) && (advert2 != tmp2))
291                         mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
292
293                 /* turn on autonegotiation, and force a renegotiate */
294                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
295                 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
296                 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
297
298                 mii->force_media = 0;
299         } else {
300                 u32 bmcr, tmp;
301
302                 /* turn off auto negotiation, set speed and duplexity */
303                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
304                 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
305                                BMCR_SPEED1000 | BMCR_FULLDPLX);
306                 if (speed == SPEED_1000)
307                         tmp |= BMCR_SPEED1000;
308                 else if (speed == SPEED_100)
309                         tmp |= BMCR_SPEED100;
310                 if (ecmd->duplex == DUPLEX_FULL) {
311                         tmp |= BMCR_FULLDPLX;
312                         mii->full_duplex = 1;
313                 } else
314                         mii->full_duplex = 0;
315                 if (bmcr != tmp)
316                         mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
317
318                 mii->force_media = 1;
319         }
320         return 0;
321 }
322
323 /**
324  * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
325  * @mii: MII interfaces
326  * @cmd: requested ethtool_link_ksettings
327  *
328  * Returns 0 for success, negative on error.
329  */
330 int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
331                                    const struct ethtool_link_ksettings *cmd)
332 {
333         struct net_device *dev = mii->dev;
334         u32 speed = cmd->base.speed;
335
336         if (speed != SPEED_10 &&
337             speed != SPEED_100 &&
338             speed != SPEED_1000)
339                 return -EINVAL;
340         if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
341                 return -EINVAL;
342         if (cmd->base.port != PORT_MII)
343                 return -EINVAL;
344         if (cmd->base.phy_address != mii->phy_id)
345                 return -EINVAL;
346         if (cmd->base.autoneg != AUTONEG_DISABLE &&
347             cmd->base.autoneg != AUTONEG_ENABLE)
348                 return -EINVAL;
349         if ((speed == SPEED_1000) && (!mii->supports_gmii))
350                 return -EINVAL;
351
352         /* ignore supported, maxtxpkt, maxrxpkt */
353
354         if (cmd->base.autoneg == AUTONEG_ENABLE) {
355                 u32 bmcr, advert, tmp;
356                 u32 advert2 = 0, tmp2 = 0;
357                 u32 advertising;
358
359                 ethtool_convert_link_mode_to_legacy_u32(
360                         &advertising, cmd->link_modes.advertising);
361
362                 if ((advertising & (ADVERTISED_10baseT_Half |
363                                     ADVERTISED_10baseT_Full |
364                                     ADVERTISED_100baseT_Half |
365                                     ADVERTISED_100baseT_Full |
366                                     ADVERTISED_1000baseT_Half |
367                                     ADVERTISED_1000baseT_Full)) == 0)
368                         return -EINVAL;
369
370                 /* advertise only what has been requested */
371                 advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
372                 tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
373                 if (mii->supports_gmii) {
374                         advert2 = mii->mdio_read(dev, mii->phy_id,
375                                                  MII_CTRL1000);
376                         tmp2 = advert2 &
377                                 ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
378                 }
379                 tmp |= ethtool_adv_to_mii_adv_t(advertising);
380
381                 if (mii->supports_gmii)
382                         tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
383                 if (advert != tmp) {
384                         mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
385                         mii->advertising = tmp;
386                 }
387                 if ((mii->supports_gmii) && (advert2 != tmp2))
388                         mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
389
390                 /* turn on autonegotiation, and force a renegotiate */
391                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
392                 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
393                 mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
394
395                 mii->force_media = 0;
396         } else {
397                 u32 bmcr, tmp;
398
399                 /* turn off auto negotiation, set speed and duplexity */
400                 bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
401                 tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
402                                BMCR_SPEED1000 | BMCR_FULLDPLX);
403                 if (speed == SPEED_1000)
404                         tmp |= BMCR_SPEED1000;
405                 else if (speed == SPEED_100)
406                         tmp |= BMCR_SPEED100;
407                 if (cmd->base.duplex == DUPLEX_FULL) {
408                         tmp |= BMCR_FULLDPLX;
409                         mii->full_duplex = 1;
410                 } else {
411                         mii->full_duplex = 0;
412                 }
413                 if (bmcr != tmp)
414                         mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
415
416                 mii->force_media = 1;
417         }
418         return 0;
419 }
420
421 /**
422  * mii_check_gmii_support - check if the MII supports Gb interfaces
423  * @mii: the MII interface
424  */
425 int mii_check_gmii_support(struct mii_if_info *mii)
426 {
427         int reg;
428
429         reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
430         if (reg & BMSR_ESTATEN) {
431                 reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
432                 if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
433                         return 1;
434         }
435
436         return 0;
437 }
438
439 /**
440  * mii_link_ok - is link status up/ok
441  * @mii: the MII interface
442  *
443  * Returns 1 if the MII reports link status up/ok, 0 otherwise.
444  */
445 int mii_link_ok (struct mii_if_info *mii)
446 {
447         /* first, a dummy read, needed to latch some MII phys */
448         mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
449         if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
450                 return 1;
451         return 0;
452 }
453
454 /**
455  * mii_nway_restart - restart NWay (autonegotiation) for this interface
456  * @mii: the MII interface
457  *
458  * Returns 0 on success, negative on error.
459  */
460 int mii_nway_restart (struct mii_if_info *mii)
461 {
462         int bmcr;
463         int r = -EINVAL;
464
465         /* if autoneg is off, it's an error */
466         bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
467
468         if (bmcr & BMCR_ANENABLE) {
469                 bmcr |= BMCR_ANRESTART;
470                 mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
471                 r = 0;
472         }
473
474         return r;
475 }
476
477 /**
478  * mii_check_link - check MII link status
479  * @mii: MII interface
480  *
481  * If the link status changed (previous != current), call
482  * netif_carrier_on() if current link status is Up or call
483  * netif_carrier_off() if current link status is Down.
484  */
485 void mii_check_link (struct mii_if_info *mii)
486 {
487         int cur_link = mii_link_ok(mii);
488         int prev_link = netif_carrier_ok(mii->dev);
489
490         if (cur_link && !prev_link)
491                 netif_carrier_on(mii->dev);
492         else if (prev_link && !cur_link)
493                 netif_carrier_off(mii->dev);
494 }
495
496 /**
497  * mii_check_media - check the MII interface for a carrier/speed/duplex change
498  * @mii: the MII interface
499  * @ok_to_print: OK to print link up/down messages
500  * @init_media: OK to save duplex mode in @mii
501  *
502  * Returns 1 if the duplex mode changed, 0 if not.
503  * If the media type is forced, always returns 0.
504  */
505 unsigned int mii_check_media (struct mii_if_info *mii,
506                               unsigned int ok_to_print,
507                               unsigned int init_media)
508 {
509         unsigned int old_carrier, new_carrier;
510         int advertise, lpa, media, duplex;
511         int lpa2 = 0;
512
513         /* check current and old link status */
514         old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
515         new_carrier = (unsigned int) mii_link_ok(mii);
516
517         /* if carrier state did not change, this is a "bounce",
518          * just exit as everything is already set correctly
519          */
520         if ((!init_media) && (old_carrier == new_carrier))
521                 return 0; /* duplex did not change */
522
523         /* no carrier, nothing much to do */
524         if (!new_carrier) {
525                 netif_carrier_off(mii->dev);
526                 if (ok_to_print)
527                         netdev_info(mii->dev, "link down\n");
528                 return 0; /* duplex did not change */
529         }
530
531         /*
532          * we have carrier, see who's on the other end
533          */
534         netif_carrier_on(mii->dev);
535
536         if (mii->force_media) {
537                 if (ok_to_print)
538                         netdev_info(mii->dev, "link up\n");
539                 return 0; /* duplex did not change */
540         }
541
542         /* get MII advertise and LPA values */
543         if ((!init_media) && (mii->advertising))
544                 advertise = mii->advertising;
545         else {
546                 advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
547                 mii->advertising = advertise;
548         }
549         lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
550         if (mii->supports_gmii)
551                 lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
552
553         /* figure out media and duplex from advertise and LPA values */
554         media = mii_nway_result(lpa & advertise);
555         duplex = (media & ADVERTISE_FULL) ? 1 : 0;
556         if (lpa2 & LPA_1000FULL)
557                 duplex = 1;
558
559         if (ok_to_print)
560                 netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
561                             lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
562                             media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
563                             100 : 10,
564                             duplex ? "full" : "half",
565                             lpa);
566
567         if ((init_media) || (mii->full_duplex != duplex)) {
568                 mii->full_duplex = duplex;
569                 return 1; /* duplex changed */
570         }
571
572         return 0; /* duplex did not change */
573 }
574
575 /**
576  * generic_mii_ioctl - main MII ioctl interface
577  * @mii_if: the MII interface
578  * @mii_data: MII ioctl data structure
579  * @cmd: MII ioctl command
580  * @duplex_chg_out: pointer to @duplex_changed status if there was no
581  *      ioctl error
582  *
583  * Returns 0 on success, negative on error.
584  */
585 int generic_mii_ioctl(struct mii_if_info *mii_if,
586                       struct mii_ioctl_data *mii_data, int cmd,
587                       unsigned int *duplex_chg_out)
588 {
589         int rc = 0;
590         unsigned int duplex_changed = 0;
591
592         if (duplex_chg_out)
593                 *duplex_chg_out = 0;
594
595         mii_data->phy_id &= mii_if->phy_id_mask;
596         mii_data->reg_num &= mii_if->reg_num_mask;
597
598         switch(cmd) {
599         case SIOCGMIIPHY:
600                 mii_data->phy_id = mii_if->phy_id;
601                 fallthrough;
602
603         case SIOCGMIIREG:
604                 mii_data->val_out =
605                         mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
606                                           mii_data->reg_num);
607                 break;
608
609         case SIOCSMIIREG: {
610                 u16 val = mii_data->val_in;
611
612                 if (mii_data->phy_id == mii_if->phy_id) {
613                         switch(mii_data->reg_num) {
614                         case MII_BMCR: {
615                                 unsigned int new_duplex = 0;
616                                 if (val & (BMCR_RESET|BMCR_ANENABLE))
617                                         mii_if->force_media = 0;
618                                 else
619                                         mii_if->force_media = 1;
620                                 if (mii_if->force_media &&
621                                     (val & BMCR_FULLDPLX))
622                                         new_duplex = 1;
623                                 if (mii_if->full_duplex != new_duplex) {
624                                         duplex_changed = 1;
625                                         mii_if->full_duplex = new_duplex;
626                                 }
627                                 break;
628                         }
629                         case MII_ADVERTISE:
630                                 mii_if->advertising = val;
631                                 break;
632                         default:
633                                 /* do nothing */
634                                 break;
635                         }
636                 }
637
638                 mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
639                                    mii_data->reg_num, val);
640                 break;
641         }
642
643         default:
644                 rc = -EOPNOTSUPP;
645                 break;
646         }
647
648         if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
649                 *duplex_chg_out = 1;
650
651         return rc;
652 }
653
654 MODULE_AUTHOR ("Jeff Garzik <[email protected]>");
655 MODULE_DESCRIPTION ("MII hardware support library");
656 MODULE_LICENSE("GPL");
657
658 EXPORT_SYMBOL(mii_link_ok);
659 EXPORT_SYMBOL(mii_nway_restart);
660 EXPORT_SYMBOL(mii_ethtool_gset);
661 EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
662 EXPORT_SYMBOL(mii_ethtool_sset);
663 EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
664 EXPORT_SYMBOL(mii_check_link);
665 EXPORT_SYMBOL(mii_check_media);
666 EXPORT_SYMBOL(mii_check_gmii_support);
667 EXPORT_SYMBOL(generic_mii_ioctl);
668
This page took 0.07054 seconds and 4 git commands to generate.