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