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