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