• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mii_get_an(struct mii_if_info * mii,u16 addr)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  */
mii_ethtool_gset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)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  */
mii_ethtool_sset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)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  */
mii_check_gmii_support(struct mii_if_info * mii)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  */
mii_link_ok(struct mii_if_info * mii)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  */
mii_nway_restart(struct mii_if_info * mii)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  */
mii_check_link(struct mii_if_info * mii)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 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  */
mii_check_media(struct mii_if_info * mii,unsigned int ok_to_print,unsigned int init_media)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 	/* if forced media, go no further */
322 	if (mii->force_media)
323 		return 0; /* duplex did not change */
324 
325 	/* check current and old link status */
326 	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
327 	new_carrier = (unsigned int) mii_link_ok(mii);
328 
329 	/* if carrier state did not change, this is a "bounce",
330 	 * just exit as everything is already set correctly
331 	 */
332 	if ((!init_media) && (old_carrier == new_carrier))
333 		return 0; /* duplex did not change */
334 
335 	/* no carrier, nothing much to do */
336 	if (!new_carrier) {
337 		netif_carrier_off(mii->dev);
338 		if (ok_to_print)
339 			netdev_info(mii->dev, "link down\n");
340 		return 0; /* duplex did not change */
341 	}
342 
343 	/*
344 	 * we have carrier, see who's on the other end
345 	 */
346 	netif_carrier_on(mii->dev);
347 
348 	/* get MII advertise and LPA values */
349 	if ((!init_media) && (mii->advertising))
350 		advertise = mii->advertising;
351 	else {
352 		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
353 		mii->advertising = advertise;
354 	}
355 	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
356 	if (mii->supports_gmii)
357 		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
358 
359 	/* figure out media and duplex from advertise and LPA values */
360 	media = mii_nway_result(lpa & advertise);
361 	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
362 	if (lpa2 & LPA_1000FULL)
363 		duplex = 1;
364 
365 	if (ok_to_print)
366 		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
367 			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
368 			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
369 			    100 : 10,
370 			    duplex ? "full" : "half",
371 			    lpa);
372 
373 	if ((init_media) || (mii->full_duplex != duplex)) {
374 		mii->full_duplex = duplex;
375 		return 1; /* duplex changed */
376 	}
377 
378 	return 0; /* duplex did not change */
379 }
380 
381 /**
382  * generic_mii_ioctl - main MII ioctl interface
383  * @mii_if: the MII interface
384  * @mii_data: MII ioctl data structure
385  * @cmd: MII ioctl command
386  * @duplex_chg_out: pointer to @duplex_changed status if there was no
387  *	ioctl error
388  *
389  * Returns 0 on success, negative on error.
390  */
generic_mii_ioctl(struct mii_if_info * mii_if,struct mii_ioctl_data * mii_data,int cmd,unsigned int * duplex_chg_out)391 int generic_mii_ioctl(struct mii_if_info *mii_if,
392 		      struct mii_ioctl_data *mii_data, int cmd,
393 		      unsigned int *duplex_chg_out)
394 {
395 	int rc = 0;
396 	unsigned int duplex_changed = 0;
397 
398 	if (duplex_chg_out)
399 		*duplex_chg_out = 0;
400 
401 	mii_data->phy_id &= mii_if->phy_id_mask;
402 	mii_data->reg_num &= mii_if->reg_num_mask;
403 
404 	switch(cmd) {
405 	case SIOCGMIIPHY:
406 		mii_data->phy_id = mii_if->phy_id;
407 		/* fall through */
408 
409 	case SIOCGMIIREG:
410 		mii_data->val_out =
411 			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
412 					  mii_data->reg_num);
413 		break;
414 
415 	case SIOCSMIIREG: {
416 		u16 val = mii_data->val_in;
417 
418 		if (mii_data->phy_id == mii_if->phy_id) {
419 			switch(mii_data->reg_num) {
420 			case MII_BMCR: {
421 				unsigned int new_duplex = 0;
422 				if (val & (BMCR_RESET|BMCR_ANENABLE))
423 					mii_if->force_media = 0;
424 				else
425 					mii_if->force_media = 1;
426 				if (mii_if->force_media &&
427 				    (val & BMCR_FULLDPLX))
428 					new_duplex = 1;
429 				if (mii_if->full_duplex != new_duplex) {
430 					duplex_changed = 1;
431 					mii_if->full_duplex = new_duplex;
432 				}
433 				break;
434 			}
435 			case MII_ADVERTISE:
436 				mii_if->advertising = val;
437 				break;
438 			default:
439 				/* do nothing */
440 				break;
441 			}
442 		}
443 
444 		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
445 				   mii_data->reg_num, val);
446 		break;
447 	}
448 
449 	default:
450 		rc = -EOPNOTSUPP;
451 		break;
452 	}
453 
454 	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
455 		*duplex_chg_out = 1;
456 
457 	return rc;
458 }
459 
460 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
461 MODULE_DESCRIPTION ("MII hardware support library");
462 MODULE_LICENSE("GPL");
463 
464 EXPORT_SYMBOL(mii_link_ok);
465 EXPORT_SYMBOL(mii_nway_restart);
466 EXPORT_SYMBOL(mii_ethtool_gset);
467 EXPORT_SYMBOL(mii_ethtool_sset);
468 EXPORT_SYMBOL(mii_check_link);
469 EXPORT_SYMBOL(mii_check_media);
470 EXPORT_SYMBOL(mii_check_gmii_support);
471 EXPORT_SYMBOL(generic_mii_ioctl);
472 
473