Home | History | Annotate | Download | only in net
      1 /*
      2 
      3 	mii.c: MII interface library
      4 
      5 	Ported to gPXE by Daniel Verkamp <daniel (at) drv.nu>
      6 	from Linux drivers/net/mii.c
      7 
      8 	Maintained by Jeff Garzik <jgarzik (at) pobox.com>
      9 	Copyright 2001,2002 Jeff Garzik
     10 
     11 	Various code came from myson803.c and other files by
     12 	Donald Becker.  Copyright:
     13 
     14 		Written 1998-2002 by Donald Becker.
     15 
     16 		This software may be used and distributed according
     17 		to the terms of the GNU General Public License (GPL),
     18 		incorporated herein by reference.  Drivers based on
     19 		or derived from this code fall under the GPL and must
     20 		retain the authorship, copyright and license notice.
     21 		This file is not a complete program and may only be
     22 		used when the entire operating system is licensed
     23 		under the GPL.
     24 
     25 		The author may be reached as becker (at) scyld.com, or C/O
     26 		Scyld Computing Corporation
     27 		410 Severn Ave., Suite 210
     28 		Annapolis MD 21403
     29 
     30 */
     31 
     32 #include <mii.h>
     33 
     34 /**
     35  * mii_link_ok - is link status up/ok
     36  * @mii: the MII interface
     37  *
     38  * Returns 1 if the MII reports link status up/ok, 0 otherwise.
     39  */
     40 int
     41 mii_link_ok ( struct mii_if_info *mii )
     42 {
     43 	/* first, a dummy read, needed to latch some MII phys */
     44 	mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR );
     45 	if ( mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR ) & BMSR_LSTATUS )
     46 		return 1;
     47 	return 0;
     48 }
     49 
     50 /**
     51  * mii_check_link - check MII link status
     52  * @mii: MII interface
     53  *
     54  * If the link status changed (previous != current), call
     55  * netif_carrier_on() if current link status is Up or call
     56  * netif_carrier_off() if current link status is Down.
     57  */
     58 void
     59 mii_check_link ( struct mii_if_info *mii )
     60 {
     61 	int cur_link = mii_link_ok ( mii );
     62 	int prev_link = netdev_link_ok ( mii->dev );
     63 
     64 	if ( cur_link && !prev_link )
     65 		netdev_link_up ( mii->dev );
     66 	else if (prev_link && !cur_link)
     67 		netdev_link_down ( mii->dev );
     68 }
     69 
     70 
     71 /**
     72  * mii_check_media - check the MII interface for a duplex change
     73  * @mii: the MII interface
     74  * @ok_to_print: OK to print link up/down messages
     75  * @init_media: OK to save duplex mode in @mii
     76  *
     77  * Returns 1 if the duplex mode changed, 0 if not.
     78  * If the media type is forced, always returns 0.
     79  */
     80 unsigned int
     81 mii_check_media ( struct mii_if_info *mii,
     82                   unsigned int ok_to_print,
     83                   unsigned int init_media )
     84 {
     85 	unsigned int old_carrier, new_carrier;
     86 	int advertise, lpa, media, duplex;
     87 	int lpa2 = 0;
     88 
     89 	/* if forced media, go no further */
     90 	if (mii->force_media)
     91 		return 0; /* duplex did not change */
     92 
     93 	/* check current and old link status */
     94 	old_carrier = netdev_link_ok ( mii->dev ) ? 1 : 0;
     95 	new_carrier = (unsigned int) mii_link_ok ( mii );
     96 
     97 	/* if carrier state did not change, this is a "bounce",
     98 	 * just exit as everything is already set correctly
     99 	 */
    100 	if ( ( ! init_media ) && ( old_carrier == new_carrier ) )
    101 		return 0; /* duplex did not change */
    102 
    103 	/* no carrier, nothing much to do */
    104 	if ( ! new_carrier ) {
    105 		netdev_link_down ( mii->dev );
    106 		if ( ok_to_print )
    107 			DBG ( "%s: link down\n", mii->dev->name);
    108 		return 0; /* duplex did not change */
    109 	}
    110 
    111 	/*
    112 	 * we have carrier, see who's on the other end
    113 	 */
    114 	netdev_link_up ( mii->dev );
    115 
    116 	/* get MII advertise and LPA values */
    117 	if ( ( ! init_media ) && ( mii->advertising ) ) {
    118 		advertise = mii->advertising;
    119 	} else {
    120 		advertise = mii->mdio_read ( mii->dev, mii->phy_id, MII_ADVERTISE );
    121 		mii->advertising = advertise;
    122 	}
    123 	lpa = mii->mdio_read ( mii->dev, mii->phy_id, MII_LPA );
    124 	if ( mii->supports_gmii )
    125 		lpa2 = mii->mdio_read ( mii->dev, mii->phy_id, MII_STAT1000 );
    126 
    127 	/* figure out media and duplex from advertise and LPA values */
    128 	media = mii_nway_result ( lpa & advertise );
    129 	duplex = ( media & ADVERTISE_FULL ) ? 1 : 0;
    130 	if ( lpa2 & LPA_1000FULL )
    131 		duplex = 1;
    132 
    133 	if ( ok_to_print )
    134 		DBG ( "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",
    135 		       mii->dev->name,
    136 		       lpa2 & ( LPA_1000FULL | LPA_1000HALF ) ? "1000" :
    137 		       media & ( ADVERTISE_100FULL | ADVERTISE_100HALF ) ? "100" : "10",
    138 		       duplex ? "full" : "half",
    139 		       lpa);
    140 
    141 	if ( ( init_media ) || ( mii->full_duplex != duplex ) ) {
    142 		mii->full_duplex = duplex;
    143 		return 1; /* duplex changed */
    144 	}
    145 
    146 	return 0; /* duplex did not change */
    147 }
    148