summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorbenno <benno@FreeBSD.org>2008-06-06 05:00:49 +0000
committerbenno <benno@FreeBSD.org>2008-06-06 05:00:49 +0000
commit38e6324da40386642b2361f5c422ea1d1722b94c (patch)
treecaa396ccc2a7d21c06159062c9fb2842fc9e1d99 /sys
parent50f80e694a91c15bfc41256ebfe514ad8e6f9164 (diff)
downloadFreeBSD-src-38e6324da40386642b2361f5c422ea1d1722b94c.zip
FreeBSD-src-38e6324da40386642b2361f5c422ea1d1722b94c.tar.gz
This is a rewritten driver for the SMSC LAN91C111. It's based in part on the
sn(4) driver and also looking at newer drivers. The reason for the rewrite is to support MII and to try and resolve some performance issues found when trying to use the sn(4) driver on the Gumstix network boards. For reference, the SMSC LAN91C111 is a non-PCI ethernet part whose lineage dates back to Ye Olde Days of ISA. It seems to get some use in the embedded space these days on parts lacking on-board MACs or on-board PCI controllers, such as the XScale PXA line of ARM CPUs. This also includes a driver for the SMSC LAN83C183 10/100 PHY. Man page to follow.
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files2
-rw-r--r--sys/dev/mii/miidevs4
-rw-r--r--sys/dev/mii/smcphy.c265
-rw-r--r--sys/dev/smc/if_smc.c1312
-rw-r--r--sys/dev/smc/if_smcreg.h261
-rw-r--r--sys/dev/smc/if_smcvar.h87
6 files changed, 1931 insertions, 0 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 9ede3b8..97f3732 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -901,6 +901,7 @@ dev/mii/rlphy.c optional miibus | rlphy
dev/mii/rlswitch.c optional rlswitch
# XXX rue only?
dev/mii/ruephy.c optional miibus | ruephy
+dev/mii/smcphy.c optional miibus | smcphy
dev/mii/tdkphy.c optional miibus | tdkphy
dev/mii/tlphy.c optional miibus | tlphy
dev/mii/ukphy.c optional miibus | mii
@@ -1104,6 +1105,7 @@ dev/smbus/smb.c optional smb
dev/smbus/smbconf.c optional smbus
dev/smbus/smbus.c optional smbus
dev/smbus/smbus_if.m optional smbus
+dev/smc/if_smc.c optional smc
dev/sn/if_sn.c optional sn
dev/sn/if_sn_isa.c optional sn isa
dev/sn/if_sn_pccard.c optional sn pccard
diff --git a/sys/dev/mii/miidevs b/sys/dev/mii/miidevs
index ac77d51..ff65217 100644
--- a/sys/dev/mii/miidevs
+++ b/sys/dev/mii/miidevs
@@ -67,6 +67,7 @@ oui QUALSEMI 0x006051 Quality Semiconductor
oui REALTEK 0x000020 RealTek Semicondctor
oui SEEQ 0x00a07d Seeq
oui SIS 0x00e006 Silicon Integrated Systems
+oui SMSC 0x0005be SMSC
oui TDK 0x00c039 TDK
oui TI 0x080028 Texas Instruments
oui VITESSE 0x0001c1 Vitesse Semiconductor
@@ -209,6 +210,9 @@ model xxSEEQ 84220 0x0004 Seeq 84220 10/100 media interface
/* Silicon Integrated Systems PHYs */
model xxSIS 900 0x0000 SiS 900 10/100 media interface
+/* SMSC PHYs */
+model SMSC LAN83C183 0x0004 SMSC LAN83C183 10/100 media interface
+
/* TDK */
model TDK 78Q2120 0x0014 TDK 78Q2120 media interface
diff --git a/sys/dev/mii/smcphy.c b/sys/dev/mii/smcphy.c
new file mode 100644
index 0000000..36771e5
--- /dev/null
+++ b/sys/dev/mii/smcphy.c
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 2006 Benno Rice. 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.
+ * 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 ``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 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for the internal PHY on the SMSC LAN91C111.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include "miidevs.h"
+
+#include "miibus_if.h"
+
+static int smcphy_probe(device_t);
+static int smcphy_attach(device_t);
+
+static int smcphy_service(struct mii_softc *, struct mii_data *, int);
+static int smcphy_reset(struct mii_softc *);
+static void smcphy_auto(struct mii_softc *);
+
+static device_method_t smcphy_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, smcphy_probe),
+ DEVMETHOD(device_attach, smcphy_attach),
+ DEVMETHOD(device_detach, mii_phy_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ { 0, 0 }
+};
+
+static devclass_t smcphy_devclass;
+
+static driver_t smcphy_driver = {
+ "smcphy",
+ smcphy_methods,
+ sizeof(struct mii_softc)
+};
+
+DRIVER_MODULE(smcphy, miibus, smcphy_driver, smcphy_devclass, 0, 0);
+
+static int
+smcphy_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_SMSC ||
+ MII_MODEL(ma->mii_id2) != MII_MODEL_SMSC_LAN83C183)
+ return (ENXIO);
+
+ device_set_desc(dev, MII_STR_SMSC_LAN83C183);
+
+ return (0);
+}
+
+static int
+smcphy_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);
+
+ sc->mii_inst = mii->mii_instance;
+ sc->mii_phy = ma->mii_phyno;
+ sc->mii_service = smcphy_service;
+ sc->mii_pdata = mii;
+
+ mii->mii_instance++;
+
+ sc->mii_flags |= MIIF_NOISOLATE;
+
+ if (smcphy_reset(sc) != 0) {
+ device_printf(dev, "reset failed\n");
+ }
+
+ /* Mask interrupts, we poll instead. */
+ PHY_WRITE(sc, 0x1e, 0xffc0);
+
+ sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
+ device_printf(dev, " ");
+ mii_add_media(sc);
+ printf("\n");
+
+ MIIBUS_MEDIAINIT(sc->mii_dev);
+ mii_phy_setmedia(sc);
+
+ return (0);
+}
+
+static int
+smcphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd)
+{
+ struct ifmedia_entry *ife;
+ int reg;
+
+ ife = mii->mii_media.ifm_cur;
+
+ 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:
+ smcphy_auto(sc);
+ break;
+
+ default:
+ mii_phy_setmedia(sc);
+ break;
+ }
+
+ break;
+
+ case MII_TICK:
+ /*
+ * If we're not currently selected, just return.
+ */
+ if (IFM_INST(ife->ifm_media) != sc->mii_inst) {
+ return (0);
+ }
+
+ if ((mii->mii_ifp->if_flags & IFF_UP) == 0) {
+ return (0);
+ }
+
+ if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) {
+ break;
+ }
+
+ /* I have no idea why BMCR_ISO gets set. */
+ reg = PHY_READ(sc, MII_BMCR);
+ if (reg & BMCR_ISO) {
+ PHY_WRITE(sc, MII_BMCR, reg & ~BMCR_ISO);
+ }
+
+ reg = PHY_READ(sc, MII_BMSR) | PHY_READ(sc, MII_BMSR);
+ if (reg & BMSR_LINK) {
+ sc->mii_ticks = 0;
+ break;
+ }
+
+ if (++sc->mii_ticks <= MII_ANEGTICKS) {
+ break;
+ }
+
+ sc->mii_ticks = 0;
+ if (smcphy_reset(sc) != 0) {
+ device_printf(sc->mii_dev, "reset failed\n");
+ }
+ smcphy_auto(sc);
+ break;
+ }
+
+ /* Update the media status. */
+ ukphy_status(sc);
+
+ /* Callback if something changed. */
+ mii_phy_update(sc, cmd);
+ return (0);
+}
+
+static int
+smcphy_reset(struct mii_softc *sc)
+{
+ u_int bmcr;
+ int timeout;
+
+ PHY_WRITE(sc, MII_BMCR, BMCR_RESET);
+
+ for (timeout = 2; timeout > 0; timeout--) {
+ DELAY(50000);
+ bmcr = PHY_READ(sc, MII_BMCR);
+ if ((bmcr & BMCR_RESET) == 0)
+ break;
+ }
+
+ if (bmcr & BMCR_RESET) {
+ return (EIO);
+ }
+
+ PHY_WRITE(sc, MII_BMCR, 0x3000);
+ return (0);
+}
+
+static void
+smcphy_auto(struct mii_softc *sc)
+{
+ uint16_t anar;
+
+ anar = BMSR_MEDIA_TO_ANAR(sc->mii_capabilities) |
+ ANAR_CSMA;
+ if (sc->mii_flags & MIIF_DOPAUSE)
+ anar |= ANAR_FC;
+ PHY_WRITE(sc, MII_ANAR, anar);
+ /* Apparently this helps. */
+ anar = PHY_READ(sc, MII_ANAR);
+ PHY_WRITE(sc, MII_BMCR, BMCR_AUTOEN | BMCR_STARTNEG);
+}
diff --git a/sys/dev/smc/if_smc.c b/sys/dev/smc/if_smc.c
new file mode 100644
index 0000000..c7657ac
--- /dev/null
+++ b/sys/dev/smc/if_smc.c
@@ -0,0 +1,1312 @@
+/*-
+ * Copyright (c) 2006 Benno Rice. 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.
+ * 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 ``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 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for SMSC LAN91C111, may work for older variants.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include "opt_device_polling.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/sockio.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/taskqueue.h>
+
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/if_mib.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+
+#include <dev/smc/if_smcreg.h>
+#include <dev/smc/if_smcvar.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+devclass_t smc_devclass;
+
+static const char *smc_chip_ids[16] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMSC LAN91C90 or LAN91C92",
+ /* 4 */ "SMSC LAN91C94",
+ /* 5 */ "SMSC LAN91C95",
+ /* 6 */ "SMSC LAN91C96",
+ /* 7 */ "SMSC LAN91C100",
+ /* 8 */ "SMSC LAN91C100FD",
+ /* 9 */ "SMSC LAN91C110FD or LAN91C111FD",
+ NULL, NULL, NULL,
+ NULL, NULL, NULL
+};
+
+static void smc_init(void *);
+static void smc_start(struct ifnet *);
+static int smc_ioctl(struct ifnet *, u_long, caddr_t);
+
+static void smc_init_locked(struct smc_softc *);
+static void smc_start_locked(struct ifnet *);
+static void smc_reset(struct smc_softc *);
+static int smc_mii_ifmedia_upd(struct ifnet *);
+static void smc_mii_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static void smc_mii_tick(void *);
+static void smc_mii_mediachg(struct smc_softc *);
+static int smc_mii_mediaioctl(struct smc_softc *, struct ifreq *, u_long);
+
+static void smc_task_rx(void *, int);
+static void smc_task_tx(void *, int);
+
+static driver_filter_t smc_intr;
+static timeout_t smc_watchdog;
+#ifdef DEVICE_POLLING
+static poll_handler_t smc_poll;
+#endif
+
+static __inline void
+smc_select_bank(struct smc_softc *sc, uint16_t bank)
+{
+
+ bus_space_write_2(sc->smc_bst, sc->smc_bsh, BSR, bank & BSR_BANK_MASK);
+}
+
+/* Never call this when not in bank 2. */
+static __inline void
+smc_mmu_wait(struct smc_softc *sc)
+{
+
+ KASSERT((bus_space_read_2(sc->smc_bst, sc->smc_bsh, BSR) &
+ BSR_BANK_MASK) == 2, ("%s: smc_mmu_wait called when not in bank 2",
+ device_get_nameunit(sc->smc_dev)));
+ while (bus_space_read_2(sc->smc_bst, sc->smc_bsh, MMUCR) & MMUCR_BUSY)
+ ;
+}
+
+static __inline uint8_t
+smc_read_1(struct smc_softc *sc, bus_addr_t offset)
+{
+
+ return (bus_space_read_1(sc->smc_bst, sc->smc_bsh, offset));
+}
+
+static __inline void
+smc_write_1(struct smc_softc *sc, bus_addr_t offset, uint8_t val)
+{
+
+ bus_space_write_1(sc->smc_bst, sc->smc_bsh, offset, val);
+}
+
+static __inline uint16_t
+smc_read_2(struct smc_softc *sc, bus_addr_t offset)
+{
+
+ return (bus_space_read_2(sc->smc_bst, sc->smc_bsh, offset));
+}
+
+static __inline void
+smc_write_2(struct smc_softc *sc, bus_addr_t offset, uint16_t val)
+{
+
+ bus_space_write_2(sc->smc_bst, sc->smc_bsh, offset, val);
+}
+
+static __inline void
+smc_read_multi_2(struct smc_softc *sc, bus_addr_t offset, uint16_t *datap,
+ bus_size_t count)
+{
+
+ bus_space_read_multi_2(sc->smc_bst, sc->smc_bsh, offset, datap, count);
+}
+
+static __inline void
+smc_write_multi_2(struct smc_softc *sc, bus_addr_t offset, uint16_t *datap,
+ bus_size_t count)
+{
+
+ bus_space_write_multi_2(sc->smc_bst, sc->smc_bsh, offset, datap, count);
+}
+
+int
+smc_probe(device_t dev)
+{
+ int rid, type, error;
+ uint16_t val;
+ struct smc_softc *sc;
+ struct resource *reg;
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+
+ sc = device_get_softc(dev);
+ rid = 0;
+ type = SYS_RES_IOPORT;
+ error = 0;
+
+ if (sc->smc_usemem)
+ type = SYS_RES_MEMORY;
+
+ reg = bus_alloc_resource(dev, type, &rid, 0, ~0, 16, RF_ACTIVE);
+ if (reg == NULL) {
+ if (bootverbose)
+ device_printf(dev,
+ "could not allocate I/O resource for probe\n");
+ return (ENXIO);
+ }
+
+ bst = rman_get_bustag(reg);
+ bsh = rman_get_bushandle(reg);
+
+ /* Check for the identification value in the BSR. */
+ val = bus_space_read_2(bst, bsh, BSR);
+ if ((val & BSR_IDENTIFY_MASK) != BSR_IDENTIFY) {
+ if (bootverbose)
+ device_printf(dev, "identification value not in BSR\n");
+ error = ENXIO;
+ goto done;
+ }
+
+ /*
+ * Try switching banks and make sure we still get the identification
+ * value.
+ */
+ bus_space_write_2(bst, bsh, BSR, 0);
+ val = bus_space_read_2(bst, bsh, BSR);
+ if ((val & BSR_IDENTIFY_MASK) != BSR_IDENTIFY) {
+ if (bootverbose)
+ device_printf(dev,
+ "identification value not in BSR after write\n");
+ error = ENXIO;
+ goto done;
+ }
+
+#if 0
+ /* Check the BAR. */
+ bus_space_write_2(bst, bsh, BSR, 1);
+ val = bus_space_read_2(bst, bsh, BAR);
+ val = BAR_ADDRESS(val);
+ if (rman_get_start(reg) != val) {
+ if (bootverbose)
+ device_printf(dev, "BAR address %x does not match "
+ "I/O resource address %lx\n", val,
+ rman_get_start(reg));
+ error = ENXIO;
+ goto done;
+ }
+#endif
+
+ /* Compare REV against known chip revisions. */
+ bus_space_write_2(bst, bsh, BSR, 3);
+ val = bus_space_read_2(bst, bsh, REV);
+ val = (val & REV_CHIP_MASK) >> REV_CHIP_SHIFT;
+ if (smc_chip_ids[val] == NULL) {
+ if (bootverbose)
+ device_printf(dev, "Unknown chip revision: %d\n", val);
+ error = ENXIO;
+ goto done;
+ }
+
+ device_set_desc(dev, smc_chip_ids[val]);
+
+done:
+ bus_release_resource(dev, type, rid, reg);
+ return (error);
+}
+
+int
+smc_attach(device_t dev)
+{
+ int type, error;
+ uint16_t val;
+ u_char eaddr[ETHER_ADDR_LEN];
+ struct smc_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ error = 0;
+
+ sc->smc_dev = dev;
+
+ /* Set up watchdog callout. */
+ callout_init(&sc->smc_watchdog, 1);
+
+ ifp = sc->smc_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ error = ENOSPC;
+ goto done;
+ }
+
+ mtx_init(&sc->smc_mtx, device_get_nameunit(dev), NULL, MTX_SPIN);
+
+ type = SYS_RES_IOPORT;
+ if (sc->smc_usemem)
+ type = SYS_RES_MEMORY;
+
+ sc->smc_reg_rid = 0;
+ sc->smc_reg = bus_alloc_resource(dev, type, &sc->smc_reg_rid, 0, ~0,
+ 16, RF_ACTIVE);
+ if (sc->smc_reg == NULL) {
+ error = ENXIO;
+ goto done;
+ }
+
+ sc->smc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->smc_irq_rid, 0,
+ ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (sc->smc_irq == NULL) {
+ error = ENXIO;
+ goto done;
+ }
+
+ sc->smc_bst = rman_get_bustag(sc->smc_reg);
+ sc->smc_bsh = rman_get_bushandle(sc->smc_reg);
+
+ SMC_LOCK(sc);
+ smc_reset(sc);
+ SMC_UNLOCK(sc);
+
+ smc_select_bank(sc, 3);
+ val = smc_read_2(sc, REV);
+ sc->smc_chip = (val & REV_CHIP_MASK) >> REV_CHIP_SHIFT;
+ sc->smc_rev = (val * REV_REV_MASK) >> REV_REV_SHIFT;
+ if (bootverbose)
+ device_printf(dev, "revision %x\n", sc->smc_rev);
+
+ callout_init(&sc->smc_mii_tick_ch, 1);
+ if (sc->smc_chip >= REV_CHIP_91110FD) {
+ mii_phy_probe(dev, &sc->smc_miibus, smc_mii_ifmedia_upd,
+ smc_mii_ifmedia_sts);
+ if (sc->smc_miibus != NULL) {
+ sc->smc_mii_tick = smc_mii_tick;
+ sc->smc_mii_mediachg = smc_mii_mediachg;
+ sc->smc_mii_mediaioctl = smc_mii_mediaioctl;
+ }
+ }
+
+ smc_select_bank(sc, 1);
+ eaddr[0] = smc_read_1(sc, IAR0);
+ eaddr[1] = smc_read_1(sc, IAR1);
+ eaddr[2] = smc_read_1(sc, IAR2);
+ eaddr[3] = smc_read_1(sc, IAR3);
+ eaddr[4] = smc_read_1(sc, IAR4);
+ eaddr[5] = smc_read_1(sc, IAR5);
+
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = smc_init;
+ ifp->if_ioctl = smc_ioctl;
+ ifp->if_start = smc_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ifp->if_capabilities = ifp->if_capenable = 0;
+
+#ifdef DEVICE_POLLING
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+
+ ether_ifattach(ifp, eaddr);
+
+ /* Set up taskqueue */
+ TASK_INIT(&sc->smc_rx, SMC_RX_PRIORITY, smc_task_rx, ifp);
+ TASK_INIT(&sc->smc_tx, SMC_TX_PRIORITY, smc_task_tx, ifp);
+ sc->smc_tq = taskqueue_create_fast("smc_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &sc->smc_tq);
+ taskqueue_start_threads(&sc->smc_tq, 1, PI_NET, "%s taskq",
+ device_get_nameunit(sc->smc_dev));
+
+ /* Mask all interrupts. */
+ sc->smc_mask = 0;
+ smc_write_1(sc, MSK, 0);
+
+ /* Wire up interrupt */
+ error = bus_setup_intr(dev, sc->smc_irq,
+ INTR_TYPE_NET|INTR_MPSAFE, smc_intr, NULL, ifp, &sc->smc_ih);
+ if (error != 0)
+ goto done;
+
+done:
+ if (error != 0)
+ smc_detach(dev);
+ return (error);
+}
+
+int
+smc_detach(device_t dev)
+{
+ int type;
+ struct smc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ callout_stop(&sc->smc_watchdog);
+
+ if (mtx_initialized(&sc->smc_mtx))
+ SMC_LOCK(sc);
+
+#ifdef DEVICE_POLLING
+ if (sc->smc_ifp->if_capenable & IFCAP_POLLING)
+ ether_poll_deregister(sc->smc_ifp);
+#endif
+
+ if (sc->smc_ih != NULL)
+ bus_teardown_intr(sc->smc_dev, sc->smc_irq, sc->smc_ih);
+
+ if (sc->smc_ifp != NULL) {
+ ether_ifdetach(sc->smc_ifp);
+ if_free(sc->smc_ifp);
+ }
+
+ if (sc->smc_miibus != NULL) {
+ device_delete_child(sc->smc_dev, sc->smc_miibus);
+ bus_generic_detach(sc->smc_dev);
+ }
+
+ if (sc->smc_reg != NULL) {
+ type = SYS_RES_IOPORT;
+ if (sc->smc_usemem)
+ type = SYS_RES_MEMORY;
+
+ bus_release_resource(sc->smc_dev, type, sc->smc_reg_rid,
+ sc->smc_reg);
+ }
+
+ if (sc->smc_irq != NULL)
+ bus_release_resource(sc->smc_dev, SYS_RES_IRQ, sc->smc_irq_rid,
+ sc->smc_irq);
+
+ if (sc->smc_tq != NULL)
+ taskqueue_free(sc->smc_tq);
+
+ if (mtx_initialized(&sc->smc_mtx))
+ mtx_destroy(&sc->smc_mtx);
+
+ return (0);
+}
+
+static void
+smc_start(struct ifnet *ifp)
+{
+ struct smc_softc *sc;
+
+ sc = ifp->if_softc;
+ SMC_LOCK(sc);
+ smc_start_locked(ifp);
+ SMC_UNLOCK(sc);
+}
+
+static void
+smc_start_locked(struct ifnet *ifp)
+{
+ struct smc_softc *sc;
+ struct mbuf *m;
+ u_int len, npages, spin_count;
+
+ sc = ifp->if_softc;
+ SMC_ASSERT_LOCKED(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
+ return;
+ if (IFQ_IS_EMPTY(&ifp->if_snd))
+ return;
+
+ /*
+ * Grab the next packet. If it's too big, drop it.
+ */
+ SMC_UNLOCK(sc);
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ SMC_LOCK(sc);
+ len = m_length(m, NULL);
+ len += (len & 1);
+ if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) {
+ if_printf(ifp, "large packet discarded\n");
+ ++ifp->if_oerrors;
+ m_freem(m);
+ return; /* XXX readcheck? */
+ }
+
+ /*
+ * Flag that we're busy.
+ */
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ sc->smc_pending = m;
+
+ /*
+ * Work out how many 256 byte "pages" we need. We have to include the
+ * control data for the packet in this calculation.
+ */
+ npages = (len * PKT_CTRL_DATA_LEN) >> 8;
+ if (npages == 0)
+ npages = 1;
+
+ /*
+ * Request memory.
+ */
+ smc_select_bank(sc, 2);
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_TX_ALLOC | npages);
+
+ /*
+ * Spin briefly to see if the allocation succeeds.
+ */
+ spin_count = TX_ALLOC_WAIT_TIME;
+ do {
+ if (smc_read_1(sc, IST) & ALLOC_INT) {
+ smc_write_1(sc, ACK, ALLOC_INT);
+ break;
+ }
+ } while (--spin_count);
+
+ /*
+ * If the allocation is taking too long, unmask the alloc interrupt
+ * and wait.
+ */
+ if (spin_count == 0) {
+ sc->smc_mask |= ALLOC_INT;
+ if ((ifp->if_capenable & IFCAP_POLLING) == 0)
+ smc_write_1(sc, MSK, sc->smc_mask);
+ return;
+ }
+
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx);
+}
+
+static void
+smc_task_tx(void *context, int pending)
+{
+ struct ifnet *ifp;
+ struct smc_softc *sc;
+ struct mbuf *m, *m0;
+ u_int packet, len;
+ uint8_t *data;
+
+ (void)pending;
+ ifp = (struct ifnet *)context;
+ sc = ifp->if_softc;
+
+ SMC_LOCK(sc);
+
+ if (sc->smc_pending == NULL) {
+ SMC_UNLOCK(sc);
+ goto next_packet;
+ }
+
+ m = m0 = sc->smc_pending;
+ sc->smc_pending = NULL;
+ smc_select_bank(sc, 2);
+
+ /*
+ * Check the allocation result.
+ */
+ packet = smc_read_1(sc, ARR);
+
+ /*
+ * If the allocation failed, requeue the packet and retry.
+ */
+ if (packet & ARR_FAILED) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ++ifp->if_oerrors;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ smc_start_locked(ifp);
+ SMC_UNLOCK(sc);
+ return;
+ }
+
+ /*
+ * Tell the device to write to our packet number.
+ */
+ smc_write_1(sc, PNR, packet);
+ smc_write_2(sc, PTR, 0 | PTR_AUTO_INCR);
+
+ /*
+ * Tell the device how long the packet is (including control data).
+ */
+ len = m_length(m, 0);
+ len += PKT_CTRL_DATA_LEN;
+ smc_write_2(sc, DATA0, 0);
+ smc_write_2(sc, DATA0, len);
+
+ /*
+ * Push the data out to the device.
+ */
+ data = NULL;
+ for (; m != NULL; m = m->m_next) {
+ data = mtod(m, uint8_t *);
+ smc_write_multi_2(sc, DATA0, (uint16_t *)data, m->m_len / 2);
+ }
+
+ /*
+ * Push out the control byte and and the odd byte if needed.
+ */
+ if ((len & 1) != 0 && data != NULL)
+ smc_write_2(sc, DATA0, (CTRL_ODD << 8) | data[m->m_len - 1]);
+ else
+ smc_write_2(sc, DATA0, 0);
+
+ /*
+ * Unmask the TX empty interrupt.
+ */
+ sc->smc_mask |= TX_EMPTY_INT;
+ if ((ifp->if_capenable & IFCAP_POLLING) == 0)
+ smc_write_1(sc, MSK, sc->smc_mask);
+
+ /*
+ * Enqueue the packet.
+ */
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_ENQUEUE);
+ callout_reset(&sc->smc_watchdog, hz * 2, smc_watchdog, ifp);
+
+ /*
+ * Finish up.
+ */
+ ifp->if_opackets++;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ SMC_UNLOCK(sc);
+ BPF_MTAP(ifp, m0);
+ m_freem(m0);
+
+next_packet:
+ /*
+ * See if there's anything else to do.
+ */
+ smc_start(ifp);
+}
+
+static void
+smc_task_rx(void *context, int pending)
+{
+ u_int packet, status, len;
+ uint8_t *data;
+ struct ifnet *ifp;
+ struct smc_softc *sc;
+ struct mbuf *m, *mhead, *mtail;
+
+ (void)pending;
+ ifp = (struct ifnet *)context;
+ sc = ifp->if_softc;
+ mhead = mtail = NULL;
+
+ SMC_LOCK(sc);
+
+ packet = smc_read_1(sc, FIFO_RX);
+ while ((packet & FIFO_EMPTY) == 0) {
+ /*
+ * Grab an mbuf and attach a cluster.
+ */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ break;
+ }
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_freem(m);
+ break;
+ }
+
+ /*
+ * Point to the start of the packet.
+ */
+ smc_select_bank(sc, 2);
+ smc_write_1(sc, PNR, packet);
+ smc_write_2(sc, PTR, 0 | PTR_READ | PTR_RCV | PTR_AUTO_INCR);
+
+ /*
+ * Grab status and packet length.
+ */
+ status = smc_read_2(sc, DATA0);
+ len = smc_read_2(sc, DATA0) & RX_LEN_MASK;
+ len -= 6;
+ if (status & RX_ODDFRM)
+ len += 1;
+
+ /*
+ * Check for errors.
+ */
+ if (status & (RX_TOOSHORT | RX_TOOLNG | RX_BADCRC | RX_ALGNERR)) {
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE);
+ ifp->if_ierrors++;
+ m_freem(m);
+ break;
+ }
+
+ /*
+ * Set the mbuf up the way we want it.
+ */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len + 2; /* XXX: Is this right? */
+ m_adj(m, ETHER_ALIGN);
+
+ /*
+ * Pull the packet out of the device. Make sure we're in the
+ * right bank first as things may have changed while we were
+ * allocating our mbuf.
+ */
+ smc_select_bank(sc, 2);
+ smc_write_1(sc, PNR, packet);
+ smc_write_2(sc, PTR, 4 | PTR_READ | PTR_RCV | PTR_AUTO_INCR);
+ data = mtod(m, uint8_t *);
+ smc_read_multi_2(sc, DATA0, (uint16_t *)data, len >> 1);
+ if (len & 1) {
+ data += len & ~1;
+ *data = smc_read_1(sc, DATA0);
+ }
+
+ /*
+ * Tell the device we're done.
+ */
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE);
+ if (m == NULL) {
+ break;
+ }
+
+ if (mhead == NULL) {
+ mhead = mtail = m;
+ m->m_next = NULL;
+ } else {
+ mtail->m_next = m;
+ mtail = m;
+ }
+ packet = smc_read_1(sc, FIFO_RX);
+ }
+
+ sc->smc_mask |= RCV_INT;
+ if ((ifp->if_capenable & IFCAP_POLLING) == 0)
+ smc_write_1(sc, MSK, sc->smc_mask);
+
+ SMC_UNLOCK(sc);
+
+ while (mhead != NULL) {
+ m = mhead;
+ mhead = mhead->m_next;
+ m->m_next = NULL;
+ ifp->if_ipackets++;
+ (*ifp->if_input)(ifp, m);
+ }
+}
+
+#ifdef DEVICE_POLLING
+static void
+smc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct smc_softc *sc;
+
+ sc = ifp->if_softc;
+
+ SMC_LOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ SMC_UNLOCK(sc);
+ return;
+ }
+ SMC_UNLOCK(sc);
+
+ if (cmd == POLL_AND_CHECK_STATUS)
+ smc_intr(ifp);
+}
+#endif
+
+static int
+smc_intr(void *context)
+{
+ struct smc_softc *sc;
+ struct ifnet *ifp;
+ u_int status, packet, counter, tcr;
+
+ ifp = (struct ifnet *)context;
+ sc = ifp->if_softc;
+
+ SMC_LOCK(sc);
+ smc_select_bank(sc, 2);
+
+ /*
+ * Get the current mask, and then block all interrupts while we're
+ * working.
+ */
+ if ((ifp->if_capenable & IFCAP_POLLING) == 0)
+ smc_write_1(sc, MSK, 0);
+
+ /*
+ * Find out what interrupts are flagged.
+ */
+ status = smc_read_1(sc, IST) & sc->smc_mask;
+
+ /*
+ * Transmit error
+ */
+ if (status & TX_INT) {
+ /*
+ * Kill off the packet if there is one and re-enable transmit.
+ */
+ packet = smc_read_1(sc, FIFO_TX);
+ if ((packet & FIFO_EMPTY) == 0) {
+ smc_write_1(sc, PNR, packet);
+ smc_write_2(sc, PTR, 0 | PTR_READ |
+ PTR_AUTO_INCR);
+ tcr = smc_read_2(sc, DATA0);
+ if ((tcr & EPHSR_TX_SUC) == 0)
+ device_printf(sc->smc_dev,
+ "bad packet\n");
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE_PKT);
+
+ smc_select_bank(sc, 0);
+ tcr = smc_read_2(sc, TCR);
+ tcr |= TCR_TXENA | TCR_PAD_EN;
+ smc_write_2(sc, TCR, tcr);
+ smc_select_bank(sc, 2);
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx);
+ }
+
+ /*
+ * Ack the interrupt.
+ */
+ smc_write_1(sc, ACK, TX_INT);
+ }
+
+ /*
+ * Receive
+ */
+ if (status & RCV_INT) {
+ smc_write_1(sc, ACK, RCV_INT);
+ sc->smc_mask &= ~RCV_INT;
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_rx);
+ }
+
+ /*
+ * Allocation
+ */
+ if (status & ALLOC_INT) {
+ smc_write_1(sc, ACK, ALLOC_INT);
+ sc->smc_mask &= ~ALLOC_INT;
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx);
+ }
+
+ /*
+ * Receive overrun
+ */
+ if (status & RX_OVRN_INT) {
+ smc_write_1(sc, ACK, RX_OVRN_INT);
+ ifp->if_ierrors++;
+ }
+
+ /*
+ * Transmit empty
+ */
+ if (status & TX_EMPTY_INT) {
+ smc_write_1(sc, ACK, TX_EMPTY_INT);
+ sc->smc_mask &= ~TX_EMPTY_INT;
+ callout_stop(&sc->smc_watchdog);
+
+ /*
+ * Update collision stats.
+ */
+ smc_select_bank(sc, 0);
+ counter = smc_read_2(sc, ECR);
+ smc_select_bank(sc, 2);
+ ifp->if_collisions +=
+ (counter & ECR_SNGLCOL_MASK) >> ECR_SNGLCOL_SHIFT;
+ ifp->if_collisions +=
+ (counter & ECR_MULCOL_MASK) >> ECR_MULCOL_SHIFT;
+
+ /*
+ * See if there are any packets to transmit.
+ */
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx);
+ }
+
+ /*
+ * Update the interrupt mask.
+ */
+ if ((ifp->if_capenable & IFCAP_POLLING) == 0)
+ smc_write_1(sc, MSK, sc->smc_mask);
+
+ SMC_UNLOCK(sc);
+
+ return (FILTER_HANDLED);
+}
+
+static u_int
+smc_mii_readbits(struct smc_softc *sc, int nbits)
+{
+ u_int mgmt, mask, val;
+
+ SMC_ASSERT_LOCKED(sc);
+ KASSERT((smc_read_2(sc, BSR) & BSR_BANK_MASK) == 3,
+ ("%s: smc_mii_readbits called with bank %d (!= 3)",
+ device_get_nameunit(sc->smc_dev),
+ smc_read_2(sc, BSR) & BSR_BANK_MASK));
+
+ /*
+ * Set up the MGMT (aka MII) register.
+ */
+ mgmt = smc_read_2(sc, MGMT) & ~(MGMT_MCLK | MGMT_MDOE | MGMT_MDO);
+ smc_write_2(sc, MGMT, mgmt);
+
+ /*
+ * Read the bits in.
+ */
+ for (mask = 1 << (nbits - 1), val = 0; mask; mask >>= 1) {
+ if (smc_read_2(sc, MGMT) & MGMT_MDI)
+ val |= mask;
+
+ smc_write_2(sc, MGMT, mgmt);
+ DELAY(1);
+ smc_write_2(sc, MGMT, mgmt | MGMT_MCLK);
+ DELAY(1);
+ }
+
+ return (val);
+}
+
+static void
+smc_mii_writebits(struct smc_softc *sc, u_int val, int nbits)
+{
+ u_int mgmt, mask;
+
+ SMC_ASSERT_LOCKED(sc);
+ KASSERT((smc_read_2(sc, BSR) & BSR_BANK_MASK) == 3,
+ ("%s: smc_mii_writebits called with bank %d (!= 3)",
+ device_get_nameunit(sc->smc_dev),
+ smc_read_2(sc, BSR) & BSR_BANK_MASK));
+
+ /*
+ * Set up the MGMT (aka MII) register).
+ */
+ mgmt = smc_read_2(sc, MGMT) & ~(MGMT_MCLK | MGMT_MDOE | MGMT_MDO);
+ mgmt |= MGMT_MDOE;
+
+ /*
+ * Push the bits out.
+ */
+ for (mask = 1 << (nbits - 1); mask; mask >>= 1) {
+ if (val & mask)
+ mgmt |= MGMT_MDO;
+ else
+ mgmt &= ~MGMT_MDO;
+
+ smc_write_2(sc, MGMT, mgmt);
+ DELAY(1);
+ smc_write_2(sc, MGMT, mgmt | MGMT_MCLK);
+ DELAY(1);
+ }
+}
+
+int
+smc_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct smc_softc *sc;
+ int val;
+
+ sc = device_get_softc(dev);
+
+ SMC_LOCK(sc);
+
+ smc_select_bank(sc, 3);
+
+ /*
+ * Send out the idle pattern.
+ */
+ smc_mii_writebits(sc, 0xffffffff, 32);
+
+ /*
+ * Start code + read opcode + phy address + phy register
+ */
+ smc_mii_writebits(sc, 6 << 10 | phy << 5 | reg, 14);
+
+ /*
+ * Turnaround + data
+ */
+ val = smc_mii_readbits(sc, 18);
+
+ /*
+ * Reset the MDIO interface.
+ */
+ smc_write_2(sc, MGMT,
+ smc_read_2(sc, MGMT) & ~(MGMT_MCLK | MGMT_MDOE | MGMT_MDO));
+
+ SMC_UNLOCK(sc);
+ return (val);
+}
+
+void
+smc_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct smc_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ SMC_LOCK(sc);
+
+ smc_select_bank(sc, 3);
+
+ /*
+ * Send idle pattern.
+ */
+ smc_mii_writebits(sc, 0xffffffff, 32);
+
+ /*
+ * Start code + write opcode + phy address + phy register + turnaround
+ * + data.
+ */
+ smc_mii_writebits(sc, 5 << 28 | phy << 23 | reg << 18 | 2 << 16 | data,
+ 32);
+
+ /*
+ * Reset MDIO interface.
+ */
+ smc_write_2(sc, MGMT,
+ smc_read_2(sc, MGMT) & ~(MGMT_MCLK | MGMT_MDOE | MGMT_MDO));
+
+ SMC_UNLOCK(sc);
+}
+
+void
+smc_miibus_statchg(device_t dev)
+{
+ struct smc_softc *sc;
+ struct mii_data *mii;
+ uint16_t tcr;
+
+ sc = device_get_softc(dev);
+ mii = device_get_softc(sc->smc_miibus);
+
+ SMC_LOCK(sc);
+
+ smc_select_bank(sc, 0);
+ tcr = smc_read_2(sc, TCR);
+
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ tcr |= TCR_SWFDUP;
+ else
+ tcr &= ~TCR_SWFDUP;
+
+ smc_write_2(sc, TCR, tcr);
+
+ SMC_UNLOCK(sc);
+}
+
+static int
+smc_mii_ifmedia_upd(struct ifnet *ifp)
+{
+ struct smc_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ if (sc->smc_miibus == NULL)
+ return (ENXIO);
+
+ mii = device_get_softc(sc->smc_miibus);
+ return (mii_mediachg(mii));
+}
+
+static void
+smc_mii_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct smc_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+ if (sc->smc_miibus == NULL)
+ return;
+
+ mii = device_get_softc(sc->smc_miibus);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+smc_mii_tick(void *context)
+{
+ struct smc_softc *sc;
+
+ sc = (struct smc_softc *)context;
+
+ if (sc->smc_miibus == NULL)
+ return;
+
+ mii_tick(device_get_softc(sc->smc_miibus));
+ callout_reset(&sc->smc_mii_tick_ch, hz, smc_mii_tick, sc);
+}
+
+static void
+smc_mii_mediachg(struct smc_softc *sc)
+{
+
+ if (sc->smc_miibus == NULL)
+ return;
+ mii_mediachg(device_get_softc(sc->smc_miibus));
+}
+
+static int
+smc_mii_mediaioctl(struct smc_softc *sc, struct ifreq *ifr, u_long command)
+{
+ struct mii_data *mii;
+
+ if (sc->smc_miibus == NULL)
+ return (EINVAL);
+
+ mii = device_get_softc(sc->smc_miibus);
+ return (ifmedia_ioctl(sc->smc_ifp, ifr, &mii->mii_media, command));
+}
+
+static void
+smc_reset(struct smc_softc *sc)
+{
+ u_int ctr;
+
+ SMC_ASSERT_LOCKED(sc);
+
+ smc_select_bank(sc, 2);
+
+ /*
+ * Mask all interrupts.
+ */
+ smc_write_1(sc, MSK, 0);
+
+ /*
+ * Tell the device to reset.
+ */
+ smc_select_bank(sc, 0);
+ smc_write_2(sc, RCR, RCR_SOFT_RST);
+
+ /*
+ * Set up the configuration register.
+ */
+ smc_select_bank(sc, 1);
+ smc_write_2(sc, CR, CR_EPH_POWER_EN);
+ DELAY(1);
+
+ /*
+ * Turn off transmit and receive.
+ */
+ smc_select_bank(sc, 0);
+ smc_write_2(sc, TCR, 0);
+ smc_write_2(sc, RCR, 0);
+
+ /*
+ * Set up the control register.
+ */
+ smc_select_bank(sc, 1);
+ ctr = smc_read_2(sc, CTR);
+ ctr |= CTR_LE_ENABLE | CTR_AUTO_RELEASE;
+ smc_write_2(sc, CTR, ctr);
+
+ /*
+ * Reset the MMU.
+ */
+ smc_select_bank(sc, 2);
+ smc_mmu_wait(sc);
+ smc_write_2(sc, MMUCR, MMUCR_CMD_MMU_RESET);
+}
+
+static void
+smc_enable(struct smc_softc *sc)
+{
+ struct ifnet *ifp;
+
+ SMC_ASSERT_LOCKED(sc);
+ ifp = sc->smc_ifp;
+
+ /*
+ * Set up the receive/PHY control register.
+ */
+ smc_select_bank(sc, 0);
+ smc_write_2(sc, RPCR, RPCR_ANEG | (RPCR_LED_LINK_ANY << RPCR_LSA_SHIFT)
+ | (RPCR_LED_ACT_ANY << RPCR_LSB_SHIFT));
+
+ /*
+ * Set up the transmit and receive control registers.
+ */
+ smc_write_2(sc, TCR, TCR_TXENA | TCR_PAD_EN);
+ smc_write_2(sc, RCR, RCR_RXEN | RCR_STRIP_CRC);
+
+ /*
+ * Set up the interrupt mask.
+ */
+ smc_select_bank(sc, 2);
+ sc->smc_mask = EPH_INT | RX_OVRN_INT | RCV_INT | TX_INT;
+ if ((ifp->if_capenable & IFCAP_POLLING) != 0)
+ smc_write_1(sc, MSK, sc->smc_mask);
+}
+
+static void
+smc_stop(struct smc_softc *sc)
+{
+
+ SMC_ASSERT_LOCKED(sc);
+
+ /*
+ * Turn off watchdog.
+ */
+ callout_stop(&sc->smc_watchdog);
+
+ /*
+ * Mask all interrupts.
+ */
+ smc_select_bank(sc, 2);
+ sc->smc_mask = 0;
+ smc_write_1(sc, MSK, 0);
+#ifdef DEVICE_POLLING
+ ether_poll_deregister(sc->smc_ifp);
+ sc->smc_ifp->if_capenable &= ~IFCAP_POLLING;
+#endif
+
+ /*
+ * Disable transmit and receive.
+ */
+ smc_select_bank(sc, 0);
+ smc_write_2(sc, TCR, 0);
+ smc_write_2(sc, RCR, 0);
+
+ sc->smc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+}
+
+static void
+smc_watchdog(void *arg)
+{
+ struct ifnet *ifp;
+
+ ifp = (struct ifnet *)arg;
+ if_printf(ifp, "watchdog timeout\n");
+ smc_intr(ifp);
+}
+
+static void
+smc_init(void *context)
+{
+ struct smc_softc *sc;
+
+ sc = (struct smc_softc *)context;
+ SMC_LOCK(sc);
+ smc_init_locked(sc);
+ SMC_UNLOCK(sc);
+}
+
+static void
+smc_init_locked(struct smc_softc *sc)
+{
+ struct ifnet *ifp;
+
+ ifp = sc->smc_ifp;
+
+ SMC_ASSERT_LOCKED(sc);
+
+ smc_reset(sc);
+ smc_enable(sc);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ smc_start_locked(ifp);
+
+ if (sc->smc_mii_tick != NULL)
+ callout_reset(&sc->smc_mii_tick_ch, hz, sc->smc_mii_tick, sc);
+
+#ifdef DEVICE_POLLING
+ SMC_UNLOCK(sc);
+ ether_poll_register(smc_poll, ifp);
+ SMC_LOCK(sc);
+ ifp->if_capenable |= IFCAP_POLLING;
+#endif
+}
+
+static int
+smc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct smc_softc *sc;
+ int error;
+
+ sc = ifp->if_softc;
+ error = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
+ SMC_LOCK(sc);
+ smc_stop(sc);
+ SMC_UNLOCK(sc);
+ } else {
+ smc_init(sc);
+ if (sc->smc_mii_mediachg != NULL)
+ sc->smc_mii_mediachg(sc);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ /* XXX
+ SMC_LOCK(sc);
+ smc_setmcast(sc);
+ SMC_UNLOCK(sc);
+ */
+ error = EINVAL;
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ if (sc->smc_mii_mediaioctl == NULL) {
+ error = EINVAL;
+ break;
+ }
+ sc->smc_mii_mediaioctl(sc, (struct ifreq *)data, cmd);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+
+ return (error);
+}
diff --git a/sys/dev/smc/if_smcreg.h b/sys/dev/smc/if_smcreg.h
new file mode 100644
index 0000000..4b93907
--- /dev/null
+++ b/sys/dev/smc/if_smcreg.h
@@ -0,0 +1,261 @@
+/*-
+ * Copyright (c) 2006 Benno Rice. 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.
+ * 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 ``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 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$
+ *
+ */
+
+#ifndef _IF_SMCREG_H_
+#define _IF_SMCREG_H_
+
+/* All Banks, Offset 0xe: Bank Select Register */
+#define BSR 0xe
+#define BSR_BANK_MASK 0x0007 /* Which bank is currently selected */
+#define BSR_IDENTIFY 0x3300 /* Static value for identification */
+#define BSR_IDENTIFY_MASK 0xff00
+
+/* Bank 0, Offset 0x0: Transmit Control Register */
+#define TCR 0x0
+#define TCR_TXENA 0x0001 /* Enable/disable transmitter */
+#define TCR_LOOP 0x0002 /* Put the PHY into loopback mode */
+#define TCR_FORCOL 0x0004 /* Force a collision */
+#define TCR_PAD_EN 0x0080 /* Pad TX frames to 64 bytes */
+#define TCR_NOCRC 0x0100 /* Disable/enable CRC */
+#define TCR_MON_CSN 0x0400 /* Monitor carrier signal */
+#define TCR_FDUPLX 0x0800 /* Enable/disable full duplex */
+#define TCR_STP_SQET 0x1000 /* Stop TX on signal quality error */
+#define TCR_EPH_LOOP 0x2000 /* Internal loopback */
+#define TCR_SWFDUP 0x8000 /* Switched full duplex */
+
+/* Bank 0, Offset 0x2: EPH Status Register */
+#define EPHSR 0x2
+#define EPHSR_TX_SUC 0x0001 /* Last TX was successful */
+#define EPHSR_SNGLCOL 0x0002 /* Single collision on last TX */
+#define EPHSR_MULCOL 0x0004 /* Multiple collisions on last TX */
+#define EPHSR_LTX_MULT 0x0008 /* Last TX was multicast */
+#define EPHSR_16COL 0x0010 /* 16 collisions on last TX */
+#define EPHSR_SQET 0x0020 /* Signal quality error test */
+#define EPHSR_LTX_BRD 0x0040 /* Last TX was broadcast */
+#define EPHSR_TX_DEFR 0x0080 /* Transmit deferred */
+#define EPHSR_LATCOL 0x0200 /* Late collision on last TX */
+#define EPHSR_LOST_CARR 0x0400 /* Lost carrier sense */
+#define EPHSR_EXC_DEF 0x0800 /* Excessive deferral */
+#define EPHSR_CTR_ROL 0x1000 /* Counter rollover */
+#define EPHSR_LINK_OK 0x4000 /* Inverse of nLNK pin */
+#define EPHSR_TXUNRN 0x8000 /* Transmit underrun */
+
+/* Bank 0, Offset 0x4: Receive Control Register */
+#define RCR 0x4
+#define RCR_RX_ABORT 0x0001 /* RX aborted */
+#define RCR_PRMS 0x0002 /* Enable/disable promiscuous mode */
+#define RCR_ALMUL 0x0004 /* Accept all multicast frames */
+#define RCR_RXEN 0x0100 /* Enable/disable receiver */
+#define RCR_STRIP_CRC 0x0200 /* Strip CRC from RX packets */
+#define RCR_ABORT_ENB 0x2000 /* Abort RX on collision */
+#define RCR_FILT_CAR 0x4000 /* Filter leading 12 bits of carrier */
+#define RCR_SOFT_RST 0x8000 /* Software reset */
+
+/* Bank 0, Offset 0x6: Counter Register */
+#define ECR 0x6
+#define ECR_SNGLCOL_MASK 0x000f /* Single collisions */
+#define ECR_SNGLCOL_SHIFT 0
+#define ECR_MULCOL_MASK 0x00f0 /* Multiple collisions */
+#define ECR_MULCOL_SHIFT 4
+#define ECR_TX_DEFR_MASK 0x0f00 /* Transmit deferrals */
+#define ECR_TX_DEFR_SHIFT 8
+#define ECR_EXC_DEF_MASK 0xf000 /* Excessive deferrals */
+#define ECR_EXC_DEF_SHIFT 12
+
+/* Bank 0, Offset 0x8: Memory Information Register */
+#define MIR 0x8
+#define MIR_SIZE_MASK 0x00ff /* Memory size (2k pages) */
+#define MIR_SIZE_SHIFT 0
+#define MIR_FREE_MASK 0xff00 /* Memory free (2k pages) */
+#define MIR_FREE_SHIFT 8
+#define MIR_PAGE_SIZE 2048
+
+/* Bank 0, Offset 0xa: Receive/PHY Control Reigster */
+#define RPCR 0xa
+#define RPCR_ANEG 0x0800 /* Put PHY in autonegotiation mode */
+#define RPCR_DPLX 0x1000 /* Put PHY in full-duplex mode */
+#define RPCR_SPEED 0x2000 /* Manual speed selection */
+#define RPCR_LSA_MASK 0x00e0 /* Select LED A function */
+#define RPCR_LSA_SHIFT 5
+#define RPCR_LSB_MASK 0x001c /* Select LED B function */
+#define RPCR_LSB_SHIFT 2
+#define RPCR_LED_LINK_ANY 0x0 /* 10baseT or 100baseTX link detected */
+#define RPCR_LED_LINK_10 0x2 /* 10baseT link detected */
+#define RPCR_LED_LINK_FDX 0x3 /* Full-duplex link detected */
+#define RPCR_LED_LINK_100 0x5 /* 100baseTX link detected */
+#define RPCR_LED_ACT_ANY 0x4 /* TX or RX activity detected */
+#define RPCR_LED_ACT_RX 0x6 /* RX activity detected */
+#define RPCR_LED_ACT_TX 0x7 /* TX activity detected */
+
+/* Bank 1, Offset 0x0: Configuration Register */
+#define CR 0x0
+#define CR_EXT_PHY 0x0200 /* Enable/disable external PHY */
+#define CR_GPCNTRL 0x0400 /* Inverse drives nCNTRL pin */
+#define CR_NO_WAIT 0x1000 /* Do not request additional waits */
+#define CR_EPH_POWER_EN 0x8000 /* Disable/enable low power mode */
+
+/* Bank 1, Offset 0x2: Base Address Register */
+#define BAR 0x2
+#define BAR_HIGH_MASK 0xe000
+#define BAR_LOW_MASK 0x1f00
+#define BAR_LOW_SHIFT 4
+#define BAR_ADDRESS(val) \
+ ((val & BAR_HIGH_MASK) | ((val & BAR_LOW_MASK) >> BAR_LOW_SHIFT))
+
+/* Bank 1, Offsets 0x4: Individual Address Registers */
+#define IAR0 0x4
+#define IAR1 0x5
+#define IAR2 0x6
+#define IAR3 0x7
+#define IAR4 0x8
+#define IAR5 0x9
+
+/* Bank 1, Offset 0xa: General Purpose Register */
+#define GPR 0xa
+
+/* Bank 1, Offset 0xc: Control Register */
+#define CTR 0xa
+#define CTR_STORE 0x0001 /* Store registers to EEPROM */
+#define CTR_RELOAD 0x0002 /* Reload registers from EEPROM */
+#define CTR_EEPROM_SELECT 0x0004 /* Select registers to store/reload */
+#define CTR_TE_ENABLE 0x0020 /* TX error causes EPH interrupt */
+#define CTR_CR_ENABLE 0x0040 /* Ctr rollover causes EPH interrupt */
+#define CTR_LE_ENABLE 0x0080 /* Link error causes EPH interrupt */
+#define CTR_AUTO_RELEASE 0x0800 /* Automatically release TX packets */
+#define CTR_RCV_BAD 0x4000 /* Receive/discard bad CRC packets */
+
+/* Bank 2, Offset 0x0: MMU Command Register */
+#define MMUCR 0x0
+#define MMUCR_BUSY 0x0001 /* MMU is busy */
+#define MMUCR_CMD_NOOP (0<<5) /* No operation */
+#define MMUCR_CMD_TX_ALLOC (1<<5) /* Alloc TX memory (256b chunks) */
+#define MMUCR_CMD_MMU_RESET (2<<5) /* Reset MMU */
+#define MMUCR_CMD_REMOVE (3<<5) /* Remove frame from RX FIFO */
+#define MMUCR_CMD_RELEASE (4<<5) /* Remove and release from RX FIFO */
+#define MMUCR_CMD_RELEASE_PKT (5<<5) /* Release packet specified in PNR */
+#define MMUCR_CMD_ENQUEUE (6<<5) /* Enqueue packet for TX */
+#define MMUCR_CMD_TX_RESET (7<<5) /* Reset TX FIFOs */
+
+/* Bank 2, Offset 0x2: Packet Number Register */
+#define PNR 0x2
+#define PNR_MASK 0x3fff
+
+/* Bank 2, Offset 0x3: Allocation Result Register */
+#define ARR 0x3
+#define ARR_FAILED 0x8000 /* Last allocation request failed */
+#define ARR_MASK 0x3000
+
+/* Bank 2, Offset 0x4: FIFO Ports Register */
+#define FIFO_TX 0x4
+#define FIFO_RX 0x5
+#define FIFO_EMPTY 0x80 /* FIFO empty */
+#define FIFO_PACKET_MASK 0x3f /* Packet number mask */
+
+/* Bank 2, Offset 0x6: Pointer Register */
+#define PTR 0x6
+#define PTR_MASK 0x07ff /* Address accessible within TX/RX */
+#define PTR_NOT_EMPTY 0x0800 /* Write Data FIFO not empty */
+#define PTR_ETEN 0x1000 /* Enable early TX underrun detection */
+#define PTR_READ 0x2000 /* Set read/write */
+#define PTR_AUTO_INCR 0x4000 /* Auto increment on read/write */
+#define PTR_RCV 0x8000 /* Read/write to/from RX/TX */
+
+/* Bank 2, Offset 0x8: Data Registers */
+#define DATA0 0x8
+#define DATA1 0xa
+
+/* Bank 2, Offset 0xc: Interrupt Status Registers */
+#define IST 0xc /* read only */
+#define ACK 0xc /* write only */
+#define MSK 0xd
+
+#define RCV_INT 0x0001 /* RX */
+#define TX_INT 0x0002 /* TX */
+#define TX_EMPTY_INT 0x0004 /* TX empty */
+#define ALLOC_INT 0x0008 /* Allocation complete */
+#define RX_OVRN_INT 0x0010 /* RX overrun */
+#define EPH_INT 0x0020 /* EPH interrupt */
+#define ERCV_INT 0x0040 /* Early RX */
+#define MD_INT 0x0080 /* MII */
+
+#define IST_PRINTF "\20\01RCV\02TX\03TX_EMPTY\04ALLOC" \
+ "\05RX_OVRN\06EPH\07ERCV\10MD"
+
+/* Bank 3, Offset 0x0: Multicast Table Registers */
+#define MT 0x0
+
+/* Bank 3, Offset 0x8: Management Interface */
+#define MGMT 0x8
+#define MGMT_MDO 0x0001 /* MII management output */
+#define MGMT_MDI 0x0002 /* MII management input */
+#define MGMT_MCLK 0x0004 /* MII management clock */
+#define MGMT_MDOE 0x0008 /* MII management output enable */
+#define MGMT_MSK_CRS100 0x4000 /* Disable CRS100 detection during TX */
+
+/* Bank 3, Offset 0xa: Revision Register */
+#define REV 0xa
+#define REV_CHIP_MASK 0x00f0 /* Chip ID */
+#define REV_CHIP_SHIFT 4
+#define REV_REV_MASK 0x000f /* Revision ID */
+#define REV_REV_SHIFT 0
+
+#define REV_CHIP_9192 3
+#define REV_CHIP_9194 4
+#define REV_CHIP_9195 5
+#define REV_CHIP_9196 6
+#define REV_CHIP_91100 7
+#define REV_CHIP_91100FD 8
+#define REV_CHIP_91110FD 9
+
+/* Bank 3, Offset 0xc: Early RCV Register */
+#define ERCV 0xc
+#define ERCV_THRESHOLD_MASK 0x001f /* ERCV int threshold (64b chunks) */
+#define ERCV_RCV_DISCARD 0x0080 /* Discard packet being received */
+
+/* Control Byte */
+#define CTRL_CRC 0x10 /* Frame has CRC */
+#define CTRL_ODD 0x20 /* Frame has odd byte count */
+
+/* Receive Frame Status */
+#define RX_MULTCAST 0x0001 /* Frame was multicast */
+#define RX_HASH_MASK 0x007e /* Hash value for multicast */
+#define RX_HASH_SHIFT 1
+#define RX_TOOSHORT 0x0400 /* Frame was too short */
+#define RX_TOOLNG 0x0800 /* Frame was too long */
+#define RX_ODDFRM 0x1000 /* Frame has odd number of bytes */
+#define RX_BADCRC 0x2000 /* Frame failed CRC */
+#define RX_BROADCAST 0x4000 /* Frame was broadcast */
+#define RX_ALGNERR 0x8000 /* Frame had alignment error */
+#define RX_LEN_MASK 0x07ff
+
+/* Length of status word + byte count + control bytes for packets */
+#define PKT_CTRL_DATA_LEN 6
+
+/* Number of times to spin on TX allocations */
+#define TX_ALLOC_WAIT_TIME 1000
+
+#endif /* IF_SMCREG_H_ */
diff --git a/sys/dev/smc/if_smcvar.h b/sys/dev/smc/if_smcvar.h
new file mode 100644
index 0000000..3481ce6
--- /dev/null
+++ b/sys/dev/smc/if_smcvar.h
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2006 Benno Rice. 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.
+ * 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 ``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 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$
+ *
+ */
+
+#ifndef _IF_SMCVAR_H_
+#define _IF_SMCVAR_H_
+
+struct smc_softc {
+ struct ifnet *smc_ifp;
+ device_t smc_dev;
+ struct mtx smc_mtx;
+ u_int smc_chip;
+ u_int smc_rev;
+ u_int smc_mask;
+
+ /* Bus space glue */
+ bus_space_tag_t smc_bst;
+ bus_space_handle_t smc_bsh;
+
+ /* Resources */
+ int smc_usemem;
+ int smc_reg_rid;
+ int smc_irq_rid;
+ struct resource *smc_reg;
+ struct resource *smc_irq;
+ void *smc_ih;
+
+ /* Tasks */
+ struct taskqueue *smc_tq;
+ struct task smc_rx;
+ struct task smc_tx;
+ struct mbuf *smc_pending;
+ struct callout smc_watchdog;
+
+ /* MII support */
+ device_t smc_miibus;
+ struct callout smc_mii_tick_ch;
+ void (*smc_mii_tick)(void *);
+ void (*smc_mii_mediachg)(struct smc_softc *);
+ int (*smc_mii_mediaioctl)(struct smc_softc *,
+ struct ifreq *, u_long);
+
+ /* DMA support */
+ void (*smc_read_packet)(struct smc_softc *,
+ bus_addr_t, uint8_t *, bus_size_t);
+ void *smc_read_arg;
+};
+
+#define SMC_LOCK(sc) mtx_lock_spin(&(sc)->smc_mtx)
+#define SMC_UNLOCK(sc) mtx_unlock_spin(&(sc)->smc_mtx)
+#define SMC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->smc_mtx, MA_OWNED)
+
+#define SMC_RX_PRIORITY 0
+#define SMC_TX_PRIORITY 10
+
+int smc_probe(device_t);
+int smc_attach(device_t);
+int smc_detach(device_t);
+
+int smc_miibus_readreg(device_t, int, int);
+void smc_miibus_writereg(device_t, int, int, int);
+void smc_miibus_statchg(device_t);
+
+#endif /* _IF_SMCVAR_H_ */
OpenPOWER on IntegriCloud