summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/smc
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-09 22:42:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2013-10-10 09:06:58 +0200
commitbceabc95c1c85d793200446fa85f1ddc6313ea29 (patch)
tree973c8bd8deca9fd69913f2895cc91e0e6114d46c /freebsd/sys/dev/smc
parentAdd FreeBSD sources as a submodule (diff)
downloadrtems-libbsd-bceabc95c1c85d793200446fa85f1ddc6313ea29.tar.bz2
Move files to match FreeBSD layout
Diffstat (limited to 'freebsd/sys/dev/smc')
-rw-r--r--freebsd/sys/dev/smc/if_smc.c1342
-rw-r--r--freebsd/sys/dev/smc/if_smcreg.h261
-rw-r--r--freebsd/sys/dev/smc/if_smcvar.h77
3 files changed, 1680 insertions, 0 deletions
diff --git a/freebsd/sys/dev/smc/if_smc.c b/freebsd/sys/dev/smc/if_smc.c
new file mode 100644
index 00000000..d943111f
--- /dev/null
+++ b/freebsd/sys/dev/smc/if_smc.c
@@ -0,0 +1,1342 @@
+#include <freebsd/machine/rtems-bsd-config.h>
+
+/*-
+ * Copyright (c) 2008 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 <freebsd/sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for SMSC LAN91C111, may work for older variants.
+ */
+
+#ifdef HAVE_KERNEL_OPTION_HEADERS
+#include <freebsd/local/opt_device_polling.h>
+#endif
+
+#include <freebsd/sys/param.h>
+#include <freebsd/sys/systm.h>
+#include <freebsd/sys/errno.h>
+#include <freebsd/sys/kernel.h>
+#include <freebsd/sys/sockio.h>
+#include <freebsd/sys/malloc.h>
+#include <freebsd/sys/mbuf.h>
+#include <freebsd/sys/queue.h>
+#include <freebsd/sys/socket.h>
+#include <freebsd/sys/syslog.h>
+#include <freebsd/sys/taskqueue.h>
+
+#include <freebsd/sys/module.h>
+#include <freebsd/sys/bus.h>
+
+#include <freebsd/machine/bus.h>
+#include <freebsd/machine/resource.h>
+#include <freebsd/sys/rman.h>
+
+#include <freebsd/net/ethernet.h>
+#include <freebsd/net/if.h>
+#include <freebsd/net/if_arp.h>
+#include <freebsd/net/if_dl.h>
+#include <freebsd/net/if_types.h>
+#include <freebsd/net/if_mib.h>
+#include <freebsd/net/if_media.h>
+
+#ifdef INET
+#include <freebsd/netinet/in.h>
+#include <freebsd/netinet/in_systm.h>
+#include <freebsd/netinet/in_var.h>
+#include <freebsd/netinet/ip.h>
+#endif
+
+#include <freebsd/net/bpf.h>
+#include <freebsd/net/bpfdesc.h>
+
+#include <freebsd/dev/smc/if_smcreg.h>
+#include <freebsd/dev/smc/if_smcvar.h>
+
+#include <freebsd/dev/mii/mii.h>
+#include <freebsd/dev/mii/miivar.h>
+
+#define SMC_LOCK(sc) mtx_lock(&(sc)->smc_mtx)
+#define SMC_UNLOCK(sc) mtx_unlock(&(sc)->smc_mtx)
+#define SMC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->smc_mtx, MA_OWNED)
+
+#define SMC_INTR_PRIORITY 0
+#define SMC_RX_PRIORITY 5
+#define SMC_TX_PRIORITY 10
+
+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 void smc_stop(struct smc_softc *);
+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_intr(void *, int);
+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_write_2(sc->smc_reg, 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_read_2(sc->smc_reg, BSR) &
+ BSR_BANK_MASK) == 2, ("%s: smc_mmu_wait called when not in bank 2",
+ device_get_nameunit(sc->smc_dev)));
+ while (bus_read_2(sc->smc_reg, MMUCR) & MMUCR_BUSY)
+ ;
+}
+
+static __inline uint8_t
+smc_read_1(struct smc_softc *sc, bus_addr_t offset)
+{
+
+ return (bus_read_1(sc->smc_reg, offset));
+}
+
+static __inline void
+smc_write_1(struct smc_softc *sc, bus_addr_t offset, uint8_t val)
+{
+
+ bus_write_1(sc->smc_reg, offset, val);
+}
+
+static __inline uint16_t
+smc_read_2(struct smc_softc *sc, bus_addr_t offset)
+{
+
+ return (bus_read_2(sc->smc_reg, offset));
+}
+
+static __inline void
+smc_write_2(struct smc_softc *sc, bus_addr_t offset, uint16_t val)
+{
+
+ bus_write_2(sc->smc_reg, 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_read_multi_2(sc->smc_reg, 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_write_multi_2(sc->smc_reg, offset, datap, count);
+}
+
+int
+smc_probe(device_t dev)
+{
+ int rid, type, error;
+ uint16_t val;
+ struct smc_softc *sc;
+ struct resource *reg;
+
+ 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);
+ }
+
+ /* Check for the identification value in the BSR. */
+ val = bus_read_2(reg, 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_write_2(reg, BSR, 0);
+ val = bus_read_2(reg, 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_write_2(reg, BSR, 1);
+ val = bus_read_2(reg, 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_write_2(reg, BSR, 3);
+ val = bus_read_2(reg, 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;
+
+ 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_DEF);
+
+ /* Set up watchdog callout. */
+ callout_init_mtx(&sc->smc_watchdog, &sc->smc_mtx, 0);
+
+ 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;
+ }
+
+ 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_mtx(&sc->smc_mii_tick_ch, &sc->smc_mtx,
+ CALLOUT_RETURNUNLOCKED);
+ if (sc->smc_chip >= REV_CHIP_91110FD) {
+ (void)mii_attach(dev, &sc->smc_miibus, ifp,
+ smc_mii_ifmedia_upd, smc_mii_ifmedia_sts, BMSR_DEFCAPMASK,
+ MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ 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, ifqmaxlen);
+ 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_intr, SMC_INTR_PRIORITY, smc_task_intr, ifp);
+ 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, sc, &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);
+ SMC_LOCK(sc);
+ smc_stop(sc);
+ SMC_UNLOCK(sc);
+
+ if (sc->smc_ifp != NULL) {
+ ether_ifdetach(sc->smc_ifp);
+ }
+
+ callout_drain(&sc->smc_watchdog);
+ callout_drain(&sc->smc_mii_tick_ch);
+
+#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_tq != NULL) {
+ taskqueue_drain(sc->smc_tq, &sc->smc_intr);
+ taskqueue_drain(sc->smc_tq, &sc->smc_rx);
+ taskqueue_drain(sc->smc_tq, &sc->smc_tx);
+ taskqueue_free(sc->smc_tq);
+ sc->smc_tq = NULL;
+ }
+
+ if (sc->smc_ifp != NULL) {
+ 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 (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.
+ */
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ 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, sc);
+
+ /*
+ * 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)
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr);
+}
+#endif
+
+static int
+smc_intr(void *context)
+{
+ struct smc_softc *sc;
+
+ sc = (struct smc_softc *)context;
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr);
+ return (FILTER_HANDLED);
+}
+
+static void
+smc_task_intr(void *context, int pending)
+{
+ struct smc_softc *sc;
+ struct ifnet *ifp;
+ u_int status, packet, counter, tcr;
+
+ (void)pending;
+ 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);
+}
+
+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);
+}
+
+int
+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);
+ return (0);
+}
+
+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;
+
+ SMC_UNLOCK(sc);
+
+ 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 callouts.
+ */
+ callout_stop(&sc->smc_watchdog);
+ callout_stop(&sc->smc_mii_tick_ch);
+
+ /*
+ * 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;
+ sc->smc_ifp->if_capenable &= ~IFCAP_POLLING_NOCOUNT;
+#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 smc_softc *sc;
+
+ sc = (struct smc_softc *)arg;
+ device_printf(sc->smc_dev, "watchdog timeout\n");
+ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr);
+}
+
+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;
+ ifp->if_capenable |= IFCAP_POLLING_NOCOUNT;
+#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/freebsd/sys/dev/smc/if_smcreg.h b/freebsd/sys/dev/smc/if_smcreg.h
new file mode 100644
index 00000000..50af8077
--- /dev/null
+++ b/freebsd/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_HH_
+#define _IF_SMCREG_HH_
+
+/* 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_HH_ */
diff --git a/freebsd/sys/dev/smc/if_smcvar.h b/freebsd/sys/dev/smc/if_smcvar.h
new file mode 100644
index 00000000..a9e4a668
--- /dev/null
+++ b/freebsd/sys/dev/smc/if_smcvar.h
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2008 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_HH_
+#define _IF_SMCVAR_HH_
+
+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;
+
+ /* 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_intr;
+ 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;
+};
+
+int smc_probe(device_t);
+int smc_attach(device_t);
+int smc_detach(device_t);
+
+int smc_miibus_readreg(device_t, int, int);
+int smc_miibus_writereg(device_t, int, int, int);
+void smc_miibus_statchg(device_t);
+
+#endif /* _IF_SMCVAR_HH_ */