1 /* $OpenBSD: mii.c,v 1.23 2015/12/29 18:35:39 mmcc 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 goto loop_end; 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 goto loop_end; 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 goto loop_end; 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 loop_end: ; 149 } 150 } 151 152 void 153 mii_detach(struct mii_data *mii, int phyloc, int offloc) 154 { 155 struct mii_softc *child, *nchild; 156 157 if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 158 panic("mii_detach: phyloc and offloc specified"); 159 160 if ((mii->mii_flags & MIIF_INITDONE) == 0) 161 return; 162 163 for (child = LIST_FIRST(&mii->mii_phys); 164 child != NULL; child = nchild) { 165 nchild = LIST_NEXT(child, mii_list); 166 if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 167 if (phyloc != MII_PHY_ANY && 168 phyloc != child->mii_phy) 169 continue; 170 if (offloc != MII_OFFSET_ANY && 171 offloc != child->mii_offset) 172 continue; 173 } 174 LIST_REMOVE(child, mii_list); 175 (void) config_detach(&child->mii_dev, DETACH_FORCE); 176 } 177 } 178 179 int 180 mii_print(void *aux, const char *pnp) 181 { 182 struct mii_attach_args *ma = aux; 183 184 if (pnp != NULL) 185 printf("OUI 0x%06x model 0x%04x rev %d at %s", 186 MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), 187 MII_REV(ma->mii_id2), pnp); 188 189 printf(" phy %d", ma->mii_phyno); 190 return (UNCONF); 191 } 192 193 int 194 mii_submatch(struct device *parent, void *match, void *aux) 195 { 196 struct cfdata *cf = match; 197 struct mii_attach_args *ma = aux; 198 199 if (ma->mii_phyno != cf->cf_loc[MIICF_PHY] && 200 cf->cf_loc[MIICF_PHY] != MIICF_PHY_DEFAULT) 201 return (0); 202 203 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 204 } 205 206 /* 207 * Media changed; notify all PHYs. 208 */ 209 int 210 mii_mediachg(struct mii_data *mii) 211 { 212 struct mii_softc *child; 213 int rv; 214 215 mii->mii_media_status = 0; 216 mii->mii_media_active = IFM_NONE; 217 218 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 219 child = LIST_NEXT(child, mii_list)) { 220 rv = PHY_SERVICE(child, mii, MII_MEDIACHG); 221 if (rv) 222 return (rv); 223 } 224 return (0); 225 } 226 227 /* 228 * Call the PHY tick routines, used during autonegotiation. 229 */ 230 void 231 mii_tick(struct mii_data *mii) 232 { 233 struct mii_softc *child; 234 235 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 236 child = LIST_NEXT(child, mii_list)) 237 (void) PHY_SERVICE(child, mii, MII_TICK); 238 } 239 240 /* 241 * Get media status from PHYs. 242 */ 243 void 244 mii_pollstat(struct mii_data *mii) 245 { 246 struct mii_softc *child; 247 248 mii->mii_media_status = 0; 249 mii->mii_media_active = IFM_NONE; 250 251 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 252 child = LIST_NEXT(child, mii_list)) 253 (void) PHY_SERVICE(child, mii, MII_POLLSTAT); 254 } 255 256 /* 257 * Inform the PHYs that the interface is down. 258 */ 259 void 260 mii_down(struct mii_data *mii) 261 { 262 struct mii_softc *child; 263 264 for (child = LIST_FIRST(&mii->mii_phys); child != NULL; 265 child = LIST_NEXT(child, mii_list)) 266 (void) PHY_SERVICE(child, mii, MII_DOWN); 267 } 268