Home | History | Annotate | Line # | Download | only in mii
mii.c revision 1.38
      1 /*	$NetBSD: mii.c,v 1.38 2005/08/25 18:35:39 drochner 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.38 2005/08/25 18:35:39 drochner 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 #include "locators.h"
     60 
     61 static int	mii_print(void *, const char *);
     62 static int	mii_submatch(struct device *, struct cfdata *,
     63 			     const locdesc_t *, void *);
     64 
     65 /*
     66  * Helper function used by network interface drivers, attaches PHYs
     67  * to the network interface driver parent.
     68  */
     69 void
     70 mii_attach(struct device *parent, struct mii_data *mii, int capmask,
     71     int phyloc, int offloc, int flags)
     72 {
     73 	struct mii_attach_args ma;
     74 	struct mii_softc *child;
     75 	int bmsr, offset = 0;
     76 	int phymin, phymax;
     77 	int locs[MIICF_NLOCS];
     78 
     79 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
     80 		panic("mii_attach: phyloc and offloc specified");
     81 
     82 	if (phyloc == MII_PHY_ANY) {
     83 		phymin = 0;
     84 		phymax = MII_NPHY - 1;
     85 	} else
     86 		phymin = phymax = phyloc;
     87 
     88 	if ((mii->mii_flags & MIIF_INITDONE) == 0) {
     89 		LIST_INIT(&mii->mii_phys);
     90 		mii->mii_flags |= MIIF_INITDONE;
     91 	}
     92 
     93 	for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) {
     94 		/*
     95 		 * Make sure we haven't already configured a PHY at this
     96 		 * address.  This allows mii_attach() to be called
     97 		 * multiple times.
     98 		 */
     99 		for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    100 		     child = LIST_NEXT(child, mii_list)) {
    101 			if (child->mii_phy == ma.mii_phyno) {
    102 				/*
    103 				 * Yes, there is already something
    104 				 * configured at this address.
    105 				 */
    106 				offset++;
    107 				continue;
    108 			}
    109 		}
    110 
    111 		/*
    112 		 * Check to see if there is a PHY at this address.  Note,
    113 		 * many braindead PHYs report 0/0 in their ID registers,
    114 		 * so we test for media in the BMSR.
    115 		 */
    116 		bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR);
    117 		if (bmsr == 0 || bmsr == 0xffff ||
    118 		    (bmsr & (BMSR_EXTSTAT|BMSR_MEDIAMASK)) == 0) {
    119 			/* Assume no PHY at this address. */
    120 			continue;
    121 		}
    122 
    123 		/*
    124 		 * There is a PHY at this address.  If we were given an
    125 		 * `offset' locator, skip this PHY if it doesn't match.
    126 		 */
    127 		if (offloc != MII_OFFSET_ANY && offloc != offset) {
    128 			offset++;
    129 			continue;
    130 		}
    131 
    132 		/*
    133 		 * Extract the IDs.  Braindead PHYs will be handled by
    134 		 * the `ukphy' driver, as we have no ID information to
    135 		 * match on.
    136 		 */
    137 		ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno,
    138 		    MII_PHYIDR1);
    139 		ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno,
    140 		    MII_PHYIDR2);
    141 
    142 		ma.mii_data = mii;
    143 		ma.mii_capmask = capmask;
    144 		ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK);
    145 
    146 		locs[MIICF_PHY] = ma.mii_phyno;
    147 
    148 		child = (struct mii_softc *)config_found_sm_loc(parent, "mii",
    149 			locs, &ma, mii_print, mii_submatch);
    150 		if (child) {
    151 			/*
    152 			 * Link it up in the parent's MII data.
    153 			 */
    154 			callout_init(&child->mii_nway_ch);
    155 			LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list);
    156 			child->mii_offset = offset;
    157 			mii->mii_instance++;
    158 		}
    159 		offset++;
    160 	}
    161 }
    162 
    163 void
    164 mii_activate(struct mii_data *mii, enum devact act, int phyloc, int offloc)
    165 {
    166 	struct mii_softc *child;
    167 
    168 	if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY)
    169 		panic("mii_activate: phyloc and offloc specified");
    170 
    171 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
    172 		return;
    173 
    174 	for (child = LIST_FIRST(&mii->mii_phys);
    175 	     child != NULL; child = LIST_NEXT(child, mii_list)) {
    176 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
    177 			if (phyloc != MII_PHY_ANY &&
    178 			    phyloc != child->mii_phy)
    179 				continue;
    180 			if (offloc != MII_OFFSET_ANY &&
    181 			    offloc != child->mii_offset)
    182 				continue;
    183 		}
    184 		switch (act) {
    185 		case DVACT_ACTIVATE:
    186 			panic("mii_activate: DVACT_ACTIVATE");
    187 			break;
    188 
    189 		case DVACT_DEACTIVATE:
    190 			if (config_deactivate(&child->mii_dev) != 0)
    191 				panic("%s: config_activate(%d) failed",
    192 				    child->mii_dev.dv_xname, act);
    193 		}
    194 	}
    195 }
    196 
    197 void
    198 mii_detach(struct mii_data *mii, int phyloc, int offloc)
    199 {
    200 	struct mii_softc *child, *nchild;
    201 
    202 	if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY)
    203 		panic("mii_detach: phyloc and offloc specified");
    204 
    205 	if ((mii->mii_flags & MIIF_INITDONE) == 0)
    206 		return;
    207 
    208 	for (child = LIST_FIRST(&mii->mii_phys);
    209 	     child != NULL; child = nchild) {
    210 		nchild = LIST_NEXT(child, mii_list);
    211 		if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) {
    212 			if (phyloc != MII_PHY_ANY &&
    213 			    phyloc != child->mii_phy)
    214 				continue;
    215 			if (offloc != MII_OFFSET_ANY &&
    216 			    offloc != child->mii_offset)
    217 				continue;
    218 		}
    219 		LIST_REMOVE(child, mii_list);
    220 		(void) config_detach(&child->mii_dev, DETACH_FORCE);
    221 	}
    222 }
    223 
    224 static int
    225 mii_print(void *aux, const char *pnp)
    226 {
    227 	struct mii_attach_args *ma = aux;
    228 
    229 	if (pnp != NULL)
    230 		aprint_normal("OUI 0x%06x model 0x%04x rev %d at %s",
    231 		    MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2),
    232 		    MII_REV(ma->mii_id2), pnp);
    233 
    234 	aprint_normal(" phy %d", ma->mii_phyno);
    235 	return (UNCONF);
    236 }
    237 
    238 static int
    239 mii_submatch(struct device *parent, struct cfdata *cf,
    240 	     const locdesc_t *locs, void *aux)
    241 {
    242 
    243 	if (cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT &&
    244 	    cf->cf_loc[MIICF_PHY] != locs[MIICF_PHY])
    245 		return (0);
    246 
    247 	return (config_match(parent, cf, aux));
    248 }
    249 
    250 /*
    251  * Media changed; notify all PHYs.
    252  */
    253 int
    254 mii_mediachg(struct mii_data *mii)
    255 {
    256 	struct mii_softc *child;
    257 	int rv;
    258 
    259 	mii->mii_media_status = 0;
    260 	mii->mii_media_active = IFM_NONE;
    261 
    262 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    263 	     child = LIST_NEXT(child, mii_list)) {
    264 		rv = PHY_SERVICE(child, mii, MII_MEDIACHG);
    265 		if (rv)
    266 			return (rv);
    267 	}
    268 	return (0);
    269 }
    270 
    271 /*
    272  * Call the PHY tick routines, used during autonegotiation.
    273  */
    274 void
    275 mii_tick(struct mii_data *mii)
    276 {
    277 	struct mii_softc *child;
    278 
    279 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    280 	     child = LIST_NEXT(child, mii_list))
    281 		(void) PHY_SERVICE(child, mii, MII_TICK);
    282 }
    283 
    284 /*
    285  * Get media status from PHYs.
    286  */
    287 void
    288 mii_pollstat(struct mii_data *mii)
    289 {
    290 	struct mii_softc *child;
    291 
    292 	mii->mii_media_status = 0;
    293 	mii->mii_media_active = IFM_NONE;
    294 
    295 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    296 	     child = LIST_NEXT(child, mii_list))
    297 		(void) PHY_SERVICE(child, mii, MII_POLLSTAT);
    298 }
    299 
    300 /*
    301  * Inform the PHYs that the interface is down.
    302  */
    303 void
    304 mii_down(struct mii_data *mii)
    305 {
    306 	struct mii_softc *child;
    307 
    308 	for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
    309 	     child = LIST_NEXT(child, mii_list))
    310 		(void) PHY_SERVICE(child, mii, MII_DOWN);
    311 }
    312 
    313 static unsigned char
    314 bitreverse(unsigned char x)
    315 {
    316 	static unsigned char nibbletab[16] = {
    317 		0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15
    318 	};
    319 
    320 	return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]);
    321 }
    322 
    323 u_int
    324 mii_oui(u_int id1, u_int id2)
    325 {
    326 	u_int h;
    327 
    328 	h = (id1 << 6) | (id2 >> 10);
    329 
    330 	return ((bitreverse(h >> 16) << 16) |
    331 		(bitreverse((h >> 8) & 255) << 8) |
    332 		bitreverse(h & 255));
    333 }
    334