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