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