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