xref: /openbmc/linux/drivers/net/mii.c (revision d9fd5a71)
1 /*
2 
3 	mii.c: MII interface library
4 
5 	Maintained by Jeff Garzik <jgarzik@pobox.com>
6 	Copyright 2001,2002 Jeff Garzik
7 
8 	Various code came from myson803.c and other files by
9 	Donald Becker.  Copyright:
10 
11 		Written 1998-2002 by Donald Becker.
12 
13 		This software may be used and distributed according
14 		to the terms of the GNU General Public License (GPL),
15 		incorporated herein by reference.  Drivers based on
16 		or derived from this code fall under the GPL and must
17 		retain the authorship, copyright and license notice.
18 		This file is not a complete program and may only be
19 		used when the entire operating system is licensed
20 		under the GPL.
21 
22 		The author may be reached as becker@scyld.com, or C/O
23 		Scyld Computing Corporation
24 		410 Severn Ave., Suite 210
25 		Annapolis MD 21403
26 
27 
28  */
29 
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/netdevice.h>
33 #include <linux/ethtool.h>
34 #include <linux/mii.h>
35 
36 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 {
38 	int advert;
39 
40 	advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
41 
42 	return mii_lpa_to_ethtool_lpa_t(advert);
43 }
44 
45 /**
46  * mii_ethtool_gset - get settings that are specified in @ecmd
47  * @mii: MII interface
48  * @ecmd: requested ethtool_cmd
49  *
50  * The @ecmd parameter is expected to have been cleared before calling
51  * mii_ethtool_gset().
52  *
53  * Returns 0 for success, negative on error.
54  */
55 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56 {
57 	struct net_device *dev = mii->dev;
58 	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59 	u32 nego;
60 
61 	ecmd->supported =
62 	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
63 	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
64 	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
65 	if (mii->supports_gmii)
66 		ecmd->supported |= SUPPORTED_1000baseT_Half |
67 			SUPPORTED_1000baseT_Full;
68 
69 	/* only supports twisted-pair */
70 	ecmd->port = PORT_MII;
71 
72 	/* only supports internal transceiver */
73 	ecmd->transceiver = XCVR_INTERNAL;
74 
75 	/* this isn't fully supported at higher layers */
76 	ecmd->phy_address = mii->phy_id;
77 	ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
78 
79 	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
80 
81 	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
82 	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
83 	if (mii->supports_gmii) {
84  		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85 		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
86 	}
87 
88 	ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
89 	if (mii->supports_gmii)
90 		ecmd->advertising |=
91 			mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
92 
93 	if (bmcr & BMCR_ANENABLE) {
94 		ecmd->advertising |= ADVERTISED_Autoneg;
95 		ecmd->autoneg = AUTONEG_ENABLE;
96 
97 		if (bmsr & BMSR_ANEGCOMPLETE) {
98 			ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
99 			ecmd->lp_advertising |=
100 					mii_stat1000_to_ethtool_lpa_t(stat1000);
101 		} else {
102 			ecmd->lp_advertising = 0;
103 		}
104 
105 		nego = ecmd->advertising & ecmd->lp_advertising;
106 
107 		if (nego & (ADVERTISED_1000baseT_Full |
108 			    ADVERTISED_1000baseT_Half)) {
109 			ethtool_cmd_speed_set(ecmd, SPEED_1000);
110 			ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
111 		} else if (nego & (ADVERTISED_100baseT_Full |
112 				   ADVERTISED_100baseT_Half)) {
113 			ethtool_cmd_speed_set(ecmd, SPEED_100);
114 			ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
115 		} else {
116 			ethtool_cmd_speed_set(ecmd, SPEED_10);
117 			ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
118 		}
119 	} else {
120 		ecmd->autoneg = AUTONEG_DISABLE;
121 
122 		ethtool_cmd_speed_set(ecmd,
123 				      ((bmcr & BMCR_SPEED1000 &&
124 					(bmcr & BMCR_SPEED100) == 0) ?
125 				       SPEED_1000 :
126 				       ((bmcr & BMCR_SPEED100) ?
127 					SPEED_100 : SPEED_10)));
128 		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
129 	}
130 
131 	mii->full_duplex = ecmd->duplex;
132 
133 	/* ignore maxtxpkt, maxrxpkt for now */
134 
135 	return 0;
136 }
137 
138 /**
139  * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
140  * @mii: MII interface
141  * @cmd: requested ethtool_link_ksettings
142  *
143  * The @cmd parameter is expected to have been cleared before calling
144  * mii_ethtool_get_link_ksettings().
145  */
146 void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
147 				    struct ethtool_link_ksettings *cmd)
148 {
149 	struct net_device *dev = mii->dev;
150 	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
151 	u32 nego, supported, advertising, lp_advertising;
152 
153 	supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
154 		     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
155 		     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
156 	if (mii->supports_gmii)
157 		supported |= SUPPORTED_1000baseT_Half |
158 			SUPPORTED_1000baseT_Full;
159 
160 	/* only supports twisted-pair */
161 	cmd->base.port = PORT_MII;
162 
163 	/* this isn't fully supported at higher layers */
164 	cmd->base.phy_address = mii->phy_id;
165 	cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
166 
167 	advertising = ADVERTISED_TP | ADVERTISED_MII;
168 
169 	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
170 	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
171 	if (mii->supports_gmii) {
172 		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
173 		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
174 	}
175 
176 	advertising |= mii_get_an(mii, MII_ADVERTISE);
177 	if (mii->supports_gmii)
178 		advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
179 
180 	if (bmcr & BMCR_ANENABLE) {
181 		advertising |= ADVERTISED_Autoneg;
182 		cmd->base.autoneg = AUTONEG_ENABLE;
183 
184 		if (bmsr & BMSR_ANEGCOMPLETE) {
185 			lp_advertising = mii_get_an(mii, MII_LPA);
186 			lp_advertising |=
187 					mii_stat1000_to_ethtool_lpa_t(stat1000);
188 		} else {
189 			lp_advertising = 0;
190 		}
191 
192 		nego = advertising & lp_advertising;
193 
194 		if (nego & (ADVERTISED_1000baseT_Full |
195 			    ADVERTISED_1000baseT_Half)) {
196 			cmd->base.speed = SPEED_1000;
197 			cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
198 		} else if (nego & (ADVERTISED_100baseT_Full |
199 				   ADVERTISED_100baseT_Half)) {
200 			cmd->base.speed = SPEED_100;
201 			cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
202 		} else {
203 			cmd->base.speed = SPEED_10;
204 			cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
205 		}
206 	} else {
207 		cmd->base.autoneg = AUTONEG_DISABLE;
208 
209 		cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
210 				    (bmcr & BMCR_SPEED100) == 0) ?
211 				   SPEED_1000 :
212 				   ((bmcr & BMCR_SPEED100) ?
213 				    SPEED_100 : SPEED_10));
214 		cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
215 			DUPLEX_FULL : DUPLEX_HALF;
216 
217 		lp_advertising = 0;
218 	}
219 
220 	mii->full_duplex = cmd->base.duplex;
221 
222 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
223 						supported);
224 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
225 						advertising);
226 	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
227 						lp_advertising);
228 
229 	/* ignore maxtxpkt, maxrxpkt for now */
230 }
231 
232 /**
233  * mii_ethtool_sset - set settings that are specified in @ecmd
234  * @mii: MII interface
235  * @ecmd: requested ethtool_cmd
236  *
237  * Returns 0 for success, negative on error.
238  */
239 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
240 {
241 	struct net_device *dev = mii->dev;
242 	u32 speed = ethtool_cmd_speed(ecmd);
243 
244 	if (speed != SPEED_10 &&
245 	    speed != SPEED_100 &&
246 	    speed != SPEED_1000)
247 		return -EINVAL;
248 	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
249 		return -EINVAL;
250 	if (ecmd->port != PORT_MII)
251 		return -EINVAL;
252 	if (ecmd->transceiver != XCVR_INTERNAL)
253 		return -EINVAL;
254 	if (ecmd->phy_address != mii->phy_id)
255 		return -EINVAL;
256 	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
257 		return -EINVAL;
258 	if ((speed == SPEED_1000) && (!mii->supports_gmii))
259 		return -EINVAL;
260 
261 	/* ignore supported, maxtxpkt, maxrxpkt */
262 
263 	if (ecmd->autoneg == AUTONEG_ENABLE) {
264 		u32 bmcr, advert, tmp;
265 		u32 advert2 = 0, tmp2 = 0;
266 
267 		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
268 					  ADVERTISED_10baseT_Full |
269 					  ADVERTISED_100baseT_Half |
270 					  ADVERTISED_100baseT_Full |
271 					  ADVERTISED_1000baseT_Half |
272 					  ADVERTISED_1000baseT_Full)) == 0)
273 			return -EINVAL;
274 
275 		/* advertise only what has been requested */
276 		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
277 		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
278 		if (mii->supports_gmii) {
279 			advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
280 			tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
281 		}
282 		tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
283 
284 		if (mii->supports_gmii)
285 			tmp2 |=
286 			      ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
287 		if (advert != tmp) {
288 			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
289 			mii->advertising = tmp;
290 		}
291 		if ((mii->supports_gmii) && (advert2 != tmp2))
292 			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
293 
294 		/* turn on autonegotiation, and force a renegotiate */
295 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
296 		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
297 		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
298 
299 		mii->force_media = 0;
300 	} else {
301 		u32 bmcr, tmp;
302 
303 		/* turn off auto negotiation, set speed and duplexity */
304 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
305 		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
306 			       BMCR_SPEED1000 | BMCR_FULLDPLX);
307 		if (speed == SPEED_1000)
308 			tmp |= BMCR_SPEED1000;
309 		else if (speed == SPEED_100)
310 			tmp |= BMCR_SPEED100;
311 		if (ecmd->duplex == DUPLEX_FULL) {
312 			tmp |= BMCR_FULLDPLX;
313 			mii->full_duplex = 1;
314 		} else
315 			mii->full_duplex = 0;
316 		if (bmcr != tmp)
317 			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
318 
319 		mii->force_media = 1;
320 	}
321 	return 0;
322 }
323 
324 /**
325  * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
326  * @mii: MII interfaces
327  * @cmd: requested ethtool_link_ksettings
328  *
329  * Returns 0 for success, negative on error.
330  */
331 int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
332 				   const struct ethtool_link_ksettings *cmd)
333 {
334 	struct net_device *dev = mii->dev;
335 	u32 speed = cmd->base.speed;
336 
337 	if (speed != SPEED_10 &&
338 	    speed != SPEED_100 &&
339 	    speed != SPEED_1000)
340 		return -EINVAL;
341 	if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
342 		return -EINVAL;
343 	if (cmd->base.port != PORT_MII)
344 		return -EINVAL;
345 	if (cmd->base.phy_address != mii->phy_id)
346 		return -EINVAL;
347 	if (cmd->base.autoneg != AUTONEG_DISABLE &&
348 	    cmd->base.autoneg != AUTONEG_ENABLE)
349 		return -EINVAL;
350 	if ((speed == SPEED_1000) && (!mii->supports_gmii))
351 		return -EINVAL;
352 
353 	/* ignore supported, maxtxpkt, maxrxpkt */
354 
355 	if (cmd->base.autoneg == AUTONEG_ENABLE) {
356 		u32 bmcr, advert, tmp;
357 		u32 advert2 = 0, tmp2 = 0;
358 		u32 advertising;
359 
360 		ethtool_convert_link_mode_to_legacy_u32(
361 			&advertising, cmd->link_modes.advertising);
362 
363 		if ((advertising & (ADVERTISED_10baseT_Half |
364 				    ADVERTISED_10baseT_Full |
365 				    ADVERTISED_100baseT_Half |
366 				    ADVERTISED_100baseT_Full |
367 				    ADVERTISED_1000baseT_Half |
368 				    ADVERTISED_1000baseT_Full)) == 0)
369 			return -EINVAL;
370 
371 		/* advertise only what has been requested */
372 		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
373 		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
374 		if (mii->supports_gmii) {
375 			advert2 = mii->mdio_read(dev, mii->phy_id,
376 						 MII_CTRL1000);
377 			tmp2 = advert2 &
378 				~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
379 		}
380 		tmp |= ethtool_adv_to_mii_adv_t(advertising);
381 
382 		if (mii->supports_gmii)
383 			tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
384 		if (advert != tmp) {
385 			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
386 			mii->advertising = tmp;
387 		}
388 		if ((mii->supports_gmii) && (advert2 != tmp2))
389 			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
390 
391 		/* turn on autonegotiation, and force a renegotiate */
392 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
393 		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
394 		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
395 
396 		mii->force_media = 0;
397 	} else {
398 		u32 bmcr, tmp;
399 
400 		/* turn off auto negotiation, set speed and duplexity */
401 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
402 		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
403 			       BMCR_SPEED1000 | BMCR_FULLDPLX);
404 		if (speed == SPEED_1000)
405 			tmp |= BMCR_SPEED1000;
406 		else if (speed == SPEED_100)
407 			tmp |= BMCR_SPEED100;
408 		if (cmd->base.duplex == DUPLEX_FULL) {
409 			tmp |= BMCR_FULLDPLX;
410 			mii->full_duplex = 1;
411 		} else {
412 			mii->full_duplex = 0;
413 		}
414 		if (bmcr != tmp)
415 			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
416 
417 		mii->force_media = 1;
418 	}
419 	return 0;
420 }
421 
422 /**
423  * mii_check_gmii_support - check if the MII supports Gb interfaces
424  * @mii: the MII interface
425  */
426 int mii_check_gmii_support(struct mii_if_info *mii)
427 {
428 	int reg;
429 
430 	reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
431 	if (reg & BMSR_ESTATEN) {
432 		reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
433 		if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
434 			return 1;
435 	}
436 
437 	return 0;
438 }
439 
440 /**
441  * mii_link_ok - is link status up/ok
442  * @mii: the MII interface
443  *
444  * Returns 1 if the MII reports link status up/ok, 0 otherwise.
445  */
446 int mii_link_ok (struct mii_if_info *mii)
447 {
448 	/* first, a dummy read, needed to latch some MII phys */
449 	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
450 	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
451 		return 1;
452 	return 0;
453 }
454 
455 /**
456  * mii_nway_restart - restart NWay (autonegotiation) for this interface
457  * @mii: the MII interface
458  *
459  * Returns 0 on success, negative on error.
460  */
461 int mii_nway_restart (struct mii_if_info *mii)
462 {
463 	int bmcr;
464 	int r = -EINVAL;
465 
466 	/* if autoneg is off, it's an error */
467 	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
468 
469 	if (bmcr & BMCR_ANENABLE) {
470 		bmcr |= BMCR_ANRESTART;
471 		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
472 		r = 0;
473 	}
474 
475 	return r;
476 }
477 
478 /**
479  * mii_check_link - check MII link status
480  * @mii: MII interface
481  *
482  * If the link status changed (previous != current), call
483  * netif_carrier_on() if current link status is Up or call
484  * netif_carrier_off() if current link status is Down.
485  */
486 void mii_check_link (struct mii_if_info *mii)
487 {
488 	int cur_link = mii_link_ok(mii);
489 	int prev_link = netif_carrier_ok(mii->dev);
490 
491 	if (cur_link && !prev_link)
492 		netif_carrier_on(mii->dev);
493 	else if (prev_link && !cur_link)
494 		netif_carrier_off(mii->dev);
495 }
496 
497 /**
498  * mii_check_media - check the MII interface for a carrier/speed/duplex change
499  * @mii: the MII interface
500  * @ok_to_print: OK to print link up/down messages
501  * @init_media: OK to save duplex mode in @mii
502  *
503  * Returns 1 if the duplex mode changed, 0 if not.
504  * If the media type is forced, always returns 0.
505  */
506 unsigned int mii_check_media (struct mii_if_info *mii,
507 			      unsigned int ok_to_print,
508 			      unsigned int init_media)
509 {
510 	unsigned int old_carrier, new_carrier;
511 	int advertise, lpa, media, duplex;
512 	int lpa2 = 0;
513 
514 	/* check current and old link status */
515 	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
516 	new_carrier = (unsigned int) mii_link_ok(mii);
517 
518 	/* if carrier state did not change, this is a "bounce",
519 	 * just exit as everything is already set correctly
520 	 */
521 	if ((!init_media) && (old_carrier == new_carrier))
522 		return 0; /* duplex did not change */
523 
524 	/* no carrier, nothing much to do */
525 	if (!new_carrier) {
526 		netif_carrier_off(mii->dev);
527 		if (ok_to_print)
528 			netdev_info(mii->dev, "link down\n");
529 		return 0; /* duplex did not change */
530 	}
531 
532 	/*
533 	 * we have carrier, see who's on the other end
534 	 */
535 	netif_carrier_on(mii->dev);
536 
537 	if (mii->force_media) {
538 		if (ok_to_print)
539 			netdev_info(mii->dev, "link up\n");
540 		return 0; /* duplex did not change */
541 	}
542 
543 	/* get MII advertise and LPA values */
544 	if ((!init_media) && (mii->advertising))
545 		advertise = mii->advertising;
546 	else {
547 		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
548 		mii->advertising = advertise;
549 	}
550 	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
551 	if (mii->supports_gmii)
552 		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
553 
554 	/* figure out media and duplex from advertise and LPA values */
555 	media = mii_nway_result(lpa & advertise);
556 	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
557 	if (lpa2 & LPA_1000FULL)
558 		duplex = 1;
559 
560 	if (ok_to_print)
561 		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
562 			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
563 			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
564 			    100 : 10,
565 			    duplex ? "full" : "half",
566 			    lpa);
567 
568 	if ((init_media) || (mii->full_duplex != duplex)) {
569 		mii->full_duplex = duplex;
570 		return 1; /* duplex changed */
571 	}
572 
573 	return 0; /* duplex did not change */
574 }
575 
576 /**
577  * generic_mii_ioctl - main MII ioctl interface
578  * @mii_if: the MII interface
579  * @mii_data: MII ioctl data structure
580  * @cmd: MII ioctl command
581  * @duplex_chg_out: pointer to @duplex_changed status if there was no
582  *	ioctl error
583  *
584  * Returns 0 on success, negative on error.
585  */
586 int generic_mii_ioctl(struct mii_if_info *mii_if,
587 		      struct mii_ioctl_data *mii_data, int cmd,
588 		      unsigned int *duplex_chg_out)
589 {
590 	int rc = 0;
591 	unsigned int duplex_changed = 0;
592 
593 	if (duplex_chg_out)
594 		*duplex_chg_out = 0;
595 
596 	mii_data->phy_id &= mii_if->phy_id_mask;
597 	mii_data->reg_num &= mii_if->reg_num_mask;
598 
599 	switch(cmd) {
600 	case SIOCGMIIPHY:
601 		mii_data->phy_id = mii_if->phy_id;
602 		fallthrough;
603 
604 	case SIOCGMIIREG:
605 		mii_data->val_out =
606 			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
607 					  mii_data->reg_num);
608 		break;
609 
610 	case SIOCSMIIREG: {
611 		u16 val = mii_data->val_in;
612 
613 		if (mii_data->phy_id == mii_if->phy_id) {
614 			switch(mii_data->reg_num) {
615 			case MII_BMCR: {
616 				unsigned int new_duplex = 0;
617 				if (val & (BMCR_RESET|BMCR_ANENABLE))
618 					mii_if->force_media = 0;
619 				else
620 					mii_if->force_media = 1;
621 				if (mii_if->force_media &&
622 				    (val & BMCR_FULLDPLX))
623 					new_duplex = 1;
624 				if (mii_if->full_duplex != new_duplex) {
625 					duplex_changed = 1;
626 					mii_if->full_duplex = new_duplex;
627 				}
628 				break;
629 			}
630 			case MII_ADVERTISE:
631 				mii_if->advertising = val;
632 				break;
633 			default:
634 				/* do nothing */
635 				break;
636 			}
637 		}
638 
639 		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
640 				   mii_data->reg_num, val);
641 		break;
642 	}
643 
644 	default:
645 		rc = -EOPNOTSUPP;
646 		break;
647 	}
648 
649 	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
650 		*duplex_chg_out = 1;
651 
652 	return rc;
653 }
654 
655 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
656 MODULE_DESCRIPTION ("MII hardware support library");
657 MODULE_LICENSE("GPL");
658 
659 EXPORT_SYMBOL(mii_link_ok);
660 EXPORT_SYMBOL(mii_nway_restart);
661 EXPORT_SYMBOL(mii_ethtool_gset);
662 EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
663 EXPORT_SYMBOL(mii_ethtool_sset);
664 EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
665 EXPORT_SYMBOL(mii_check_link);
666 EXPORT_SYMBOL(mii_check_media);
667 EXPORT_SYMBOL(mii_check_gmii_support);
668 EXPORT_SYMBOL(generic_mii_ioctl);
669 
670