diff options
author | brooks <brooks@FreeBSD.org> | 2013-10-18 20:44:19 +0000 |
---|---|---|
committer | brooks <brooks@FreeBSD.org> | 2013-10-18 20:44:19 +0000 |
commit | 6e05225f6e411da2d91b0acc7c9139c2dd3b4192 (patch) | |
tree | 62788cbe5a10013e199a51d75a0e9376bd579f75 /sys/dev/altera | |
parent | 7b91ff85997059c8ae7e8bb07d1f1a23f8333471 (diff) | |
download | FreeBSD-src-6e05225f6e411da2d91b0acc7c9139c2dd3b4192.zip FreeBSD-src-6e05225f6e411da2d91b0acc7c9139c2dd3b4192.tar.gz |
MFP4: 221483, 221567, 221568, 221670, 221677, 221678, 221800, 221801,
221804, 221805, 222004, 222006, 222055, 222820, 1135077, 1135118, 1136259
Add atse(4), a driver for the Altera Triple Speed Ethernet MegaCore.
The current driver support gigabit Ethernet speeds only and works with
the MegaCore only in the internal FIFO configuration in the soon to be
open sourced BERI CPU configuration.
Submitted by: bz
MFC after: 3 days
Sponsored by: DARPA/AFRL
Diffstat (limited to 'sys/dev/altera')
-rw-r--r-- | sys/dev/altera/atse/a_api.h | 98 | ||||
-rw-r--r-- | sys/dev/altera/atse/if_atse.c | 1979 | ||||
-rw-r--r-- | sys/dev/altera/atse/if_atse_fdt.c | 216 | ||||
-rw-r--r-- | sys/dev/altera/atse/if_atse_nexus.c | 258 | ||||
-rw-r--r-- | sys/dev/altera/atse/if_atsereg.h | 486 |
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 */ |