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
mii_get_an(struct mii_if_info * mii,u16 addr)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 */
mii_ethtool_gset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)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 */
mii_ethtool_get_link_ksettings(struct mii_if_info * mii,struct ethtool_link_ksettings * cmd)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 */
mii_ethtool_sset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)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 */
mii_ethtool_set_link_ksettings(struct mii_if_info * mii,const struct ethtool_link_ksettings * cmd)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 */
mii_check_gmii_support(struct mii_if_info * mii)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 */
mii_link_ok(struct mii_if_info * mii)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 */
mii_nway_restart(struct mii_if_info * mii)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 */
mii_check_link(struct mii_if_info * mii)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 */
mii_check_media(struct mii_if_info * mii,unsigned int ok_to_print,unsigned int init_media)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 */
generic_mii_ioctl(struct mii_if_info * mii_if,struct mii_ioctl_data * mii_data,int cmd,unsigned int * duplex_chg_out)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
669