xref: /linux/drivers/net/mii.c (revision 8a5617bdc111aa7ba49c81fa694fde63d3474f94)
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/mdio.h>
35 
36 static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37 {
38 	u32 result = 0;
39 	int advert;
40 
41 	advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
42 	if (advert & LPA_LPACK)
43 		result |= ADVERTISED_Autoneg;
44 	if (advert & ADVERTISE_10HALF)
45 		result |= ADVERTISED_10baseT_Half;
46 	if (advert & ADVERTISE_10FULL)
47 		result |= ADVERTISED_10baseT_Full;
48 	if (advert & ADVERTISE_100HALF)
49 		result |= ADVERTISED_100baseT_Half;
50 	if (advert & ADVERTISE_100FULL)
51 		result |= ADVERTISED_100baseT_Full;
52 	if (advert & ADVERTISE_PAUSE_CAP)
53 		result |= ADVERTISED_Pause;
54 	if (advert & ADVERTISE_PAUSE_ASYM)
55 		result |= ADVERTISED_Asym_Pause;
56 
57 	return result;
58 }
59 
60 /**
61  * mii_ethtool_gset - get settings that are specified in @ecmd
62  * @mii: MII interface
63  * @ecmd: requested ethtool_cmd
64  *
65  * Returns 0 for success, negative on error.
66  */
67 int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
68 {
69 	struct net_device *dev = mii->dev;
70 	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
71 	u32 nego;
72 
73 	ecmd->supported =
74 	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
75 	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
76 	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
77 	if (mii->supports_gmii)
78 		ecmd->supported |= SUPPORTED_1000baseT_Half |
79 			SUPPORTED_1000baseT_Full;
80 
81 	/* only supports twisted-pair */
82 	ecmd->port = PORT_MII;
83 
84 	/* only supports internal transceiver */
85 	ecmd->transceiver = XCVR_INTERNAL;
86 
87 	/* this isn't fully supported at higher layers */
88 	ecmd->phy_address = mii->phy_id;
89 	ecmd->mdio_support = MDIO_SUPPORTS_C22;
90 
91 	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
92 
93 	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
94 	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
95 	if (mii->supports_gmii) {
96  		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
97 		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
98 	}
99 	if (bmcr & BMCR_ANENABLE) {
100 		ecmd->advertising |= ADVERTISED_Autoneg;
101 		ecmd->autoneg = AUTONEG_ENABLE;
102 
103 		ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
104 		if (ctrl1000 & ADVERTISE_1000HALF)
105 			ecmd->advertising |= ADVERTISED_1000baseT_Half;
106 		if (ctrl1000 & ADVERTISE_1000FULL)
107 			ecmd->advertising |= ADVERTISED_1000baseT_Full;
108 
109 		if (bmsr & BMSR_ANEGCOMPLETE) {
110 			ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
111 			if (stat1000 & LPA_1000HALF)
112 				ecmd->lp_advertising |=
113 					ADVERTISED_1000baseT_Half;
114 			if (stat1000 & LPA_1000FULL)
115 				ecmd->lp_advertising |=
116 					ADVERTISED_1000baseT_Full;
117 		} else {
118 			ecmd->lp_advertising = 0;
119 		}
120 
121 		nego = ecmd->advertising & ecmd->lp_advertising;
122 
123 		if (nego & (ADVERTISED_1000baseT_Full |
124 			    ADVERTISED_1000baseT_Half)) {
125 			ecmd->speed = SPEED_1000;
126 			ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
127 		} else if (nego & (ADVERTISED_100baseT_Full |
128 				   ADVERTISED_100baseT_Half)) {
129 			ecmd->speed = SPEED_100;
130 			ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
131 		} else {
132 			ecmd->speed = SPEED_10;
133 			ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
134 		}
135 	} else {
136 		ecmd->autoneg = AUTONEG_DISABLE;
137 
138 		ecmd->speed = ((bmcr & BMCR_SPEED1000 &&
139 				(bmcr & BMCR_SPEED100) == 0) ? SPEED_1000 :
140 			       (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10);
141 		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
142 	}
143 
144 	mii->full_duplex = ecmd->duplex;
145 
146 	/* ignore maxtxpkt, maxrxpkt for now */
147 
148 	return 0;
149 }
150 
151 /**
152  * mii_ethtool_sset - set settings that are specified in @ecmd
153  * @mii: MII interface
154  * @ecmd: requested ethtool_cmd
155  *
156  * Returns 0 for success, negative on error.
157  */
158 int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
159 {
160 	struct net_device *dev = mii->dev;
161 
162 	if (ecmd->speed != SPEED_10 &&
163 	    ecmd->speed != SPEED_100 &&
164 	    ecmd->speed != SPEED_1000)
165 		return -EINVAL;
166 	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
167 		return -EINVAL;
168 	if (ecmd->port != PORT_MII)
169 		return -EINVAL;
170 	if (ecmd->transceiver != XCVR_INTERNAL)
171 		return -EINVAL;
172 	if (ecmd->phy_address != mii->phy_id)
173 		return -EINVAL;
174 	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
175 		return -EINVAL;
176 	if ((ecmd->speed == SPEED_1000) && (!mii->supports_gmii))
177 		return -EINVAL;
178 
179 	/* ignore supported, maxtxpkt, maxrxpkt */
180 
181 	if (ecmd->autoneg == AUTONEG_ENABLE) {
182 		u32 bmcr, advert, tmp;
183 		u32 advert2 = 0, tmp2 = 0;
184 
185 		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
186 					  ADVERTISED_10baseT_Full |
187 					  ADVERTISED_100baseT_Half |
188 					  ADVERTISED_100baseT_Full |
189 					  ADVERTISED_1000baseT_Half |
190 					  ADVERTISED_1000baseT_Full)) == 0)
191 			return -EINVAL;
192 
193 		/* advertise only what has been requested */
194 		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
195 		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
196 		if (mii->supports_gmii) {
197 			advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
198 			tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
199 		}
200 		if (ecmd->advertising & ADVERTISED_10baseT_Half)
201 			tmp |= ADVERTISE_10HALF;
202 		if (ecmd->advertising & ADVERTISED_10baseT_Full)
203 			tmp |= ADVERTISE_10FULL;
204 		if (ecmd->advertising & ADVERTISED_100baseT_Half)
205 			tmp |= ADVERTISE_100HALF;
206 		if (ecmd->advertising & ADVERTISED_100baseT_Full)
207 			tmp |= ADVERTISE_100FULL;
208 		if (mii->supports_gmii) {
209 			if (ecmd->advertising & ADVERTISED_1000baseT_Half)
210 				tmp2 |= ADVERTISE_1000HALF;
211 			if (ecmd->advertising & ADVERTISED_1000baseT_Full)
212 				tmp2 |= ADVERTISE_1000FULL;
213 		}
214 		if (advert != tmp) {
215 			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
216 			mii->advertising = tmp;
217 		}
218 		if ((mii->supports_gmii) && (advert2 != tmp2))
219 			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
220 
221 		/* turn on autonegotiation, and force a renegotiate */
222 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
223 		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
224 		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
225 
226 		mii->force_media = 0;
227 	} else {
228 		u32 bmcr, tmp;
229 
230 		/* turn off auto negotiation, set speed and duplexity */
231 		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
232 		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
233 			       BMCR_SPEED1000 | BMCR_FULLDPLX);
234 		if (ecmd->speed == SPEED_1000)
235 			tmp |= BMCR_SPEED1000;
236 		else if (ecmd->speed == SPEED_100)
237 			tmp |= BMCR_SPEED100;
238 		if (ecmd->duplex == DUPLEX_FULL) {
239 			tmp |= BMCR_FULLDPLX;
240 			mii->full_duplex = 1;
241 		} else
242 			mii->full_duplex = 0;
243 		if (bmcr != tmp)
244 			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
245 
246 		mii->force_media = 1;
247 	}
248 	return 0;
249 }
250 
251 /**
252  * mii_check_gmii_support - check if the MII supports Gb interfaces
253  * @mii: the MII interface
254  */
255 int mii_check_gmii_support(struct mii_if_info *mii)
256 {
257 	int reg;
258 
259 	reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
260 	if (reg & BMSR_ESTATEN) {
261 		reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
262 		if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
263 			return 1;
264 	}
265 
266 	return 0;
267 }
268 
269 /**
270  * mii_link_ok - is link status up/ok
271  * @mii: the MII interface
272  *
273  * Returns 1 if the MII reports link status up/ok, 0 otherwise.
274  */
275 int mii_link_ok (struct mii_if_info *mii)
276 {
277 	/* first, a dummy read, needed to latch some MII phys */
278 	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
279 	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
280 		return 1;
281 	return 0;
282 }
283 
284 /**
285  * mii_nway_restart - restart NWay (autonegotiation) for this interface
286  * @mii: the MII interface
287  *
288  * Returns 0 on success, negative on error.
289  */
290 int mii_nway_restart (struct mii_if_info *mii)
291 {
292 	int bmcr;
293 	int r = -EINVAL;
294 
295 	/* if autoneg is off, it's an error */
296 	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
297 
298 	if (bmcr & BMCR_ANENABLE) {
299 		bmcr |= BMCR_ANRESTART;
300 		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
301 		r = 0;
302 	}
303 
304 	return r;
305 }
306 
307 /**
308  * mii_check_link - check MII link status
309  * @mii: MII interface
310  *
311  * If the link status changed (previous != current), call
312  * netif_carrier_on() if current link status is Up or call
313  * netif_carrier_off() if current link status is Down.
314  */
315 void mii_check_link (struct mii_if_info *mii)
316 {
317 	int cur_link = mii_link_ok(mii);
318 	int prev_link = netif_carrier_ok(mii->dev);
319 
320 	if (cur_link && !prev_link)
321 		netif_carrier_on(mii->dev);
322 	else if (prev_link && !cur_link)
323 		netif_carrier_off(mii->dev);
324 }
325 
326 /**
327  * mii_check_media - check the MII interface for a duplex change
328  * @mii: the MII interface
329  * @ok_to_print: OK to print link up/down messages
330  * @init_media: OK to save duplex mode in @mii
331  *
332  * Returns 1 if the duplex mode changed, 0 if not.
333  * If the media type is forced, always returns 0.
334  */
335 unsigned int mii_check_media (struct mii_if_info *mii,
336 			      unsigned int ok_to_print,
337 			      unsigned int init_media)
338 {
339 	unsigned int old_carrier, new_carrier;
340 	int advertise, lpa, media, duplex;
341 	int lpa2 = 0;
342 
343 	/* if forced media, go no further */
344 	if (mii->force_media)
345 		return 0; /* duplex did not change */
346 
347 	/* check current and old link status */
348 	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
349 	new_carrier = (unsigned int) mii_link_ok(mii);
350 
351 	/* if carrier state did not change, this is a "bounce",
352 	 * just exit as everything is already set correctly
353 	 */
354 	if ((!init_media) && (old_carrier == new_carrier))
355 		return 0; /* duplex did not change */
356 
357 	/* no carrier, nothing much to do */
358 	if (!new_carrier) {
359 		netif_carrier_off(mii->dev);
360 		if (ok_to_print)
361 			netdev_info(mii->dev, "link down\n");
362 		return 0; /* duplex did not change */
363 	}
364 
365 	/*
366 	 * we have carrier, see who's on the other end
367 	 */
368 	netif_carrier_on(mii->dev);
369 
370 	/* get MII advertise and LPA values */
371 	if ((!init_media) && (mii->advertising))
372 		advertise = mii->advertising;
373 	else {
374 		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
375 		mii->advertising = advertise;
376 	}
377 	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
378 	if (mii->supports_gmii)
379 		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
380 
381 	/* figure out media and duplex from advertise and LPA values */
382 	media = mii_nway_result(lpa & advertise);
383 	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
384 	if (lpa2 & LPA_1000FULL)
385 		duplex = 1;
386 
387 	if (ok_to_print)
388 		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
389 			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
390 			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
391 			    100 : 10,
392 			    duplex ? "full" : "half",
393 			    lpa);
394 
395 	if ((init_media) || (mii->full_duplex != duplex)) {
396 		mii->full_duplex = duplex;
397 		return 1; /* duplex changed */
398 	}
399 
400 	return 0; /* duplex did not change */
401 }
402 
403 /**
404  * generic_mii_ioctl - main MII ioctl interface
405  * @mii_if: the MII interface
406  * @mii_data: MII ioctl data structure
407  * @cmd: MII ioctl command
408  * @duplex_chg_out: pointer to @duplex_changed status if there was no
409  *	ioctl error
410  *
411  * Returns 0 on success, negative on error.
412  */
413 int generic_mii_ioctl(struct mii_if_info *mii_if,
414 		      struct mii_ioctl_data *mii_data, int cmd,
415 		      unsigned int *duplex_chg_out)
416 {
417 	int rc = 0;
418 	unsigned int duplex_changed = 0;
419 
420 	if (duplex_chg_out)
421 		*duplex_chg_out = 0;
422 
423 	mii_data->phy_id &= mii_if->phy_id_mask;
424 	mii_data->reg_num &= mii_if->reg_num_mask;
425 
426 	switch(cmd) {
427 	case SIOCGMIIPHY:
428 		mii_data->phy_id = mii_if->phy_id;
429 		/* fall through */
430 
431 	case SIOCGMIIREG:
432 		mii_data->val_out =
433 			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
434 					  mii_data->reg_num);
435 		break;
436 
437 	case SIOCSMIIREG: {
438 		u16 val = mii_data->val_in;
439 
440 		if (mii_data->phy_id == mii_if->phy_id) {
441 			switch(mii_data->reg_num) {
442 			case MII_BMCR: {
443 				unsigned int new_duplex = 0;
444 				if (val & (BMCR_RESET|BMCR_ANENABLE))
445 					mii_if->force_media = 0;
446 				else
447 					mii_if->force_media = 1;
448 				if (mii_if->force_media &&
449 				    (val & BMCR_FULLDPLX))
450 					new_duplex = 1;
451 				if (mii_if->full_duplex != new_duplex) {
452 					duplex_changed = 1;
453 					mii_if->full_duplex = new_duplex;
454 				}
455 				break;
456 			}
457 			case MII_ADVERTISE:
458 				mii_if->advertising = val;
459 				break;
460 			default:
461 				/* do nothing */
462 				break;
463 			}
464 		}
465 
466 		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
467 				   mii_data->reg_num, val);
468 		break;
469 	}
470 
471 	default:
472 		rc = -EOPNOTSUPP;
473 		break;
474 	}
475 
476 	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
477 		*duplex_chg_out = 1;
478 
479 	return rc;
480 }
481 
482 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
483 MODULE_DESCRIPTION ("MII hardware support library");
484 MODULE_LICENSE("GPL");
485 
486 EXPORT_SYMBOL(mii_link_ok);
487 EXPORT_SYMBOL(mii_nway_restart);
488 EXPORT_SYMBOL(mii_ethtool_gset);
489 EXPORT_SYMBOL(mii_ethtool_sset);
490 EXPORT_SYMBOL(mii_check_link);
491 EXPORT_SYMBOL(mii_check_media);
492 EXPORT_SYMBOL(mii_check_gmii_support);
493 EXPORT_SYMBOL(generic_mii_ioctl);
494 
495