1 1.59 andvar /* $NetBSD: mii.c,v 1.59 2024/02/08 20:11:55 andvar Exp $ */ 2 1.6 thorpej 3 1.6 thorpej /*- 4 1.55 thorpej * Copyright (c) 1998, 2000, 2020 The NetBSD Foundation, Inc. 5 1.6 thorpej * All rights reserved. 6 1.6 thorpej * 7 1.6 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.6 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.6 thorpej * NASA Ames Research Center. 10 1.1 bouyer * 11 1.1 bouyer * Redistribution and use in source and binary forms, with or without 12 1.1 bouyer * modification, are permitted provided that the following conditions 13 1.1 bouyer * are met: 14 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 15 1.1 bouyer * notice, this list of conditions and the following disclaimer. 16 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 bouyer * notice, this list of conditions and the following disclaimer in the 18 1.1 bouyer * documentation and/or other materials provided with the distribution. 19 1.1 bouyer * 20 1.6 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.6 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.6 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.6 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.6 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.6 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.6 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.6 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.6 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.6 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.6 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.6 thorpej */ 32 1.6 thorpej 33 1.6 thorpej /* 34 1.59 andvar * MII bus layer, glues MII-capable network interface drivers to shareable 35 1.22 thorpej * PHY drivers. 36 1.1 bouyer */ 37 1.28 lukem 38 1.28 lukem #include <sys/cdefs.h> 39 1.59 andvar __KERNEL_RCSID(0, "$NetBSD: mii.c,v 1.59 2024/02/08 20:11:55 andvar Exp $"); 40 1.55 thorpej 41 1.55 thorpej #define __IFMEDIA_PRIVATE 42 1.1 bouyer 43 1.1 bouyer #include <sys/param.h> 44 1.6 thorpej #include <sys/device.h> 45 1.1 bouyer #include <sys/systm.h> 46 1.1 bouyer #include <sys/socket.h> 47 1.1 bouyer 48 1.1 bouyer #include <net/if.h> 49 1.1 bouyer #include <net/if_media.h> 50 1.1 bouyer 51 1.6 thorpej #include <dev/mii/mii.h> 52 1.6 thorpej #include <dev/mii/miivar.h> 53 1.1 bouyer 54 1.36 drochner #include "locators.h" 55 1.36 drochner 56 1.35 thorpej static int mii_print(void *, const char *); 57 1.1 bouyer 58 1.6 thorpej /* 59 1.6 thorpej * Helper function used by network interface drivers, attaches PHYs 60 1.6 thorpej * to the network interface driver parent. 61 1.6 thorpej */ 62 1.6 thorpej void 63 1.47 xtraeme mii_attach(device_t parent, struct mii_data *mii, int capmask, 64 1.27 thorpej int phyloc, int offloc, int flags) 65 1.6 thorpej { 66 1.6 thorpej struct mii_attach_args ma; 67 1.6 thorpej struct mii_softc *child; 68 1.52 msaitoh int offset = 0; 69 1.52 msaitoh uint16_t bmsr; 70 1.15 thorpej int phymin, phymax; 71 1.38 drochner int locs[MIICF_NLOCS]; 72 1.52 msaitoh int rv; 73 1.1 bouyer 74 1.55 thorpej KASSERT(mii->mii_media.ifm_lock != NULL); 75 1.55 thorpej 76 1.34 yamt if (phyloc != MII_PHY_ANY && offloc != MII_OFFSET_ANY) 77 1.18 thorpej panic("mii_attach: phyloc and offloc specified"); 78 1.1 bouyer 79 1.15 thorpej if (phyloc == MII_PHY_ANY) { 80 1.15 thorpej phymin = 0; 81 1.15 thorpej phymax = MII_NPHY - 1; 82 1.15 thorpej } else 83 1.15 thorpej phymin = phymax = phyloc; 84 1.15 thorpej 85 1.55 thorpej mii_lock(mii); 86 1.55 thorpej 87 1.15 thorpej if ((mii->mii_flags & MIIF_INITDONE) == 0) { 88 1.15 thorpej LIST_INIT(&mii->mii_phys); 89 1.55 thorpej cv_init(&mii->mii_probe_cv, "mii_attach"); 90 1.15 thorpej mii->mii_flags |= MIIF_INITDONE; 91 1.15 thorpej } 92 1.15 thorpej 93 1.55 thorpej /* 94 1.55 thorpej * Probing temporarily unlocks the MII; wait until anyone 95 1.55 thorpej * else who might be doing this to finish. 96 1.55 thorpej */ 97 1.55 thorpej mii->mii_probe_waiters++; 98 1.55 thorpej for (;;) { 99 1.55 thorpej if (mii->mii_flags & MIIF_EXITING) { 100 1.55 thorpej mii->mii_probe_waiters--; 101 1.55 thorpej cv_signal(&mii->mii_probe_cv); 102 1.55 thorpej mii_unlock(mii); 103 1.55 thorpej return; 104 1.55 thorpej } 105 1.55 thorpej if ((mii->mii_flags & MIIF_PROBING) == 0) 106 1.55 thorpej break; 107 1.55 thorpej cv_wait(&mii->mii_probe_cv, mii->mii_media.ifm_lock); 108 1.55 thorpej } 109 1.55 thorpej mii->mii_probe_waiters--; 110 1.55 thorpej 111 1.55 thorpej /* ...and old others off. */ 112 1.55 thorpej mii->mii_flags |= MIIF_PROBING; 113 1.55 thorpej 114 1.15 thorpej for (ma.mii_phyno = phymin; ma.mii_phyno <= phymax; ma.mii_phyno++) { 115 1.13 thorpej /* 116 1.13 thorpej * Make sure we haven't already configured a PHY at this 117 1.18 thorpej * address. This allows mii_attach() to be called 118 1.13 thorpej * multiple times. 119 1.13 thorpej */ 120 1.42 dyoung LIST_FOREACH(child, &mii->mii_phys, mii_list) { 121 1.13 thorpej if (child->mii_phy == ma.mii_phyno) { 122 1.13 thorpej /* 123 1.13 thorpej * Yes, there is already something 124 1.13 thorpej * configured at this address. 125 1.13 thorpej */ 126 1.14 thorpej offset++; 127 1.13 thorpej continue; 128 1.13 thorpej } 129 1.13 thorpej } 130 1.13 thorpej 131 1.6 thorpej /* 132 1.11 thorpej * Check to see if there is a PHY at this address. Note, 133 1.11 thorpej * many braindead PHYs report 0/0 in their ID registers, 134 1.11 thorpej * so we test for media in the BMSR. 135 1.11 thorpej */ 136 1.52 msaitoh bmsr = 0; 137 1.52 msaitoh rv = (*mii->mii_readreg)(parent, ma.mii_phyno, MII_BMSR, 138 1.52 msaitoh &bmsr); 139 1.52 msaitoh if ((rv != 0) || bmsr == 0 || bmsr == 0xffff || 140 1.53 msaitoh (bmsr & (BMSR_EXTSTAT | BMSR_MEDIAMASK)) == 0) { 141 1.11 thorpej /* Assume no PHY at this address. */ 142 1.15 thorpej continue; 143 1.15 thorpej } 144 1.15 thorpej 145 1.15 thorpej /* 146 1.15 thorpej * There is a PHY at this address. If we were given an 147 1.15 thorpej * `offset' locator, skip this PHY if it doesn't match. 148 1.15 thorpej */ 149 1.15 thorpej if (offloc != MII_OFFSET_ANY && offloc != offset) { 150 1.15 thorpej offset++; 151 1.11 thorpej continue; 152 1.11 thorpej } 153 1.11 thorpej 154 1.11 thorpej /* 155 1.11 thorpej * Extract the IDs. Braindead PHYs will be handled by 156 1.11 thorpej * the `ukphy' driver, as we have no ID information to 157 1.11 thorpej * match on. 158 1.6 thorpej */ 159 1.52 msaitoh ma.mii_id1 = ma.mii_id2 = 0; 160 1.52 msaitoh rv = (*mii->mii_readreg)(parent, ma.mii_phyno, 161 1.52 msaitoh MII_PHYIDR1, &ma.mii_id1); 162 1.52 msaitoh rv |= (*mii->mii_readreg)(parent, ma.mii_phyno, 163 1.52 msaitoh MII_PHYIDR2, &ma.mii_id2); 164 1.52 msaitoh if (rv != 0) 165 1.52 msaitoh continue; 166 1.1 bouyer 167 1.6 thorpej ma.mii_data = mii; 168 1.6 thorpej ma.mii_capmask = capmask; 169 1.29 thorpej ma.mii_flags = flags | (mii->mii_flags & MIIF_INHERIT_MASK); 170 1.1 bouyer 171 1.38 drochner locs[MIICF_PHY] = ma.mii_phyno; 172 1.37 drochner 173 1.55 thorpej mii_unlock(mii); 174 1.55 thorpej 175 1.56 thorpej child = device_private( 176 1.56 thorpej config_found(parent, &ma, mii_print, 177 1.57 thorpej CFARGS(.submatch = config_stdsubmatch, 178 1.57 thorpej .iattr = "mii", 179 1.57 thorpej .locators = locs))); 180 1.37 drochner if (child) { 181 1.53 msaitoh /* Link it up in the parent's MII data. */ 182 1.58 riastrad if (child->mii_flags & MIIF_AUTOTSLEEP) 183 1.58 riastrad cv_init(&child->mii_nway_cv, "miiauto"); 184 1.58 riastrad else 185 1.58 riastrad callout_init(&child->mii_nway_ch, 0); 186 1.55 thorpej mii_lock(mii); 187 1.6 thorpej LIST_INSERT_HEAD(&mii->mii_phys, child, mii_list); 188 1.13 thorpej child->mii_offset = offset; 189 1.6 thorpej mii->mii_instance++; 190 1.55 thorpej } else { 191 1.55 thorpej mii_lock(mii); 192 1.6 thorpej } 193 1.13 thorpej offset++; 194 1.17 thorpej } 195 1.55 thorpej 196 1.55 thorpej /* All done! */ 197 1.55 thorpej mii->mii_flags &= ~MIIF_PROBING; 198 1.55 thorpej cv_signal(&mii->mii_probe_cv); 199 1.55 thorpej mii_unlock(mii); 200 1.17 thorpej } 201 1.17 thorpej 202 1.17 thorpej void 203 1.27 thorpej mii_detach(struct mii_data *mii, int phyloc, int offloc) 204 1.17 thorpej { 205 1.17 thorpej struct mii_softc *child, *nchild; 206 1.55 thorpej bool exiting = false; 207 1.17 thorpej 208 1.17 thorpej if (phyloc != MII_PHY_ANY && offloc != MII_PHY_ANY) 209 1.18 thorpej panic("mii_detach: phyloc and offloc specified"); 210 1.17 thorpej 211 1.55 thorpej mii_lock(mii); 212 1.55 thorpej 213 1.55 thorpej if ((mii->mii_flags & MIIF_INITDONE) == 0 || 214 1.55 thorpej (mii->mii_flags & MIIF_EXITING) != 0) { 215 1.55 thorpej mii_unlock(mii); 216 1.17 thorpej return; 217 1.55 thorpej } 218 1.55 thorpej 219 1.55 thorpej /* 220 1.55 thorpej * XXX This is probably not the best hueristic. Consider 221 1.55 thorpej * XXX adding an argument to mii_detach(). 222 1.55 thorpej */ 223 1.55 thorpej if (phyloc == MII_PHY_ANY && MII_PHY_ANY == MII_OFFSET_ANY) 224 1.55 thorpej exiting = true; 225 1.55 thorpej 226 1.55 thorpej if (exiting) { 227 1.55 thorpej mii->mii_flags |= MIIF_EXITING; 228 1.55 thorpej cv_broadcast(&mii->mii_probe_cv); 229 1.55 thorpej } 230 1.17 thorpej 231 1.55 thorpej /* Wait for everyone else to get out. */ 232 1.55 thorpej mii->mii_probe_waiters++; 233 1.55 thorpej for (;;) { 234 1.55 thorpej /* 235 1.55 thorpej * If we've been waiting to do a less-than-exit, and 236 1.55 thorpej * *someone else* initiated an exit, then get out. 237 1.55 thorpej */ 238 1.55 thorpej if (!exiting && (mii->mii_flags & MIIF_EXITING) != 0) { 239 1.55 thorpej mii->mii_probe_waiters--; 240 1.55 thorpej cv_signal(&mii->mii_probe_cv); 241 1.55 thorpej mii_unlock(mii); 242 1.55 thorpej return; 243 1.55 thorpej } 244 1.55 thorpej if ((mii->mii_flags & MIIF_PROBING) == 0 && 245 1.55 thorpej (!exiting || (exiting && mii->mii_probe_waiters == 1))) 246 1.55 thorpej break; 247 1.55 thorpej cv_wait(&mii->mii_probe_cv, mii->mii_media.ifm_lock); 248 1.55 thorpej } 249 1.55 thorpej mii->mii_probe_waiters--; 250 1.55 thorpej 251 1.55 thorpej if (!exiting) 252 1.55 thorpej mii->mii_flags |= MIIF_PROBING; 253 1.55 thorpej 254 1.55 thorpej LIST_FOREACH_SAFE(child, &mii->mii_phys, mii_list, nchild) { 255 1.17 thorpej if (phyloc != MII_PHY_ANY || offloc != MII_OFFSET_ANY) { 256 1.17 thorpej if (phyloc != MII_PHY_ANY && 257 1.17 thorpej phyloc != child->mii_phy) 258 1.17 thorpej continue; 259 1.17 thorpej if (offloc != MII_OFFSET_ANY && 260 1.17 thorpej offloc != child->mii_offset) 261 1.17 thorpej continue; 262 1.17 thorpej } 263 1.55 thorpej LIST_REMOVE(child, mii_list); 264 1.55 thorpej mii_unlock(mii); 265 1.47 xtraeme (void)config_detach(child->mii_dev, DETACH_FORCE); 266 1.55 thorpej mii_lock(mii); 267 1.55 thorpej } 268 1.55 thorpej 269 1.55 thorpej if (exiting) { 270 1.55 thorpej cv_destroy(&mii->mii_probe_cv); 271 1.55 thorpej } else { 272 1.55 thorpej mii->mii_flags &= ~MIIF_PROBING; 273 1.55 thorpej cv_signal(&mii->mii_probe_cv); 274 1.6 thorpej } 275 1.55 thorpej 276 1.55 thorpej mii_unlock(mii); 277 1.4 bouyer } 278 1.1 bouyer 279 1.35 thorpej static int 280 1.27 thorpej mii_print(void *aux, const char *pnp) 281 1.1 bouyer { 282 1.6 thorpej struct mii_attach_args *ma = aux; 283 1.2 thorpej 284 1.6 thorpej if (pnp != NULL) 285 1.33 thorpej aprint_normal("OUI 0x%06x model 0x%04x rev %d at %s", 286 1.6 thorpej MII_OUI(ma->mii_id1, ma->mii_id2), MII_MODEL(ma->mii_id2), 287 1.6 thorpej MII_REV(ma->mii_id2), pnp); 288 1.6 thorpej 289 1.33 thorpej aprint_normal(" phy %d", ma->mii_phyno); 290 1.53 msaitoh return UNCONF; 291 1.1 bouyer } 292 1.1 bouyer 293 1.43 dyoung static inline int 294 1.43 dyoung phy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 295 1.43 dyoung { 296 1.53 msaitoh 297 1.47 xtraeme if (!device_is_active(sc->mii_dev)) 298 1.43 dyoung return ENXIO; 299 1.43 dyoung return PHY_SERVICE(sc, mii, cmd); 300 1.43 dyoung } 301 1.43 dyoung 302 1.44 dyoung int 303 1.44 dyoung mii_ifmedia_change(struct mii_data *mii) 304 1.44 dyoung { 305 1.55 thorpej 306 1.55 thorpej KASSERT(mii_locked(mii) || 307 1.55 thorpej ifmedia_islegacy(&mii->mii_media)); 308 1.44 dyoung return ifmedia_change(&mii->mii_media, mii->mii_ifp); 309 1.44 dyoung } 310 1.44 dyoung 311 1.6 thorpej /* 312 1.6 thorpej * Media changed; notify all PHYs. 313 1.6 thorpej */ 314 1.6 thorpej int 315 1.27 thorpej mii_mediachg(struct mii_data *mii) 316 1.1 bouyer { 317 1.6 thorpej struct mii_softc *child; 318 1.55 thorpej int rv = 0; 319 1.55 thorpej 320 1.55 thorpej IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media); 321 1.55 thorpej KASSERT(mii_locked(mii)); 322 1.1 bouyer 323 1.6 thorpej mii->mii_media_status = 0; 324 1.6 thorpej mii->mii_media_active = IFM_NONE; 325 1.6 thorpej 326 1.42 dyoung LIST_FOREACH(child, &mii->mii_phys, mii_list) { 327 1.43 dyoung rv = phy_service(child, mii, MII_MEDIACHG); 328 1.6 thorpej if (rv) 329 1.55 thorpej break; 330 1.1 bouyer } 331 1.55 thorpej IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media); 332 1.55 thorpej return rv; 333 1.1 bouyer } 334 1.1 bouyer 335 1.6 thorpej /* 336 1.6 thorpej * Call the PHY tick routines, used during autonegotiation. 337 1.6 thorpej */ 338 1.6 thorpej void 339 1.27 thorpej mii_tick(struct mii_data *mii) 340 1.1 bouyer { 341 1.6 thorpej struct mii_softc *child; 342 1.1 bouyer 343 1.55 thorpej IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media); 344 1.55 thorpej KASSERT(mii_locked(mii)); 345 1.55 thorpej 346 1.42 dyoung LIST_FOREACH(child, &mii->mii_phys, mii_list) 347 1.43 dyoung (void)phy_service(child, mii, MII_TICK); 348 1.55 thorpej 349 1.55 thorpej IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media); 350 1.1 bouyer } 351 1.1 bouyer 352 1.6 thorpej /* 353 1.6 thorpej * Get media status from PHYs. 354 1.6 thorpej */ 355 1.2 thorpej void 356 1.27 thorpej mii_pollstat(struct mii_data *mii) 357 1.1 bouyer { 358 1.6 thorpej struct mii_softc *child; 359 1.1 bouyer 360 1.55 thorpej IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media); 361 1.55 thorpej KASSERT(mii_locked(mii)); 362 1.55 thorpej 363 1.6 thorpej mii->mii_media_status = 0; 364 1.6 thorpej mii->mii_media_active = IFM_NONE; 365 1.1 bouyer 366 1.42 dyoung LIST_FOREACH(child, &mii->mii_phys, mii_list) 367 1.43 dyoung (void)phy_service(child, mii, MII_POLLSTAT); 368 1.55 thorpej 369 1.55 thorpej IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media); 370 1.16 thorpej } 371 1.16 thorpej 372 1.16 thorpej /* 373 1.16 thorpej * Inform the PHYs that the interface is down. 374 1.16 thorpej */ 375 1.16 thorpej void 376 1.27 thorpej mii_down(struct mii_data *mii) 377 1.16 thorpej { 378 1.16 thorpej struct mii_softc *child; 379 1.16 thorpej 380 1.55 thorpej IFMEDIA_LOCK_FOR_LEGACY(&mii->mii_media); 381 1.55 thorpej KASSERT(mii_locked(mii)); 382 1.55 thorpej 383 1.42 dyoung LIST_FOREACH(child, &mii->mii_phys, mii_list) 384 1.43 dyoung (void)phy_service(child, mii, MII_DOWN); 385 1.55 thorpej 386 1.55 thorpej IFMEDIA_UNLOCK_FOR_LEGACY(&mii->mii_media); 387 1.23 drochner } 388 1.23 drochner 389 1.23 drochner static unsigned char 390 1.23 drochner bitreverse(unsigned char x) 391 1.23 drochner { 392 1.51 matt static const unsigned char nibbletab[16] = { 393 1.23 drochner 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 394 1.23 drochner }; 395 1.23 drochner 396 1.23 drochner return ((nibbletab[x & 15] << 4) | nibbletab[x >> 4]); 397 1.23 drochner } 398 1.23 drochner 399 1.32 thorpej u_int 400 1.54 msaitoh mii_oui(uint16_t id1, uint16_t id2) 401 1.23 drochner { 402 1.32 thorpej u_int h; 403 1.23 drochner 404 1.23 drochner h = (id1 << 6) | (id2 >> 10); 405 1.23 drochner 406 1.23 drochner return ((bitreverse(h >> 16) << 16) | 407 1.23 drochner (bitreverse((h >> 8) & 255) << 8) | 408 1.23 drochner bitreverse(h & 255)); 409 1.1 bouyer } 410