summaryrefslogtreecommitdiffstats
path: root/sys/dev/mii/mii.c
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>1999-08-21 17:40:53 +0000
committerwpaul <wpaul@FreeBSD.org>1999-08-21 17:40:53 +0000
commitcdea47dc6eedf22eb73e5b4675affbc31669814e (patch)
tree52556fc37b962523cea5a60841a8698aa96054e7 /sys/dev/mii/mii.c
parent2cf7e92c711cd5cfa74fa596ee64157497f96bf3 (diff)
downloadFreeBSD-src-cdea47dc6eedf22eb73e5b4675affbc31669814e.zip
FreeBSD-src-cdea47dc6eedf22eb73e5b4675affbc31669814e.tar.gz
This commit adds support for the NetBSD MII abstraction layer and
MII-compliant PHY drivers. Many 10/100 ethernet NICs available today either use an MII transceiver or have built-in transceivers that can be programmed using an MII interface. It makes sense then to separate this support out into common code instead of duplicating it in all of the NIC drivers. The mii code also handles all of the media detection, selection and reporting via the ifmedia interface. This is basically the same code from NetBSD's /sys/dev/mii, except it's been adapted to FreeBSD's bus architecture. The advantage to this is that it automatically allows everything to be turned into a loadable module. There are some common functions for use in drivers once an miibus has been attached (mii_mediachg(), mii_pollstat(), mii_tick()) as well as individual PHY drivers. There is also a generic driver for all PHYs that aren't handled by a specific driver. It's possible to do this because all 10/100 PHYs implement the same general register set in addition to their vendor-specific register sets, so for the most part you can use one driver for pretty much any PHY. There are a couple of oddball exceptions though, hence the need to have specific drivers. There are two layers: the generic "miibus" layer and the PHY driver layer. The drivers are child devices of "miibus" and the "miibus" is a child of a given NIC driver. The "miibus" code and the PHY drivers can actually be compiled and kldoaded as completely separate modules or compiled together into one module. For the moment I'm using the latter approach since the code is relatively small. Currently there are only three PHY drivers here: the generic driver, the built-in 3Com XL driver and the NS DP83840 driver. I'll be adding others later as I convert various NIC drivers to use this code. I realize that I'm cvs adding this stuff instead of importing it onto a separate vendor branch, but in my opinion the import approach doesn't really offer any significant advantage: I'm going to be maintaining this stuff and writing my own PHY drivers one way or the other.
Diffstat (limited to 'sys/dev/mii/mii.c')
-rw-r--r--sys/dev/mii/mii.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/sys/dev/mii/mii.c b/sys/dev/mii/mii.c
new file mode 100644
index 0000000..1673acf
--- /dev/null
+++ b/sys/dev/mii/mii.c
@@ -0,0 +1,326 @@
+/* $NetBSD: mii.c,v 1.12 1999/08/03 19:41:49 drochner Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * MII bus layer, glues MII-capable network interface drivers to sharable
+ * PHY drivers. This exports an interface compatible with BSD/OS 3.0's,
+ * plus some NetBSD extensions.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include "miibus_if.h"
+
+#if !defined(lint)
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+static int miibus_readreg __P((device_t, int, int));
+static int miibus_writereg __P((device_t, int, int, int));
+static void miibus_statchg __P((device_t));
+static void miibus_mediainit __P((device_t));
+
+static device_method_t miibus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, miibus_probe),
+ DEVMETHOD(device_attach, miibus_attach),
+ DEVMETHOD(device_detach, miibus_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, miibus_readreg),
+ DEVMETHOD(miibus_writereg, miibus_writereg),
+ DEVMETHOD(miibus_statchg, miibus_statchg),
+ DEVMETHOD(miibus_mediainit, miibus_mediainit),
+
+ { 0, 0 }
+};
+
+devclass_t miibus_devclass;
+
+driver_t miibus_driver = {
+ "miibus",
+ miibus_methods,
+ sizeof(struct mii_data)
+};
+
+/*
+ * Helper function used by network interface drivers, attaches PHYs
+ * to the network interface driver parent.
+ */
+
+int miibus_probe(dev)
+ device_t dev;
+{
+ struct mii_attach_args ma, *args;
+ struct mii_data *mii;
+ device_t child, parent;
+ int bmsr, capmask = 0xFFFFFFFF;
+
+ mii = device_get_softc(dev);
+ parent = device_get_parent(dev);
+ LIST_INIT(&mii->mii_phys);
+
+ for (ma.mii_phyno = 0; ma.mii_phyno < MII_NPHY; ma.mii_phyno++) {
+ /*
+ * Check to see if there is a PHY at this address. Note,
+ * many braindead PHYs report 0/0 in their ID registers,
+ * so we test for media in the BMSR.
+ */
+ bmsr = MIIBUS_READREG(parent, ma.mii_phyno, MII_BMSR);
+ if (bmsr == 0 || bmsr == 0xffff ||
+ (bmsr & BMSR_MEDIAMASK) == 0) {
+ /* Assume no PHY at this address. */
+ continue;
+ }
+
+ /*
+ * Extract the IDs. Braindead PHYs will be handled by
+ * the `ukphy' driver, as we have no ID information to
+ * match on.
+ */
+ ma.mii_id1 = MIIBUS_READREG(parent, ma.mii_phyno,
+ MII_PHYIDR1);
+ ma.mii_id2 = MIIBUS_READREG(parent, ma.mii_phyno,
+ MII_PHYIDR2);
+
+ ma.mii_data = mii;
+ ma.mii_capmask = capmask;
+
+ args = malloc(sizeof(struct mii_attach_args),
+ M_DEVBUF, M_NOWAIT);
+ bcopy((char *)&ma, (char *)args, sizeof(ma));
+ child = device_add_child(dev, NULL, -1, args);
+
+ break;
+ }
+
+ if (ma.mii_phyno == MII_NPHY)
+ return(ENXIO);
+
+ device_set_desc(dev, "MII bus");
+
+ return(0);
+}
+
+int miibus_attach(dev)
+ device_t dev;
+{
+ void **v;
+ ifm_change_cb_t ifmedia_upd;
+ ifm_stat_cb_t ifmedia_sts;
+ struct mii_data *mii;
+
+ mii = device_get_softc(dev);
+ mii->mii_ifp = device_get_softc(device_get_parent(dev));
+ v = device_get_ivars(dev);
+ ifmedia_upd = v[0];
+ ifmedia_sts = v[1];
+ ifmedia_init(&mii->mii_media, IFM_IMASK, ifmedia_upd, ifmedia_sts);
+ bus_generic_attach(dev);
+
+ return(0);
+}
+
+int miibus_detach(dev)
+ device_t dev;
+{
+ struct mii_data *mii;
+
+ bus_generic_detach(dev);
+ mii = device_get_softc(dev);
+ ifmedia_removeall(&mii->mii_media);
+ mii->mii_ifp = NULL;
+
+ return(0);
+}
+
+static int miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
+{
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ return(MIIBUS_READREG(parent, phy, reg));
+}
+
+static int miibus_writereg(dev, phy, reg, data)
+ device_t dev;
+ int phy, reg, data;
+{
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ return(MIIBUS_WRITEREG(parent, phy, reg, data));
+}
+
+static void miibus_statchg(dev)
+ device_t dev;
+{
+ device_t parent;
+
+ parent = device_get_parent(dev);
+ MIIBUS_STATCHG(parent);
+ return;
+}
+
+static void miibus_mediainit(dev)
+ device_t dev;
+{
+ struct mii_data *mii;
+ struct ifmedia_entry *m;
+ int media = 0;
+
+ mii = device_get_softc(dev);
+ for (m = LIST_FIRST(&mii->mii_media.ifm_list); m != NULL;
+ m = LIST_NEXT(m, ifm_list)) {
+ media = m->ifm_media;
+ if (media == (IFM_ETHER|IFM_AUTO))
+ break;
+ }
+
+ ifmedia_set(&mii->mii_media, media);
+
+ return;
+}
+
+int mii_phy_probe(dev, child, ifmedia_upd, ifmedia_sts)
+ device_t dev;
+ device_t *child;
+ ifm_change_cb_t ifmedia_upd;
+ ifm_stat_cb_t ifmedia_sts;
+{
+ void **v;
+ int bmsr, i;
+
+ v = malloc(sizeof(vm_offset_t) * 2, M_DEVBUF, M_NOWAIT);
+ v[0] = ifmedia_upd;
+ v[1] = ifmedia_sts;
+ *child = device_add_child(dev, "miibus", -1, v);
+
+ for (i = 0; i < MII_NPHY; i++) {
+ bmsr = MIIBUS_READREG(dev, i, MII_BMSR);
+ if (bmsr == 0 || bmsr == 0xffff ||
+ (bmsr & BMSR_MEDIAMASK) == 0) {
+ /* Assume no PHY at this address. */
+ continue;
+ } else
+ break;
+ }
+
+ if (i == MII_NPHY) {
+ device_delete_child(dev, *child);
+ *child = NULL;
+ return(ENXIO);
+ }
+
+ bus_generic_attach(dev);
+
+ return(0);
+}
+
+/*
+ * Media changed; notify all PHYs.
+ */
+int
+mii_mediachg(mii)
+ struct mii_data *mii;
+{
+ struct mii_softc *child;
+ int rv;
+
+ mii->mii_media_status = 0;
+ mii->mii_media_active = IFM_NONE;
+
+ for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
+ child = LIST_NEXT(child, mii_list)) {
+ rv = (*child->mii_service)(child, mii, MII_MEDIACHG);
+ if (rv)
+ return (rv);
+ }
+ return (0);
+}
+
+/*
+ * Call the PHY tick routines, used during autonegotiation.
+ */
+void
+mii_tick(mii)
+ struct mii_data *mii;
+{
+ struct mii_softc *child;
+
+ for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
+ child = LIST_NEXT(child, mii_list))
+ (void) (*child->mii_service)(child, mii, MII_TICK);
+}
+
+/*
+ * Get media status from PHYs.
+ */
+void
+mii_pollstat(mii)
+ struct mii_data *mii;
+{
+ struct mii_softc *child;
+
+ mii->mii_media_status = 0;
+ mii->mii_media_active = IFM_NONE;
+
+ for (child = LIST_FIRST(&mii->mii_phys); child != NULL;
+ child = LIST_NEXT(child, mii_list))
+ (void) (*child->mii_service)(child, mii, MII_POLLSTAT);
+}
OpenPOWER on IntegriCloud