Home | History | Annotate | Line # | Download | only in mii
mii.c revision 1.30
      1 /*	$NetBSD: mii.c,v 1.30 2002/09/27 03:18:16 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
      9  * NASA Ames Research Center.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  * 3. All advertising materials mentioning features or use of this software
     20  *    must display the following acknowledgement:
     21  *	This product includes software developed by the NetBSD
     22  *	Foundation, Inc. and its contributors.
     23  * 4. Neither the name of The NetBSD Foundation nor the names of its
     24  *    contributors may be used to endorse or promote products derived
     25  *    from this software without specific prior written permission.
     26  *
     27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37  * POSSIBILITY OF SUCH DAMAGE.
     38  */
     39 
     40 /*
     41  * MII bus layer, glues MII-capable network interface drivers to sharable
     42  * PHY drivers.
     43  */
     44 
     45 #include <sys/cdefs.h>
     46 __KERNEL_RCSID(0, "$NetBSD: mii.c,v 1.30 2002/09/27 03:18:16 thorpej Exp $");
     47 
     48 #include <sys/param.h>
     49 #include <sys/device.h>
     50 #include <sys/systm.h>
     51 #include <sys/socket.h>
     52 
     53 #include <net/if.h>
     54 #include <net/if_media.h>
     55 
     56 #include <dev/mii/mii.h>
     57 #include <dev/mii/miivar.h>
     58 
     59 int	mii_print(void *, const char *);
     60 int	mii_submatch(struct device *, struct cfdata *, void *);
     61 
     62 /*
     63  * Helper function used by network interface drivers, attaches PHYs
     64  * to the network interface driver parent.
     65  */
     66 void
     67 mii_attach(struct device *parent, struct mii_data *mii, int capmask,
     68     int phyloc, int offloc, int flags)
     69 {
     70 	struct mii_attach_args ma;
     71 	struct mii_softc *child;
     72 	int bmsr, offset = 0;
     73 	int phymin, phymax;
     74 
     75 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
     76 		panic("mii_attach: phyloc and offloc specified");
     77 
     78 	if (phyloc == MII_PHY_ANY) {
     79 		phymin = 0;
     80 		phymax = MII_NPHY - 1;
     81 	} else
     82 		phymin = phymax = phyloc;
     83 
     84 	if ((mii->mii_flags & MIIF_INITDONE) == 0) {
     85 		LIST_INIT(&mii->mii_phys);
     86 		mii->mii_flags |= MIIF_INITDONE;
     87 	}
     88 
     89 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
     90 		/*
     91 		 * Make sure we haven't already configured a PHY at this
     92 		 * address.  This allows mii_attach() to be called
     93 		 * multiple times.
     94 		 */
     95 		for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
     96 		     child = LIST_NEXT(child, mii_list)) {
     97 			if (child->mii_phy == ma.mii_phyno) {
     98 				/*
     99 				 * Yes, there is already something
    100 				 * configured at this address.
    101 				 */
    102 				offset++;
    103 				continue;
    104 			}
    105 		}
    106 
    107 		/*
    108 		 * Check to see if there is a PHY at this address.  Note,
    109 		 * many braindead PHYs report 0/0 in their ID registers,
    110 		 * so we test for media in the BMSR.
    111 		 */
    112 		bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR);
    113 		if (bmsr == 0 || bmsr == 0xffff ||
    114 		    (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) {
    115 			/* Assume no PHY at this address. */
    116 			continue;
    117 		}
    118 
    119 		/*
    120 		 * There is a PHY at this address.  If we were given an
    121 		 * `offset' locator, skip this PHY if it doesn't match.
    122 		 */
    123 		if (offloc != MII_OFFSET_ANY && offloc != offset) {
    124 			offset++;
    125 			continue;
    126 		}
    127 
    128 		/*
    129 		 * Extract the IDs.  Braindead PHYs will be handled by
    130 		 * the `ukphy' driver, as we have no ID information to
    131 		 * match on.
    132 		 */
    133 		ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno,
    134 		    MII_PHYIDR1);
    135 		ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno,
    136 		    MII_PHYIDR2);
    137 
    138 		ma.mii_data = mii;
    139 		ma.mii_capmask = capmask;
    140 		ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK);
    141 
    142 		if ((child = (struct mii_softc *)config_found_sm(parent, &ma,
    143 		    mii_print, mii_submatch)) != NULL) {
    144 			/*
    145 			 * Link it up in the parent's MII data.
    146 			 */
    147 			callout_init(&child->mii_nway_ch);
    148 			LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
    149 			child->mii_offset = offset;
    150 			mii->mii_instance++;
    151 		}
    152 		offset++;
    153 	}
    154 }
    155 
    156 void
    157 mii_activate(struct mii_data *mii, enum devact act, int phyloc, int offloc)
    158 {
    159 	struct mii_softc *child;
    160 
    161 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
    162 		panic("mii_activate: phyloc and offloc specified");
    163 
    164 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
    165 		return;
    166 
    167 	for (child = LIST_FIRST(&mii->mii_phys);
    168 	     child != NULL; child = LIST_NEXT(child, mii_list)) {
    169 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
    170 			if (phyloc != MII_PHY_ANY &&
    171 			    phyloc != child->mii_phy)
    172 				continue;
    173 			if (offloc != MII_OFFSET_ANY &&
    174 			    offloc != child->mii_offset)
    175 				continue;
    176 		}
    177 		switch (act) {
    178 		case DVACT_ACTIVATE:
    179 			panic("mii_activate: DVACT_ACTIVATE");
    180 			break;
    181 
    182 		case DVACT_DEACTIVATE:
    183 			if (config_deactivate(&child->mii_dev) != 0)
    184 				panic("%s: config_activate(%d) failed\n",
    185 				    child->mii_dev.dv_xname, act);
    186 		}
    187 	}
    188 }
    189 
    190 void
    191 mii_detach(struct mii_data *mii, int phyloc, int offloc)
    192 {
    193 	struct mii_softc *child, *nchild;
    194 
    195 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
    196 		panic("mii_detach: phyloc and offloc specified");
    197 
    198 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
    199 		return;
    200 
    201 	for (child = LIST_FIRST(&mii->mii_phys);
    202 	     child != NULL; child = nchild) {
    203 		nchild = LIST_NEXT(child, mii_list);
    204 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
    205 			if (phyloc != MII_PHY_ANY &&
    206 			    phyloc != child->mii_phy)
    207 				continue;
    208 			if (offloc != MII_OFFSET_ANY &&
    209 			    offloc != child->mii_offset)
    210 				continue;
    211 		}
    212 		LIST_REMOVE(child, mii_list);
    213 		(void) config_detach(&child->mii_dev, DETACH_FORCE);
    214 	}
    215 }
    216 
    217 int
    218 mii_print(void *aux, const char *pnp)
    219 {
    220 	struct mii_attach_args *ma = aux;
    221 
    222 	if (pnp != NULL)
    223 		printf("OUI 0x%06x model 0x%04x rev %d at %s",
    224 		    MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2),
    225 		    MII_REV(ma->mii_id2), pnp);
    226 
    227 	printf(" phy %d", ma->mii_phyno);
    228 	return (UNCONF);
    229 }
    230 
    231 int
    232 mii_submatch(struct device *parent, struct cfdata *cf, void *aux)
    233 {
    234 	struct mii_attach_args *ma = aux;
    235 
    236 	if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] &&
    237 	    cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT)
    238 		return (0);
    239 
    240 	return (config_match(parent, cf, aux));
    241 }
    242 
    243 /*
    244  * Media changed; notify all PHYs.
    245  */
    246 int
    247 mii_mediachg(struct mii_data *mii)
    248 {
    249 	struct mii_softc *child;
    250 	int rv;
    251 
    252 	mii->mii_media_status = 0;
    253 	mii->mii_media_active = IFM_NONE;
    254 
    255 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    256 	     child = LIST_NEXT(child, mii_list)) {
    257 		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
    258 		if (rv)
    259 			return (rv);
    260 	}
    261 	return (0);
    262 }
    263 
    264 /*
    265  * Call the PHY tick routines, used during autonegotiation.
    266  */
    267 void
    268 mii_tick(struct mii_data *mii)
    269 {
    270 	struct mii_softc *child;
    271 
    272 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    273 	     child = LIST_NEXT(child, mii_list))
    274 		(void) PHY_SERVICE(child, mii, MII_TICK);
    275 }
    276 
    277 /*
    278  * Get media status from PHYs.
    279  */
    280 void
    281 mii_pollstat(struct mii_data *mii)
    282 {
    283 	struct mii_softc *child;
    284 
    285 	mii->mii_media_status = 0;
    286 	mii->mii_media_active = IFM_NONE;
    287 
    288 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    289 	     child = LIST_NEXT(child, mii_list))
    290 		(void) PHY_SERVICE(child, mii, MII_POLLSTAT);
    291 }
    292 
    293 /*
    294  * Inform the PHYs that the interface is down.
    295  */
    296 void
    297 mii_down(struct mii_data *mii)
    298 {
    299 	struct mii_softc *child;
    300 
    301 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    302 	     child = LIST_NEXT(child, mii_list))
    303 		(void) PHY_SERVICE(child, mii, MII_DOWN);
    304 }
    305 
    306 static unsigned char
    307 bitreverse(unsigned char x)
    308 {
    309 	static unsigned char nibbletab[16] = {
    310 		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
    311 	};
    312 
    313 	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
    314 }
    315 
    316 int
    317 mii_oui(int id1, int id2)
    318 {
    319 	int h;
    320 
    321 	h = (id1 << 6) | (id2 >> 10);
    322 
    323 	return ((bitreverse(h >> 16) << 16) |
    324 		(bitreverse((h >> 8) & 255) << 8) |
    325 		bitreverse(h & 255));
    326 }
    327