summaryrefslogtreecommitdiffstats
path: root/sys/dev/mii
diff options
context:
space:
mode:
authorjon <jon@FreeBSD.org>2000-10-19 08:34:32 +0000
committerjon <jon@FreeBSD.org>2000-10-19 08:34:32 +0000
commit4580b26b321a7db757880cf45873ce30aa10fd27 (patch)
treecff7f58d8da222e806bc63d80206f2c95b70295c /sys/dev/mii
parente3c86757f4279913a9a3eaa797fdcc8c2b9dba4f (diff)
downloadFreeBSD-src-4580b26b321a7db757880cf45873ce30aa10fd27.zip
FreeBSD-src-4580b26b321a7db757880cf45873ce30aa10fd27.tar.gz
NEWCARD/Cardbus -
This commit adds support for Xircom X3201 based cardbus cards. Support for the TDK 78Q2120 MII is also added. IBM Etherjet, Intel and Xircom cards uses these chips. Note that as a result of this commit, some Intel/DEC 21143 based cardbus cards will also attach, but not get link. That is being looked at.
Diffstat (limited to 'sys/dev/mii')
-rw-r--r--sys/dev/mii/tdkphy.c362
-rw-r--r--sys/dev/mii/tdkphyreg.h82
2 files changed, 444 insertions, 0 deletions
diff --git a/sys/dev/mii/tdkphy.c b/sys/dev/mii/tdkphy.c
new file mode 100644
index 0000000..e0cc5d8
--- /dev/null
+++ b/sys/dev/mii/tdkphy.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2000,2001 Jonathan Chen.
+ * All rights reserved.
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Driver for the TDK 78Q2120 MII
+ *
+ * References:
+ * Datasheet for the 78Q2120 - http://www.tsc.tdk.com/lan/78q2120.pdf
+ * Most of this code stolen from ukphy.c
+ */
+
+/*
+ * The TDK 78Q2120 is found on some Xircom X3201 based cardbus cards. It's just
+ * like any other normal phy, except it does auto negotiation in a different
+ * way.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <machine/clock.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mii/miidevs.h>
+
+#include <dev/mii/tdkphyreg.h>
+
+#include "miibus_if.h"
+
+#if !defined(lint)
+static const char rcsid[] =
+ "$Id: tdkphy.c,v 1.3 2000/10/14 06:20:56 jon Exp $";
+#endif
+
+static int tdkphy_probe __P((device_t));
+static int tdkphy_attach __P((device_t));
+static int tdkphy_detach __P((device_t));
+
+static device_method_t tdkphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, tdkphy_probe),
+ DEVMETHOD(device_attach, tdkphy_attach),
+ DEVMETHOD(device_detach, tdkphy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ { 0, 0 }
+};
+
+static devclass_t tdkphy_devclass;
+
+static driver_t tdkphy_driver = {
+ "tdkphy",
+ tdkphy_methods,
+ sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(tdkphy, miibus, tdkphy_driver, tdkphy_devclass, 0, 0);
+
+int tdkphy_service __P((struct mii_softc *, struct mii_data *, int));
+void tdkphy_status __P((struct mii_softc *));
+
+static int
+tdkphy_probe(device_t dev)
+{
+ struct mii_attach_args *ma;
+ ma = device_get_ivars(dev);
+ if ((MII_OUI(ma->mii_id1, ma->mii_id2) != MII_OUI_TDK ||
+ MII_MODEL(ma->mii_id2) != MII_MODEL_TDK_78Q2120))
+ return (ENXIO);
+
+ device_set_desc(dev, MII_STR_TDK_78Q2120);
+ return (0);
+}
+
+static int
+tdkphy_attach(device_t dev)
+{
+ struct mii_softc *sc;
+ struct mii_attach_args *ma;
+ struct mii_data *mii;
+ sc = device_get_softc(dev);
+ ma = device_get_ivars(dev);
+ sc->mii_dev = device_get_parent(dev);
+ mii = device_get_softc(sc->mii_dev);
+ LIST_INSERT_HEAD(&mii->mii_phys, sc, mii_list);
+
+ if (bootverbose)
+ device_printf(dev, "OUI 0x%06x, model 0x%04x, rev. %d\n",
+ MII_OUI(ma->mii_id1, ma->mii_id2),
+ MII_MODEL(ma->mii_id2), MII_REV(ma->mii_id2));
+
+ sc->mii_inst = mii->mii_instance;
+ sc->mii_phy = ma->mii_phyno;
+ sc->mii_service = tdkphy_service;
+ sc->mii_pdata = mii;
+
+ mii->mii_instance++;
+
+ sc->mii_flags |= MIIF_NOISOLATE;
+
+#define ADD(m, c) ifmedia_add(&mii->mii_media, (m), (c), NULL)
+
+ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst),
+ BMCR_ISO);
+#if 0
+ ADD(IFM_MAKEWORD(IFM_ETHER, IFM_100_TX, IFM_LOOP, sc->mii_inst),
+ BMCR_LOOP|BMCR_S100);
+#endif
+
+ mii_phy_reset(sc);
+
+ sc->mii_capabilities =
+ PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+ device_printf(dev, " ");
+ if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0)
+ printf("no media present");
+ else
+ mii_add_media(mii, sc->mii_capabilities,
+ sc->mii_inst);
+ printf("\n");
+#undef ADD
+
+ MIIBUS_MEDIAINIT(sc->mii_dev);
+
+ return(0);
+}
+
+static int tdkphy_detach(device_t dev)
+{
+ struct mii_softc *sc;
+ struct mii_data *mii;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(device_get_parent(dev));
+ sc->mii_dev = NULL;
+ LIST_REMOVE(sc, mii_list);
+
+ return(0);
+}
+
+int
+tdkphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+ struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
+ int reg;
+
+ switch (cmd) {
+ case MII_POLLSTAT:
+ /*
+ * If we're not polling our PHY instance, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+ break;
+
+ case MII_MEDIACHG:
+ /*
+ * If the media indicates a different PHY instance,
+ * isolate ourselves.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
+ reg = PHY_READ(sc, MII_BMCR);
+ PHY_WRITE(sc, MII_BMCR, reg | BMCR_ISO);
+ return (0);
+ }
+
+ /*
+ * If the interface is not up, don't do anything.
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ break;
+
+ switch (IFM_SUBTYPE(ife->ifm_media)) {
+ case IFM_AUTO:
+ /*
+ * If we're already in auto mode, just return.
+ */
+ if (PHY_READ(sc, MII_BMCR) & BMCR_AUTOEN)
+ return (0);
+ (void) mii_phy_auto(sc, 1);
+ break;
+ case IFM_100_T4:
+ /*
+ * Not supported on MII
+ */
+ return (EINVAL);
+ default:
+ /*
+ * BMCR data is stored in the ifmedia entry.
+ */
+ PHY_WRITE(sc, MII_ANAR,
+ mii_anar(ife->ifm_media));
+ PHY_WRITE(sc, MII_BMCR, ife->ifm_data);
+ }
+ break;
+
+ case MII_TICK:
+ /*
+ * If we're not currently selected, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst)
+ return (0);
+
+ /*
+ * Only used for autonegotiation.
+ */
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO)
+ return (0);
+
+ /*
+ * Is the interface even up?
+ */
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
+ return (0);
+
+ /*
+ * Check to see if we have link. If we do, we don't
+ * need to restart the autonegotiation process. Read
+ * the BMSR twice in case it's latched.
+ */
+ reg = PHY_READ(sc, MII_BMSR) |
+ PHY_READ(sc, MII_BMSR);
+ if (reg & BMSR_LINK)
+ return (0);
+
+ /*
+ * Only retry autonegotiation every 5 seconds.
+ */
+ if (++sc->mii_ticks != 5)
+ return (0);
+
+ sc->mii_ticks = 0;
+ mii_phy_reset(sc);
+ if (mii_phy_auto(sc, 0) == EJUSTRETURN)
+ return (0);
+ break;
+ }
+
+ /* Update the media status. */
+ tdkphy_status(sc);
+ if (sc->mii_pdata->mii_media_active & IFM_FDX)
+ PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) | BMCR_FDX);
+ else
+ PHY_WRITE(sc, MII_BMCR, PHY_READ(sc, MII_BMCR) & ~BMCR_FDX);
+ /* Callback if something changed. */
+ if (sc->mii_active != mii->mii_media_active || cmd == MII_MEDIACHG) {
+ MIIBUS_STATCHG(sc->mii_dev);
+ sc->mii_active = mii->mii_media_active;
+ }
+ return (0);
+}
+
+void
+tdkphy_status(struct mii_softc *phy)
+{
+ struct mii_data *mii = phy->mii_pdata;
+ int bmsr, bmcr, anlpar, diag;
+
+ mii->mii_media_status = IFM_AVALID;
+ mii->mii_media_active = IFM_ETHER;
+
+ bmsr = PHY_READ(phy, MII_BMSR) | PHY_READ(phy, MII_BMSR);
+ if (bmsr & BMSR_LINK)
+ mii->mii_media_status |= IFM_ACTIVE;
+
+ bmcr = PHY_READ(phy, MII_BMCR);
+ if (bmcr & BMCR_ISO) {
+ mii->mii_media_active |= IFM_NONE;
+ mii->mii_media_status = 0;
+ return;
+ }
+
+ if (bmcr & BMCR_LOOP)
+ mii->mii_media_active |= IFM_LOOP;
+
+ if (bmcr & BMCR_AUTOEN) {
+ /*
+ * NWay autonegotiation takes the highest-order common
+ * bit of the ANAR and ANLPAR (i.e. best media advertised
+ * both by us and our link partner).
+ */
+ if ((bmsr & BMSR_ACOMP) == 0) {
+ /* Erg, still trying, I guess... */
+ mii->mii_media_active |= IFM_NONE;
+ return;
+ }
+
+ anlpar = PHY_READ(phy, MII_ANAR) & PHY_READ(phy, MII_ANLPAR);
+ /*
+ * ANLPAR doesn't get set on my card, but we check it anyway,
+ * since it is mentioned in the 78Q2120 specs.
+ */
+ if (anlpar & ANLPAR_T4)
+ mii->mii_media_active |= IFM_100_T4;
+ else if (anlpar & ANLPAR_TX_FD)
+ mii->mii_media_active |= IFM_100_TX|IFM_FDX;
+ else if (anlpar & ANLPAR_TX)
+ mii->mii_media_active |= IFM_100_TX;
+ else if (anlpar & ANLPAR_10_FD)
+ mii->mii_media_active |= IFM_10_T|IFM_FDX;
+ else if (anlpar & ANLPAR_10)
+ mii->mii_media_active |= IFM_10_T;
+ else {
+ /*
+ * ANLPAR isn't set, which leaves two possibilities:
+ * 1) Auto negotiation failed
+ * 2) Auto negotiation completed, but the card forgot
+ * to set ANLPAR.
+ * So we check the MII_DIAG(18) register...
+ */
+ diag = PHY_READ(phy, MII_DIAG);
+ if (diag & DIAG_NEGFAIL) /* assume 10baseT if no neg */
+ mii->mii_media_active |= IFM_10_T;
+ else {
+ if (diag & DIAG_DUPLEX)
+ mii->mii_media_active |= IFM_FDX;
+ if (diag & DIAG_RATE_100)
+ mii->mii_media_active |= IFM_100_TX;
+ else
+ mii->mii_media_active |= IFM_10_T;
+ }
+ }
+ } else {
+ mii->mii_media_active = mii_media_from_bmcr(bmcr);
+ }
+}
diff --git a/sys/dev/mii/tdkphyreg.h b/sys/dev/mii/tdkphyreg.h
new file mode 100644
index 0000000..1f10625
--- /dev/null
+++ b/sys/dev/mii/tdkphyreg.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2000,2001 Jonathan Chen.
+ * All rights reserved.
+ *
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Register definitions for TDK 78Q2120
+ */
+
+#ifndef _DEV_MII_TDKPHYREG_H_
+#define _DEV_MII_TDKPHYREG_H_
+
+#define MII_VENDOR 16
+#define VENDOR_RXCC 0x0001
+#define VENDOR_PCSBP 0x0002
+#define VENDOR_RVSPOL 0x0010
+#define VENDOR_NOAPOL 0x0020
+#define VENDOR_GPIO0DIR 0x0040
+#define VENDOR_GPIO0DAT 0x0080
+#define VENDOR_GPIO1DIR 0x0100
+#define VENDOR_GPIO1DAT 0x0200
+#define VENDOR_10BTLOOP 0x0400
+#define VENDOR_NOSQE 0x0800
+#define VENDOR_TXHIM 0x1000
+#define VENDOR_INTLVL 0x4000
+#define VENDOR_RPTR 0x8000
+
+#define MII_INT 17
+#define INT_STAT_MASK 0x00ff
+#define INT_STAT_ACOMP 0x0001
+#define INT_STAT_RFAULT 0x0002
+#define INT_STAT_LSCHG 0x0004
+#define INT_STAT_LPACK 0x0008
+#define INT_STAT_PDF 0x0010
+#define INT_STAT_PRX 0x0020
+#define INT_STAT_RXERR 0x0040
+#define INT_STAT_JABBER 0x0080
+#define INT_CTRL_MASK 0xff00
+#define INT_CTRL_ACOMP 0x0100
+#define INT_CTRL_RFAULT 0x0200
+#define INT_CTRL_LSCHG 0x0400
+#define INT_CTRL_LPACK 0x0800
+#define INT_CTRL_PDF 0x1000
+#define INT_CTRL_PRX 0x2000
+#define INT_CTRL_RXERR 0x4000
+#define INT_CTRL_JABBER 0x8000
+
+
+#define MII_DIAG 18
+#define DIAG_RLOCK 0x0100
+#define DIAG_RPASS 0x0200
+#define DIAG_RATE_100 0x0400
+#define DIAG_DUPLEX 0x0800
+#define DIAG_NEGFAIL 0x1000
+
+
+#endif
OpenPOWER on IntegriCloud