diff options
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 */ |