summaryrefslogtreecommitdiffstats
path: root/sys/dev/altera
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/altera')
-rw-r--r--sys/dev/altera/atse/a_api.h98
-rw-r--r--sys/dev/altera/atse/if_atse.c1979
-rw-r--r--sys/dev/altera/atse/if_atse_fdt.c216
-rw-r--r--sys/dev/altera/atse/if_atse_nexus.c258
-rw-r--r--sys/dev/altera/atse/if_atsereg.h486
5 files changed, 3037 insertions, 0 deletions
diff --git a/sys/dev/altera/atse/a_api.h b/sys/dev/altera/atse/a_api.h
new file mode 100644
index 0000000..ad34e9e
--- /dev/null
+++ b/sys/dev/altera/atse/a_api.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2012 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC research programme.
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Altera, Embedded Peripherals IP, User Guide, v. 11.0, June 2011.
+ * UG-01085-11.0.
+ */
+
+#ifndef _A_API_H
+#define _A_API_H
+
+/* Table 16-1. Memory Map. */
+#define A_ONCHIP_FIFO_MEM_CORE_DATA 0x00
+#define A_ONCHIP_FIFO_MEM_CORE_METADATA 0x04
+
+#define A_ONCHIP_FIFO_MEM_CORE_SOP (1<<0)
+#define A_ONCHIP_FIFO_MEM_CORE_EOP (1<<1)
+#define A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK 0x000000f7
+#define A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT 2
+ /* Reserved (1<<7) */
+#define A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK 0x0000ff00
+#define A_ONCHIP_FIFO_MEM_CORE_CHANNEL_SHIFT 8
+#define A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK 0x00ff0000
+#define A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT 16
+ /* Reserved 0xff000000 */
+
+/* Table 16-3. FIFO Status Register Memory Map. */
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL 0x00
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_I_STATUS 0x04
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT 0x08
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE 0x0c
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTFULL 0x10
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTEMPTY 0x14
+
+/* Table 16-5. Status Bit Field Descriptions. */
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_FULL (1<<0)
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_EMPTY (1<<1)
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTFULL (1<<2)
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTEMPTY (1<<3)
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_OVERFLOW (1<<4)
+#define A_ONCHIP_FIFO_MEM_CORE_STATUS_UNDERFLOW (1<<5)
+
+/* Table 16-6. Event Bit Field Descriptions. */
+/* XXX Datasheet has weird bit fields. Validate. */
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY (1<<0)
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_FULL (1<<1)
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTEMPTY (1<<2)
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTFULL (1<<3)
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW (1<<4)
+#define A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW (1<<5)
+
+/* Table 16-7. InterruptEnable Bit Field Descriptions. */
+/* XXX Datasheet has weird bit fields. Validate. */
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY (1<<0)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_FULL (1<<1)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY (1<<2)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL (1<<3)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW (1<<4)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW (1<<5)
+#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALL \
+ (A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY| \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_FULL| \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY| \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL| \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW| \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW)
+
+#endif /* _A_API_H */
+
+/* end */
diff --git a/sys/dev/altera/atse/if_atse.c b/sys/dev/altera/atse/if_atse.c
new file mode 100644
index 0000000..f6ed625
--- /dev/null
+++ b/sys/dev/altera/atse/if_atse.c
@@ -0,0 +1,1979 @@
+/*-
+ * Copyright (c) 2012,2013 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC research programme.
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Altera Triple-Speed Ethernet MegaCore, Function User Guide
+ * UG-01008-3.0, Software Version: 12.0, June 2012.
+ * Available at the time of writing at:
+ * http://www.altera.com/literature/ug/ug_ethernet.pdf
+ *
+ * We are using an Marvell E1111 (Alaska) PHY on the DE4. See mii/e1000phy.c.
+ */
+/*
+ * XXX-BZ NOTES:
+ * - ifOutBroadcastPkts are only counted if both ether dst and src are all-1s;
+ * seems an IP core bug, they count ether broadcasts as multicast. Is this
+ * still the case?
+ * - figure out why the TX FIFO fill status and intr did not work as expected.
+ * - test 100Mbit/s and 10Mbit/s
+ * - blacklist the one special factory programmed ethernet address (for now
+ * hardcoded, later from loader?)
+ * - resolve all XXX, left as reminders to shake out details later
+ * - Jumbo frame support
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_device_polling.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/jail.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <net/bpf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/altera/atse/if_atsereg.h>
+#include <dev/altera/atse/a_api.h>
+
+MODULE_DEPEND(atse, ether, 1, 1, 1);
+MODULE_DEPEND(atse, miibus, 1, 1, 1);
+
+
+#define ATSE_WATCHDOG_TIME 5
+
+#ifdef DEVICE_POLLING
+static poll_handler_t atse_poll;
+#endif
+
+/* XXX once we'd do parallel attach, we need a global lock for this. */
+#define ATSE_ETHERNET_OPTION_BITS_UNDEF 0
+#define ATSE_ETHERNET_OPTION_BITS_READ 1
+static int atse_ethernet_option_bits_flag = ATSE_ETHERNET_OPTION_BITS_UNDEF;
+static uint8_t atse_ethernet_option_bits[ALTERA_ETHERNET_OPTION_BITS_LEN];
+
+/*
+ * Softc and critical resource locking.
+ */
+#define ATSE_LOCK(_sc) mtx_lock(&(_sc)->atse_mtx)
+#define ATSE_UNLOCK(_sc) mtx_unlock(&(_sc)->atse_mtx)
+#define ATSE_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->atse_mtx, MA_OWNED)
+
+#ifdef DEBUG
+#define DPRINTF(format, ...) printf(format, __VA_ARGS__)
+#else
+#define DPRINTF(format, ...)
+#endif
+
+/* a_api.c functions; factor out? */
+static inline void
+a_onchip_fifo_mem_core_write(struct resource *res, uint32_t off,
+ uint32_t val4, const char *desc, const char *f, const int l)
+{
+
+ val4 = htole32(val4);
+ DPRINTF("[%s:%d] FIFOW %s 0x%08x = 0x%08x\n", f, l, desc, off, val4);
+ bus_write_4(res, off, val4);
+}
+static inline uint32_t
+a_onchip_fifo_mem_core_read(struct resource *res, uint32_t off,
+ const char *desc, const char *f, const int l)
+{
+ uint32_t val4;
+
+ val4 = le32toh(bus_read_4(res, off));
+ DPRINTF("[%s:%d] FIFOR %s 0x%08x = 0x%08x\n", f, l, desc, off, val4);
+ return (val4);
+}
+
+/* The FIFO does an endian convertion, so we must not do it as well. */
+/* XXX-BZ in fact we should do a htobe32 so le would be fine as well? */
+#define ATSE_TX_DATA_WRITE(sc, val4) \
+ bus_write_4((sc)->atse_tx_mem_res, A_ONCHIP_FIFO_MEM_CORE_DATA, val4)
+
+#define ATSE_TX_META_WRITE(sc, val4) \
+ a_onchip_fifo_mem_core_write((sc)->atse_tx_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_METADATA, \
+ (val4), "TXM", __func__, __LINE__)
+#define ATSE_TX_META_READ(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_tx_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_METADATA, \
+ "TXM", __func__, __LINE__)
+
+#define ATSE_TX_READ_FILL_LEVEL(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL, \
+ "TX_FILL", __func__, __LINE__)
+#define ATSE_RX_READ_FILL_LEVEL(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL, \
+ "RX_FILL", __func__, __LINE__)
+
+/* The FIFO does an endian convertion, so we must not do it as well. */
+/* XXX-BZ in fact we shoudl do a htobe32 so le would be fine as well? */
+#define ATSE_RX_DATA_READ(sc) \
+ bus_read_4((sc)->atse_rx_mem_res, A_ONCHIP_FIFO_MEM_CORE_DATA)
+#define ATSE_RX_META_READ(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_rx_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_METADATA, \
+ "RXM", __func__, __LINE__)
+
+#define ATSE_RX_EVENT_READ(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ "RX_EVENT", __func__, __LINE__)
+
+#define ATSE_TX_EVENT_READ(sc) \
+ a_onchip_fifo_mem_core_read((sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ "TX_EVENT", __func__, __LINE__)
+
+#define ATSE_RX_EVENT_CLEAR(sc) \
+ do { \
+ uint32_t val4; \
+ \
+ val4 = a_onchip_fifo_mem_core_read( \
+ (sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ "RX_EVENT", __func__, __LINE__); \
+ if (val4 != 0x00) \
+ a_onchip_fifo_mem_core_write( \
+ (sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ val4, "RX_EVENT", __func__, __LINE__); \
+ } while(0)
+#define ATSE_TX_EVENT_CLEAR(sc) \
+ do { \
+ uint32_t val4; \
+ \
+ val4 = a_onchip_fifo_mem_core_read( \
+ (sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ "TX_EVENT", __func__, __LINE__); \
+ if (val4 != 0x00) \
+ a_onchip_fifo_mem_core_write( \
+ (sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, \
+ val4, "TX_EVENT", __func__, __LINE__); \
+ } while(0)
+
+#define ATSE_RX_INTR_ENABLE(sc) \
+ a_onchip_fifo_mem_core_write((sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE, \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_ALL, \
+ "RX_INTR", __func__, __LINE__) /* XXX-BZ review later. */
+#define ATSE_RX_INTR_DISABLE(sc) \
+ a_onchip_fifo_mem_core_write((sc)->atse_rxc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE, 0, \
+ "RX_INTR", __func__, __LINE__)
+#define ATSE_TX_INTR_ENABLE(sc) \
+ a_onchip_fifo_mem_core_write((sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE, \
+ A_ONCHIP_FIFO_MEM_CORE_INTR_ALL, \
+ "TX_INTR", __func__, __LINE__) /* XXX-BZ review later. */
+#define ATSE_TX_INTR_DISABLE(sc) \
+ a_onchip_fifo_mem_core_write((sc)->atse_txc_mem_res, \
+ A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE, 0, \
+ "TX_INTR", __func__, __LINE__)
+
+/*
+ * Register space access macros.
+ */
+static inline void
+csr_write_4(struct atse_softc *sc, uint32_t reg, uint32_t val4,
+ const char *f, const int l)
+{
+
+ val4 = htole32(val4);
+ DPRINTF("[%s:%d] CSR W %s 0x%08x (0x%08x) = 0x%08x\n", f, l,
+ "atse_mem_res", reg, reg * 4, val4);
+ bus_write_4(sc->atse_mem_res, reg * 4, val4);
+}
+
+static inline uint32_t
+csr_read_4(struct atse_softc *sc, uint32_t reg, const char *f, const int l)
+{
+ uint32_t val4;
+
+ val4 = le32toh(bus_read_4(sc->atse_mem_res, reg * 4));
+ DPRINTF("[%s:%d] CSR R %s 0x%08x (0x%08x) = 0x%08x\n", f, l,
+ "atse_mem_res", reg, reg * 4, val4);
+ return (val4);
+}
+
+/*
+ * See page 5-2 that it's all dword offsets and the MS 16 bits need to be zero
+ * on write and ignored on read.
+ */
+static inline void
+pxx_write_2(struct atse_softc *sc, bus_addr_t bmcr, uint32_t reg, uint16_t val,
+ const char *f, const int l, const char *s)
+{
+ uint32_t val4;
+
+ val4 = htole32(val & 0x0000ffff);
+ DPRINTF("[%s:%d] %s W %s 0x%08x (0x%08jx) = 0x%08x\n", f, l, s,
+ "atse_mem_res", reg, (bmcr + reg) * 4, val4);
+ bus_write_4(sc->atse_mem_res, (bmcr + reg) * 4, val4);
+}
+
+static inline uint16_t
+pxx_read_2(struct atse_softc *sc, bus_addr_t bmcr, uint32_t reg, const char *f,
+ const int l, const char *s)
+{
+ uint32_t val4;
+ uint16_t val;
+
+ val4 = bus_read_4(sc->atse_mem_res, (bmcr + reg) * 4);
+ val = le32toh(val4) & 0x0000ffff;
+ DPRINTF("[%s:%d] %s R %s 0x%08x (0x%08jx) = 0x%04x\n", f, l, s,
+ "atse_mem_res", reg, (bmcr + reg) * 4, val);
+ return (val);
+}
+
+#define CSR_WRITE_4(sc, reg, val) \
+ csr_write_4((sc), (reg), (val), __func__, __LINE__)
+#define CSR_READ_4(sc, reg) \
+ csr_read_4((sc), (reg), __func__, __LINE__)
+#define PCS_WRITE_2(sc, reg, val) \
+ pxx_write_2((sc), sc->atse_bmcr0, (reg), (val), __func__, __LINE__, \
+ "PCS")
+#define PCS_READ_2(sc, reg) \
+ pxx_read_2((sc), sc->atse_bmcr0, (reg), __func__, __LINE__, "PCS")
+#define PHY_WRITE_2(sc, reg, val) \
+ pxx_write_2((sc), sc->atse_bmcr1, (reg), (val), __func__, __LINE__, \
+ "PHY")
+#define PHY_READ_2(sc, reg) \
+ pxx_read_2((sc), sc->atse_bmcr1, (reg), __func__, __LINE__, "PHY")
+
+static void atse_tick(void *);
+static int atse_detach(device_t);
+
+devclass_t atse_devclass;
+
+static int
+atse_tx_locked(struct atse_softc *sc, int *sent)
+{
+ struct mbuf *m;
+ uint32_t val4, fill_level;
+ int c;
+
+ ATSE_LOCK_ASSERT(sc);
+
+ m = sc->atse_tx_m;
+ KASSERT(m != NULL, ("%s: m is null: sc=%p", __func__, sc));
+ KASSERT(m->m_flags & M_PKTHDR, ("%s: not a pkthdr: m=%p", __func__, m));
+
+ /*
+ * Copy to buffer to minimize our pain as we can only store
+ * double words which, after the first mbuf gets out of alignment
+ * quite quickly.
+ */
+ if (sc->atse_tx_m_offset == 0) {
+ m_copydata(m, 0, m->m_pkthdr.len, sc->atse_tx_buf);
+ sc->atse_tx_buf_len = m->m_pkthdr.len;
+ }
+
+ fill_level = ATSE_TX_READ_FILL_LEVEL(sc);
+#if 0 /* Returns 0xdeadc0de. */
+ val4 = ATSE_TX_META_READ(sc);
+#endif
+ if (sc->atse_tx_m_offset == 0) {
+ /* Write start of packet. */
+ val4 = A_ONCHIP_FIFO_MEM_CORE_SOP;
+ val4 &= ~A_ONCHIP_FIFO_MEM_CORE_EOP;
+ ATSE_TX_META_WRITE(sc, val4);
+ }
+
+ /* TX FIFO is single clock mode, so we have the full FIFO. */
+ c = 0;
+ while ((sc->atse_tx_buf_len - sc->atse_tx_m_offset) > 4 &&
+ fill_level < AVALON_FIFO_TX_BASIC_OPTS_DEPTH) {
+
+ bcopy(&sc->atse_tx_buf[sc->atse_tx_m_offset], &val4,
+ sizeof(val4));
+ ATSE_TX_DATA_WRITE(sc, val4);
+ sc->atse_tx_m_offset += sizeof(val4);
+ c += sizeof(val4);
+
+ fill_level++;
+ if (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH)
+ fill_level = ATSE_TX_READ_FILL_LEVEL(sc);
+ }
+ if (sent != NULL)
+ *sent += c;
+
+ /* Set EOP *before* writing the last symbol. */
+ if (sc->atse_tx_m_offset >= (sc->atse_tx_buf_len - 4) &&
+ fill_level < AVALON_FIFO_TX_BASIC_OPTS_DEPTH) {
+ int leftm;
+ uint32_t x;
+
+ /* Set EndOfPacket. */
+ val4 = A_ONCHIP_FIFO_MEM_CORE_EOP;
+ /* Set EMPTY. */
+ leftm = sc->atse_tx_buf_len - sc->atse_tx_m_offset;
+ val4 |= ((4 - leftm) << A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT);
+ x = val4;
+ ATSE_TX_META_WRITE(sc, val4);
+
+ /* Write last symbol. */
+ val4 = 0;
+ bcopy(sc->atse_tx_buf + sc->atse_tx_m_offset, &val4, leftm);
+ ATSE_TX_DATA_WRITE(sc, val4);
+
+ if (sent != NULL)
+ *sent += leftm;
+
+ /* OK, the packet is gone. */
+ sc->atse_tx_m = NULL;
+ sc->atse_tx_m_offset = 0;
+
+ /* If anyone is interested give them a copy. */
+ BPF_MTAP(sc->atse_ifp, m);
+
+ m_freem(m);
+ return (0);
+ }
+
+ return (EBUSY);
+}
+
+static void
+atse_start_locked(struct ifnet *ifp)
+{
+ struct atse_softc *sc;
+ int error, sent;
+
+ sc = ifp->if_softc;
+ ATSE_LOCK_ASSERT(sc);
+
+ if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
+ IFF_DRV_RUNNING || (sc->atse_flags & ATSE_FLAGS_LINK) == 0)
+ return;
+
+#if 1
+ /*
+ * Disable the watchdog while sending, we are batching packets.
+ * Though we should never reach 5 seconds, and are holding the lock,
+ * but who knows.
+ */
+ sc->atse_watchdog_timer = 0;
+#endif
+
+ if (sc->atse_tx_m != NULL) {
+ error = atse_tx_locked(sc, &sent);
+ if (error != 0)
+ goto done;
+ }
+ /* We have more space to send so continue ... */
+ for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, sc->atse_tx_m);
+ sc->atse_tx_m_offset = 0;
+ if (sc->atse_tx_m == NULL)
+ break;
+ error = atse_tx_locked(sc, &sent);
+ if (error != 0)
+ goto done;
+ }
+
+done:
+ /* If the IP core walks into Nekromanteion try to bail out. */
+ if (sent > 0)
+ sc->atse_watchdog_timer = ATSE_WATCHDOG_TIME;
+}
+
+static void
+atse_start(struct ifnet *ifp)
+{
+ struct atse_softc *sc;
+
+ sc = ifp->if_softc;
+ ATSE_LOCK(sc);
+ atse_start_locked(ifp);
+ ATSE_UNLOCK(sc);
+}
+
+static int
+atse_stop_locked(struct atse_softc *sc)
+{
+ struct ifnet *ifp;
+ uint32_t mask, val4;
+ int i;
+
+ ATSE_LOCK_ASSERT(sc);
+
+ sc->atse_watchdog_timer = 0;
+ callout_stop(&sc->atse_tick);
+
+ ifp = sc->atse_ifp;
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+ ATSE_RX_INTR_DISABLE(sc);
+ ATSE_TX_INTR_DISABLE(sc);
+ ATSE_RX_EVENT_CLEAR(sc);
+ ATSE_TX_EVENT_CLEAR(sc);
+
+ /* Disable MAC transmit and receive datapath. */
+ mask = BASE_CFG_COMMAND_CONFIG_TX_ENA|BASE_CFG_COMMAND_CONFIG_RX_ENA;
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ val4 &= ~mask;
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+ /* Wait for bits to be cleared; i=100 is excessive. */
+ for (i = 0; i < 100; i++) {
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ if ((val4 & mask) == 0)
+ break;
+ DELAY(10);
+ }
+ if ((val4 & mask) != 0)
+ device_printf(sc->atse_dev, "Disabling MAC TX/RX timed out.\n");
+ /* Punt. */
+
+ sc->atse_flags &= ~ATSE_FLAGS_LINK;
+
+ /* XXX-BZ free the RX/TX rings. */
+
+ return (0);
+}
+
+static uint8_t
+atse_mchash(struct atse_softc *sc __unused, const uint8_t *addr)
+{
+ int i, j;
+ uint8_t x, y;
+
+ x = 0;
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ y = addr[i] & 0x01;
+ for (j = 1; j < 8; j++)
+ y ^= (addr[i] >> j) & 0x01;
+ x |= (y << i);
+ }
+ return (x);
+}
+
+static int
+atse_rxfilter_locked(struct atse_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *ifma;
+ uint32_t val4;
+ int i;
+
+ /* XXX-BZ can we find out if we have the MHASH synthesized? */
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ /* For simplicity always hash full 48 bits of addresses. */
+ if ((val4 & BASE_CFG_COMMAND_CONFIG_MHASH_SEL) != 0)
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_MHASH_SEL;
+
+ ifp = sc->atse_ifp;
+ if (ifp->if_flags & IFF_PROMISC)
+ val4 |= BASE_CFG_COMMAND_CONFIG_PROMIS_EN;
+ else
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_PROMIS_EN;
+
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+
+ if (ifp->if_flags & IFF_ALLMULTI) {
+ /* Accept all multicast addresses. */
+ for (i = 0; i <= MHASH_LEN; i++)
+ CSR_WRITE_4(sc, MHASH_START + i, 0x1);
+ } else {
+ /*
+ * Can hold MHASH_LEN entries.
+ * XXX-BZ bitstring.h would be more general.
+ */
+ uint64_t h;
+
+ h = 0;
+ /*
+ * Re-build and re-program hash table. First build the
+ * bit-field "yes" or "no" for each slot per address, then
+ * do all the programming afterwards.
+ */
+ if_maddr_rlock(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ h |= (1 << atse_mchash(sc,
+ LLADDR((struct sockaddr_dl *)ifma->ifma_addr)));
+ }
+ if_maddr_runlock(ifp);
+ for (i = 0; i <= MHASH_LEN; i++)
+ CSR_WRITE_4(sc, MHASH_START + i,
+ (h & (1 << i)) ? 0x01 : 0x00);
+ }
+
+ return (0);
+}
+
+static int
+atse_ethernet_option_bits_read_fdt(device_t dev)
+{
+ struct resource *res;
+ device_t fdev;
+ int i, rid;
+
+ if (atse_ethernet_option_bits_flag & ATSE_ETHERNET_OPTION_BITS_READ)
+ return (0);
+
+ fdev = device_find_child(device_get_parent(dev), "cfi", 0);
+ if (fdev == NULL)
+ return (ENOENT);
+
+ rid = 0;
+ res = bus_alloc_resource_any(fdev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (res == NULL)
+ return (ENXIO);
+
+ for (i = 0; i < ALTERA_ETHERNET_OPTION_BITS_LEN; i++)
+ atse_ethernet_option_bits[i] = bus_read_1(res,
+ ALTERA_ETHERNET_OPTION_BITS_OFF + i);
+
+ bus_release_resource(fdev, SYS_RES_MEMORY, rid, res);
+ atse_ethernet_option_bits_flag |= ATSE_ETHERNET_OPTION_BITS_READ;
+
+ return (0);
+}
+
+static int
+atse_ethernet_option_bits_read(device_t dev)
+{
+ int error;
+
+ error = atse_ethernet_option_bits_read_fdt(dev);
+ if (error == 0)
+ return (0);
+
+ device_printf(dev, "Cannot read Ethernet addresses from flash.\n");
+ return (error);
+}
+
+static int
+atse_get_eth_address(struct atse_softc *sc)
+{
+ unsigned long hostid;
+ uint32_t val4;
+ int unit;
+
+ /*
+ * Make sure to only ever do this once. Otherwise a reset would
+ * possibly change our ethernet address, which is not good at all.
+ */
+ if (sc->atse_eth_addr[0] != 0x00 || sc->atse_eth_addr[1] != 0x00 ||
+ sc->atse_eth_addr[2] != 0x00)
+ return (0);
+
+ if ((atse_ethernet_option_bits_flag &
+ ATSE_ETHERNET_OPTION_BITS_READ) == 0)
+ goto get_random;
+
+ val4 = atse_ethernet_option_bits[0] << 24;
+ val4 |= atse_ethernet_option_bits[1] << 16;
+ val4 |= atse_ethernet_option_bits[2] << 8;
+ val4 |= atse_ethernet_option_bits[3];
+ /* They chose "safe". */
+ if (val4 != le32toh(0x00005afe)) {
+ device_printf(sc->atse_dev, "Magic '5afe' is not safe: 0x%08x. "
+ "Falling back to random numbers for hardware address.\n",
+ val4);
+ goto get_random;
+ }
+
+ sc->atse_eth_addr[0] = atse_ethernet_option_bits[4];
+ sc->atse_eth_addr[1] = atse_ethernet_option_bits[5];
+ sc->atse_eth_addr[2] = atse_ethernet_option_bits[6];
+ sc->atse_eth_addr[3] = atse_ethernet_option_bits[7];
+ sc->atse_eth_addr[4] = atse_ethernet_option_bits[8];
+ sc->atse_eth_addr[5] = atse_ethernet_option_bits[9];
+
+ /* Handle factory default ethernet addresss: 00:07:ed:ff:ed:15 */
+ if (sc->atse_eth_addr[0] == 0x00 && sc->atse_eth_addr[1] == 0x07 &&
+ sc->atse_eth_addr[2] == 0xed && sc->atse_eth_addr[3] == 0xff &&
+ sc->atse_eth_addr[4] == 0xed && sc->atse_eth_addr[5] == 0x15) {
+
+ device_printf(sc->atse_dev, "Factory programmed Ethernet "
+ "hardware address blacklisted. Falling back to random "
+ "address to avoid collisions.\n");
+ device_printf(sc->atse_dev, "Please re-program your flash.\n");
+ goto get_random;
+ }
+
+ if (sc->atse_eth_addr[0] == 0x00 && sc->atse_eth_addr[1] == 0x00 &&
+ sc->atse_eth_addr[2] == 0x00 && sc->atse_eth_addr[3] == 0x00 &&
+ sc->atse_eth_addr[4] == 0x00 && sc->atse_eth_addr[5] == 0x00) {
+ device_printf(sc->atse_dev, "All zero's Ethernet hardware "
+ "address blacklisted. Falling back to random address.\n");
+ device_printf(sc->atse_dev, "Please re-program your flash.\n");
+ goto get_random;
+ }
+
+ if (ETHER_IS_MULTICAST(sc->atse_eth_addr)) {
+ device_printf(sc->atse_dev, "Multicast Ethernet hardware "
+ "address blacklisted. Falling back to random address.\n");
+ device_printf(sc->atse_dev, "Please re-program your flash.\n");
+ goto get_random;
+ }
+
+ /*
+ * If we find an Altera prefixed address with a 0x0 ending
+ * adjust by device unit. If not and this is not the first
+ * Ethernet, go to random.
+ */
+ unit = device_get_unit(sc->atse_dev);
+ if (unit == 0x00)
+ return (0);
+
+ if (unit > 0x0f) {
+ device_printf(sc->atse_dev, "We do not support Ethernet "
+ "addresses for more than 16 MACs. Falling back to "
+ "random hadware address.\n");
+ goto get_random;
+ }
+ if ((sc->atse_eth_addr[0] & ~0x2) != 0 ||
+ sc->atse_eth_addr[1] != 0x07 || sc->atse_eth_addr[2] != 0xed ||
+ (sc->atse_eth_addr[5] & 0x0f) != 0x0) {
+ device_printf(sc->atse_dev, "Ethernet address not meeting our "
+ "multi-MAC standards. Falling back to random hadware "
+ "address.\n");
+ goto get_random;
+ }
+ sc->atse_eth_addr[5] |= (unit & 0x0f);
+
+ return (0);
+
+get_random:
+ /*
+ * Fall back to random code we also use on bridge(4).
+ */
+ getcredhostid(curthread->td_ucred, &hostid);
+ if (hostid == 0) {
+ arc4rand(sc->atse_eth_addr, ETHER_ADDR_LEN, 1);
+ sc->atse_eth_addr[0] &= ~1;/* clear multicast bit */
+ sc->atse_eth_addr[0] |= 2; /* set the LAA bit */
+ } else {
+ sc->atse_eth_addr[0] = 0x2;
+ sc->atse_eth_addr[1] = (hostid >> 24) & 0xff;
+ sc->atse_eth_addr[2] = (hostid >> 16) & 0xff;
+ sc->atse_eth_addr[3] = (hostid >> 8 ) & 0xff;
+ sc->atse_eth_addr[4] = hostid & 0xff;
+ sc->atse_eth_addr[5] = sc->atse_unit & 0xff;
+ }
+
+ return (0);
+}
+
+static int
+atse_set_eth_address(struct atse_softc *sc, int n)
+{
+ uint32_t v0, v1;
+
+ v0 = (sc->atse_eth_addr[3] << 24) | (sc->atse_eth_addr[2] << 16) |
+ (sc->atse_eth_addr[1] << 8) | sc->atse_eth_addr[0];
+ v1 = (sc->atse_eth_addr[5] << 8) | sc->atse_eth_addr[4];
+
+ if (n & ATSE_ETH_ADDR_DEF) {
+ CSR_WRITE_4(sc, BASE_CFG_MAC_0, v0);
+ CSR_WRITE_4(sc, BASE_CFG_MAC_1, v1);
+ }
+ if (n & ATSE_ETH_ADDR_SUPP1) {
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_0_0, v0);
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_0_1, v1);
+ }
+ if (n & ATSE_ETH_ADDR_SUPP2) {
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_1_0, v0);
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_1_1, v1);
+ }
+ if (n & ATSE_ETH_ADDR_SUPP3) {
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_2_0, v0);
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_2_1, v1);
+ }
+ if (n & ATSE_ETH_ADDR_SUPP4) {
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_3_0, v0);
+ CSR_WRITE_4(sc, SUPPL_ADDR_SMAC_3_1, v1);
+ }
+
+ return (0);
+}
+
+static int
+atse_reset(struct atse_softc *sc)
+{
+ int i;
+ uint32_t val4, mask;
+ uint16_t val;
+
+ /* 1. External PHY Initialization using MDIO. */
+ /*
+ * We select the right MDIO space in atse_attach() and let MII do
+ * anything else.
+ */
+
+ /* 2. PCS Configuration Register Initialization. */
+ /* a. Set auto negotiation link timer to 1.6ms for SGMII. */
+ PCS_WRITE_2(sc, PCS_EXT_LINK_TIMER_0, 0x0D40);
+ PCS_WRITE_2(sc, PCS_EXT_LINK_TIMER_1, 0x0003);
+
+ /* b. Configure SGMII. */
+ val = PCS_EXT_IF_MODE_SGMII_ENA|PCS_EXT_IF_MODE_USE_SGMII_AN;
+ PCS_WRITE_2(sc, PCS_EXT_IF_MODE, val);
+
+ /* c. Enable auto negotiation. */
+ /* Ignore Bits 6,8,13; should be set,set,unset. */
+ val = PCS_READ_2(sc, PCS_CONTROL);
+ val &= ~(PCS_CONTROL_ISOLATE|PCS_CONTROL_POWERDOWN);
+ val &= ~PCS_CONTROL_LOOPBACK; /* Make this a -link1 option? */
+ val |= PCS_CONTROL_AUTO_NEGOTIATION_ENABLE;
+ PCS_WRITE_2(sc, PCS_CONTROL, val);
+
+ /* d. PCS reset. */
+ val = PCS_READ_2(sc, PCS_CONTROL);
+ val |= PCS_CONTROL_RESET;
+ PCS_WRITE_2(sc, PCS_CONTROL, val);
+ /* Wait for reset bit to clear; i=100 is excessive. */
+ for (i = 0; i < 100; i++) {
+ val = PCS_READ_2(sc, PCS_CONTROL);
+ if ((val & PCS_CONTROL_RESET) == 0)
+ break;
+ DELAY(10);
+ }
+ if ((val & PCS_CONTROL_RESET) != 0) {
+ device_printf(sc->atse_dev, "PCS reset timed out.\n");
+ return (ENXIO);
+ }
+
+ /* 3. MAC Configuration Register Initialization. */
+ /* a. Disable MAC transmit and receive datapath. */
+ mask = BASE_CFG_COMMAND_CONFIG_TX_ENA|BASE_CFG_COMMAND_CONFIG_RX_ENA;
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ val4 &= ~mask;
+ /* Samples in the manual do have the SW_RESET bit set here, why? */
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+ /* Wait for bits to be cleared; i=100 is excessive. */
+ for (i = 0; i < 100; i++) {
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ if ((val4 & mask) == 0)
+ break;
+ DELAY(10);
+ }
+ if ((val4 & mask) != 0) {
+ device_printf(sc->atse_dev, "Disabling MAC TX/RX timed out.\n");
+ return (ENXIO);
+ }
+ /* b. MAC FIFO configuration. */
+ CSR_WRITE_4(sc, BASE_CFG_TX_SECTION_EMPTY, FIFO_DEPTH_TX - 16);
+ CSR_WRITE_4(sc, BASE_CFG_TX_ALMOST_FULL, 3);
+ CSR_WRITE_4(sc, BASE_CFG_TX_ALMOST_EMPTY, 8);
+ CSR_WRITE_4(sc, BASE_CFG_RX_SECTION_EMPTY, FIFO_DEPTH_RX - 16);
+ CSR_WRITE_4(sc, BASE_CFG_RX_ALMOST_FULL, 8);
+ CSR_WRITE_4(sc, BASE_CFG_RX_ALMOST_EMPTY, 8);
+#if 0
+ CSR_WRITE_4(sc, BASE_CFG_TX_SECTION_FULL, 16);
+ CSR_WRITE_4(sc, BASE_CFG_RX_SECTION_FULL, 16);
+#else
+ /* For store-and-forward mode, set this threshold to 0. */
+ CSR_WRITE_4(sc, BASE_CFG_TX_SECTION_FULL, 0);
+ CSR_WRITE_4(sc, BASE_CFG_RX_SECTION_FULL, 0);
+#endif
+ /* c. MAC address configuration. */
+ /* Also intialize supplementary addresses to our primary one. */
+ /* XXX-BZ FreeBSD really needs to grow and API for using these. */
+ atse_get_eth_address(sc);
+ atse_set_eth_address(sc, ATSE_ETH_ADDR_ALL);
+
+ /* d. MAC function configuration. */
+ CSR_WRITE_4(sc, BASE_CFG_FRM_LENGTH, 1518); /* Default. */
+ CSR_WRITE_4(sc, BASE_CFG_TX_IPG_LENGTH, 12);
+ CSR_WRITE_4(sc, BASE_CFG_PAUSE_QUANT, 0xFFFF);
+
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ /*
+ * If 1000BASE-X/SGMII PCS is initialized, set the ETH_SPEED (bit 3)
+ * and ENA_10 (bit 25) in command_config register to 0. If half duplex
+ * is reported in the PHY/PCS status register, set the HD_ENA (bit 10)
+ * to 1 in command_config register.
+ * BZ: We shoot for 1000 instead.
+ */
+#if 0
+ val4 |= BASE_CFG_COMMAND_CONFIG_ETH_SPEED;
+#else
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ETH_SPEED;
+#endif
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ENA_10;
+#if 0
+ /*
+ * We do not want to set this, otherwise, we could not even send
+ * random raw ethernet frames for various other research. By default
+ * FreeBSD will use the right ether source address.
+ */
+ val4 |= BASE_CFG_COMMAND_CONFIG_TX_ADDR_INS;
+#endif
+ val4 |= BASE_CFG_COMMAND_CONFIG_PAD_EN;
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_CRC_FWD;
+#if 0
+ val4 |= BASE_CFG_COMMAND_CONFIG_CNTL_FRM_ENA;
+#endif
+#if 1
+ val4 |= BASE_CFG_COMMAND_CONFIG_RX_ERR_DISC;
+#endif
+ val &= ~BASE_CFG_COMMAND_CONFIG_LOOP_ENA; /* link0? */
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+
+ /*
+ * Make sure we do not enable 32bit alignment; FreeBSD cannot
+ * cope with the additional padding (though we should!?).
+ * Also make sure we get the CRC appended.
+ */
+ val4 = CSR_READ_4(sc, TX_CMD_STAT);
+ val4 &= ~(TX_CMD_STAT_OMIT_CRC|TX_CMD_STAT_TX_SHIFT16);
+ CSR_WRITE_4(sc, TX_CMD_STAT, val4);
+ val4 = CSR_READ_4(sc, RX_CMD_STAT);
+ val4 &= ~RX_CMD_STAT_RX_SHIFT16;
+ CSR_WRITE_4(sc, RX_CMD_STAT, val4);
+
+ /* e. Reset MAC. */
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ val4 |= BASE_CFG_COMMAND_CONFIG_SW_RESET;
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+ /* Wait for bits to be cleared; i=100 is excessive. */
+ for (i = 0; i < 100; i++) {
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ if ((val4 & BASE_CFG_COMMAND_CONFIG_SW_RESET) == 0)
+ break;
+ DELAY(10);
+ }
+ if ((val4 & BASE_CFG_COMMAND_CONFIG_SW_RESET) != 0) {
+ device_printf(sc->atse_dev, "MAC reset timed out.\n");
+ return (ENXIO);
+ }
+
+ /* f. Enable MAC transmit and receive datapath. */
+ mask = BASE_CFG_COMMAND_CONFIG_TX_ENA|BASE_CFG_COMMAND_CONFIG_RX_ENA;
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ val4 |= mask;
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+ /* Wait for bits to be cleared; i=100 is excessive. */
+ for (i = 0; i < 100; i++) {
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+ if ((val4 & mask) == mask)
+ break;
+ DELAY(10);
+ }
+ if ((val4 & mask) != mask) {
+ device_printf(sc->atse_dev, "Enabling MAC TX/RX timed out.\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+atse_init_locked(struct atse_softc *sc)
+{
+ struct ifnet *ifp;
+ struct mii_data *mii;
+ uint8_t *eaddr;
+
+ ATSE_LOCK_ASSERT(sc);
+ ifp = sc->atse_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+ return;
+
+ /*
+ * Must update the ether address if changed. Given we do not handle
+ * in atse_ioctl() but it's in the general framework, just always
+ * do it here before atse_reset().
+ */
+ eaddr = IF_LLADDR(sc->atse_ifp);
+ bcopy(eaddr, &sc->atse_eth_addr, ETHER_ADDR_LEN);
+
+ /* Make things frind to halt, cleanup, ... */
+ atse_stop_locked(sc);
+ /* ... reset, ... */
+ atse_reset(sc);
+
+ /* ... and fire up the engine again. */
+ atse_rxfilter_locked(sc);
+
+ /* Memory rings? DMA engine? */
+
+ sc->atse_rx_buf_len = 0;
+ sc->atse_flags &= ATSE_FLAGS_LINK; /* Preserve. */
+
+#ifdef DEVICE_POLLING
+ /* Only enable interrupts if we are not polling. */
+ if (ifp->if_capenable & IFCAP_POLLING) {
+ ATSE_RX_INTR_DISABLE(sc);
+ ATSE_TX_INTR_DISABLE(sc);
+ ATSE_RX_EVENT_CLEAR(sc);
+ ATSE_TX_EVENT_CLEAR(sc);
+ } else
+#endif
+ {
+ ATSE_RX_INTR_ENABLE(sc);
+ ATSE_TX_INTR_ENABLE(sc);
+ }
+
+ mii = device_get_softc(sc->atse_miibus);
+
+ sc->atse_flags &= ~ATSE_FLAGS_LINK;
+ mii_mediachg(mii);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ callout_reset(&sc->atse_tick, hz, atse_tick, sc);
+}
+
+static void
+atse_init(void *xsc)
+{
+ struct atse_softc *sc;
+
+ sc = (struct atse_softc *)xsc;
+ ATSE_LOCK(sc);
+ atse_init_locked(sc);
+ ATSE_UNLOCK(sc);
+}
+
+static int
+atse_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct atse_softc *sc;
+ struct ifreq *ifr;
+ int error, mask;
+
+
+ error = 0;
+ sc = ifp->if_softc;
+ ifr = (struct ifreq *)data;
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ ATSE_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 &&
+ ((ifp->if_flags ^ sc->atse_if_flags) &
+ (IFF_PROMISC | IFF_ALLMULTI)) != 0)
+ atse_rxfilter_locked(sc);
+ else
+ atse_init_locked(sc);
+ } else if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ atse_stop_locked(sc);
+ sc->atse_if_flags = ifp->if_flags;
+ ATSE_UNLOCK(sc);
+ break;
+ case SIOCSIFCAP:
+ ATSE_LOCK(sc);
+ mask = ifr->ifr_reqcap ^ ifp->if_capenable;
+#ifdef DEVICE_POLLING
+ if ((mask & IFCAP_POLLING) != 0 &&
+ (IFCAP_POLLING & ifp->if_capabilities) != 0) {
+ ifp->if_capenable ^= IFCAP_POLLING;
+ if ((IFCAP_POLLING & ifp->if_capenable) != 0) {
+
+ error = ether_poll_register(atse_poll, ifp);
+ if (error != 0) {
+ ATSE_UNLOCK(sc);
+ break;
+ }
+ /* Disable interrupts. */
+ ATSE_RX_INTR_DISABLE(sc);
+ ATSE_TX_INTR_DISABLE(sc);
+ ATSE_RX_EVENT_CLEAR(sc);
+ ATSE_TX_EVENT_CLEAR(sc);
+
+ /*
+ * Do not allow disabling of polling if we do
+ * not have interrupts.
+ */
+ } else if (sc->atse_rx_irq_res != NULL ||
+ sc->atse_tx_irq_res != NULL) {
+ error = ether_poll_deregister(ifp);
+ /* Enable interrupts. */
+ ATSE_RX_INTR_ENABLE(sc);
+ ATSE_TX_INTR_ENABLE(sc);
+ } else {
+ ifp->if_capenable ^= IFCAP_POLLING;
+ error = EINVAL;
+ }
+ }
+#endif /* DEVICE_POLLING */
+ ATSE_UNLOCK(sc);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ATSE_LOCK(sc);
+ atse_rxfilter_locked(sc);
+ ATSE_UNLOCK(sc);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ {
+ struct mii_data *mii;
+ struct ifreq *ifr;
+
+ mii = device_get_softc(sc->atse_miibus);
+ ifr = (struct ifreq *)data;
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ break;
+ }
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+
+ return (error);
+}
+
+static void
+atse_watchdog(struct atse_softc *sc)
+{
+
+ ATSE_LOCK_ASSERT(sc);
+
+ if (sc->atse_watchdog_timer == 0 || --sc->atse_watchdog_timer > 0)
+ return;
+
+ device_printf(sc->atse_dev, "watchdog timeout\n");
+ sc->atse_ifp->if_oerrors++;
+
+ sc->atse_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ atse_init_locked(sc);
+
+ if (!IFQ_DRV_IS_EMPTY(&sc->atse_ifp->if_snd))
+ atse_start_locked(sc->atse_ifp);
+}
+
+static void
+atse_tick(void *xsc)
+{
+ struct atse_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+
+ sc = (struct atse_softc *)xsc;
+ ATSE_LOCK_ASSERT(sc);
+ ifp = sc->atse_ifp;
+
+ mii = device_get_softc(sc->atse_miibus);
+ mii_tick(mii);
+ atse_watchdog(sc);
+ if ((sc->atse_flags & ATSE_FLAGS_LINK) == 0)
+ atse_miibus_statchg(sc->atse_dev);
+ callout_reset(&sc->atse_tick, hz, atse_tick, sc);
+}
+
+/*
+ * Set media options.
+ */
+static int
+atse_ifmedia_upd(struct ifnet *ifp)
+{
+ struct atse_softc *sc;
+ struct mii_data *mii;
+ struct mii_softc *miisc;
+ int error;
+
+ sc = ifp->if_softc;
+
+ ATSE_LOCK(sc);
+ mii = device_get_softc(sc->atse_miibus);
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ PHY_RESET(miisc);
+ error = mii_mediachg(mii);
+ ATSE_UNLOCK(sc);
+
+ return (error);
+}
+
+static void
+atse_update_rx_err(struct atse_softc *sc, uint32_t mask)
+{
+ int i;
+
+ /* RX error are 6 bits, we only know 4 of them. */
+ for (i = 0; i < ATSE_RX_ERR_MAX; i++)
+ if ((mask & (1 << i)) != 0)
+ sc->atse_rx_err[i]++;
+}
+
+static int
+atse_rx_locked(struct atse_softc *sc)
+{
+ struct ifnet *ifp;
+ struct mbuf *m;
+ uint32_t fill, i, j;
+ uint32_t data, meta;
+ int rx_npkts = 0;
+
+ ATSE_LOCK_ASSERT(sc);
+
+ ifp = sc->atse_ifp;
+ j = 0;
+ meta = 0;
+ do {
+outer:
+ if (sc->atse_rx_cycles <= 0)
+ return (rx_npkts);
+ sc->atse_rx_cycles--;
+
+ if (sc->atse_rx_m == NULL) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ return (rx_npkts);
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ /* Make sure upper layers will be aligned. */
+ m_adj(m, 2);
+ sc->atse_rx_m = m;
+ }
+
+ fill = ATSE_RX_READ_FILL_LEVEL(sc);
+ for (i = 0; i < fill; i++) {
+ /*
+ * XXX-BZ for whatever reason the FIFO requires the
+ * the data read before we can access the meta data.
+ */
+ data = ATSE_RX_DATA_READ(sc);
+ meta = ATSE_RX_META_READ(sc);
+ if (meta & A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) {
+ /* XXX-BZ evaluate error. */
+ atse_update_rx_err(sc, ((meta &
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) >>
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT) & 0xff);
+ ifp->if_ierrors++;
+ sc->atse_rx_buf_len = 0;
+ /*
+ * Should still read till EOP or next SOP.
+ *
+ * XXX-BZ might also depend on
+ * BASE_CFG_COMMAND_CONFIG_RX_ERR_DISC
+ */
+ sc->atse_flags |= ATSE_FLAGS_ERROR;
+ return (rx_npkts);
+ }
+ if ((meta & A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK) != 0)
+ device_printf(sc->atse_dev, "%s: unexpected "
+ "channel %u\n", __func__, (meta &
+ A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK) >>
+ A_ONCHIP_FIFO_MEM_CORE_CHANNEL_SHIFT);
+
+ if (meta & A_ONCHIP_FIFO_MEM_CORE_SOP) {
+ /*
+ * There is no need to clear SOP between 1st
+ * and subsequent packet data junks.
+ */
+ if (sc->atse_rx_buf_len != 0 &&
+ (sc->atse_flags & ATSE_FLAGS_SOP_SEEN) == 0)
+ {
+ device_printf(sc->atse_dev, "%s: SOP "
+ "without empty buffer: %u\n",
+ __func__, sc->atse_rx_buf_len);
+ /* XXX-BZ any better counter? */
+ ifp->if_ierrors++;
+ }
+
+ if ((sc->atse_flags & ATSE_FLAGS_SOP_SEEN) == 0)
+ {
+ sc->atse_flags |= ATSE_FLAGS_SOP_SEEN;
+ sc->atse_rx_buf_len = 0;
+ }
+ }
+#if 0 /* We had to read the data before we could access meta data. See above. */
+ data = ATSE_RX_DATA_READ(sc);
+#endif
+ /* Make sure to not overflow the mbuf data size. */
+ if (sc->atse_rx_buf_len >= sc->atse_rx_m->m_len - 4) {
+ /*
+ * XXX-BZ Error. We need more mbufs and are
+ * not setup for this yet.
+ */
+ ifp->if_ierrors++;
+ sc->atse_flags |= ATSE_FLAGS_ERROR;
+ }
+ if ((sc->atse_flags & ATSE_FLAGS_ERROR) == 0)
+ /*
+ * MUST keep this bcopy as m_data after m_adj
+ * for IP header aligment is on half-word
+ * and not word alignment.
+ */
+ bcopy(&data, (uint8_t *)(sc->atse_rx_m->m_data +
+ sc->atse_rx_buf_len), sizeof(data));
+ if (meta & A_ONCHIP_FIFO_MEM_CORE_EOP) {
+ uint8_t empty;
+
+ empty = (meta &
+ A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK) >>
+ A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT;
+ sc->atse_rx_buf_len += (4 - empty);
+
+ ifp->if_ipackets++;
+ rx_npkts++;
+
+ m = sc->atse_rx_m;
+ m->m_pkthdr.len = m->m_len =
+ sc->atse_rx_buf_len;
+ sc->atse_rx_m = NULL;
+
+ sc->atse_rx_buf_len = 0;
+ sc->atse_flags &= ~ATSE_FLAGS_SOP_SEEN;
+ if (sc->atse_flags & ATSE_FLAGS_ERROR) {
+ sc->atse_flags &= ~ATSE_FLAGS_ERROR;
+ m_freem(m);
+ /* Need to start with a new packet. */
+ goto outer;
+ }
+
+ m->m_pkthdr.rcvif = ifp;
+
+ ATSE_UNLOCK(sc);
+ (*ifp->if_input)(ifp, m);
+ ATSE_LOCK(sc);
+ goto outer; /* Need a new mbuf. */
+ } else {
+ sc->atse_rx_buf_len += sizeof(data);
+ }
+ } /* for */
+
+ /* XXX-BZ could optimize in case of another packet waiting. */
+ } while ((meta & A_ONCHIP_FIFO_MEM_CORE_EOP) == 0 || fill > 0);
+
+ return (rx_npkts);
+}
+
+
+/*
+ * Report current media status.
+ */
+static void
+atse_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct atse_softc *sc;
+ struct mii_data *mii;
+
+ sc = ifp->if_softc;
+
+ ATSE_LOCK(sc);
+ mii = device_get_softc(sc->atse_miibus);
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+ ATSE_UNLOCK(sc);
+}
+
+static void
+atse_intr(void *arg)
+{
+ struct atse_softc *sc;
+ struct ifnet *ifp;
+ uint32_t rx, tx;
+
+ sc = (struct atse_softc *)arg;
+ ifp = sc->atse_ifp;
+
+ ATSE_LOCK(sc);
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING) {
+ ATSE_UNLOCK(sc);
+ return;
+ }
+#endif
+
+ ATSE_RX_INTR_DISABLE(sc);
+ ATSE_TX_INTR_DISABLE(sc);
+
+ rx = ATSE_RX_EVENT_READ(sc);
+ tx = ATSE_TX_EVENT_READ(sc);
+ if (rx != 0) {
+ if (rx & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW|
+ A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) {
+ /* XXX-BZ ERROR HANDLING. */
+ atse_update_rx_err(sc, ((rx &
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) >>
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT) & 0xff);
+ ifp->if_ierrors++;
+ }
+ if ((rx & A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY) != 0) {
+ sc->atse_rx_cycles = RX_CYCLES_IN_INTR;
+ atse_rx_locked(sc);
+ }
+ }
+ if (tx != 0) {
+ /* XXX-BZ build histogram. */
+ if (tx & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW|
+ A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) {
+ /* XXX-BZ ERROR HANDLING. */
+ ifp->if_oerrors++;
+ }
+ if (tx & A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY)
+ sc->atse_watchdog_timer = 0;
+#if 0
+ if (tx & (A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY|
+ A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTEMPTY))
+ atse_start_locked(ifp);
+#endif
+ }
+
+ /* Clear events before re-enabling intrs. */
+ ATSE_TX_EVENT_CLEAR(sc);
+ ATSE_RX_EVENT_CLEAR(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ /* Re-enable interrupts. */
+ ATSE_RX_INTR_ENABLE(sc);
+ ATSE_TX_INTR_ENABLE(sc);
+
+ if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ atse_start_locked(ifp);
+ }
+
+ ATSE_UNLOCK(sc);
+}
+
+#ifdef DEVICE_POLLING
+static int
+atse_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
+{
+ struct atse_softc *sc;
+ int rx_npkts = 0;
+
+ sc = ifp->if_softc;
+ ATSE_LOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ ATSE_UNLOCK(sc);
+ return (rx_npkts);
+ }
+
+ sc->atse_rx_cycles = count;
+ rx_npkts = atse_rx_locked(sc);
+ atse_start_locked(ifp);
+
+ if (sc->atse_rx_cycles > 0 || cmd == POLL_AND_CHECK_STATUS) {
+ uint32_t rx, tx;
+
+ rx = ATSE_RX_EVENT_READ(sc);
+ tx = ATSE_TX_EVENT_READ(sc);
+
+ if (rx & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW|
+ A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) {
+ /* XXX-BZ ERROR HANDLING. */
+ atse_update_rx_err(sc, ((rx &
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) >>
+ A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT) & 0xff);
+ ifp->if_ierrors++;
+ }
+ if (tx & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW|
+ A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) {
+ /* XXX-BZ ERROR HANDLING. */
+ ifp->if_oerrors++;
+ }
+ if (tx & A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY)
+ sc->atse_watchdog_timer = 0;
+
+#if 0
+ if (/* Severe error; if only we could find out. */) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ atse_init_locked(sc);
+ }
+#endif
+ }
+
+ ATSE_UNLOCK(sc);
+ return (rx_npkts);
+}
+#endif /* DEVICE_POLLING */
+
+static struct atse_mac_stats_regs {
+ const char *name;
+ const char *descr; /* Mostly copied from Altera datasheet. */
+} atse_mac_stats_regs[] = {
+ [0x1a] =
+ { "aFramesTransmittedOK",
+ "The number of frames that are successfully transmitted including "
+ "the pause frames." },
+ { "aFramesReceivedOK",
+ "The number of frames that are successfully received including the "
+ "pause frames." },
+ { "aFrameCheckSequenceErrors",
+ "The number of receive frames with CRC error." },
+ { "aAlignmentErrors",
+ "The number of receive frames with alignment error." },
+ { "aOctetsTransmittedOK",
+ "The lower 32 bits of the number of data and padding octets that "
+ "are successfully transmitted." },
+ { "aOctetsReceivedOK",
+ "The lower 32 bits of the number of data and padding octets that "
+ " are successfully received." },
+ { "aTxPAUSEMACCtrlFrames",
+ "The number of pause frames transmitted." },
+ { "aRxPAUSEMACCtrlFrames",
+ "The number received pause frames received." },
+ { "ifInErrors",
+ "The number of errored frames received." },
+ { "ifOutErrors",
+ "The number of transmit frames with either a FIFO overflow error, "
+ "a FIFO underflow error, or a error defined by the user "
+ "application." },
+ { "ifInUcastPkts",
+ "The number of valid unicast frames received." },
+ { "ifInMulticastPkts",
+ "The number of valid multicast frames received. The count does "
+ "not include pause frames." },
+ { "ifInBroadcastPkts",
+ "The number of valid broadcast frames received." },
+ { "ifOutDiscards",
+ "This statistics counter is not in use. The MAC function does not "
+ "discard frames that are written to the FIFO buffer by the user "
+ "application." },
+ { "ifOutUcastPkts",
+ "The number of valid unicast frames transmitted." },
+ { "ifOutMulticastPkts",
+ "The number of valid multicast frames transmitted, excluding pause "
+ "frames." },
+ { "ifOutBroadcastPkts",
+ "The number of valid broadcast frames transmitted." },
+ { "etherStatsDropEvents",
+ "The number of frames that are dropped due to MAC internal errors "
+ "when FIFO buffer overflow persists." },
+ { "etherStatsOctets",
+ "The lower 32 bits of the total number of octets received. This "
+ "count includes both good and errored frames." },
+ { "etherStatsPkts",
+ "The total number of good and errored frames received." },
+ { "etherStatsUndersizePkts",
+ "The number of frames received with length less than 64 bytes. "
+ "This count does not include errored frames." },
+ { "etherStatsOversizePkts",
+ "The number of frames received that are longer than the value "
+ "configured in the frm_length register. This count does not "
+ "include errored frames." },
+ { "etherStatsPkts64Octets",
+ "The number of 64-byte frames received. This count includes good "
+ "and errored frames." },
+ { "etherStatsPkts65to127Octets",
+ "The number of received good and errored frames between the length "
+ "of 65 and 127 bytes." },
+ { "etherStatsPkts128to255Octets",
+ "The number of received good and errored frames between the length "
+ "of 128 and 255 bytes." },
+ { "etherStatsPkts256to511Octets",
+ "The number of received good and errored frames between the length "
+ "of 256 and 511 bytes." },
+ { "etherStatsPkts512to1023Octets",
+ "The number of received good and errored frames between the length "
+ "of 512 and 1023 bytes." },
+ { "etherStatsPkts1024to1518Octets",
+ "The number of received good and errored frames between the length "
+ "of 1024 and 1518 bytes." },
+ { "etherStatsPkts1519toXOctets",
+ "The number of received good and errored frames between the length "
+ "of 1519 and the maximum frame length configured in the frm_length "
+ "register." },
+ { "etherStatsJabbers",
+ "Too long frames with CRC error." },
+ { "etherStatsFragments",
+ "Too short frames with CRC error." },
+ /* 0x39 unused, 0x3a/b non-stats. */
+ [0x3c] =
+ /* Extended Statistics Counters */
+ { "msb_aOctetsTransmittedOK",
+ "Upper 32 bits of the number of data and padding octets that are "
+ "successfully transmitted." },
+ { "msb_aOctetsReceivedOK",
+ "Upper 32 bits of the number of data and padding octets that are "
+ "successfully received." },
+ { "msb_etherStatsOctets",
+ "Upper 32 bits of the total number of octets received. This count "
+ "includes both good and errored frames." }
+};
+
+static int
+sysctl_atse_mac_stats_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct atse_softc *sc;
+ int error, offset, s;
+
+ sc = arg1;
+ offset = arg2;
+
+ s = CSR_READ_4(sc, offset);
+ error = sysctl_handle_int(oidp, &s, 0, req);
+ if (error || !req->newptr)
+ return (error);
+
+ return (0);
+}
+
+static struct atse_rx_err_stats_regs {
+ const char *name;
+ const char *descr;
+} atse_rx_err_stats_regs[] = {
+
+#define ATSE_RX_ERR_FIFO_THRES_EOP 0 /* FIFO threshold reached, on EOP. */
+#define ATSE_RX_ERR_ELEN 1 /* Frame/payload length not valid. */
+#define ATSE_RX_ERR_CRC32 2 /* CRC-32 error. */
+#define ATSE_RX_ERR_FIFO_THRES_TRUNC 3 /* FIFO thresh., truncated frame. */
+#define ATSE_RX_ERR_4 4 /* ? */
+#define ATSE_RX_ERR_5 5 /* / */
+
+ { "rx_err_fifo_thres_eop",
+ "FIFO threshold reached, reported on EOP." },
+ { "rx_err_fifo_elen",
+ "Frame or payload length not valid." },
+ { "rx_err_fifo_crc32",
+ "CRC-32 error." },
+ { "rx_err_fifo_thres_trunc",
+ "FIFO threshold reached, truncated frame" },
+ { "rx_err_4",
+ "?" },
+ { "rx_err_5",
+ "?" },
+};
+
+static int
+sysctl_atse_rx_err_stats_proc(SYSCTL_HANDLER_ARGS)
+{
+ struct atse_softc *sc;
+ int error, offset, s;
+
+ sc = arg1;
+ offset = arg2;
+
+ s = sc->atse_rx_err[offset];
+ error = sysctl_handle_int(oidp, &s, 0, req);
+ if (error || !req->newptr)
+ return (error);
+
+ return (0);
+}
+
+static void
+atse_sysctl_stats_attach(device_t dev)
+{
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid *soid;
+ struct atse_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ sctx = device_get_sysctl_ctx(dev);
+ soid = device_get_sysctl_tree(dev);
+
+ /* MAC statistics. */
+ for (i = 0; i < sizeof(atse_mac_stats_regs) /
+ sizeof(*atse_mac_stats_regs); i++) {
+ if (atse_mac_stats_regs[i].name == NULL ||
+ atse_mac_stats_regs[i].descr == NULL)
+ continue;
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
+ atse_mac_stats_regs[i].name, CTLTYPE_UINT|CTLFLAG_RD,
+ sc, i, sysctl_atse_mac_stats_proc, "IU",
+ atse_mac_stats_regs[i].descr);
+ }
+
+ /* rx_err[]. */
+ for (i = 0; i < ATSE_RX_ERR_MAX; i++) {
+ if (atse_rx_err_stats_regs[i].name == NULL ||
+ atse_rx_err_stats_regs[i].descr == NULL)
+ continue;
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
+ atse_rx_err_stats_regs[i].name, CTLTYPE_UINT|CTLFLAG_RD,
+ sc, i, sysctl_atse_rx_err_stats_proc, "IU",
+ atse_rx_err_stats_regs[i].descr);
+ }
+}
+
+/*
+ * Generic device handling routines.
+ */
+int
+atse_attach(device_t dev)
+{
+ struct atse_softc *sc;
+ struct ifnet *ifp;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ atse_ethernet_option_bits_read(dev);
+
+ mtx_init(&sc->atse_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+
+ callout_init_mtx(&sc->atse_tick, &sc->atse_mtx, 0);
+
+ sc->atse_tx_buf = malloc(ETHER_MAX_LEN_JUMBO, M_DEVBUF, M_WAITOK);
+
+ /*
+ * We are only doing single-PHY with this driver currently. The
+ * defaults would be right so that BASE_CFG_MDIO_ADDR0 points to the
+ * 1st PHY address (0) apart from the fact that BMCR0 is always
+ * the PCS mapping, so we always use BMCR1. See Table 5-1 0xA0-0xBF.
+ */
+#if 0 /* Always PCS. */
+ sc->atse_bmcr0 = MDIO_0_START;
+ CSR_WRITE_4(sc, BASE_CFG_MDIO_ADDR0, 0x00);
+#endif
+ /* Always use matching PHY for atse[0..]. */
+ sc->atse_phy_addr = device_get_unit(dev);
+ sc->atse_bmcr1 = MDIO_1_START;
+ CSR_WRITE_4(sc, BASE_CFG_MDIO_ADDR1, sc->atse_phy_addr);
+
+ /* Reset the adapter. */
+ atse_reset(sc);
+
+ /* Setup interface. */
+ ifp = sc->atse_ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(dev, "if_alloc() failed\n");
+ error = ENOSPC;
+ goto err;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = atse_ioctl;
+ ifp->if_start = atse_start;
+ ifp->if_init = atse_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, ATSE_TX_LIST_CNT - 1);
+ ifp->if_snd.ifq_drv_maxlen = ATSE_TX_LIST_CNT - 1;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ /* MII setup. */
+ error = mii_attach(dev, &sc->atse_miibus, ifp, atse_ifmedia_upd,
+ atse_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+ if (error != 0) {
+ device_printf(dev, "attaching PHY failed: %d\n", error);
+ goto err;
+ }
+
+ /* Call media-indepedent attach routine. */
+ ether_ifattach(ifp, sc->atse_eth_addr);
+
+ /* Tell the upper layer(s) about vlan mtu support. */
+ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+ ifp->if_capabilities |= IFCAP_VLAN_MTU;
+ ifp->if_capenable = ifp->if_capabilities;
+#ifdef DEVICE_POLLING
+ /* We will enable polling by default if no irqs available. See below. */
+ ifp->if_capabilities |= IFCAP_POLLING;
+#endif
+
+ /* Hook up interrupts. */
+ if (sc->atse_rx_irq_res != NULL) {
+ error = bus_setup_intr(dev, sc->atse_rx_irq_res, INTR_TYPE_NET |
+ INTR_MPSAFE, NULL, atse_intr, sc, &sc->atse_rx_intrhand);
+ if (error != 0) {
+ device_printf(dev, "enabling RX IRQ failed\n");
+ ether_ifdetach(ifp);
+ goto err;
+ }
+ }
+
+ if (sc->atse_tx_irq_res != NULL) {
+ error = bus_setup_intr(dev, sc->atse_tx_irq_res, INTR_TYPE_NET |
+ INTR_MPSAFE, NULL, atse_intr, sc, &sc->atse_tx_intrhand);
+ if (error != 0) {
+ bus_teardown_intr(dev, sc->atse_rx_irq_res,
+ sc->atse_rx_intrhand);
+ device_printf(dev, "enabling TX IRQ failed\n");
+ ether_ifdetach(ifp);
+ goto err;
+ }
+ }
+
+ if ((ifp->if_capenable & IFCAP_POLLING) != 0 ||
+ (sc->atse_rx_irq_res == NULL && sc->atse_tx_irq_res == NULL)) {
+#ifdef DEVICE_POLLING
+ /* If not on and no IRQs force it on. */
+ if (sc->atse_rx_irq_res == NULL && sc->atse_tx_irq_res == NULL){
+ ifp->if_capenable |= IFCAP_POLLING;
+ device_printf(dev, "forcing to polling due to no "
+ "interrupts\n");
+ }
+ error = ether_poll_register(atse_poll, ifp);
+ if (error != 0)
+ goto err;
+#else
+ device_printf(dev, "no DEVICE_POLLING in kernel and no IRQs\n");
+ error = ENXIO;
+#endif
+ } else {
+ ATSE_RX_INTR_ENABLE(sc);
+ ATSE_TX_INTR_ENABLE(sc);
+ }
+
+err:
+ if (error != 0)
+ atse_detach(dev);
+
+ if (error == 0)
+ atse_sysctl_stats_attach(dev);
+
+ return (error);
+}
+
+static int
+atse_detach(device_t dev)
+{
+ struct atse_softc *sc;
+ struct ifnet *ifp;
+
+ sc = device_get_softc(dev);
+ KASSERT(mtx_initialized(&sc->atse_mtx), ("%s: mutex not initialized",
+ device_get_nameunit(dev)));
+ ifp = sc->atse_ifp;
+
+#ifdef DEVICE_POLLING
+ if (ifp->if_capenable & IFCAP_POLLING)
+ ether_poll_deregister(ifp);
+#endif
+
+ /* Only cleanup if attach succeeded. */
+ if (device_is_attached(dev)) {
+ ATSE_LOCK(sc);
+ atse_stop_locked(sc);
+ ATSE_UNLOCK(sc);
+ callout_drain(&sc->atse_tick);
+ ether_ifdetach(ifp);
+ }
+ if (sc->atse_miibus != NULL)
+ device_delete_child(dev, sc->atse_miibus);
+
+ if (sc->atse_tx_intrhand)
+ bus_teardown_intr(dev, sc->atse_tx_irq_res,
+ sc->atse_tx_intrhand);
+ if (sc->atse_rx_intrhand)
+ bus_teardown_intr(dev, sc->atse_rx_irq_res,
+ sc->atse_rx_intrhand);
+
+ if (ifp != NULL)
+ if_free(ifp);
+
+ if (sc->atse_tx_buf != NULL)
+ free(sc->atse_tx_buf, M_DEVBUF);
+
+ mtx_destroy(&sc->atse_mtx);
+
+ return (0);
+}
+
+/* Shared between nexus anf fdt implementation. */
+void
+atse_detach_resources(device_t dev)
+{
+ struct atse_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (sc->atse_txc_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->atse_txc_mem_rid,
+ sc->atse_txc_mem_res);
+ sc->atse_txc_mem_res = NULL;
+ }
+ if (sc->atse_tx_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->atse_tx_mem_rid,
+ sc->atse_tx_mem_res);
+ sc->atse_tx_mem_res = NULL;
+ }
+ if (sc->atse_tx_irq_res != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->atse_tx_irq_rid,
+ sc->atse_tx_irq_res);
+ sc->atse_tx_irq_res = NULL;
+ }
+ if (sc->atse_rxc_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->atse_rxc_mem_rid,
+ sc->atse_rxc_mem_res);
+ sc->atse_rxc_mem_res = NULL;
+ }
+ if (sc->atse_rx_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->atse_rx_mem_rid,
+ sc->atse_rx_mem_res);
+ sc->atse_rx_mem_res = NULL;
+ }
+ if (sc->atse_rx_irq_res != NULL) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->atse_rx_irq_rid,
+ sc->atse_rx_irq_res);
+ sc->atse_rx_irq_res = NULL;
+ }
+ if (sc->atse_mem_res != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->atse_mem_rid,
+ sc->atse_mem_res);
+ sc->atse_mem_res = NULL;
+ }
+}
+
+int
+atse_detach_dev(device_t dev)
+{
+ int error;
+
+ error = atse_detach(dev);
+ if (error) {
+ /* We are basically in undefined state now. */
+ device_printf(dev, "atse_detach() failed: %d\n", error);
+ return (error);
+ }
+
+ atse_detach_resources(dev);
+
+ return (0);
+}
+
+int
+atse_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct atse_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * We currently do not support re-mapping of MDIO space on-the-fly
+ * but de-facto hard-code the phy#.
+ */
+ if (phy != sc->atse_phy_addr)
+ return (0);
+
+ return (PHY_READ_2(sc, reg));
+}
+
+int
+atse_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct atse_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * We currently do not support re-mapping of MDIO space on-the-fly
+ * but de-facto hard-code the phy#.
+ */
+ if (phy != sc->atse_phy_addr)
+ return (0);
+
+ PHY_WRITE_2(sc, reg, data);
+ return (0);
+}
+
+void
+atse_miibus_statchg(device_t dev)
+{
+ struct atse_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ uint32_t val4;
+
+ sc = device_get_softc(dev);
+ ATSE_LOCK_ASSERT(sc);
+
+ mii = device_get_softc(sc->atse_miibus);
+ ifp = sc->atse_ifp;
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ val4 = CSR_READ_4(sc, BASE_CFG_COMMAND_CONFIG);
+
+ /* Assume no link. */
+ sc->atse_flags &= ~ATSE_FLAGS_LINK;
+
+ if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID)) {
+
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_10_T:
+ val4 |= BASE_CFG_COMMAND_CONFIG_ENA_10;
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ETH_SPEED;
+ sc->atse_flags |= ATSE_FLAGS_LINK;
+ break;
+ case IFM_100_TX:
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ENA_10;
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ETH_SPEED;
+ sc->atse_flags |= ATSE_FLAGS_LINK;
+ break;
+ case IFM_1000_T:
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_ENA_10;
+ val4 |= BASE_CFG_COMMAND_CONFIG_ETH_SPEED;
+ sc->atse_flags |= ATSE_FLAGS_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((sc->atse_flags & ATSE_FLAGS_LINK) == 0) {
+ /* XXX-BZ need to stop the MAC? */
+ return;
+ }
+
+ if (IFM_OPTIONS(mii->mii_media_active & IFM_FDX) != 0)
+ val4 &= ~BASE_CFG_COMMAND_CONFIG_HD_ENA;
+ else
+ val4 |= BASE_CFG_COMMAND_CONFIG_HD_ENA;
+ /* XXX-BZ flow control? */
+
+ /* Make sure the MAC is activated. */
+ val4 |= BASE_CFG_COMMAND_CONFIG_TX_ENA;
+ val4 |= BASE_CFG_COMMAND_CONFIG_RX_ENA;
+
+ CSR_WRITE_4(sc, BASE_CFG_COMMAND_CONFIG, val4);
+}
+
+/* end */
diff --git a/sys/dev/altera/atse/if_atse_fdt.c b/sys/dev/altera/atse/if_atse_fdt.c
new file mode 100644
index 0000000..30c6b67
--- /dev/null
+++ b/sys/dev/altera/atse/if_atse_fdt.c
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 2013 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC research programme.
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/altera/atse/if_atsereg.h>
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+static int
+atse_probe_fdt(device_t dev)
+{
+
+ if (ofw_bus_is_compatible(dev, "altera,atse")) {
+ device_set_desc(dev, "Altera Triple-Speed Ethernet MegaCore");
+ return (BUS_PROBE_DEFAULT);
+ }
+ return (ENXIO);
+}
+
+static int
+atse_attach_fdt(device_t dev)
+{
+ struct atse_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->atse_dev = dev;
+ sc->atse_unit = device_get_unit(dev);
+
+ /*
+ * FDT has the list of our resources. Given we are using multiple
+ * memory regions and possibly multiple interrupts, we need to attach
+ * them in the order specified in .dts:
+ * MAC, RX and RXC FIFO, TX and TXC FIFO; RX INTR, TX INTR.
+ */
+
+ /* MAC: Avalon-MM, atse management register region. */
+ sc->atse_mem_rid = 0;
+ sc->atse_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_mem_rid, RF_ACTIVE);
+ if (sc->atse_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for ctrl region\n");
+ error = ENXIO;
+ goto err;
+ }
+ if (bootverbose)
+ device_printf(sc->atse_dev, "MAC ctrl region at mem %p-%p\n",
+ (void *)rman_get_start(sc->atse_mem_res),
+ (void *)(rman_get_start(sc->atse_mem_res) +
+ rman_get_size(sc->atse_mem_res)));
+
+ /*
+ * RX and RXC FIFO memory regions.
+ * 0x00: 2 * 32bit FIFO data,
+ * 0x20: 8 * 32bit FIFO ctrl, Avalon-ST Sink to Avalon-MM R-Slave.
+ */
+ sc->atse_rx_mem_rid = 1;
+ sc->atse_rx_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_rx_mem_rid, RF_ACTIVE);
+ if (sc->atse_rx_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for RX FIFO\n");
+ error = ENXIO;
+ goto err;
+ }
+ if (bootverbose)
+ device_printf(sc->atse_dev, "RX FIFO at mem %p-%p\n",
+ (void *)rman_get_start(sc->atse_rx_mem_res),
+ (void *)(rman_get_start(sc->atse_rx_mem_res) +
+ rman_get_size(sc->atse_rx_mem_res)));
+
+ sc->atse_rxc_mem_rid = 2;
+ sc->atse_rxc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_rxc_mem_rid, RF_ACTIVE);
+ if (sc->atse_rxc_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for RXC FIFO\n");
+ error = ENXIO;
+ goto err;
+ }
+ if (bootverbose)
+ device_printf(sc->atse_dev, "RXC FIFO at mem %p-%p\n",
+ (void *)rman_get_start(sc->atse_rxc_mem_res),
+ (void *)(rman_get_start(sc->atse_rxc_mem_res) +
+ rman_get_size(sc->atse_rxc_mem_res)));
+
+ /*
+ * TX and TXC FIFO memory regions.
+ * 0x00: 2 * 32bit FIFO data,
+ * 0x20: 8 * 32bit FIFO ctrl, Avalon-MM W-Slave to Avalon-ST Source.
+ */
+ sc->atse_tx_mem_rid = 3;
+ sc->atse_tx_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_tx_mem_rid, RF_ACTIVE);
+ if (sc->atse_tx_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for TX FIFO\n");
+ error = ENXIO;
+ goto err;
+ }
+ if (bootverbose)
+ device_printf(sc->atse_dev, "TX FIFO at mem %p-%p\n",
+ (void *)rman_get_start(sc->atse_tx_mem_res),
+ (void *)(rman_get_start(sc->atse_tx_mem_res) +
+ rman_get_size(sc->atse_tx_mem_res)));
+
+ sc->atse_txc_mem_rid = 4;
+ sc->atse_txc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_txc_mem_rid, RF_ACTIVE);
+ if (sc->atse_txc_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for TXC FIFO\n");
+ error = ENXIO;
+ goto err;
+ }
+ if (bootverbose)
+ device_printf(sc->atse_dev, "TXC FIFO at mem %p-%p\n",
+ (void *)rman_get_start(sc->atse_txc_mem_res),
+ (void *)(rman_get_start(sc->atse_txc_mem_res) +
+ rman_get_size(sc->atse_txc_mem_res)));
+
+ /* (Optional) RX and TX IRQ. */
+ sc->atse_rx_irq_rid = 0;
+ sc->atse_rx_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->atse_rx_irq_rid, RF_ACTIVE | RF_SHAREABLE);
+ sc->atse_tx_irq_rid = 1;
+ sc->atse_tx_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->atse_tx_irq_rid, RF_ACTIVE | RF_SHAREABLE);
+
+ error = atse_attach(dev);
+ if (error)
+ goto err;
+
+ return (0);
+
+err:
+ /* Cleanup. */
+ atse_detach_resources(dev);
+
+ return (error);
+}
+
+static device_method_t atse_methods_fdt[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, atse_probe_fdt),
+ DEVMETHOD(device_attach, atse_attach_fdt),
+ DEVMETHOD(device_detach, atse_detach_dev),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, atse_miibus_readreg),
+ DEVMETHOD(miibus_writereg, atse_miibus_writereg),
+ DEVMETHOD(miibus_statchg, atse_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t atse_driver_fdt = {
+ "atse",
+ atse_methods_fdt,
+ sizeof(struct atse_softc)
+};
+
+DRIVER_MODULE(atse, simplebus, atse_driver_fdt, atse_devclass, 0, 0);
+DRIVER_MODULE(miibus, atse, miibus_driver, miibus_devclass, 0, 0);
+
+/* end */
diff --git a/sys/dev/altera/atse/if_atse_nexus.c b/sys/dev/altera/atse/if_atse_nexus.c
new file mode 100644
index 0000000..392e5b6
--- /dev/null
+++ b/sys/dev/altera/atse/if_atse_nexus.c
@@ -0,0 +1,258 @@
+/*-
+ * Copyright (c) 2012,2013 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC research programme.
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_device_polling.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_media.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/altera/atse/if_atsereg.h>
+
+/* "device miibus" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+MODULE_DEPEND(atse, ether, 1, 1, 1);
+MODULE_DEPEND(atse, miibus, 1, 1, 1);
+
+/*
+ * Device routines for interacting with nexus (probe, attach, detach) & helpers.
+ * XXX We should add suspend/resume later.
+ */
+static int
+atse_resource_int(device_t dev, const char *resname, int *v)
+{
+ int error;
+
+ error = resource_int_value(device_get_name(dev), device_get_unit(dev),
+ resname, v);
+ if (error != 0) {
+ /* If it does not exist, we fail, so not ingoring ENOENT. */
+ device_printf(dev, "could not fetch '%s' hint\n", resname);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+atse_resource_long(device_t dev, const char *resname, long *v)
+{
+ int error;
+
+ error = resource_long_value(device_get_name(dev), device_get_unit(dev),
+ resname, v);
+ if (error != 0) {
+ /* If it does not exist, we fail, so not ingoring ENOENT. */
+ device_printf(dev, "could not fetch '%s' hint\n", resname);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+atse_probe_nexus(device_t dev)
+{
+ struct resource *res;
+ long l;
+ int error, rid;
+
+ /*
+ * It is almost impossible to properly probe this device. We must
+ * rely on hints being set correctly. So try to get hints and
+ * one memory mapping. Must cleanup and do again in attach but
+ * should not probe successfully if not able to attach later.
+ */
+ error = atse_resource_int(dev, "rx_irq", &rid);
+ error += atse_resource_long(dev, "rx_maddr", &l);
+ error += atse_resource_long(dev, "rx_msize", &l);
+ error += atse_resource_long(dev, "rxc_maddr", &l);
+ error += atse_resource_long(dev, "rxc_msize", &l);
+ error += atse_resource_int(dev, "tx_irq", &rid);
+ error += atse_resource_long(dev, "tx_maddr", &l);
+ error += atse_resource_long(dev, "tx_msize", &l);
+ error += atse_resource_long(dev, "txc_maddr", &l);
+ error += atse_resource_long(dev, "txc_msize", &l);
+ if (error != 0)
+ return (ENXIO);
+
+ rid = 0;
+ res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (res == NULL)
+ return (ENXIO);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, res);
+
+ /* Success. */
+ device_set_desc(dev, "Altera Triple-Speed Ethernet MegaCore");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+atse_attach_nexus(device_t dev)
+{
+ struct atse_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->atse_dev = dev;
+ sc->atse_unit = device_get_unit(dev);
+
+ /* Get RX and TX IRQ and FIFO information from hints. */
+ error = atse_resource_int(dev, "rx_irq", &sc->atse_rx_irq);
+ error += atse_resource_long(dev, "rx_maddr", &sc->atse_rx_maddr);
+ error += atse_resource_long(dev, "rx_msize", &sc->atse_rx_msize);
+ error += atse_resource_long(dev, "rxc_maddr", &sc->atse_rxc_maddr);
+ error += atse_resource_long(dev, "rxc_msize", &sc->atse_rxc_msize);
+ error += atse_resource_int(dev, "tx_irq", &sc->atse_tx_irq);
+ error += atse_resource_long(dev, "tx_maddr", &sc->atse_tx_maddr);
+ error += atse_resource_long(dev, "tx_msize", &sc->atse_tx_msize);
+ error += atse_resource_long(dev, "txc_maddr", &sc->atse_txc_maddr);
+ error += atse_resource_long(dev, "txc_msize", &sc->atse_txc_msize);
+ if (error != 0)
+ return (error);
+
+ /* Avalon-MM, atse management register region. */
+ sc->atse_mem_rid = 0;
+ sc->atse_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->atse_mem_rid, RF_ACTIVE);
+ if (sc->atse_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for ctrl region\n");
+ return (ENXIO);
+ }
+
+ /*
+ * (Optional) RX IRQ and memory mapped regions.
+ * 0x00: 2 * 32bit FIFO data,
+ * 0x20: 8 * 32bit FIFO ctrl, Avalon-ST Sink to Avalon-MM R-Slave.
+ */
+ sc->atse_rx_irq_rid = 0;
+ sc->atse_rx_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &sc->atse_rx_irq_rid, sc->atse_rx_irq, sc->atse_rx_irq, 1,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ sc->atse_rx_mem_rid = 0;
+ sc->atse_rx_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &sc->atse_rx_mem_rid, sc->atse_rx_maddr, sc->atse_rx_maddr +
+ sc->atse_rx_msize, sc->atse_rx_msize, RF_ACTIVE);
+ if (sc->atse_rx_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for RX\n");
+ goto err;
+ }
+ sc->atse_rxc_mem_rid = 0;
+ sc->atse_rxc_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &sc->atse_rxc_mem_rid, sc->atse_rxc_maddr, sc->atse_rxc_maddr +
+ sc->atse_rxc_msize, sc->atse_rxc_msize, RF_ACTIVE);
+ if (sc->atse_rxc_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for RX control\n");
+ goto err;
+ }
+
+ /*
+ * (Optional) TX IRQ and memory mapped regions.
+ * 0x00: 2 * 32bit FIFO data,
+ * 0x20: 8 * 32bit FIFO ctrl, Avalon-MM W-Slave to Avalon-ST Source.
+ */
+ sc->atse_tx_irq_rid = 0;
+ sc->atse_tx_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ,
+ &sc->atse_tx_irq_rid, sc->atse_tx_irq, sc->atse_tx_irq, 1,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ sc->atse_tx_mem_rid = 0;
+ sc->atse_tx_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &sc->atse_tx_mem_rid, sc->atse_tx_maddr, sc->atse_tx_maddr +
+ sc->atse_tx_msize, sc->atse_tx_msize, RF_ACTIVE);
+ if (sc->atse_tx_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for TX\n");
+ goto err;
+ }
+ sc->atse_txc_mem_rid = 0;
+ sc->atse_txc_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY,
+ &sc->atse_txc_mem_rid, sc->atse_txc_maddr, sc->atse_txc_maddr +
+ sc->atse_txc_msize, sc->atse_txc_msize, RF_ACTIVE);
+ if (sc->atse_txc_mem_res == NULL) {
+ device_printf(dev, "failed to map memory for TX control\n");
+ goto err;
+ }
+
+ error = atse_attach(dev);
+ if (error)
+ goto err;
+
+ return (0);
+
+err:
+ /* Cleanup. */
+ atse_detach_resources(dev);
+
+ return (error);
+}
+
+static device_method_t atse_methods_nexus[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, atse_probe_nexus),
+ DEVMETHOD(device_attach, atse_attach_nexus),
+ DEVMETHOD(device_detach, atse_detach_dev),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, atse_miibus_readreg),
+ DEVMETHOD(miibus_writereg, atse_miibus_writereg),
+ DEVMETHOD(miibus_statchg, atse_miibus_statchg),
+
+ DEVMETHOD_END
+};
+
+static driver_t atse_driver_nexus = {
+ "atse",
+ atse_methods_nexus,
+ sizeof(struct atse_softc)
+};
+
+DRIVER_MODULE(atse, nexus, atse_driver_nexus, atse_devclass, 0, 0);
+DRIVER_MODULE(miibus, atse, miibus_driver, miibus_devclass, 0, 0);
+
+/* end */
diff --git a/sys/dev/altera/atse/if_atsereg.h b/sys/dev/altera/atse/if_atsereg.h
new file mode 100644
index 0000000..8caba4f
--- /dev/null
+++ b/sys/dev/altera/atse/if_atsereg.h
@@ -0,0 +1,486 @@
+/*-
+ * Copyright (c) 2012 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC research programme.
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_IF_ATSEREG_H
+#define _DEV_IF_ATSEREG_H
+
+#define ATSE_VENDOR 0x6af7
+#define ATSE_DEVICE 0x00bd
+
+/* See hints file/fdt for ctrl port and Avalon FIFO addresses. */
+
+/* Section 3. Parameter Settings. */
+/*
+ * This is a lot of options that affect the way things are synthesized.
+ * We cannot really make them all hints and most of them might be stale.
+ */
+
+/* 3-1 Core Configuration */
+#if 0
+static const char *atse_core_core_variation[] = {
+ [0] = "10/100/1000 Mbps Ethernet MAC only",
+ [1] = "10/100/1000 Mbps Ethernet MAC with 1000BASE-X/SGMII PCS",
+ [2] = "1000BASE-X/SGMII PCS only",
+ [3] = "1000 Mbps Small MAC",
+ [4] = "10/100 Mbps Small MAC",
+ NULL
+};
+static const char *atse_core_interface[] = {
+ [0] = "MII", /* Core variation 4. */
+ [1] = "GMII", /* Core variation 3. */
+ [2] = "RGMII", /* Core variation 0,1,3. */
+ [3] = "MII/GMII", /* Core variation 0,1. */
+ NULL
+};
+#endif
+#define CORE_CORE_VARIATION 1 /* atse_core_core_variation[] */
+#define CORE_INTERFACE 3 /* atse_core_interface[] */
+#define CORE_USE_INTERNAL_FIFO 1
+#define CORE_NUMBER_OF_PORTS 1 /* Internal FIFO count. */
+#define CORE_USE_TRANSCEIVER_BLOCK 1 /* SGMII PCS transceiver:
+ * LVDS I/O. */
+
+/* 3-2 MAC Options. */
+/* Ethernet MAC Options. */
+#define MAC_ENABLE_10_100_HDX_SUPPORT 0
+#define MAC_ENABLE_RG_G_MII_LOOPBACK 0
+#define MAC_ENABLE_SUPL_MAC_UCAST_ADDR 0 /* Supplementary MAC unicast. */
+#define MAC_INCLUDE_STATISTICS_COUNTERS 0
+#define MAC_STATISTICS_COUNTERS_64BIT 0
+#define MAC_INCLUDE_MC_HASHTABLE 0 /* Multicast. */
+#define MAC_ALIGN_PKTHDR_32BIT 1
+#define MAC_ENABLE_FDX_FLOW_CTRL 0
+#define MAC_ENABLE_VLAN_DETECTION 0 /* VLAN and stacked VLANs. */
+#define MAC_ENABLE_MAGIC_PKT_DETECTION 0
+/* MDIO Module. */
+#define MAC_MDIO_INCLUDE_MDIO_MODULE 1
+#define MAC_MDIO_HOST_CLOCK_DIVISOR 40 /* Not just On/Off. */
+
+/* 3-4 FIFO Options. */
+/* Width and Memory Type. */
+#if 0
+static char *fifo_memory_block[] = {
+ [0] = "M4K",
+ [1] = "M9K",
+ [2] = "M144K",
+ [3] = "MRAM",
+ [4] = "AUTO",
+ NULL
+};
+#endif
+#define FIFO_MEMORY_BLOCK 4
+#define FIFO_WITDH 32 /* Other: 8 bits. */
+/* Depth. */
+#define FIFO_DEPTH_TX 2048 /* 64 .. 64k, 2048x32bits. */
+#define FIFO_DEPTH_RX 2048 /* 64 .. 64k, 2048x32bits. */
+
+#define ATSE_TX_LIST_CNT 5 /* Certainly not bufferbloat. */
+
+/* 3-4 PCS/Transceiver Options */
+/* PCS Options. */
+#define PCS_TXRX_PHY_ID 0x00000000 /* 32 bits */
+#define PCS_TXRX_ENABLE_SGMII_BRIDGE 0
+/* Transceiver Options. */
+#define PCS_TXRX_EXP_POWER_DOWN_SIGNAL 0 /* Export power down signal. */
+#define PCS_TXRX_ENABLE_DYNAMIC_RECONF 0 /* Dynamic trans. reconfig. */
+#define PCS_TXRX_STARTING_CHANNEL 0 /* 0..284. */
+
+
+/* -------------------------------------------------------------------------- */
+
+/* XXX more values based on the bitmaps provided. Cleanup. */
+/* See regs above. */
+#define AVALON_FIFO_TX_BLOCK_DIAGRAM 0
+#define AVALON_FIFO_TX_BLOCK_DIAGRAM_SHOW_SIGANLS 0
+#define AVALON_FIFO_TX_PARAM_SINGLE_RESET_MODE 0
+#define AVALON_FIFO_TX_BASIC_OPTS_DEPTH 16
+#define AVALON_FIFO_TX_BASIC_OPTS_ALLOW_BACKPRESSURE 1
+#define AVALON_FIFO_TX_BASIC_OPTS_CLOCK_SETTING "Single Clock Mode"
+#define AVALON_FIFO_TX_BASIC_OPTS_FIFO_IMPL "Construct FIFO from embedded memory blocks"
+#define AVALON_FIFO_TX_STATUS_PORT_CREATE_STATUS_INT_FOR_INPUT 1
+#define AVALON_FIFO_TX_STATUS_PORT_CREATE_STATUS_INT_FOR_OUTPUT 0
+#define AVALON_FIFO_TX_STATUS_PORT_ENABLE_IRQ_FOR_STATUS_PORT 1
+#define AVALON_FIFO_TX_INPUT_TYPE "AVALONMM_WRITE"
+#define AVALON_FIFO_TX_OUTPUT_TYPE "AVALONST_SOURCE"
+#define AVALON_FIFO_TX_AVALON_MM_PORT_SETTINGS_DATA_WIDTH ""
+#define AVALON_FIFO_TX_AVALON_ST_PORT_SETTINGS_BITS_PER_SYMBOL 8
+#define AVALON_FIFO_TX_AVALON_ST_PORT_SETTINGS_SYM_PER_BEAT 4
+#define AVALON_FIFO_TX_AVALON_ST_PORT_SETTINGS_ERROR_WIDTH 1
+#define AVALON_FIFO_TX_AVALON_ST_PORT_SETTINGS_CHANNEL_WIDTH 0
+#define AVALON_FIFO_TX_AVALON_ST_PORT_SETTINGS_ENABLE_PACKET_DATA 1
+
+#define AVALON_FIFO_RX_BLOCK_DIAGRAM 0
+#define AVALON_FIFO_RX_BLOCK_DIAGRAM_SHOW_SIGNALS 0
+#define AVALON_FIFO_RX_PARAM_SINGLE_RESET_MODE 0
+#define AVALON_FIFO_RX_BASIC_OPTS_DEPTH 16
+#define AVALON_FIFO_RX_BASIC_OPTS_ALLOW_BACKPRESSURE 1
+#define AVALON_FIFO_RX_BASIC_OPTS_CLOCK_SETTING "Single Clock Mode"
+#define AVALON_FIFO_RX_BASIC_OPTS_FIFO_IMPL "Construct FIFO from embedded memory blocks"
+#define AVALON_FIFO_RX_STATUS_PORT_CREATE_STATUS_INT_FOR_INPUT 1
+#define AVALON_FIFO_RX_STATUS_PORT_CREATE_STATUS_INT_FOR_OUTPUT 0
+#define AVALON_FIFO_RX_STATUS_PORT_ENABLE_IRQ_FOR_STATUS_PORT 1
+#define AVALON_FIFO_RX_INPUT_TYPE "AVALONST_SINK"
+#define AVALON_FIFO_RX_OUTPUT_TYPE "AVALONMM_READ"
+#define AVALON_FIFO_RX_AVALON_MM_PORT_SETTINGS_DATA_WIDTH ""
+#define AVALON_FIFO_RX_AVALON_ST_PORT_SETTINGS_BITS_PER_SYMBOL 8
+#define AVALON_FIFO_RX_AVALON_ST_PORT_SETTINGS_SYM_PER_BEAT 4
+#define AVALON_FIFO_RX_AVALON_ST_PORT_SETTINGS_ERROR_WIDTH 6
+#define AVALON_FIFO_RX_AVALON_ST_PORT_SETTINGS_CHANNEL_WIDTH 0
+#define AVALON_FIFO_RX_AVALON_ST_PORT_SETTINGS_ENABLE_PACKET_DATA 1
+
+/* -------------------------------------------------------------------------- */
+
+/* 5. Configuration Register Space. */
+
+/* 5-1, MAC Configuration Register Space; Dword offsets. */
+/* 0x00 - 0x17, Base Configuration. */
+#define BASE_CONFIG_REV 0x00 /* ro, IP Core ver. */
+#define BASE_CFG_REV_VER_MASK 0x0000FFFF
+#define BASE_CFG_REV_CUST_VERSION__MASK 0xFFFF0000
+
+#define BASE_CFG_SCRATCH 0x01 /* rw, 0 */
+
+#define BASE_CFG_COMMAND_CONFIG 0x02 /* rw, 0 */
+#define BASE_CFG_COMMAND_CONFIG_TX_ENA (1<<0) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_RX_ENA (1<<1) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_XON_GEN (1<<2) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_ETH_SPEED (1<<3) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_PROMIS_EN (1<<4) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_PAD_EN (1<<5) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_CRC_FWD (1<<6) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_PAUSE_FWD (1<<7) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_PAUSE_IGNORE (1<<8) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_TX_ADDR_INS (1<<9) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_HD_ENA (1<<10) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_EXCESS_COL (1<<11) /* ro */
+#define BASE_CFG_COMMAND_CONFIG_LATE_COL (1<<12) /* ro */
+#define BASE_CFG_COMMAND_CONFIG_SW_RESET (1<<13) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_MHASH_SEL (1<<14) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_LOOP_ENA (1<<15) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_TX_ADDR_SEL (1<<16|1<<17|1<<18) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_MAGIC_ENA (1<<19) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_SLEEP (1<<20) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_WAKEUP (1<<21) /* ro */
+#define BASE_CFG_COMMAND_CONFIG_XOFF_GEN (1<<22) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_CNTL_FRM_ENA (1<<23) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_NO_LGTH_CHECK (1<<24) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_ENA_10 (1<<25) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_RX_ERR_DISC (1<<26) /* rw */
+#define BASE_CFG_COMMAND_CONFIG_DISABLE_READ_TIMEOUT (1<<27) /* rw */
+ /* 28-30 Reserved. */ /* - */
+#define BASE_CFG_COMMAND_CONFIG_CNT_RESET (1<<31) /* rw */
+
+#define BASE_CFG_MAC_0 0x03 /* rw, 0 */
+#define BASE_CFG_MAC_1 0x04 /* rw, 0 */
+#define BASE_CFG_FRM_LENGTH 0x05 /* rw/ro, 1518 */
+#define BASE_CFG_PAUSE_QUANT 0x06 /* rw, 0 */
+#define BASE_CFG_RX_SECTION_EMPTY 0x07 /* rw/ro, 0 */
+#define BASE_CFG_RX_SECTION_FULL 0x08 /* rw/ro, 0 */
+#define BASE_CFG_TX_SECTION_EMPTY 0x09 /* rw/ro, 0 */
+#define BASE_CFG_TX_SECTION_FULL 0x0A /* rw/ro, 0 */
+#define BASE_CFG_RX_ALMOST_EMPTY 0x0B /* rw/ro, 0 */
+#define BASE_CFG_RX_ALMOST_FULL 0x0C /* rw/ro, 0 */
+#define BASE_CFG_TX_ALMOST_EMPTY 0x0D /* rw/ro, 0 */
+#define BASE_CFG_TX_ALMOST_FULL 0x0E /* rw/ro, 0 */
+#define BASE_CFG_MDIO_ADDR0 0x0F /* rw, 0 */
+#define BASE_CFG_MDIO_ADDR1 0x10 /* rw, 1 */
+#define BASE_CFG_HOLDOFF_QUANT 0x11 /* rw, 0xFFFF */
+/* 0x12-0x16 Reserved. */ /* -, 0 */
+#define BASE_CFG_TX_IPG_LENGTH 0x17 /* rw, 0 */
+
+/* 0x18 - 0x38, Statistics Counters. */
+#define STATS_A_MAC_ID_0 0x18 /* ro */
+#define STATS_A_MAC_ID_1 0x19 /* ro */
+#define STATS_A_FRAMES_TX_OK 0x1A /* ro */
+#define STATS_A_FRAMES_RX_OK 0x1B /* ro */
+#define STATS_A_FCS_ERRORS 0x1C /* ro */
+#define STATS_A_ALIGNMENT_ERRORS 0x1D /* ro */
+#define STATS_A_OCTETS_TX_OK 0x1E /* ro */
+#define STATS_A_OCTETS_RX_OK 0x1F /* ro */
+#define STATS_A_TX_PAUSE_MAX_CTRL_FRAME 0x20 /* ro */
+#define STATS_A_RX_PAUSE_MAX_CTRL_FRAME 0x21 /* ro */
+#define STATS_IF_IN_ERRORS 0x22 /* ro */
+#define STATS_IF_OUT_ERRORS 0x23 /* ro */
+#define STATS_IF_IN_UCAST_PKTS 0x24 /* ro */
+#define STATS_IF_IN_MULTICAST_PKTS 0x25 /* ro */
+#define STATS_IF_IN_BROADCAST_PKTS 0x26 /* ro */
+#define STATS_IF_OUT_DISCARDS 0x27 /* ro */
+#define STATS_IF_OUT_UCAST_PKTS 0x28 /* ro */
+#define STATS_IF_OUT_MULTICAST_PKTS 0x29 /* ro */
+#define STATS_IF_OUT_BROADCAST_PKTS 0x2A /* ro */
+#define STATS_ETHER_STATS_DROP_EVENT 0x2B /* ro */
+#define STATS_ETHER_STATS_OCTETS 0x2C /* ro */
+#define STATS_ETHER_STATS_PKTS 0x2D /* ro */
+#define STATS_ETHER_STATS_USIZE_PKTS 0x2E /* ro */
+#define STATS_ETHER_STATS_OSIZE_PKTS 0x2F /* ro */
+#define STATS_ETHER_STATS_PKTS_64_OCTETS 0x30 /* ro */
+#define STATS_ETHER_STATS_PKTS_65_TO_127_OCTETS 0x31 /* ro */
+#define STATS_ETHER_STATS_PKTS_128_TO_255_OCTETS 0x32 /* ro */
+#define STATS_ETHER_STATS_PKTS_256_TO_511_OCTETS 0x33 /* ro */
+#define STATS_ETHER_STATS_PKTS_512_TO_1023_OCTETS 0x34 /* ro */
+#define STATS_ETHER_STATS_PKTS_1024_TO_1518_OCTETS 0x35 /* ro */
+#define STATS_ETHER_STATS_PKTS_1519_TO_X_OCTETS 0x36 /* ro */
+#define STATS_ETHER_STATS_JABBERS 0x37 /* ro */
+#define STATS_ETHER_STATS_FRAGMENTS 0x38 /* ro */
+ /* 0x39, Reserved. */ /* - */
+
+/* 0x3A, Transmit Command. */
+#define TX_CMD_STAT 0x3A /* rw */
+#define TX_CMD_STAT_OMIT_CRC (1<<17)
+#define TX_CMD_STAT_TX_SHIFT16 (1<<18)
+
+/* 0x3B, Receive Command. */
+#define RX_CMD_STAT 0x3B /* rw */
+#define RX_CMD_STAT_RX_SHIFT16 (1<<25)
+
+/* 0x3C - 0x3E, Extended Statistics Counters. */
+#define ESTATS_MSB_A_OCTETS_TX_OK 0x3C /* ro */
+#define ESTATS_MSB_A_OCTETS_RX_OK 0x3D /* ro */
+#define ESTATS_MSB_ETHER_STATS_OCTETS 0x3E /* ro */
+
+/* 0x3F, Reserved. */
+
+/* 0x40 - 0x7F, Multicast Hash Table. */
+#define MHASH_START 0x40
+#define MHASH_LEN 0x3F
+
+/* 0x80 - 0x9F, MDIO Space 0 or PCS Function Configuration. */
+#define MDIO_0_START 0x80
+
+/* The following are offsets to the first PCS register at 0x80. */
+/* See sys/dev/mii/mii.h. */
+#define PCS_CONTROL 0x00 /* rw */
+ /* Bits 0:4, Reserved. */ /* - */
+#define PCS_CONTROL_UNIDIRECTIONAL_ENABLE (1<<5) /* rw */
+#define PCS_CONTROL_SPEED_SELECTION (1<<6|1<<13) /* ro */
+#define PCS_CONTROL_COLLISION_TEST (1<<7) /* ro */
+#define PCS_CONTROL_DUPLEX_MODE (1<<8) /* ro */
+#define PCS_CONTROL_RESTART_AUTO_NEGOTIATION (1<<9) /* rw */
+#define PCS_CONTROL_ISOLATE (1<<10) /* rw */
+#define PCS_CONTROL_POWERDOWN (1<<11) /* rw */
+#define PCS_CONTROL_AUTO_NEGOTIATION_ENABLE (1<<12) /* rw */
+ /* See bit 6 above. */ /* ro */
+#define PCS_CONTROL_LOOPBACK (1<<14) /* rw */
+#define PCS_CONTROL_RESET (1<<15) /* rw */
+
+#define PCS_STATUS 0x01 /* ro */
+#define PCS_STATUS_EXTENDED_CAPABILITY (1<<0) /* ro */
+#define PCS_STATUS_JABBER_DETECT (1<<1) /* -, 0 */
+#define PCS_STATUS_LINK_STATUS (1<<2) /* ro */
+#define PCS_STATUS_AUTO_NEGOTIATION_ABILITY (1<<3) /* ro */
+#define PCS_STATUS_REMOTE_FAULT (1<<4) /* -, 0 */
+#define PCS_STATUS_AUTO_NEGOTIATION_COMPLETE (1<<5) /* ro */
+#define PCS_STATUS_MF_PREAMBLE_SUPPRESSION (1<<6) /* -, 0 */
+#define PCS_STATUS_UNIDIRECTIONAL_ABILITY (1<<7) /* ro */
+#define PCS_STATUS_EXTENDED_STATUS (1<<8) /* -, 0 */
+#define PCS_STATUS_100BASET2_HALF_DUPLEX (1<<9) /* ro */
+#define PCS_STATUS_100BASET2_FULL_DUPLEX (1<<10) /* ro */
+#define PCS_STATUS_10MBPS_HALF_DUPLEX (1<<11) /* ro */
+#define PCS_STATUS_10MBPS_FULL_DUPLEX (1<<12) /* ro */
+#define PCS_STATUS_100BASE_X_HALF_DUPLEX (1<<13) /* ro */
+#define PCS_STATUS_100BASE_X_FULL_DUPLEX (1<<14) /* ro */
+#define PCS_STATUS_100BASE_T4 (1<<15) /* ro */
+
+#define PCS_PHY_IDENTIFIER_0 0x02 /* ro */
+#define PCS_PHY_IDENTIFIER_1 0x03 /* ro */
+
+#define PCS_DEV_ABILITY 0x04 /* rw */
+ /* 1000BASE-X */
+ /* Bits 0:4, Reserved. */ /* - */
+#define PCS_DEV_ABILITY_1000BASE_X_FD (1<<5) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_HD (1<<6) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_PS1 (1<<7) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_PS2 (1<<8) /* rw */
+ /* Bits 9:11, Reserved. */ /* - */
+#define PCS_DEV_ABILITY_1000BASE_X_RF1 (1<<12) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_RF2 (1<<13) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_ACK (1<<14) /* rw */
+#define PCS_DEV_ABILITY_1000BASE_X_NP (1<<15) /* rw */
+
+#define PCS_PARTNER_ABILITY 0x05 /* ro */
+ /* 1000BASE-X */
+ /* Bits 0:4, Reserved. */ /* - */
+#define PCS_PARTNER_ABILITY_1000BASE_X_FD (1<<5) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_HD (1<<6) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_PS1 (1<<7) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_PS2 (1<<8) /* ro */
+ /* Bits 9:11, Reserved. */ /* - */
+#define PCS_PARTNER_ABILITY_1000BASE_X_RF1 (1<<12) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_RF2 (1<<13) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_ACK (1<<14) /* ro */
+#define PCS_PARTNER_ABILITY_1000BASE_X_NP (1<<15) /* ro */
+ /* SGMII */
+ /* Bits 0:9, Reserved. */ /* - */
+#define PCS_PARTNER_ABILITY_SGMII_COPPER_SPEED0 (1<<10) /* ro */
+#define PCS_PARTNER_ABILITY_SGMII_COPPER_SPEED1 (1<<11) /* ro */
+#define PCS_PARTNER_ABILITY_SGMII_COPPER_DUPLEX_STATUS (1<<12) /* ro */
+ /* Bit 13, Reserved. */ /* - */
+#define PCS_PARTNER_ABILITY_SGMII_ACK (1<<14) /* ro */
+#define PCS_PARTNER_ABILITY_SGMII_COPPER_LINK_STATUS (1<<15) /* ro */
+
+#define PCS_AN_EXPANSION 0x06 /* ro */
+#define PCS_AN_EXPANSION_LINK_PARTNER_AUTO_NEGOTIATION_ABLE (1<<0) /* ro */
+#define PCS_AN_EXPANSION_PAGE_RECEIVE (1<<1) /* ro */
+#define PCS_AN_EXPANSION_NEXT_PAGE_ABLE (1<<2) /* -, 0 */
+ /* Bits 3:15, Reserved. */ /* - */
+
+#define PCS_DEVICE_NEXT_PAGE 0x07 /* ro */
+#define PCS_PARTNER_NEXT_PAGE 0x08 /* ro */
+#define PCS_MASTER_SLAVE_CNTL 0x09 /* ro */
+#define PCS_MASTER_SLAVE_STAT 0x0A /* ro */
+ /* 0x0B - 0x0E, Reserved */ /* - */
+#define PCS_EXTENDED_STATUS 0x0F /* ro */
+/* Specific Extended Registers. */
+#define PCS_EXT_SCRATCH 0x10 /* rw */
+#define PCS_EXT_REV 0x11 /* ro */
+#define PCS_EXT_LINK_TIMER_0 0x12 /* rw */
+#define PCS_EXT_LINK_TIMER_1 0x13 /* rw */
+#define PCS_EXT_IF_MODE 0x14 /* rw */
+#define PCS_EXT_IF_MODE_SGMII_ENA (1<<0) /* rw */
+#define PCS_EXT_IF_MODE_USE_SGMII_AN (1<<1) /* rw */
+#define PCS_EXT_IF_MODE_SGMII_SPEED1 (1<<2) /* rw */
+#define PCS_EXT_IF_MODE_SGMII_SPEED0 (1<<3) /* rw */
+#define PCS_EXT_IF_MODE_SGMII_DUPLEX (1<<4) /* rw */
+ /* Bits 5:15, Reserved. */ /* - */
+
+#define PCS_EXT_DISABLE_READ_TIMEOUT 0x15 /* rw */
+#define PCS_EXT_READ_TIMEOUT 0x16 /* r0 */
+ /* 0x17-0x1F, Reserved. */
+
+/* 0xA0 - 0xBF, MDIO Space 1. */
+#define MDIO_1_START 0xA0
+#define ATSE_BMCR MDIO_1_START
+
+/* 0xC0 - 0xC7, Supplementary Address. */
+#define SUPPL_ADDR_SMAC_0_0 0xC0 /* rw */
+#define SUPPL_ADDR_SMAC_0_1 0xC1 /* rw */
+#define SUPPL_ADDR_SMAC_1_0 0xC2 /* rw */
+#define SUPPL_ADDR_SMAC_1_1 0xC3 /* rw */
+#define SUPPL_ADDR_SMAC_2_0 0xC4 /* rw */
+#define SUPPL_ADDR_SMAC_2_1 0xC5 /* rw */
+#define SUPPL_ADDR_SMAC_3_0 0xC6 /* rw */
+#define SUPPL_ADDR_SMAC_3_1 0xC7 /* rw */
+
+/* 0xC8 - 0xCF, Reserved; set to zero, ignore on read. */
+/* 0xD7 - 0xFF, Reserved; set to zero, ignore on read. */
+
+
+/* -------------------------------------------------------------------------- */
+
+/* DE4 Intel Strata Flash Ethernet Option Bits area. */
+/* XXX-BZ this is something a loader will have to handle for us. */
+#define ALTERA_ETHERNET_OPTION_BITS_OFF 0x00008000
+#define ALTERA_ETHERNET_OPTION_BITS_LEN 0x00007fff
+
+/* -------------------------------------------------------------------------- */
+
+struct atse_softc {
+ struct ifnet *atse_ifp;
+ struct mbuf *atse_rx_m;
+ struct mbuf *atse_tx_m;
+ uint8_t *atse_tx_buf;
+ struct resource *atse_mem_res;
+ struct resource *atse_rx_irq_res;
+ struct resource *atse_rx_mem_res;
+ struct resource *atse_rxc_mem_res;
+ struct resource *atse_tx_irq_res;
+ struct resource *atse_tx_mem_res;
+ struct resource *atse_txc_mem_res;
+ device_t atse_miibus;
+ device_t atse_dev;
+ int atse_unit;
+ int atse_mem_rid;
+ int atse_rx_irq_rid;
+ int atse_rx_mem_rid;
+ int atse_rxc_mem_rid;
+ int atse_tx_irq_rid;
+ int atse_tx_mem_rid;
+ int atse_txc_mem_rid;
+ int atse_phy_addr;
+ int atse_if_flags;
+ int atse_rx_irq;
+ int atse_tx_irq;
+ u_long atse_rx_maddr;
+ u_long atse_rx_msize;
+ u_long atse_tx_maddr;
+ u_long atse_tx_msize;
+ u_long atse_rxc_maddr;
+ u_long atse_rxc_msize;
+ u_long atse_txc_maddr;
+ u_long atse_txc_msize;
+ void *atse_rx_intrhand;
+ void *atse_tx_intrhand;
+ bus_addr_t atse_bmcr0;
+ bus_addr_t atse_bmcr1;
+ uint32_t atse_flags;
+#define ATSE_FLAGS_LINK 0x00000001
+#define ATSE_FLAGS_ERROR 0x00000002
+#define ATSE_FLAGS_SOP_SEEN 0x00000004
+ uint8_t atse_eth_addr[ETHER_ADDR_LEN];
+#define ATSE_ETH_ADDR_DEF 0x01
+#define ATSE_ETH_ADDR_SUPP1 0x02
+#define ATSE_ETH_ADDR_SUPP2 0x04
+#define ATSE_ETH_ADDR_SUPP3 0x08
+#define ATSE_ETH_ADDR_SUPP4 0x10
+#define ATSE_ETH_ADDR_ALL 0x1f
+ uint16_t atse_watchdog_timer;
+ uint16_t atse_tx_m_offset;
+ uint16_t atse_tx_buf_len;
+ uint16_t atse_rx_buf_len;
+ int16_t atse_rx_cycles; /* POLLING */
+#define RX_CYCLES_IN_INTR 5
+ uint32_t atse_rx_err[6];
+#define ATSE_RX_ERR_FIFO_THRES_EOP 0 /* FIFO threshold reached, on EOP. */
+#define ATSE_RX_ERR_ELEN 1 /* Frame/payload length not valid. */
+#define ATSE_RX_ERR_CRC32 2 /* CRC-32 error. */
+#define ATSE_RX_ERR_FIFO_THRES_TRUNC 3 /* FIFO thresh., truncated frame. */
+#define ATSE_RX_ERR_4 4 /* ? */
+#define ATSE_RX_ERR_5 5 /* / */
+#define ATSE_RX_ERR_MAX 6
+ struct callout atse_tick;
+ struct mtx atse_mtx;
+};
+
+
+int atse_attach(device_t);
+int atse_detach_dev(device_t);
+void atse_detach_resources(device_t);
+
+int atse_miibus_readreg(device_t, int, int);
+int atse_miibus_writereg(device_t, int, int, int);
+void atse_miibus_statchg(device_t);
+
+extern devclass_t atse_devclass;
+
+#endif /* _DEV_IF_ATSEREG_H */
+
+/* end */
OpenPOWER on IntegriCloud