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