xref: /openbmc/linux/drivers/net/mii.c (revision 2a598d0b)
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 
666