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