1 /* $OpenBSD: mii.c,v 1.9 2000/08/26 20:04:17 nate Exp $ */ 2 /* $NetBSD: mii.c,v 1.19 2000/02/02 17:09:44 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 10 * NASA Ames Research Center. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the NetBSD 23 * Foundation, Inc. and its contributors. 24 * 4. Neither the name of The NetBSD Foundation nor the names of its 25 * contributors may be used to endorse or promote products derived 26 * from this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 * POSSIBILITY OF SUCH DAMAGE. 39 */ 40 41 /* 42 * MII bus layer, glues MII-capable network interface drivers to sharable 43 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 44 * plus some NetBSD extensions. 45 */ 46 47 #include <sys/param.h> 48 #include <sys/device.h> 49 #include <sys/systm.h> 50 #include <sys/socket.h> 51 52 #include <net/if.h> 53 #include <net/if_media.h> 54 55 #include <dev/mii/mii.h> 56 #include <dev/mii/miivar.h> 57 58 int mii_print __P((void *, const char *)); 59 int mii_submatch __P((struct device *, void *, void *)); 60 61 #define MIICF_PHY 0 /* cf_loc index */ 62 #define MIICF_PHY_DEFAULT (-1) /* default phy device */ 63 64 /* 65 * Helper function used by network interface drivers, attaches PHYs 66 * to the network interface driver parent. 67 */ 68 void 69 mii_attach(parent, mii, capmask, phyloc, offloc, flags) 70 struct device *parent; 71 struct mii_data *mii; 72 int capmask, phyloc, offloc, flags; 73 { 74 struct mii_attach_args ma; 75 struct mii_softc *child; 76 int bmsr, offset = 0; 77 int phymin, phymax; 78 79 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_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_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; 145 146 if ((child = (struct mii_softc *)config_found_sm(parent, &ma, 147 mii_print, mii_submatch)) != NULL) { 148 /* 149 * Link it up in the parent's MII data. 150 */ 151 #if defined(__NetBSD__) 152 callout_init(&child->mii_nway_ch); 153 #endif 154 LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list); 155 child->mii_offset = offset; 156 mii->mii_instance++; 157 } 158 offset++; 159 } 160 } 161 162 void 163 mii_activate(mii, act, phyloc, offloc) 164 struct mii_data *mii; 165 enum devact act; 166 int phyloc, offloc; 167 { 168 struct mii_softc *child; 169 170 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_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\n", 194 child->mii_dev.dv_xname, act); 195 } 196 } 197 } 198 199 void 200 mii_detach(mii, phyloc, offloc) 201 struct mii_data *mii; 202 int phyloc, offloc; 203 { 204 struct mii_softc *child, *nchild; 205 206 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 207 panic("mii_detach: phyloc and offloc specified"); 208 209 if ((mii->mii_flags & MIIF_INITDONE) == 0) 210 return; 211 212 for (child = LIST_FIRST(&mii->mii_phys); 213 child != NULL; child = nchild) { 214 nchild = LIST_NEXT(child, mii_list); 215 if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 216 if (phyloc != MII_PHY_ANY && 217 phyloc != child->mii_phy) 218 continue; 219 if (offloc != MII_OFFSET_ANY && 220 offloc != child->mii_offset) 221 continue; 222 } 223 LIST_REMOVE(child, mii_list); 224 (void) config_detach(&child->mii_dev, DETACH_FORCE); 225 } 226 } 227 228 int 229 mii_print(aux, pnp) 230 void *aux; 231 const char *pnp; 232 { 233 struct mii_attach_args *ma = aux; 234 235 if (pnp != NULL) 236 printf("OUI 0x%06x model 0x%04x rev %d at %s", 237 MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), 238 MII_REV(ma->mii_id2), pnp); 239 240 printf(" phy %d", ma->mii_phyno); 241 return (UNCONF); 242 } 243 244 int 245 mii_submatch(parent, match, aux) 246 struct device *parent; 247 void *match, *aux; 248 { 249 struct cfdata *cf = match; 250 struct mii_attach_args *ma = aux; 251 252 if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] && 253 cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT) 254 return (0); 255 256 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 257 } 258 259 /* 260 * Media changed; notify all PHYs. 261 */ 262 int 263 mii_mediachg(mii) 264 struct mii_data *mii; 265 { 266 struct mii_softc *child; 267 int rv; 268 269 mii->mii_media_status = 0; 270 mii->mii_media_active = IFM_NONE; 271 272 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 273 child = LIST_NEXT(child, mii_list)) { 274 rv = (*child->mii_service)(child, mii, MII_MEDIACHG); 275 if (rv) 276 return (rv); 277 } 278 return (0); 279 } 280 281 /* 282 * Call the PHY tick routines, used during autonegotiation. 283 */ 284 void 285 mii_tick(mii) 286 struct mii_data *mii; 287 { 288 struct mii_softc *child; 289 290 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 291 child = LIST_NEXT(child, mii_list)) 292 (void) (*child->mii_service)(child, mii, MII_TICK); 293 } 294 295 /* 296 * Get media status from PHYs. 297 */ 298 void 299 mii_pollstat(mii) 300 struct mii_data *mii; 301 { 302 struct mii_softc *child; 303 304 mii->mii_media_status = 0; 305 mii->mii_media_active = IFM_NONE; 306 307 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 308 child = LIST_NEXT(child, mii_list)) 309 (void) (*child->mii_service)(child, mii, MII_POLLSTAT); 310 } 311 312 /* 313 * Inform the PHYs that the interface is down. 314 */ 315 void 316 mii_down(mii) 317 struct mii_data *mii; 318 { 319 struct mii_softc *child; 320 321 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 322 child = LIST_NEXT(child, mii_list)) 323 (void) (*child->mii_service)(child, mii, MII_DOWN); 324 } 325