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