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