summaryrefslogtreecommitdiffstats
path: root/sys/dev/mii/mii.c
diff options
context:
space:
mode:
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