1 /* $OpenBSD: mii.c,v 1.21 2010/04/20 20:42:16 deraadt 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 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * MII bus layer, glues MII-capable network interface drivers to sharable 36 * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 37 * plus some NetBSD extensions. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/device.h> 42 #include <sys/systm.h> 43 #include <sys/socket.h> 44 45 #include <net/if.h> 46 #include <net/if_media.h> 47 48 #include <dev/mii/mii.h> 49 #include <dev/mii/miivar.h> 50 51 int mii_print(void *, const char *); 52 int mii_submatch(struct device *, void *, void *); 53 54 #define MIICF_PHY 0 /* cf_loc index */ 55 #define MIICF_PHY_DEFAULT (-1) /* default phy device */ 56 57 /* 58 * Helper function used by network interface drivers, attaches PHYs 59 * to the network interface driver parent. 60 */ 61 void 62 mii_attach(struct device *parent, struct mii_data *mii, int capmask, 63 int phyloc, int offloc, int flags) 64 { 65 struct mii_attach_args ma; 66 struct mii_softc *child; 67 int bmsr, offset = 0; 68 int phymin, phymax; 69 70 if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) 71 panic("mii_attach: phyloc and offloc specified"); 72 73 if (phyloc == MII_PHY_ANY) { 74 phymin = 0; 75 phymax = MII_NPHY - 1; 76 } else 77 phymin = phymax = phyloc; 78 79 if ((mii->mii_flags & MIIF_INITDONE) == 0) { 80 LIST_INIT(&mii->mii_phys); 81 mii->mii_flags |= MIIF_INITDONE; 82 } 83 84 for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 85 /* 86 * Make sure we haven't already configured a PHY at this 87 * address. This allows mii_attach() to be called 88 * multiple times. 89 */ 90 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 91 child = LIST_NEXT(child, mii_list)) { 92 if (child->mii_phy == ma.mii_phyno) { 93 /* 94 * Yes, there is already something 95 * configured at this address. 96 */ 97 offset++; 98 continue; 99 } 100 } 101 102 /* 103 * Check to see if there is a PHY at this address. Note, 104 * many braindead PHYs report 0/0 in their ID registers, 105 * so we test for media in the BMSR. 106 */ 107 bmsr = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR); 108 if (bmsr == 0 || bmsr == 0xffff || 109 (bmsr & (BMSR_MEDIAMASK|BMSR_EXTSTAT)) == 0) { 110 /* Assume no PHY at this address. */ 111 continue; 112 } 113 114 /* 115 * There is a PHY at this address. If we were given an 116 * `offset' locator, skip this PHY if it doesn't match. 117 */ 118 if (offloc != MII_OFFSET_ANY && offloc != offset) { 119 offset++; 120 continue; 121 } 122 123 /* 124 * Extract the IDs. Braindead PHYs will be handled by 125 * the `ukphy' driver, as we have no ID information to 126 * match on. 127 */ 128 ma.mii_id1 = (*mii->mii_readreg)(parent, ma.mii_phyno, 129 MII_PHYIDR1); 130 ma.mii_id2 = (*mii->mii_readreg)(parent, ma.mii_phyno, 131 MII_PHYIDR2); 132 133 ma.mii_data = mii; 134 ma.mii_capmask = capmask; 135 ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK); 136 137 if ((child = (struct mii_softc *)config_found_sm(parent, &ma, 138 mii_print, mii_submatch)) != NULL) { 139 /* 140 * Link it up in the parent's MII data. 141 */ 142 LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list); 143 child->mii_offset = offset; 144 mii->mii_instance++; 145 } 146 offset++; 147 } 148 } 149 150 void 151 mii_detach(struct mii_data *mii, int phyloc, int offloc) 152 { 153 struct mii_softc *child, *nchild; 154 155 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 156 panic("mii_detach: phyloc and offloc specified"); 157 158 if ((mii->mii_flags & MIIF_INITDONE) == 0) 159 return; 160 161 for (child = LIST_FIRST(&mii->mii_phys); 162 child != NULL; child = nchild) { 163 nchild = LIST_NEXT(child, mii_list); 164 if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 165 if (phyloc != MII_PHY_ANY && 166 phyloc != child->mii_phy) 167 continue; 168 if (offloc != MII_OFFSET_ANY && 169 offloc != child->mii_offset) 170 continue; 171 } 172 LIST_REMOVE(child, mii_list); 173 (void) config_detach(&child->mii_dev, DETACH_FORCE); 174 } 175 } 176 177 int 178 mii_print(void *aux, const char *pnp) 179 { 180 struct mii_attach_args *ma = aux; 181 182 if (pnp != NULL) 183 printf("OUI 0x%06x model 0x%04x rev %d at %s", 184 MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), 185 MII_REV(ma->mii_id2), pnp); 186 187 printf(" phy %d", ma->mii_phyno); 188 return (UNCONF); 189 } 190 191 int 192 mii_submatch(struct device *parent, void *match, void *aux) 193 { 194 struct cfdata *cf = match; 195 struct mii_attach_args *ma = aux; 196 197 if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] && 198 cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT) 199 return (0); 200 201 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 202 } 203 204 /* 205 * Media changed; notify all PHYs. 206 */ 207 int 208 mii_mediachg(struct mii_data *mii) 209 { 210 struct mii_softc *child; 211 int rv; 212 213 mii->mii_media_status = 0; 214 mii->mii_media_active = IFM_NONE; 215 216 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 217 child = LIST_NEXT(child, mii_list)) { 218 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 219 if (rv) 220 return (rv); 221 } 222 return (0); 223 } 224 225 /* 226 * Call the PHY tick routines, used during autonegotiation. 227 */ 228 void 229 mii_tick(struct mii_data *mii) 230 { 231 struct mii_softc *child; 232 233 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 234 child = LIST_NEXT(child, mii_list)) 235 (void) PHY_SERVICE(child, mii, MII_TICK); 236 } 237 238 /* 239 * Get media status from PHYs. 240 */ 241 void 242 mii_pollstat(struct mii_data *mii) 243 { 244 struct mii_softc *child; 245 246 mii->mii_media_status = 0; 247 mii->mii_media_active = IFM_NONE; 248 249 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 250 child = LIST_NEXT(child, mii_list)) 251 (void) PHY_SERVICE(child, mii, MII_POLLSTAT); 252 } 253 254 /* 255 * Inform the PHYs that the interface is down. 256 */ 257 void 258 mii_down(struct mii_data *mii) 259 { 260 struct mii_softc *child; 261 262 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 263 child = LIST_NEXT(child, mii_list)) 264 (void) PHY_SERVICE(child, mii, MII_DOWN); 265 } 266