Linux Audio

Check our new training course

Loading...
v6.8
  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
 36static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
 37{
 
 38	int advert;
 39
 40	advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 41
 42	return mii_lpa_to_ethtool_lpa_t(advert);
 43}
 44
 45/**
 46 * mii_ethtool_gset - get settings that are specified in @ecmd
 47 * @mii: MII interface
 48 * @ecmd: requested ethtool_cmd
 49 *
 50 * The @ecmd parameter is expected to have been cleared before calling
 51 * mii_ethtool_gset().
 
 
 52 */
 53void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
 54{
 55	struct net_device *dev = mii->dev;
 56	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
 57	u32 nego;
 58
 59	ecmd->supported =
 60	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 61	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
 62	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
 63	if (mii->supports_gmii)
 64		ecmd->supported |= SUPPORTED_1000baseT_Half |
 65			SUPPORTED_1000baseT_Full;
 66
 67	/* only supports twisted-pair */
 68	ecmd->port = PORT_MII;
 69
 70	/* only supports internal transceiver */
 71	ecmd->transceiver = XCVR_INTERNAL;
 72
 73	/* this isn't fully supported at higher layers */
 74	ecmd->phy_address = mii->phy_id;
 75	ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
 76
 77	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
 78
 79	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
 80	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
 81	if (mii->supports_gmii) {
 82		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
 83		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
 84	}
 85
 86	ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
 87	if (mii->supports_gmii)
 88		ecmd->advertising |=
 89			mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
 90
 91	if (bmcr & BMCR_ANENABLE) {
 92		ecmd->advertising |= ADVERTISED_Autoneg;
 93		ecmd->autoneg = AUTONEG_ENABLE;
 94
 
 
 
 
 
 
 95		if (bmsr & BMSR_ANEGCOMPLETE) {
 96			ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
 97			ecmd->lp_advertising |=
 98					mii_stat1000_to_ethtool_lpa_t(stat1000);
 
 
 
 
 99		} else {
100			ecmd->lp_advertising = 0;
101		}
102
103		nego = ecmd->advertising & ecmd->lp_advertising;
104
105		if (nego & (ADVERTISED_1000baseT_Full |
106			    ADVERTISED_1000baseT_Half)) {
107			ethtool_cmd_speed_set(ecmd, SPEED_1000);
108			ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
109		} else if (nego & (ADVERTISED_100baseT_Full |
110				   ADVERTISED_100baseT_Half)) {
111			ethtool_cmd_speed_set(ecmd, SPEED_100);
112			ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
113		} else {
114			ethtool_cmd_speed_set(ecmd, SPEED_10);
115			ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
116		}
117	} else {
118		ecmd->autoneg = AUTONEG_DISABLE;
119
120		ethtool_cmd_speed_set(ecmd,
121				      ((bmcr & BMCR_SPEED1000 &&
122					(bmcr & BMCR_SPEED100) == 0) ?
123				       SPEED_1000 :
124				       ((bmcr & BMCR_SPEED100) ?
125					SPEED_100 : SPEED_10)));
126		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
127	}
128
129	mii->full_duplex = ecmd->duplex;
130
131	/* ignore maxtxpkt, maxrxpkt for now */
132}
133
134/**
135 * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
136 * @mii: MII interface
137 * @cmd: requested ethtool_link_ksettings
138 *
139 * The @cmd parameter is expected to have been cleared before calling
140 * mii_ethtool_get_link_ksettings().
141 */
142void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
143				    struct ethtool_link_ksettings *cmd)
144{
145	struct net_device *dev = mii->dev;
146	u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
147	u32 nego, supported, advertising, lp_advertising;
148
149	supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
150		     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
151		     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
152	if (mii->supports_gmii)
153		supported |= SUPPORTED_1000baseT_Half |
154			SUPPORTED_1000baseT_Full;
155
156	/* only supports twisted-pair */
157	cmd->base.port = PORT_MII;
158
159	/* this isn't fully supported at higher layers */
160	cmd->base.phy_address = mii->phy_id;
161	cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
162
163	advertising = ADVERTISED_TP | ADVERTISED_MII;
164
165	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
166	bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
167	if (mii->supports_gmii) {
168		ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
169		stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
170	}
171
172	advertising |= mii_get_an(mii, MII_ADVERTISE);
173	if (mii->supports_gmii)
174		advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
175
176	if (bmcr & BMCR_ANENABLE) {
177		advertising |= ADVERTISED_Autoneg;
178		cmd->base.autoneg = AUTONEG_ENABLE;
179
180		if (bmsr & BMSR_ANEGCOMPLETE) {
181			lp_advertising = mii_get_an(mii, MII_LPA);
182			lp_advertising |=
183					mii_stat1000_to_ethtool_lpa_t(stat1000);
184		} else {
185			lp_advertising = 0;
186		}
187
188		nego = advertising & lp_advertising;
189
190		if (nego & (ADVERTISED_1000baseT_Full |
191			    ADVERTISED_1000baseT_Half)) {
192			cmd->base.speed = SPEED_1000;
193			cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
194		} else if (nego & (ADVERTISED_100baseT_Full |
195				   ADVERTISED_100baseT_Half)) {
196			cmd->base.speed = SPEED_100;
197			cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
198		} else {
199			cmd->base.speed = SPEED_10;
200			cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
201		}
202	} else {
203		cmd->base.autoneg = AUTONEG_DISABLE;
204
205		cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
206				    (bmcr & BMCR_SPEED100) == 0) ?
207				   SPEED_1000 :
208				   ((bmcr & BMCR_SPEED100) ?
209				    SPEED_100 : SPEED_10));
210		cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
211			DUPLEX_FULL : DUPLEX_HALF;
212
213		lp_advertising = 0;
214	}
215
216	mii->full_duplex = cmd->base.duplex;
217
218	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
219						supported);
220	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
221						advertising);
222	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
223						lp_advertising);
224
225	/* ignore maxtxpkt, maxrxpkt for now */
226}
227
228/**
229 * mii_ethtool_sset - set settings that are specified in @ecmd
230 * @mii: MII interface
231 * @ecmd: requested ethtool_cmd
232 *
233 * Returns 0 for success, negative on error.
234 */
235int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
236{
237	struct net_device *dev = mii->dev;
238	u32 speed = ethtool_cmd_speed(ecmd);
239
240	if (speed != SPEED_10 &&
241	    speed != SPEED_100 &&
242	    speed != SPEED_1000)
243		return -EINVAL;
244	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
245		return -EINVAL;
246	if (ecmd->port != PORT_MII)
247		return -EINVAL;
248	if (ecmd->transceiver != XCVR_INTERNAL)
249		return -EINVAL;
250	if (ecmd->phy_address != mii->phy_id)
251		return -EINVAL;
252	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
253		return -EINVAL;
254	if ((speed == SPEED_1000) && (!mii->supports_gmii))
255		return -EINVAL;
256
257	/* ignore supported, maxtxpkt, maxrxpkt */
258
259	if (ecmd->autoneg == AUTONEG_ENABLE) {
260		u32 bmcr, advert, tmp;
261		u32 advert2 = 0, tmp2 = 0;
262
263		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
264					  ADVERTISED_10baseT_Full |
265					  ADVERTISED_100baseT_Half |
266					  ADVERTISED_100baseT_Full |
267					  ADVERTISED_1000baseT_Half |
268					  ADVERTISED_1000baseT_Full)) == 0)
269			return -EINVAL;
270
271		/* advertise only what has been requested */
272		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
273		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
274		if (mii->supports_gmii) {
275			advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
276			tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
277		}
278		tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
279
280		if (mii->supports_gmii)
281			tmp2 |=
282			      ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
283		if (advert != tmp) {
284			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
285			mii->advertising = tmp;
286		}
287		if ((mii->supports_gmii) && (advert2 != tmp2))
288			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
289
290		/* turn on autonegotiation, and force a renegotiate */
291		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
292		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
293		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
294
295		mii->force_media = 0;
296	} else {
297		u32 bmcr, tmp;
298
299		/* turn off auto negotiation, set speed and duplexity */
300		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
301		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
302			       BMCR_SPEED1000 | BMCR_FULLDPLX);
303		if (speed == SPEED_1000)
304			tmp |= BMCR_SPEED1000;
305		else if (speed == SPEED_100)
306			tmp |= BMCR_SPEED100;
307		if (ecmd->duplex == DUPLEX_FULL) {
308			tmp |= BMCR_FULLDPLX;
309			mii->full_duplex = 1;
310		} else
311			mii->full_duplex = 0;
312		if (bmcr != tmp)
313			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
314
315		mii->force_media = 1;
316	}
317	return 0;
318}
319
320/**
321 * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
322 * @mii: MII interfaces
323 * @cmd: requested ethtool_link_ksettings
324 *
325 * Returns 0 for success, negative on error.
326 */
327int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
328				   const struct ethtool_link_ksettings *cmd)
329{
330	struct net_device *dev = mii->dev;
331	u32 speed = cmd->base.speed;
332
333	if (speed != SPEED_10 &&
334	    speed != SPEED_100 &&
335	    speed != SPEED_1000)
336		return -EINVAL;
337	if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
338		return -EINVAL;
339	if (cmd->base.port != PORT_MII)
340		return -EINVAL;
341	if (cmd->base.phy_address != mii->phy_id)
342		return -EINVAL;
343	if (cmd->base.autoneg != AUTONEG_DISABLE &&
344	    cmd->base.autoneg != AUTONEG_ENABLE)
345		return -EINVAL;
346	if ((speed == SPEED_1000) && (!mii->supports_gmii))
347		return -EINVAL;
348
349	/* ignore supported, maxtxpkt, maxrxpkt */
350
351	if (cmd->base.autoneg == AUTONEG_ENABLE) {
352		u32 bmcr, advert, tmp;
353		u32 advert2 = 0, tmp2 = 0;
354		u32 advertising;
355
356		ethtool_convert_link_mode_to_legacy_u32(
357			&advertising, cmd->link_modes.advertising);
358
359		if ((advertising & (ADVERTISED_10baseT_Half |
360				    ADVERTISED_10baseT_Full |
361				    ADVERTISED_100baseT_Half |
362				    ADVERTISED_100baseT_Full |
363				    ADVERTISED_1000baseT_Half |
364				    ADVERTISED_1000baseT_Full)) == 0)
365			return -EINVAL;
366
367		/* advertise only what has been requested */
368		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
369		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
370		if (mii->supports_gmii) {
371			advert2 = mii->mdio_read(dev, mii->phy_id,
372						 MII_CTRL1000);
373			tmp2 = advert2 &
374				~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
375		}
376		tmp |= ethtool_adv_to_mii_adv_t(advertising);
377
378		if (mii->supports_gmii)
379			tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
380		if (advert != tmp) {
381			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
382			mii->advertising = tmp;
383		}
384		if ((mii->supports_gmii) && (advert2 != tmp2))
385			mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
386
387		/* turn on autonegotiation, and force a renegotiate */
388		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
389		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
390		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
391
392		mii->force_media = 0;
393	} else {
394		u32 bmcr, tmp;
395
396		/* turn off auto negotiation, set speed and duplexity */
397		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
398		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
399			       BMCR_SPEED1000 | BMCR_FULLDPLX);
400		if (speed == SPEED_1000)
401			tmp |= BMCR_SPEED1000;
402		else if (speed == SPEED_100)
403			tmp |= BMCR_SPEED100;
404		if (cmd->base.duplex == DUPLEX_FULL) {
405			tmp |= BMCR_FULLDPLX;
406			mii->full_duplex = 1;
407		} else {
408			mii->full_duplex = 0;
409		}
410		if (bmcr != tmp)
411			mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
412
413		mii->force_media = 1;
414	}
415	return 0;
416}
417
418/**
419 * mii_check_gmii_support - check if the MII supports Gb interfaces
420 * @mii: the MII interface
421 */
422int mii_check_gmii_support(struct mii_if_info *mii)
423{
424	int reg;
425
426	reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
427	if (reg & BMSR_ESTATEN) {
428		reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
429		if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
430			return 1;
431	}
432
433	return 0;
434}
435
436/**
437 * mii_link_ok - is link status up/ok
438 * @mii: the MII interface
439 *
440 * Returns 1 if the MII reports link status up/ok, 0 otherwise.
441 */
442int mii_link_ok (struct mii_if_info *mii)
443{
444	/* first, a dummy read, needed to latch some MII phys */
445	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
446	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
447		return 1;
448	return 0;
449}
450
451/**
452 * mii_nway_restart - restart NWay (autonegotiation) for this interface
453 * @mii: the MII interface
454 *
455 * Returns 0 on success, negative on error.
456 */
457int mii_nway_restart (struct mii_if_info *mii)
458{
459	int bmcr;
460	int r = -EINVAL;
461
462	/* if autoneg is off, it's an error */
463	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
464
465	if (bmcr & BMCR_ANENABLE) {
466		bmcr |= BMCR_ANRESTART;
467		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
468		r = 0;
469	}
470
471	return r;
472}
473
474/**
475 * mii_check_link - check MII link status
476 * @mii: MII interface
477 *
478 * If the link status changed (previous != current), call
479 * netif_carrier_on() if current link status is Up or call
480 * netif_carrier_off() if current link status is Down.
481 */
482void mii_check_link (struct mii_if_info *mii)
483{
484	int cur_link = mii_link_ok(mii);
485	int prev_link = netif_carrier_ok(mii->dev);
486
487	if (cur_link && !prev_link)
488		netif_carrier_on(mii->dev);
489	else if (prev_link && !cur_link)
490		netif_carrier_off(mii->dev);
491}
492
493/**
494 * mii_check_media - check the MII interface for a carrier/speed/duplex change
495 * @mii: the MII interface
496 * @ok_to_print: OK to print link up/down messages
497 * @init_media: OK to save duplex mode in @mii
498 *
499 * Returns 1 if the duplex mode changed, 0 if not.
500 * If the media type is forced, always returns 0.
501 */
502unsigned int mii_check_media (struct mii_if_info *mii,
503			      unsigned int ok_to_print,
504			      unsigned int init_media)
505{
506	unsigned int old_carrier, new_carrier;
507	int advertise, lpa, media, duplex;
508	int lpa2 = 0;
509
 
 
 
 
510	/* check current and old link status */
511	old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
512	new_carrier = (unsigned int) mii_link_ok(mii);
513
514	/* if carrier state did not change, this is a "bounce",
515	 * just exit as everything is already set correctly
516	 */
517	if ((!init_media) && (old_carrier == new_carrier))
518		return 0; /* duplex did not change */
519
520	/* no carrier, nothing much to do */
521	if (!new_carrier) {
522		netif_carrier_off(mii->dev);
523		if (ok_to_print)
524			netdev_info(mii->dev, "link down\n");
525		return 0; /* duplex did not change */
526	}
527
528	/*
529	 * we have carrier, see who's on the other end
530	 */
531	netif_carrier_on(mii->dev);
532
533	if (mii->force_media) {
534		if (ok_to_print)
535			netdev_info(mii->dev, "link up\n");
536		return 0; /* duplex did not change */
537	}
538
539	/* get MII advertise and LPA values */
540	if ((!init_media) && (mii->advertising))
541		advertise = mii->advertising;
542	else {
543		advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
544		mii->advertising = advertise;
545	}
546	lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
547	if (mii->supports_gmii)
548		lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
549
550	/* figure out media and duplex from advertise and LPA values */
551	media = mii_nway_result(lpa & advertise);
552	duplex = (media & ADVERTISE_FULL) ? 1 : 0;
553	if (lpa2 & LPA_1000FULL)
554		duplex = 1;
555
556	if (ok_to_print)
557		netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
558			    lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
559			    media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
560			    100 : 10,
561			    duplex ? "full" : "half",
562			    lpa);
563
564	if ((init_media) || (mii->full_duplex != duplex)) {
565		mii->full_duplex = duplex;
566		return 1; /* duplex changed */
567	}
568
569	return 0; /* duplex did not change */
570}
571
572/**
573 * generic_mii_ioctl - main MII ioctl interface
574 * @mii_if: the MII interface
575 * @mii_data: MII ioctl data structure
576 * @cmd: MII ioctl command
577 * @duplex_chg_out: pointer to @duplex_changed status if there was no
578 *	ioctl error
579 *
580 * Returns 0 on success, negative on error.
581 */
582int generic_mii_ioctl(struct mii_if_info *mii_if,
583		      struct mii_ioctl_data *mii_data, int cmd,
584		      unsigned int *duplex_chg_out)
585{
586	int rc = 0;
587	unsigned int duplex_changed = 0;
588
589	if (duplex_chg_out)
590		*duplex_chg_out = 0;
591
592	mii_data->phy_id &= mii_if->phy_id_mask;
593	mii_data->reg_num &= mii_if->reg_num_mask;
594
595	switch(cmd) {
596	case SIOCGMIIPHY:
597		mii_data->phy_id = mii_if->phy_id;
598		fallthrough;
599
600	case SIOCGMIIREG:
601		mii_data->val_out =
602			mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
603					  mii_data->reg_num);
604		break;
605
606	case SIOCSMIIREG: {
607		u16 val = mii_data->val_in;
608
609		if (mii_data->phy_id == mii_if->phy_id) {
610			switch(mii_data->reg_num) {
611			case MII_BMCR: {
612				unsigned int new_duplex = 0;
613				if (val & (BMCR_RESET|BMCR_ANENABLE))
614					mii_if->force_media = 0;
615				else
616					mii_if->force_media = 1;
617				if (mii_if->force_media &&
618				    (val & BMCR_FULLDPLX))
619					new_duplex = 1;
620				if (mii_if->full_duplex != new_duplex) {
621					duplex_changed = 1;
622					mii_if->full_duplex = new_duplex;
623				}
624				break;
625			}
626			case MII_ADVERTISE:
627				mii_if->advertising = val;
628				break;
629			default:
630				/* do nothing */
631				break;
632			}
633		}
634
635		mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
636				   mii_data->reg_num, val);
637		break;
638	}
639
640	default:
641		rc = -EOPNOTSUPP;
642		break;
643	}
644
645	if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
646		*duplex_chg_out = 1;
647
648	return rc;
649}
650
651MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
652MODULE_DESCRIPTION ("MII hardware support library");
653MODULE_LICENSE("GPL");
654
655EXPORT_SYMBOL(mii_link_ok);
656EXPORT_SYMBOL(mii_nway_restart);
657EXPORT_SYMBOL(mii_ethtool_gset);
658EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
659EXPORT_SYMBOL(mii_ethtool_sset);
660EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
661EXPORT_SYMBOL(mii_check_link);
662EXPORT_SYMBOL(mii_check_media);
663EXPORT_SYMBOL(mii_check_gmii_support);
664EXPORT_SYMBOL(generic_mii_ioctl);
665
v3.1
  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