1 /* $NetBSD: mii.c,v 1.20 2000/03/23 07:01:36 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. This exports an interface compatible with BSD/OS 3.0's, 43 * plus some NetBSD extensions. 44 */ 45 46 #include <sys/param.h> 47 #include <sys/device.h> 48 #include <sys/systm.h> 49 #include <sys/socket.h> 50 51 #include <net/if.h> 52 #include <net/if_media.h> 53 54 #include <dev/mii/mii.h> 55 #include <dev/mii/miivar.h> 56 57 int mii_print __P((void *, const char *)); 58 int mii_submatch __P((struct device *, struct cfdata *, void *)); 59 60 /* 61 * Helper function used by network interface drivers, attaches PHYs 62 * to the network interface driver parent. 63 */ 64 void 65 mii_attach(parent, mii, capmask, phyloc, offloc, flags) 66 struct device *parent; 67 struct mii_data *mii; 68 int capmask, phyloc, offloc, 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_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; 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(mii, act, phyloc, offloc) 158 struct mii_data *mii; 159 enum devact act; 160 int phyloc, offloc; 161 { 162 struct mii_softc *child; 163 164 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 165 panic("mii_activate: phyloc and offloc specified"); 166 167 if ((mii->mii_flags & MIIF_INITDONE) == 0) 168 return; 169 170 for (child = LIST_FIRST(&mii->mii_phys); 171 child != NULL; child = LIST_NEXT(child, mii_list)) { 172 if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 173 if (phyloc != MII_PHY_ANY && 174 phyloc != child->mii_phy) 175 continue; 176 if (offloc != MII_OFFSET_ANY && 177 offloc != child->mii_offset) 178 continue; 179 } 180 switch (act) { 181 case DVACT_ACTIVATE: 182 panic("mii_activate: DVACT_ACTIVATE"); 183 break; 184 185 case DVACT_DEACTIVATE: 186 if (config_deactivate(&child->mii_dev) != 0) 187 panic("%s: config_activate(%d) failed\n", 188 child->mii_dev.dv_xname, act); 189 } 190 } 191 } 192 193 void 194 mii_detach(mii, phyloc, offloc) 195 struct mii_data *mii; 196 int phyloc, offloc; 197 { 198 struct mii_softc *child, *nchild; 199 200 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 201 panic("mii_detach: phyloc and offloc specified"); 202 203 if ((mii->mii_flags & MIIF_INITDONE) == 0) 204 return; 205 206 for (child = LIST_FIRST(&mii->mii_phys); 207 child != NULL; child = nchild) { 208 nchild = LIST_NEXT(child, mii_list); 209 if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 210 if (phyloc != MII_PHY_ANY && 211 phyloc != child->mii_phy) 212 continue; 213 if (offloc != MII_OFFSET_ANY && 214 offloc != child->mii_offset) 215 continue; 216 } 217 LIST_REMOVE(child, mii_list); 218 (void) config_detach(&child->mii_dev, DETACH_FORCE); 219 } 220 } 221 222 int 223 mii_print(aux, pnp) 224 void *aux; 225 const char *pnp; 226 { 227 struct mii_attach_args *ma = aux; 228 229 if (pnp != NULL) 230 printf("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 printf(" phy %d", ma->mii_phyno); 235 return (UNCONF); 236 } 237 238 int 239 mii_submatch(parent, cf, aux) 240 struct device *parent; 241 struct cfdata *cf; 242 void *aux; 243 { 244 struct mii_attach_args *ma = aux; 245 246 if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] && 247 cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT) 248 return (0); 249 250 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 251 } 252 253 /* 254 * Media changed; notify all PHYs. 255 */ 256 int 257 mii_mediachg(mii) 258 struct mii_data *mii; 259 { 260 struct mii_softc *child; 261 int rv; 262 263 mii->mii_media_status = 0; 264 mii->mii_media_active = IFM_NONE; 265 266 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 267 child = LIST_NEXT(child, mii_list)) { 268 rv = (*child->mii_service)(child, mii, MII_MEDIACHG); 269 if (rv) 270 return (rv); 271 } 272 return (0); 273 } 274 275 /* 276 * Call the PHY tick routines, used during autonegotiation. 277 */ 278 void 279 mii_tick(mii) 280 struct mii_data *mii; 281 { 282 struct mii_softc *child; 283 284 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 285 child = LIST_NEXT(child, mii_list)) 286 (void) (*child->mii_service)(child, mii, MII_TICK); 287 } 288 289 /* 290 * Get media status from PHYs. 291 */ 292 void 293 mii_pollstat(mii) 294 struct mii_data *mii; 295 { 296 struct mii_softc *child; 297 298 mii->mii_media_status = 0; 299 mii->mii_media_active = IFM_NONE; 300 301 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 302 child = LIST_NEXT(child, mii_list)) 303 (void) (*child->mii_service)(child, mii, MII_POLLSTAT); 304 } 305 306 /* 307 * Inform the PHYs that the interface is down. 308 */ 309 void 310 mii_down(mii) 311 struct mii_data *mii; 312 { 313 struct mii_softc *child; 314 315 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 316 child = LIST_NEXT(child, mii_list)) 317 (void) (*child->mii_service)(child, mii, MII_DOWN); 318 } 319