mii.c revision 84333
150120Swpaul/* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */ 250120Swpaul 350120Swpaul/*- 450120Swpaul * Copyright (c) 1998 The NetBSD Foundation, Inc. 550120Swpaul * All rights reserved. 650120Swpaul * 750120Swpaul * This code is derived from software contributed to The NetBSD Foundation 850120Swpaul * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 950120Swpaul * NASA Ames Research Center. 1050120Swpaul * 1150120Swpaul * Redistribution and use in source and binary forms, with or without 1250120Swpaul * modification, are permitted provided that the following conditions 1350120Swpaul * are met: 1450120Swpaul * 1. Redistributions of source code must retain the above copyright 1550120Swpaul * notice, this list of conditions and the following disclaimer. 1650120Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1750120Swpaul * notice, this list of conditions and the following disclaimer in the 1850120Swpaul * documentation and/or other materials provided with the distribution. 1950120Swpaul * 3. All advertising materials mentioning features or use of this software 2050120Swpaul * must display the following acknowledgement: 2150120Swpaul * This product includes software developed by the NetBSD 2250120Swpaul * Foundation, Inc. and its contributors. 2350120Swpaul * 4. Neither the name of The NetBSD Foundation nor the names of its 2450120Swpaul * contributors may be used to endorse or promote products derived 2550120Swpaul * from this software without specific prior written permission. 2650120Swpaul * 2750120Swpaul * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2850120Swpaul * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2950120Swpaul * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 3050120Swpaul * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3150120Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3250120Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3350120Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3450120Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3550120Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3650120Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3750120Swpaul * POSSIBILITY OF SUCH DAMAGE. 3850120Swpaul */ 3950120Swpaul 4050120Swpaul/* 4150120Swpaul * MII bus layer, glues MII-capable network interface drivers to sharable 4250120Swpaul * PHY drivers. This exports an interface compatible with BSD/OS 3.0's, 4350120Swpaul * plus some NetBSD extensions. 4450120Swpaul */ 4550120Swpaul 4650120Swpaul#include <sys/param.h> 4750120Swpaul#include <sys/systm.h> 4850120Swpaul#include <sys/socket.h> 4950120Swpaul#include <sys/malloc.h> 5050120Swpaul#include <sys/module.h> 5150120Swpaul#include <sys/bus.h> 5250120Swpaul 5350120Swpaul#include <net/if.h> 5450120Swpaul#include <net/if_media.h> 5550120Swpaul 5650120Swpaul#include <dev/mii/mii.h> 5750120Swpaul#include <dev/mii/miivar.h> 5850120Swpaul 5959757SpeterMODULE_VERSION(miibus, 1); 6059757Speter 6150120Swpaul#include "miibus_if.h" 6250120Swpaul 6350120Swpaul#if !defined(lint) 6450120Swpaulstatic const char rcsid[] = 6550959Speter "$FreeBSD: head/sys/dev/mii/mii.c 84333 2001-10-01 22:57:57Z mjacob $"; 6650120Swpaul#endif 6750120Swpaul 6850120Swpaulstatic int miibus_readreg __P((device_t, int, int)); 6950120Swpaulstatic int miibus_writereg __P((device_t, int, int, int)); 7050120Swpaulstatic void miibus_statchg __P((device_t)); 7184140Sjlemonstatic void miibus_linkchg __P((device_t)); 7250120Swpaulstatic void miibus_mediainit __P((device_t)); 7350120Swpaul 7450120Swpaulstatic device_method_t miibus_methods[] = { 7550120Swpaul /* device interface */ 7650120Swpaul DEVMETHOD(device_probe, miibus_probe), 7750120Swpaul DEVMETHOD(device_attach, miibus_attach), 7850120Swpaul DEVMETHOD(device_detach, miibus_detach), 7950120Swpaul DEVMETHOD(device_shutdown, bus_generic_shutdown), 8050120Swpaul 8150120Swpaul /* bus interface */ 8250120Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 8350120Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 8450120Swpaul 8550120Swpaul /* MII interface */ 8650120Swpaul DEVMETHOD(miibus_readreg, miibus_readreg), 8750120Swpaul DEVMETHOD(miibus_writereg, miibus_writereg), 8850120Swpaul DEVMETHOD(miibus_statchg, miibus_statchg), 8984140Sjlemon DEVMETHOD(miibus_linkchg, miibus_linkchg), 9050120Swpaul DEVMETHOD(miibus_mediainit, miibus_mediainit), 9150120Swpaul 9250120Swpaul { 0, 0 } 9350120Swpaul}; 9450120Swpaul 9550120Swpauldevclass_t miibus_devclass; 9650120Swpaul 9750120Swpauldriver_t miibus_driver = { 9850120Swpaul "miibus", 9950120Swpaul miibus_methods, 10050120Swpaul sizeof(struct mii_data) 10150120Swpaul}; 10250120Swpaul 10350120Swpaul/* 10450120Swpaul * Helper function used by network interface drivers, attaches PHYs 10550120Swpaul * to the network interface driver parent. 10650120Swpaul */ 10750120Swpaul 10850120Swpaulint miibus_probe(dev) 10950120Swpaul device_t dev; 11050120Swpaul{ 11150120Swpaul struct mii_attach_args ma, *args; 11250120Swpaul struct mii_data *mii; 11350577Swpaul device_t child = NULL, parent; 11450120Swpaul int bmsr, capmask = 0xFFFFFFFF; 11550120Swpaul 11650120Swpaul mii = device_get_softc(dev); 11750120Swpaul parent = device_get_parent(dev); 11850120Swpaul LIST_INIT(&mii->mii_phys); 11950120Swpaul 12050120Swpaul for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) { 12150120Swpaul /* 12250120Swpaul * Check to see if there is a PHY at this address. Note, 12350120Swpaul * many braindead PHYs report 0/0 in their ID registers, 12450120Swpaul * so we test for media in the BMSR. 12550120Swpaul */ 12650120Swpaul bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR); 12750120Swpaul if (bmsr == 0 || bmsr == 0xffff || 12850120Swpaul (bmsr & BMSR_MEDIAMASK) == 0) { 12950120Swpaul /* Assume no PHY at this address. */ 13050120Swpaul continue; 13150120Swpaul } 13250120Swpaul 13350120Swpaul /* 13450120Swpaul * Extract the IDs. Braindead PHYs will be handled by 13550120Swpaul * the `ukphy' driver, as we have no ID information to 13650120Swpaul * match on. 13750120Swpaul */ 13850120Swpaul ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno, 13950120Swpaul MII_PHYIDR1); 14050120Swpaul ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno, 14150120Swpaul MII_PHYIDR2); 14250120Swpaul 14350120Swpaul ma.mii_data = mii; 14450120Swpaul ma.mii_capmask = capmask; 14550120Swpaul 14650120Swpaul args = malloc(sizeof(struct mii_attach_args), 14750120Swpaul M_DEVBUF, M_NOWAIT); 14850120Swpaul bcopy((char *)&ma, (char *)args, sizeof(ma)); 14954073Smdodd child = device_add_child(dev, NULL, -1); 15054073Smdodd device_set_ivars(child, args); 15150120Swpaul } 15250120Swpaul 15350391Swpaul if (child == NULL) 15450120Swpaul return(ENXIO); 15550120Swpaul 15650120Swpaul device_set_desc(dev, "MII bus"); 15750120Swpaul 15850120Swpaul return(0); 15950120Swpaul} 16050120Swpaul 16150120Swpaulint miibus_attach(dev) 16250120Swpaul device_t dev; 16350120Swpaul{ 16450120Swpaul void **v; 16550120Swpaul ifm_change_cb_t ifmedia_upd; 16650120Swpaul ifm_stat_cb_t ifmedia_sts; 16750120Swpaul struct mii_data *mii; 16850120Swpaul 16950120Swpaul mii = device_get_softc(dev); 17084333Smjacob /* 17184333Smjacob * Note that each NIC's softc must start with an ifnet structure. 17284333Smjacob */ 17350120Swpaul mii->mii_ifp = device_get_softc(device_get_parent(dev)); 17450120Swpaul v = device_get_ivars(dev); 17550120Swpaul ifmedia_upd = v[0]; 17650120Swpaul ifmedia_sts = v[1]; 17750120Swpaul ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts); 17850120Swpaul bus_generic_attach(dev); 17950120Swpaul 18050120Swpaul return(0); 18150120Swpaul} 18250120Swpaul 18350120Swpaulint miibus_detach(dev) 18450120Swpaul device_t dev; 18550120Swpaul{ 18650120Swpaul struct mii_data *mii; 18750120Swpaul 18850120Swpaul bus_generic_detach(dev); 18950120Swpaul mii = device_get_softc(dev); 19050120Swpaul ifmedia_removeall(&mii->mii_media); 19150120Swpaul mii->mii_ifp = NULL; 19250120Swpaul 19350120Swpaul return(0); 19450120Swpaul} 19550120Swpaul 19650120Swpaulstatic int miibus_readreg(dev, phy, reg) 19750120Swpaul device_t dev; 19850120Swpaul int phy, reg; 19950120Swpaul{ 20050120Swpaul device_t parent; 20150120Swpaul 20250120Swpaul parent = device_get_parent(dev); 20350120Swpaul return(MIIBUS_READREG(parent, phy, reg)); 20450120Swpaul} 20550120Swpaul 20650120Swpaulstatic int miibus_writereg(dev, phy, reg, data) 20750120Swpaul device_t dev; 20850120Swpaul int phy, reg, data; 20950120Swpaul{ 21050120Swpaul device_t parent; 21150120Swpaul 21250120Swpaul parent = device_get_parent(dev); 21350120Swpaul return(MIIBUS_WRITEREG(parent, phy, reg, data)); 21450120Swpaul} 21550120Swpaul 21650120Swpaulstatic void miibus_statchg(dev) 21750120Swpaul device_t dev; 21850120Swpaul{ 21950120Swpaul device_t parent; 22050120Swpaul 22150120Swpaul parent = device_get_parent(dev); 22250120Swpaul MIIBUS_STATCHG(parent); 22350120Swpaul return; 22450120Swpaul} 22550120Swpaul 22684140Sjlemonstatic void 22784140Sjlemonmiibus_linkchg(dev) 22884140Sjlemon device_t dev; 22984140Sjlemon{ 23084140Sjlemon struct mii_data *mii; 23184140Sjlemon struct ifnet *ifp; 23284140Sjlemon device_t parent; 23384140Sjlemon int link; 23484140Sjlemon 23584140Sjlemon parent = device_get_parent(dev); 23684140Sjlemon MIIBUS_LINKCHG(parent); 23784140Sjlemon 23884140Sjlemon mii = device_get_softc(dev); 23984333Smjacob /* 24084333Smjacob * Note that each NIC's softc must start with an ifnet structure. 24184333Smjacob */ 24284140Sjlemon ifp = device_get_softc(parent); 24384140Sjlemon 24484140Sjlemon if (mii->mii_media_status & IFM_AVALID) { 24584140Sjlemon if (mii->mii_media_status & IFM_ACTIVE) 24684140Sjlemon link = NOTE_LINKUP; 24784140Sjlemon else 24884140Sjlemon link = NOTE_LINKDOWN; 24984140Sjlemon } else { 25084140Sjlemon link = NOTE_LINKINV; 25184140Sjlemon } 25284140Sjlemon 25384140Sjlemon KNOTE(&ifp->if_klist, link); 25484140Sjlemon} 25584140Sjlemon 25650120Swpaulstatic void miibus_mediainit(dev) 25750120Swpaul device_t dev; 25850120Swpaul{ 25950120Swpaul struct mii_data *mii; 26050120Swpaul struct ifmedia_entry *m; 26150120Swpaul int media = 0; 26250120Swpaul 26350577Swpaul /* Poke the parent in case it has any media of its own to add. */ 26450577Swpaul MIIBUS_MEDIAINIT(device_get_parent(dev)); 26550577Swpaul 26650120Swpaul mii = device_get_softc(dev); 26772012Sphk LIST_FOREACH(m, &mii->mii_media.ifm_list, ifm_list) { 26850120Swpaul media = m->ifm_media; 26950120Swpaul if (media == (IFM_ETHER|IFM_AUTO)) 27050120Swpaul break; 27150120Swpaul } 27250120Swpaul 27350120Swpaul ifmedia_set(&mii->mii_media, media); 27450120Swpaul 27550120Swpaul return; 27650120Swpaul} 27750120Swpaul 27850120Swpaulint mii_phy_probe(dev, child, ifmedia_upd, ifmedia_sts) 27950120Swpaul device_t dev; 28050120Swpaul device_t *child; 28150120Swpaul ifm_change_cb_t ifmedia_upd; 28250120Swpaul ifm_stat_cb_t ifmedia_sts; 28350120Swpaul{ 28450120Swpaul void **v; 28550120Swpaul int bmsr, i; 28650120Swpaul 28750120Swpaul v = malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT); 28884333Smjacob if (v == 0) { 28984333Smjacob return (ENOMEM); 29084333Smjacob } 29150120Swpaul v[0] = ifmedia_upd; 29250120Swpaul v[1] = ifmedia_sts; 29354073Smdodd *child = device_add_child(dev, "miibus", -1); 29454073Smdodd device_set_ivars(*child, v); 29550120Swpaul 29650120Swpaul for (i = 0; i < MII_NPHY; i++) { 29750120Swpaul bmsr = MIIBUS_READREG(dev, i, MII_BMSR); 29850120Swpaul if (bmsr == 0 || bmsr == 0xffff || 29950120Swpaul (bmsr & BMSR_MEDIAMASK) == 0) { 30050120Swpaul /* Assume no PHY at this address. */ 30150120Swpaul continue; 30250120Swpaul } else 30350120Swpaul break; 30450120Swpaul } 30550120Swpaul 30650120Swpaul if (i == MII_NPHY) { 30750120Swpaul device_delete_child(dev, *child); 30850120Swpaul *child = NULL; 30950120Swpaul return(ENXIO); 31050120Swpaul } 31150120Swpaul 31250120Swpaul bus_generic_attach(dev); 31350120Swpaul 31450120Swpaul return(0); 31550120Swpaul} 31650120Swpaul 31750120Swpaul/* 31850120Swpaul * Media changed; notify all PHYs. 31950120Swpaul */ 32050120Swpaulint 32150120Swpaulmii_mediachg(mii) 32250120Swpaul struct mii_data *mii; 32350120Swpaul{ 32450120Swpaul struct mii_softc *child; 32550120Swpaul int rv; 32650120Swpaul 32750120Swpaul mii->mii_media_status = 0; 32850120Swpaul mii->mii_media_active = IFM_NONE; 32950120Swpaul 33072012Sphk LIST_FOREACH(child, &mii->mii_phys, mii_list) { 33150120Swpaul rv = (*child->mii_service)(child, mii, MII_MEDIACHG); 33250120Swpaul if (rv) 33350120Swpaul return (rv); 33450120Swpaul } 33550120Swpaul return (0); 33650120Swpaul} 33750120Swpaul 33850120Swpaul/* 33950120Swpaul * Call the PHY tick routines, used during autonegotiation. 34050120Swpaul */ 34150120Swpaulvoid 34250120Swpaulmii_tick(mii) 34350120Swpaul struct mii_data *mii; 34450120Swpaul{ 34550120Swpaul struct mii_softc *child; 34650120Swpaul 34772012Sphk LIST_FOREACH(child, &mii->mii_phys, mii_list) 34850120Swpaul (void) (*child->mii_service)(child, mii, MII_TICK); 34950120Swpaul} 35050120Swpaul 35150120Swpaul/* 35250120Swpaul * Get media status from PHYs. 35350120Swpaul */ 35450120Swpaulvoid 35550120Swpaulmii_pollstat(mii) 35650120Swpaul struct mii_data *mii; 35750120Swpaul{ 35850120Swpaul struct mii_softc *child; 35950120Swpaul 36050120Swpaul mii->mii_media_status = 0; 36150120Swpaul mii->mii_media_active = IFM_NONE; 36250120Swpaul 36372012Sphk LIST_FOREACH(child, &mii->mii_phys, mii_list) 36450120Swpaul (void) (*child->mii_service)(child, mii, MII_POLLSTAT); 36550120Swpaul} 366