xref: /openbmc/linux/drivers/net/mii.c (revision 87c2ce3b)
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_check_gmii_support(struct mii_if_info *mii)
211 {
212 	int reg;
213 
214 	reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
215 	if (reg & BMSR_ESTATEN) {
216 		reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
217 		if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
218 			return 1;
219 	}
220 
221 	return 0;
222 }
223 
224 int mii_link_ok (struct mii_if_info *mii)
225 {
226 	/* first, a dummy read, needed to latch some MII phys */
227 	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
228 	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
229 		return 1;
230 	return 0;
231 }
232 
233 int mii_nway_restart (struct mii_if_info *mii)
234 {
235 	int bmcr;
236 	int r = -EINVAL;
237 
238 	/* if autoneg is off, it's an error */
239 	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
240 
241 	if (bmcr & BMCR_ANENABLE) {
242 		bmcr |= BMCR_ANRESTART;
243 		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
244 		r = 0;
245 	}
246 
247 	return r;
248 }
249 
250 void mii_check_link (struct mii_if_info *mii)
251 {
252 	int cur_link = mii_link_ok(mii);
253 	int prev_link = netif_carrier_ok(mii->dev);
254 
255 	if (cur_link && !prev_link)
256 		netif_carrier_on(mii->dev);
257 	else if (prev_link && !cur_link)
258 		netif_carrier_off(mii->dev);
259 }
260 
261 unsigned int mii_check_media (struct mii_if_info *mii,
262 			      unsigned int ok_to_print,
263 			      unsigned int init_media)
264 {
265 	unsigned int old_carrier, new_carrier;
266 	int advertise, lpa, media, duplex;
267 	int lpa2 = 0;
268 
269 	/* if forced media, go no further */
270 	if (mii->force_media)
271 		return 0; /* duplex did not change */
272 
273 	/* check current and old link status */
274 	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
275 	new_carrier = (unsigned int) mii_link_ok(mii);
276 
277 	/* if carrier state did not change, this is a "bounce",
278 	 * just exit as everything is already set correctly
279 	 */
280 	if ((!init_media) && (old_carrier == new_carrier))
281 		return 0; /* duplex did not change */
282 
283 	/* no carrier, nothing much to do */
284 	if (!new_carrier) {
285 		netif_carrier_off(mii->dev);
286 		if (ok_to_print)
287 			printk(KERN_INFO "%s: link down\n", mii->dev->name);
288 		return 0; /* duplex did not change */
289 	}
290 
291 	/*
292 	 * we have carrier, see who's on the other end
293 	 */
294 	netif_carrier_on(mii->dev);
295 
296 	/* get MII advertise and LPA values */
297 	if ((!init_media) && (mii->advertising))
298 		advertise = mii->advertising;
299 	else {
300 		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
301 		mii->advertising = advertise;
302 	}
303 	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
304 	if (mii->supports_gmii)
305 		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
306 
307 	/* figure out media and duplex from advertise and LPA values */
308 	media = mii_nway_result(lpa & advertise);
309 	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
310 	if (lpa2 & LPA_1000FULL)
311 		duplex = 1;
312 
313 	if (ok_to_print)
314 		printk(KERN_INFO "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",
315 		       mii->dev->name,
316 		       lpa2 & (LPA_1000FULL | LPA_1000HALF) ? "1000" :
317 		       media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? "100" : "10",
318 		       duplex ? "full" : "half",
319 		       lpa);
320 
321 	if ((init_media) || (mii->full_duplex != duplex)) {
322 		mii->full_duplex = duplex;
323 		return 1; /* duplex changed */
324 	}
325 
326 	return 0; /* duplex did not change */
327 }
328 
329 int generic_mii_ioctl(struct mii_if_info *mii_if,
330 		      struct mii_ioctl_data *mii_data, int cmd,
331 		      unsigned int *duplex_chg_out)
332 {
333 	int rc = 0;
334 	unsigned int duplex_changed = 0;
335 
336 	if (duplex_chg_out)
337 		*duplex_chg_out = 0;
338 
339 	mii_data->phy_id &= mii_if->phy_id_mask;
340 	mii_data->reg_num &= mii_if->reg_num_mask;
341 
342 	switch(cmd) {
343 	case SIOCGMIIPHY:
344 		mii_data->phy_id = mii_if->phy_id;
345 		/* fall through */
346 
347 	case SIOCGMIIREG:
348 		mii_data->val_out =
349 			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
350 					  mii_data->reg_num);
351 		break;
352 
353 	case SIOCSMIIREG: {
354 		u16 val = mii_data->val_in;
355 
356 		if (!capable(CAP_NET_ADMIN))
357 			return -EPERM;
358 
359 		if (mii_data->phy_id == mii_if->phy_id) {
360 			switch(mii_data->reg_num) {
361 			case MII_BMCR: {
362 				unsigned int new_duplex = 0;
363 				if (val & (BMCR_RESET|BMCR_ANENABLE))
364 					mii_if->force_media = 0;
365 				else
366 					mii_if->force_media = 1;
367 				if (mii_if->force_media &&
368 				    (val & BMCR_FULLDPLX))
369 					new_duplex = 1;
370 				if (mii_if->full_duplex != new_duplex) {
371 					duplex_changed = 1;
372 					mii_if->full_duplex = new_duplex;
373 				}
374 				break;
375 			}
376 			case MII_ADVERTISE:
377 				mii_if->advertising = val;
378 				break;
379 			default:
380 				/* do nothing */
381 				break;
382 			}
383 		}
384 
385 		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
386 				   mii_data->reg_num, val);
387 		break;
388 	}
389 
390 	default:
391 		rc = -EOPNOTSUPP;
392 		break;
393 	}
394 
395 	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
396 		*duplex_chg_out = 1;
397 
398 	return rc;
399 }
400 
401 MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
402 MODULE_DESCRIPTION ("MII hardware support library");
403 MODULE_LICENSE("GPL");
404 
405 EXPORT_SYMBOL(mii_link_ok);
406 EXPORT_SYMBOL(mii_nway_restart);
407 EXPORT_SYMBOL(mii_ethtool_gset);
408 EXPORT_SYMBOL(mii_ethtool_sset);
409 EXPORT_SYMBOL(mii_check_link);
410 EXPORT_SYMBOL(mii_check_media);
411 EXPORT_SYMBOL(mii_check_gmii_support);
412 EXPORT_SYMBOL(generic_mii_ioctl);
413 
414