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