diff options
author | marius <marius@FreeBSD.org> | 2009-06-15 18:22:41 +0000 |
---|---|---|
committer | marius <marius@FreeBSD.org> | 2009-06-15 18:22:41 +0000 |
commit | 6c0f8373ed31bb2300d516edbae32f2e9dbf72b3 (patch) | |
tree | 94a8433e796ffe8fced15b2a1a94604cbac0af7f /sys/dev | |
parent | 84d5b1472aacb5d5e2616dab2e337fd7ddd1d21f (diff) | |
download | FreeBSD-src-6c0f8373ed31bb2300d516edbae32f2e9dbf72b3.zip FreeBSD-src-6c0f8373ed31bb2300d516edbae32f2e9dbf72b3.tar.gz |
Add cas(4), a driver for Sun Cassini/Cassini+ and National Semiconductor
DP83065 Saturn Gigabit Ethernet controllers. These are the successors
of the Sun GEM controllers and still have a similar but extended transmit
logic. As such this driver is based on gem(4).
Thanks to marcel@ for providing a Sun Quad GigaSwift Ethernet UTP (QGE)
card which was vital for getting this driver to work on architectures
not using Open Firmware.
Approved by: re (kib)
MFC after: 2 weeks
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cas/if_cas.c | 2772 | ||||
-rw-r--r-- | sys/dev/cas/if_casreg.h | 1002 | ||||
-rw-r--r-- | sys/dev/cas/if_casvar.h | 260 |
3 files changed, 4034 insertions, 0 deletions
diff --git a/sys/dev/cas/if_cas.c b/sys/dev/cas/if_cas.c new file mode 100644 index 0000000..cba28af --- /dev/null +++ b/sys/dev/cas/if_cas.c @@ -0,0 +1,2772 @@ +/*- + * Copyright (C) 2001 Eduardo Horvath. + * Copyright (c) 2001-2003 Thomas Moestl + * Copyright (c) 2007-2009 Marius Strobl <marius@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: gem.c,v 1.21 2002/06/01 23:50:58 lukem Exp + * from: FreeBSD: if_gem.c 182060 2008-08-23 15:03:26Z marius + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * driver for Sun Cassini/Cassini+ and National Semiconductor DP83065 + * Saturn Gigabit Ethernet controllers + */ + +#if 0 +#define CAS_DEBUG +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/callout.h> +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/refcount.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/rman.h> + +#include <net/bpf.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_vlan_var.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> + +#include <machine/bus.h> +#if defined(__powerpc__) || defined(__sparc64__) +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> +#endif +#include <machine/resource.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/cas/if_casreg.h> +#include <dev/cas/if_casvar.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include "miibus_if.h" + +#define RINGASSERT(n , min, max) \ + CTASSERT(powerof2(n) && (n) >= (min) && (n) <= (max)) + +RINGASSERT(CAS_NRXCOMP, 128, 32768); +RINGASSERT(CAS_NRXDESC, 32, 8192); +RINGASSERT(CAS_NRXDESC2, 32, 8192); +RINGASSERT(CAS_NTXDESC, 32, 8192); + +#undef RINGASSERT + +#define CCDASSERT(m, a) \ + CTASSERT((offsetof(struct cas_control_data, m) & ((a) - 1)) == 0) + +CCDASSERT(ccd_rxcomps, CAS_RX_COMP_ALIGN); +CCDASSERT(ccd_rxdescs, CAS_RX_DESC_ALIGN); +CCDASSERT(ccd_rxdescs2, CAS_RX_DESC_ALIGN); + +#undef CCDASSERT + +#define CAS_TRIES 10000 + +/* + * According to documentation, the hardware has support for basic TCP + * checksum offloading only, in practice this can be also used for UDP + * however (i.e. the problem of previous Sun NICs that a checksum of 0x0 + * is not converted to 0xffff no longer exists). + */ +#define CAS_CSUM_FEATURES (CSUM_TCP | CSUM_UDP) + +static inline void cas_add_rxdesc(struct cas_softc *sc, u_int idx); +static int cas_attach(struct cas_softc *sc); +static int cas_bitwait(struct cas_softc *sc, bus_addr_t r, uint32_t clr, + uint32_t set); +static void cas_cddma_callback(void *xsc, bus_dma_segment_t *segs, + int nsegs, int error); +static void cas_detach(struct cas_softc *sc); +static int cas_disable_rx(struct cas_softc *sc); +static int cas_disable_tx(struct cas_softc *sc); +static void cas_eint(struct cas_softc *sc, u_int status); +static void cas_free(void *arg1, void* arg2); +static void cas_init(void *xsc); +static void cas_init_locked(struct cas_softc *sc); +static void cas_init_regs(struct cas_softc *sc); +static void cas_intr(void *v); +static int cas_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); +static int cas_load_txmbuf(struct cas_softc *sc, struct mbuf **m_head); +static int cas_mediachange(struct ifnet *ifp); +static void cas_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr); +static void cas_meminit(struct cas_softc *sc); +static void cas_mifinit(struct cas_softc *sc); +static int cas_mii_readreg(device_t dev, int phy, int reg); +static void cas_mii_statchg(device_t dev); +static int cas_mii_writereg(device_t dev, int phy, int reg, int val); +static void cas_reset(struct cas_softc *sc); +static int cas_reset_rx(struct cas_softc *sc); +static int cas_reset_tx(struct cas_softc *sc); +static void cas_resume(struct cas_softc *sc); +static u_int cas_descsize(u_int sz); +static void cas_rint(struct cas_softc *sc); +static void cas_rint_timeout(void *arg); +static inline void cas_rxcksum(struct mbuf *m, uint16_t cksum); +static inline void cas_rxcompinit(struct cas_rx_comp *rxcomp); +static u_int cas_rxcompsize(u_int sz); +static void cas_rxdma_callback(void *xsc, bus_dma_segment_t *segs, + int nsegs, int error); +static void cas_setladrf(struct cas_softc *sc); +static void cas_start(struct ifnet *ifp); +static void cas_start_locked(struct ifnet *ifp); +static void cas_stop(struct ifnet *ifp); +static void cas_suspend(struct cas_softc *sc); +static void cas_tick(void *arg); +static void cas_tint(struct cas_softc *sc); +static inline void cas_txkick(struct cas_softc *sc); +static int cas_watchdog(struct cas_softc *sc); + +static devclass_t cas_devclass; + +MODULE_DEPEND(cas, ether, 1, 1, 1); +MODULE_DEPEND(cas, miibus, 1, 1, 1); + +#ifdef CAS_DEBUG +#include <sys/ktr.h> +#define KTR_CAS KTR_CT2 +#endif + +static int +cas_attach(struct cas_softc *sc) +{ + struct cas_txsoft *txs; + struct ifnet *ifp; + int error, i; + uint32_t v; + + /* Set up ifnet structure. */ + ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) + return (ENOSPC); + ifp->if_softc = sc; + if_initname(ifp, device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = cas_start; + ifp->if_ioctl = cas_ioctl; + ifp->if_init = cas_init; + IFQ_SET_MAXLEN(&ifp->if_snd, CAS_TXQUEUELEN); + ifp->if_snd.ifq_drv_maxlen = CAS_TXQUEUELEN; + IFQ_SET_READY(&ifp->if_snd); + + callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0); + callout_init_mtx(&sc->sc_rx_ch, &sc->sc_mtx, 0); + + /* Make sure the chip is stopped. */ + cas_reset(sc); + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE, 0, NULL, NULL, + &sc->sc_pdmatag); + if (error != 0) + goto fail_ifnet; + + error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + CAS_PAGE_SIZE, 1, CAS_PAGE_SIZE, 0, NULL, NULL, &sc->sc_rdmatag); + if (error != 0) + goto fail_ptag; + + error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + MCLBYTES * CAS_NTXSEGS, CAS_NTXSEGS, MCLBYTES, + BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_tdmatag); + if (error != 0) + goto fail_rtag; + + error = bus_dma_tag_create(sc->sc_pdmatag, CAS_TX_DESC_ALIGN, 0, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, + sizeof(struct cas_control_data), 1, + sizeof(struct cas_control_data), 0, + NULL, NULL, &sc->sc_cdmatag); + if (error != 0) + goto fail_ttag; + + /* + * Allocate the control data structures, create and load the + * DMA map for it. + */ + if ((error = bus_dmamem_alloc(sc->sc_cdmatag, + (void **)&sc->sc_control_data, + BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, + &sc->sc_cddmamap)) != 0) { + device_printf(sc->sc_dev, + "unable to allocate control data, error = %d\n", error); + goto fail_ctag; + } + + sc->sc_cddma = 0; + if ((error = bus_dmamap_load(sc->sc_cdmatag, sc->sc_cddmamap, + sc->sc_control_data, sizeof(struct cas_control_data), + cas_cddma_callback, sc, 0)) != 0 || sc->sc_cddma == 0) { + device_printf(sc->sc_dev, + "unable to load control data DMA map, error = %d\n", + error); + goto fail_cmem; + } + + /* + * Initialize the transmit job descriptors. + */ + STAILQ_INIT(&sc->sc_txfreeq); + STAILQ_INIT(&sc->sc_txdirtyq); + + /* + * Create the transmit buffer DMA maps. + */ + error = ENOMEM; + for (i = 0; i < CAS_TXQUEUELEN; i++) { + txs = &sc->sc_txsoft[i]; + txs->txs_mbuf = NULL; + txs->txs_ndescs = 0; + if ((error = bus_dmamap_create(sc->sc_tdmatag, 0, + &txs->txs_dmamap)) != 0) { + device_printf(sc->sc_dev, + "unable to create TX DMA map %d, error = %d\n", + i, error); + goto fail_txd; + } + STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); + } + + /* + * Allocate the receive buffers, create and load the DMA maps + * for them. + */ + for (i = 0; i < CAS_NRXDESC; i++) { + if ((error = bus_dmamem_alloc(sc->sc_rdmatag, + &sc->sc_rxdsoft[i].rxds_buf, BUS_DMA_WAITOK, + &sc->sc_rxdsoft[i].rxds_dmamap)) != 0) { + device_printf(sc->sc_dev, + "unable to allocate RX buffer %d, error = %d\n", + i, error); + goto fail_rxmem; + } + + sc->sc_rxdptr = i; + sc->sc_rxdsoft[i].rxds_paddr = 0; + if ((error = bus_dmamap_load(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_dmamap, sc->sc_rxdsoft[i].rxds_buf, + CAS_PAGE_SIZE, cas_rxdma_callback, sc, 0)) != 0 || + sc->sc_rxdsoft[i].rxds_paddr == 0) { + device_printf(sc->sc_dev, + "unable to load RX DMA map %d, error = %d\n", + i, error); + goto fail_rxmap; + } + } + + CAS_WRITE_4(sc, CAS_PCS_DATAPATH, CAS_PCS_DATAPATH_MII); + + cas_mifinit(sc); + + /* + * Look for an external PHY. + */ + error = ENXIO; + v = CAS_READ_4(sc, CAS_MIF_CONF); + if ((v & CAS_MIF_CONF_MDI1) != 0) { + v |= CAS_MIF_CONF_PHY_SELECT; + CAS_WRITE_4(sc, CAS_MIF_CONF, v); + switch (sc->sc_variant) { + default: + sc->sc_phyad = -1; + break; + } + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + cas_mediachange, cas_mediastatus); + } + + /* + * Fall back on an internal PHY if no external PHY was found. + */ + if (error != 0 && (v & CAS_MIF_CONF_MDI0) != 0) { + v &= ~CAS_MIF_CONF_PHY_SELECT; + CAS_WRITE_4(sc, CAS_MIF_CONF, v); + switch (sc->sc_variant) { + default: + sc->sc_phyad = -1; + break; + } + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + cas_mediachange, cas_mediastatus); + } + + /* + * Try the external PCS SERDES if we didn't find any PHYs. + */ + if (error != 0) { + CAS_WRITE_4(sc, CAS_PCS_DATAPATH, CAS_PCS_DATAPATH_SERDES); + CAS_WRITE_4(sc, CAS_PCS_SERDES_CTRL, CAS_PCS_SERDES_CTRL_ESD); + CAS_WRITE_4(sc, CAS_PCS_CONF_EN, CAS_PCS_CONF_EN); + sc->sc_flags |= CAS_SERDES; + sc->sc_phyad = CAS_PHYAD_EXTERNAL; + error = mii_phy_probe(sc->sc_dev, &sc->sc_miibus, + cas_mediachange, cas_mediastatus); + } + + if (error != 0) { + device_printf(sc->sc_dev, "PHY probe failed: %d\n", error); + goto fail_rxmap; + } + sc->sc_mii = device_get_softc(sc->sc_miibus); + + /* + * From this point forward, the attachment cannot fail. A failure + * before this point releases all resources that may have been + * allocated. + */ + + /* Announce FIFO sizes. */ + v = CAS_READ_4(sc, CAS_TX_FIFO_SIZE); + device_printf(sc->sc_dev, "%ukB RX FIFO, %ukB TX FIFO\n", + CAS_RX_FIFO_SIZE / 1024, v / 16); + + /* Attach the interface. */ + ether_ifattach(ifp, sc->sc_enaddr); + + /* + * Tell the upper layer(s) we support long frames/checksum offloads. + */ + ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); + ifp->if_capabilities = IFCAP_VLAN_MTU; + if ((sc->sc_flags & CAS_NO_CSUM) == 0) { + ifp->if_capabilities |= IFCAP_HWCSUM; + ifp->if_hwassist = CAS_CSUM_FEATURES; + } + ifp->if_capenable = ifp->if_capabilities; + + return (0); + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_rxmap: + for (i = 0; i < CAS_NRXDESC; i++) + if (sc->sc_rxdsoft[i].rxds_paddr != 0) + bus_dmamap_unload(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_dmamap); + fail_rxmem: + for (i = 0; i < CAS_NRXDESC; i++) + if (sc->sc_rxdsoft[i].rxds_buf != NULL) + bus_dmamem_free(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_buf, + sc->sc_rxdsoft[i].rxds_dmamap); + fail_txd: + for (i = 0; i < CAS_TXQUEUELEN; i++) + if (sc->sc_txsoft[i].txs_dmamap != NULL) + bus_dmamap_destroy(sc->sc_tdmatag, + sc->sc_txsoft[i].txs_dmamap); + bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cddmamap); + fail_cmem: + bus_dmamem_free(sc->sc_cdmatag, sc->sc_control_data, + sc->sc_cddmamap); + fail_ctag: + bus_dma_tag_destroy(sc->sc_cdmatag); + fail_ttag: + bus_dma_tag_destroy(sc->sc_tdmatag); + fail_rtag: + bus_dma_tag_destroy(sc->sc_rdmatag); + fail_ptag: + bus_dma_tag_destroy(sc->sc_pdmatag); + fail_ifnet: + if_free(ifp); + return (error); +} + +static void +cas_detach(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + int i; + + CAS_LOCK(sc); + cas_stop(ifp); + CAS_UNLOCK(sc); + callout_drain(&sc->sc_tick_ch); + callout_drain(&sc->sc_rx_ch); + ether_ifdetach(ifp); + if_free(ifp); + device_delete_child(sc->sc_dev, sc->sc_miibus); + + for (i = 0; i < CAS_NRXDESC; i++) + if (sc->sc_rxdsoft[i].rxds_dmamap != NULL) + bus_dmamap_sync(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_dmamap, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + for (i = 0; i < CAS_NRXDESC; i++) + if (sc->sc_rxdsoft[i].rxds_paddr != 0) + bus_dmamap_unload(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_dmamap); + for (i = 0; i < CAS_NRXDESC; i++) + if (sc->sc_rxdsoft[i].rxds_buf != NULL) + bus_dmamem_free(sc->sc_rdmatag, + sc->sc_rxdsoft[i].rxds_buf, + sc->sc_rxdsoft[i].rxds_dmamap); + for (i = 0; i < CAS_TXQUEUELEN; i++) + if (sc->sc_txsoft[i].txs_dmamap != NULL) + bus_dmamap_destroy(sc->sc_tdmatag, + sc->sc_txsoft[i].txs_dmamap); + CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_cdmatag, sc->sc_cddmamap); + bus_dmamem_free(sc->sc_cdmatag, sc->sc_control_data, + sc->sc_cddmamap); + bus_dma_tag_destroy(sc->sc_cdmatag); + bus_dma_tag_destroy(sc->sc_tdmatag); + bus_dma_tag_destroy(sc->sc_rdmatag); + bus_dma_tag_destroy(sc->sc_pdmatag); +} + +static void +cas_suspend(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + CAS_LOCK(sc); + cas_stop(ifp); + CAS_UNLOCK(sc); +} + +static void +cas_resume(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + CAS_LOCK(sc); + /* + * On resume all registers have to be initialized again like + * after power-on. + */ + sc->sc_flags &= ~CAS_INITED; + if (ifp->if_flags & IFF_UP) + cas_init_locked(sc); + CAS_UNLOCK(sc); +} + +static inline void +cas_rxcksum(struct mbuf *m, uint16_t cksum) +{ + struct ether_header *eh; + struct ip *ip; + struct udphdr *uh; + uint16_t *opts; + int32_t hlen, len, pktlen; + uint32_t temp32; + + pktlen = m->m_pkthdr.len; + if (pktlen < sizeof(struct ether_header) + sizeof(struct ip)) + return; + eh = mtod(m, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_IP)) + return; + ip = (struct ip *)(eh + 1); + if (ip->ip_v != IPVERSION) + return; + + hlen = ip->ip_hl << 2; + pktlen -= sizeof(struct ether_header); + if (hlen < sizeof(struct ip)) + return; + if (ntohs(ip->ip_len) < hlen) + return; + if (ntohs(ip->ip_len) != pktlen) + return; + if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) + return; /* Cannot handle fragmented packet. */ + + switch (ip->ip_p) { + case IPPROTO_TCP: + if (pktlen < (hlen + sizeof(struct tcphdr))) + return; + break; + case IPPROTO_UDP: + if (pktlen < (hlen + sizeof(struct udphdr))) + return; + uh = (struct udphdr *)((uint8_t *)ip + hlen); + if (uh->uh_sum == 0) + return; /* no checksum */ + break; + default: + return; + } + + cksum = ~cksum; + /* checksum fixup for IP options */ + len = hlen - sizeof(struct ip); + if (len > 0) { + opts = (uint16_t *)(ip + 1); + for (; len > 0; len -= sizeof(uint16_t), opts++) { + temp32 = cksum - *opts; + temp32 = (temp32 >> 16) + (temp32 & 65535); + cksum = temp32 & 65535; + } + } + m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; + m->m_pkthdr.csum_data = cksum; +} + +static void +cas_cddma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct cas_softc *sc = xsc; + + if (error != 0) + return; + if (nsegs != 1) + panic("%s: bad control buffer segment count", __func__); + sc->sc_cddma = segs[0].ds_addr; +} + +static void +cas_rxdma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct cas_softc *sc = xsc; + + if (error != 0) + return; + if (nsegs != 1) + panic("%s: bad RX buffer segment count", __func__); + sc->sc_rxdsoft[sc->sc_rxdptr].rxds_paddr = segs[0].ds_addr; +} + +static void +cas_tick(void *arg) +{ + struct cas_softc *sc = arg; + struct ifnet *ifp; + uint32_t v; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + ifp = sc->sc_ifp; + /* + * Unload collision and error counters. + */ + ifp->if_collisions += + CAS_READ_4(sc, CAS_MAC_NORM_COLL_CNT) + + CAS_READ_4(sc, CAS_MAC_FIRST_COLL_CNT); + v = CAS_READ_4(sc, CAS_MAC_EXCESS_COLL_CNT) + + CAS_READ_4(sc, CAS_MAC_LATE_COLL_CNT); + ifp->if_collisions += v; + ifp->if_oerrors += v; + ifp->if_ierrors += + CAS_READ_4(sc, CAS_MAC_RX_LEN_ERR_CNT) + + CAS_READ_4(sc, CAS_MAC_RX_ALIGN_ERR) + + CAS_READ_4(sc, CAS_MAC_RX_CRC_ERR_CNT) + + CAS_READ_4(sc, CAS_MAC_RX_CODE_VIOL); + + /* + * Then clear the hardware counters. + */ + CAS_WRITE_4(sc, CAS_MAC_NORM_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_FIRST_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_EXCESS_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_LATE_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_LEN_ERR_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_ALIGN_ERR, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_CRC_ERR_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_CODE_VIOL, 0); + + mii_tick(sc->sc_mii); + + if (cas_watchdog(sc) == EJUSTRETURN) + return; + + callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc); +} + +static int +cas_bitwait(struct cas_softc *sc, bus_addr_t r, uint32_t clr, uint32_t set) +{ + int i; + uint32_t reg; + + for (i = CAS_TRIES; i--; DELAY(100)) { + reg = CAS_READ_4(sc, r); + if ((reg & clr) == 0 && (reg & set) == set) + return (1); + } + return (0); +} + +static void +cas_reset(struct cas_softc *sc) +{ + +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__); +#endif + /* Disable all interrupts in order to avoid spurious ones. */ + CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff); + + cas_reset_rx(sc); + cas_reset_tx(sc); + + /* + * Do a full reset modulo the result of the last auto-negotiation + * when using the SERDES. + */ + CAS_WRITE_4(sc, CAS_RESET, CAS_RESET_RX | CAS_RESET_TX | + ((sc->sc_flags & CAS_SERDES) != 0 ? CAS_RESET_PCS_DIS : 0)); + CAS_BARRIER(sc, CAS_RESET, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + DELAY(3000); + if (!cas_bitwait(sc, CAS_RESET, CAS_RESET_RX | CAS_RESET_TX, 0)) + device_printf(sc->sc_dev, "cannot reset device\n"); +} + +static void +cas_stop(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + struct cas_txsoft *txs; + +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__); +#endif + + callout_stop(&sc->sc_tick_ch); + callout_stop(&sc->sc_rx_ch); + + /* Disable all interrupts in order to avoid spurious ones. */ + CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff); + + cas_reset_tx(sc); + cas_reset_rx(sc); + + /* + * Release any queued transmit buffers. + */ + while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); + if (txs->txs_ndescs != 0) { + bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); + if (txs->txs_mbuf != NULL) { + m_freem(txs->txs_mbuf); + txs->txs_mbuf = NULL; + } + } + STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); + } + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + sc->sc_flags &= ~CAS_LINK; + sc->sc_wdog_timer = 0; +} + +static int +cas_reset_rx(struct cas_softc *sc) +{ + + /* + * Resetting while DMA is in progress can cause a bus hang, so we + * disable DMA first. + */ + cas_disable_rx(sc); + CAS_WRITE_4(sc, CAS_RX_CONF, 0); + CAS_BARRIER(sc, CAS_RX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_RX_CONF, CAS_RX_CONF_RXDMA_EN, 0)) + device_printf(sc->sc_dev, "cannot disable RX DMA\n"); + + /* Finally, reset the ERX. */ + CAS_WRITE_4(sc, CAS_RESET, CAS_RESET_RX | + ((sc->sc_flags & CAS_SERDES) != 0 ? CAS_RESET_PCS_DIS : 0)); + CAS_BARRIER(sc, CAS_RESET, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_RESET, CAS_RESET_RX | CAS_RESET_TX, 0)) { + device_printf(sc->sc_dev, "cannot reset receiver\n"); + return (1); + } + return (0); +} + +static int +cas_reset_tx(struct cas_softc *sc) +{ + + /* + * Resetting while DMA is in progress can cause a bus hang, so we + * disable DMA first. + */ + cas_disable_tx(sc); + CAS_WRITE_4(sc, CAS_TX_CONF, 0); + CAS_BARRIER(sc, CAS_TX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_TX_CONF, CAS_TX_CONF_TXDMA_EN, 0)) + device_printf(sc->sc_dev, "cannot disable TX DMA\n"); + + /* Finally, reset the ETX. */ + CAS_WRITE_4(sc, CAS_RESET, CAS_RESET_TX | + ((sc->sc_flags & CAS_SERDES) != 0 ? CAS_RESET_PCS_DIS : 0)); + CAS_BARRIER(sc, CAS_RESET, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_RESET, CAS_RESET_RX | CAS_RESET_TX, 0)) { + device_printf(sc->sc_dev, "cannot reset transmitter\n"); + return (1); + } + return (0); +} + +static int +cas_disable_rx(struct cas_softc *sc) +{ + + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, + CAS_READ_4(sc, CAS_MAC_RX_CONF) & ~CAS_MAC_RX_CONF_EN); + CAS_BARRIER(sc, CAS_MAC_RX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + return (cas_bitwait(sc, CAS_MAC_RX_CONF, CAS_MAC_RX_CONF_EN, 0)); +} + +static int +cas_disable_tx(struct cas_softc *sc) +{ + + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, + CAS_READ_4(sc, CAS_MAC_TX_CONF) & ~CAS_MAC_TX_CONF_EN); + CAS_BARRIER(sc, CAS_MAC_TX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + return (cas_bitwait(sc, CAS_MAC_TX_CONF, CAS_MAC_TX_CONF_EN, 0)); +} + +static inline void +cas_rxcompinit(struct cas_rx_comp *rxcomp) +{ + + rxcomp->crc_word1 = 0; + rxcomp->crc_word2 = 0; + rxcomp->crc_word3 = + htole64(CAS_SET(ETHER_HDR_LEN + sizeof(struct ip), CAS_RC3_CSO)); + rxcomp->crc_word4 = htole64(CAS_RC4_ZERO); +} + +static void +cas_meminit(struct cas_softc *sc) +{ + int i; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + /* + * Initialize the transmit descriptor ring. + */ + for (i = 0; i < CAS_NTXDESC; i++) { + sc->sc_txdescs[i].cd_flags = 0; + sc->sc_txdescs[i].cd_buf_ptr = 0; + } + sc->sc_txfree = CAS_MAXTXFREE; + sc->sc_txnext = 0; + sc->sc_txwin = 0; + + /* + * Initialize the receive completion ring. + */ + for (i = 0; i < CAS_NRXCOMP; i++) + cas_rxcompinit(&sc->sc_rxcomps[i]); + sc->sc_rxcptr = 0; + + /* + * Initialize the first receive descriptor ring. We leave + * the second one zeroed as we don't actually use it. + */ + for (i = 0; i < CAS_NRXDESC; i++) + CAS_INIT_RXDESC(sc, i, i); + sc->sc_rxdptr = 0; + + CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +} + +static u_int +cas_descsize(u_int sz) +{ + + switch (sz) { + case 32: + return (CAS_DESC_32); + case 64: + return (CAS_DESC_64); + case 128: + return (CAS_DESC_128); + case 256: + return (CAS_DESC_256); + case 512: + return (CAS_DESC_512); + case 1024: + return (CAS_DESC_1K); + case 2048: + return (CAS_DESC_2K); + case 4096: + return (CAS_DESC_4K); + case 8192: + return (CAS_DESC_8K); + default: + printf("%s: invalid descriptor ring size %d\n", __func__, sz); + return (CAS_DESC_32); + } +} + +static u_int +cas_rxcompsize(u_int sz) +{ + + switch (sz) { + case 128: + return (CAS_RX_CONF_COMP_128); + case 256: + return (CAS_RX_CONF_COMP_256); + case 512: + return (CAS_RX_CONF_COMP_512); + case 1024: + return (CAS_RX_CONF_COMP_1K); + case 2048: + return (CAS_RX_CONF_COMP_2K); + case 4096: + return (CAS_RX_CONF_COMP_4K); + case 8192: + return (CAS_RX_CONF_COMP_8K); + case 16384: + return (CAS_RX_CONF_COMP_16K); + case 32768: + return (CAS_RX_CONF_COMP_32K); + default: + printf("%s: invalid dcompletion ring size %d\n", __func__, sz); + return (CAS_RX_CONF_COMP_128); + } +} + +static void +cas_init(void *xsc) +{ + struct cas_softc *sc = xsc; + + CAS_LOCK(sc); + cas_init_locked(sc); + CAS_UNLOCK(sc); +} + +/* + * Initialization of interface; set up initialization block + * and transmit/receive descriptor rings. + */ +static void +cas_init_locked(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + uint32_t v; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: %s: calling stop", device_get_name(sc->sc_dev), + __func__); +#endif + /* + * Initialization sequence. The numbered steps below correspond + * to the sequence outlined in section 6.3.5.1 in the Ethernet + * Channel Engine manual (part of the PCIO manual). + * See also the STP2002-STQ document from Sun Microsystems. + */ + + /* step 1 & 2. Reset the Ethernet Channel. */ + cas_stop(ifp); + cas_reset(sc); +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: %s: restarting", device_get_name(sc->sc_dev), + __func__); +#endif + + /* Re-initialize the MIF. */ + cas_mifinit(sc); + + /* step 3. Setup data structures in host memory. */ + cas_meminit(sc); + + /* step 4. TX MAC registers & counters */ + cas_init_regs(sc); + + /* step 5. RX MAC registers & counters */ + cas_setladrf(sc); + + /* step 6 & 7. Program Ring Base Addresses. */ + CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_HI, + (((uint64_t)CAS_CDTXDADDR(sc, 0)) >> 32)); + CAS_WRITE_4(sc, CAS_TX_DESC3_BASE_LO, + CAS_CDTXDADDR(sc, 0) & 0xffffffff); + + CAS_WRITE_4(sc, CAS_RX_COMP_BASE_HI, + (((uint64_t)CAS_CDRXCADDR(sc, 0)) >> 32)); + CAS_WRITE_4(sc, CAS_RX_COMP_BASE_LO, + CAS_CDRXCADDR(sc, 0) & 0xffffffff); + + CAS_WRITE_4(sc, CAS_RX_DESC_BASE_HI, + (((uint64_t)CAS_CDRXDADDR(sc, 0)) >> 32)); + CAS_WRITE_4(sc, CAS_RX_DESC_BASE_LO, + CAS_CDRXDADDR(sc, 0) & 0xffffffff); + + if ((sc->sc_flags & CAS_REG_PLUS) != 0) { + CAS_WRITE_4(sc, CAS_RX_DESC2_BASE_HI, + (((uint64_t)CAS_CDRXD2ADDR(sc, 0)) >> 32)); + CAS_WRITE_4(sc, CAS_RX_DESC2_BASE_LO, + CAS_CDRXD2ADDR(sc, 0) & 0xffffffff); + } + +#ifdef CAS_DEBUG + CTR5(KTR_CAS, + "loading TXDR %lx, RXCR %lx, RXDR %lx, RXD2R %lx, cddma %lx", + CAS_CDTXDADDR(sc, 0), CAS_CDRXCADDR(sc, 0), CAS_CDRXDADDR(sc, 0), + CAS_CDRXD2ADDR(sc, 0), sc->sc_cddma); +#endif + + /* step 8. Global Configuration & Interrupt Masks */ + + /* Disable weighted round robin. */ + CAS_WRITE_4(sc, CAS_CAW, CAS_CAW_RR_DIS); + + /* + * Enable infinite bursts for revisions without PCI issues if + * applicable. Doing so greatly improves the TX performance on + * !__sparc64__. + */ + CAS_WRITE_4(sc, CAS_INF_BURST, +#if !defined(__sparc64__) + (sc->sc_flags & CAS_TABORT) == 0 ? CAS_INF_BURST_EN : +#endif + 0); + + /* Set up interrupts. */ + CAS_WRITE_4(sc, CAS_INTMASK, + ~(CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_TAG_ERR | + CAS_INTR_RX_DONE | CAS_INTR_RX_BUF_NA | CAS_INTR_RX_TAG_ERR | + CAS_INTR_RX_COMP_FULL | CAS_INTR_RX_BUF_AEMPTY | + CAS_INTR_RX_COMP_AFULL | CAS_INTR_RX_LEN_MMATCH | + CAS_INTR_PCI_ERROR_INT +#ifdef CAS_DEBUG + | CAS_INTR_PCS_INT | CAS_INTR_MIF +#endif + )); + CAS_WRITE_4(sc, CAS_MAC_RX_MASK, ~CAS_MAC_RX_OVERFLOW); + CAS_WRITE_4(sc, CAS_MAC_TX_MASK, + ~(CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_MAX_PKT_ERR)); +#ifdef CAS_DEBUG + CAS_WRITE_4(sc, CAS_MAC_CTRL_MASK, + ~(CAS_MAC_CTRL_PAUSE_RCVD | CAS_MAC_CTRL_PAUSE | + CAS_MAC_CTRL_NON_PAUSE)); +#else + CAS_WRITE_4(sc, CAS_MAC_CTRL_MASK, + CAS_MAC_CTRL_PAUSE_RCVD | CAS_MAC_CTRL_PAUSE | + CAS_MAC_CTRL_NON_PAUSE); +#endif + + /* Enable PCI error interrupts. */ + CAS_WRITE_4(sc, CAS_ERROR_MASK, + ~(CAS_ERROR_DTRTO | CAS_ERROR_OTHER | CAS_ERROR_DMAW_ZERO | + CAS_ERROR_DMAR_ZERO | CAS_ERROR_RTRTO)); + + /* Enable PCI error interrupts in BIM configuration. */ + CAS_WRITE_4(sc, CAS_BIM_CONF, + CAS_BIM_CONF_DPAR_EN | CAS_BIM_CONF_RMA_EN | CAS_BIM_CONF_RTA_EN); + + /* + * step 9. ETX Configuration: encode receive descriptor ring size, + * enable DMA and disable pre-interrupt writeback completion. + */ + v = cas_descsize(CAS_NTXDESC) << CAS_TX_CONF_DESC3_SHFT; + CAS_WRITE_4(sc, CAS_TX_CONF, v | CAS_TX_CONF_TXDMA_EN | + CAS_TX_CONF_RDPP_DIS | CAS_TX_CONF_PICWB_DIS); + + /* step 10. ERX Configuration */ + + /* + * Encode receive completion and descriptor ring sizes, set the + * swivel offset. + */ + v = cas_rxcompsize(CAS_NRXCOMP) << CAS_RX_CONF_COMP_SHFT; + v |= cas_descsize(CAS_NRXDESC) << CAS_RX_CONF_DESC_SHFT; + if ((sc->sc_flags & CAS_REG_PLUS) != 0) + v |= cas_descsize(CAS_NRXDESC2) << CAS_RX_CONF_DESC2_SHFT; + CAS_WRITE_4(sc, CAS_RX_CONF, + v | (ETHER_ALIGN << CAS_RX_CONF_SOFF_SHFT)); + + /* Set the PAUSE thresholds. We use the maximum OFF threshold. */ + CAS_WRITE_4(sc, CAS_RX_PTHRS, + ((111 * 64) << CAS_RX_PTHRS_XOFF_SHFT) | + ((15 * 64) << CAS_RX_PTHRS_XON_SHFT)); + + /* RX blanking */ + CAS_WRITE_4(sc, CAS_RX_BLANK, + (15 << CAS_RX_BLANK_TIME_SHFT) | (5 << CAS_RX_BLANK_PKTS_SHFT)); + + /* Set RX_COMP_AFULL threshold to half of the RX completions. */ + CAS_WRITE_4(sc, CAS_RX_AEMPTY_THRS, + (CAS_NRXCOMP / 2) << CAS_RX_AEMPTY_COMP_SHFT); + + /* Initialize the RX page size register as appropriate for 8k. */ + CAS_WRITE_4(sc, CAS_RX_PSZ, + (CAS_RX_PSZ_8K << CAS_RX_PSZ_SHFT) | + (4 << CAS_RX_PSZ_MB_CNT_SHFT) | + (CAS_RX_PSZ_MB_STRD_2K << CAS_RX_PSZ_MB_STRD_SHFT) | + (CAS_RX_PSZ_MB_OFF_64 << CAS_RX_PSZ_MB_OFF_SHFT)); + + /* Disable RX random early detection. */ + CAS_WRITE_4(sc, CAS_RX_RED, 0); + + /* Zero the RX reassembly DMA table. */ + for (v = 0; v <= CAS_RX_REAS_DMA_ADDR_LC; v++) { + CAS_WRITE_4(sc, CAS_RX_REAS_DMA_ADDR, v); + CAS_WRITE_4(sc, CAS_RX_REAS_DMA_DATA_LO, 0); + CAS_WRITE_4(sc, CAS_RX_REAS_DMA_DATA_MD, 0); + CAS_WRITE_4(sc, CAS_RX_REAS_DMA_DATA_HI, 0); + } + + /* Ensure the RX control FIFO and RX IPP FIFO addresses are zero. */ + CAS_WRITE_4(sc, CAS_RX_CTRL_FIFO, 0); + CAS_WRITE_4(sc, CAS_RX_IPP_ADDR, 0); + + /* Finally, enable RX DMA. */ + CAS_WRITE_4(sc, CAS_RX_CONF, + CAS_READ_4(sc, CAS_RX_CONF) | CAS_RX_CONF_RXDMA_EN); + + /* step 11. Configure Media. */ + + /* step 12. RX_MAC Configuration Register */ + v = CAS_READ_4(sc, CAS_MAC_RX_CONF) & ~CAS_MAC_RX_CONF_STRPPAD; + v |= CAS_MAC_RX_CONF_EN | CAS_MAC_RX_CONF_STRPFCS; + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, 0); + CAS_BARRIER(sc, CAS_MAC_RX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_MAC_RX_CONF, CAS_MAC_RX_CONF_EN, 0)) + device_printf(sc->sc_dev, "cannot configure RX MAC\n"); + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, v); + + /* step 13. TX_MAC Configuration Register */ + v = CAS_READ_4(sc, CAS_MAC_TX_CONF); + v |= CAS_MAC_TX_CONF_EN; + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, 0); + CAS_BARRIER(sc, CAS_MAC_TX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_MAC_TX_CONF, CAS_MAC_TX_CONF_EN, 0)) + device_printf(sc->sc_dev, "cannot configure TX MAC\n"); + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, v); + + /* step 14. Issue Transmit Pending command. */ + + /* step 15. Give the reciever a swift kick. */ + CAS_WRITE_4(sc, CAS_RX_KICK, CAS_NRXDESC - 4); + CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, 0); + if ((sc->sc_flags & CAS_REG_PLUS) != 0) + CAS_WRITE_4(sc, CAS_RX_KICK2, CAS_NRXDESC2 - 4); + + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + mii_mediachg(sc->sc_mii); + + /* Start the one second timer. */ + sc->sc_wdog_timer = 0; + callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc); +} + +static int +cas_load_txmbuf(struct cas_softc *sc, struct mbuf **m_head) +{ + bus_dma_segment_t txsegs[CAS_NTXSEGS]; + struct cas_txsoft *txs; + struct ip *ip; + struct mbuf *m; + uint64_t cflags; + int error, nexttx, nsegs, offset, seg; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + /* Get a work queue entry. */ + if ((txs = STAILQ_FIRST(&sc->sc_txfreeq)) == NULL) { + /* Ran out of descriptors. */ + return (ENOBUFS); + } + + cflags = 0; + if (((*m_head)->m_pkthdr.csum_flags & CAS_CSUM_FEATURES) != 0) { + if (M_WRITABLE(*m_head) == 0) { + m = m_dup(*m_head, M_DONTWAIT); + m_freem(*m_head); + *m_head = m; + if (m == NULL) + return (ENOBUFS); + } + offset = sizeof(struct ether_header); + m = m_pullup(*m_head, offset + sizeof(struct ip)); + if (m == NULL) { + *m_head = NULL; + return (ENOBUFS); + } + ip = (struct ip *)(mtod(m, caddr_t) + offset); + offset += (ip->ip_hl << 2); + cflags = (offset << CAS_TD_CKSUM_START_SHFT) | + ((offset + m->m_pkthdr.csum_data) << + CAS_TD_CKSUM_STUFF_SHFT) | CAS_TD_CKSUM_EN; + *m_head = m; + } + + error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, txs->txs_dmamap, + *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT); + if (error == EFBIG) { + m = m_collapse(*m_head, M_DONTWAIT, CAS_NTXSEGS); + if (m == NULL) { + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + *m_head = m; + error = bus_dmamap_load_mbuf_sg(sc->sc_tdmatag, + txs->txs_dmamap, *m_head, txsegs, &nsegs, + BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(*m_head); + *m_head = NULL; + return (error); + } + } else if (error != 0) + return (error); + /* If nsegs is wrong then the stack is corrupt. */ + KASSERT(nsegs <= CAS_NTXSEGS, + ("%s: too many DMA segments (%d)", __func__, nsegs)); + if (nsegs == 0) { + m_freem(*m_head); + *m_head = NULL; + return (EIO); + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. Note, we always reserve one descriptor + * at the end of the ring as a termination point, in + * order to prevent wrap-around. + */ + if (nsegs > sc->sc_txfree - 1) { + txs->txs_ndescs = 0; + bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); + return (ENOBUFS); + } + + txs->txs_ndescs = nsegs; + txs->txs_firstdesc = sc->sc_txnext; + nexttx = txs->txs_firstdesc; + for (seg = 0; seg < nsegs; seg++, nexttx = CAS_NEXTTX(nexttx)) { +#ifdef CAS_DEBUG + CTR6(KTR_CAS, + "%s: mapping seg %d (txd %d), len %lx, addr %#lx (%#lx)", + __func__, seg, nexttx, txsegs[seg].ds_len, + txsegs[seg].ds_addr, htole64(txsegs[seg].ds_addr)); +#endif + sc->sc_txdescs[nexttx].cd_buf_ptr = + htole64(txsegs[seg].ds_addr); + KASSERT(txsegs[seg].ds_len < + CAS_TD_BUF_LEN_MASK >> CAS_TD_BUF_LEN_SHFT, + ("%s: segment size too large!", __func__)); + sc->sc_txdescs[nexttx].cd_flags = + htole64(txsegs[seg].ds_len << CAS_TD_BUF_LEN_SHFT); + txs->txs_lastdesc = nexttx; + } + + /* Set EOF on the last descriptor. */ +#ifdef CAS_DEBUG + CTR3(KTR_CAS, "%s: end of frame at segment %d, TX %d", + __func__, seg, nexttx); +#endif + sc->sc_txdescs[txs->txs_lastdesc].cd_flags |= + htole64(CAS_TD_END_OF_FRAME); + + /* Lastly set SOF on the first descriptor. */ +#ifdef CAS_DEBUG + CTR3(KTR_CAS, "%s: start of frame at segment %d, TX %d", + __func__, seg, nexttx); +#endif + if (sc->sc_txwin += nsegs > CAS_NTXSEGS * 2 / 3) { + sc->sc_txwin = 0; + sc->sc_txdescs[txs->txs_firstdesc].cd_flags |= + htole64(cflags | CAS_TD_START_OF_FRAME | CAS_TD_INT_ME); + } else + sc->sc_txdescs[txs->txs_firstdesc].cd_flags |= + htole64(cflags | CAS_TD_START_OF_FRAME); + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, + BUS_DMASYNC_PREWRITE); + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: setting firstdesc=%d, lastdesc=%d, ndescs=%d", + __func__, txs->txs_firstdesc, txs->txs_lastdesc, + txs->txs_ndescs); +#endif + STAILQ_REMOVE_HEAD(&sc->sc_txfreeq, txs_q); + STAILQ_INSERT_TAIL(&sc->sc_txdirtyq, txs, txs_q); + txs->txs_mbuf = *m_head; + + sc->sc_txnext = CAS_NEXTTX(txs->txs_lastdesc); + sc->sc_txfree -= txs->txs_ndescs; + + return (0); +} + +static void +cas_init_regs(struct cas_softc *sc) +{ + int i; + const u_char *laddr = IF_LLADDR(sc->sc_ifp); + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + /* These registers are not cleared on reset. */ + if ((sc->sc_flags & CAS_INITED) == 0) { + /* magic values */ + CAS_WRITE_4(sc, CAS_MAC_IPG0, 0); + CAS_WRITE_4(sc, CAS_MAC_IPG1, 8); + CAS_WRITE_4(sc, CAS_MAC_IPG2, 4); + + /* min frame length */ + CAS_WRITE_4(sc, CAS_MAC_MIN_FRAME, ETHER_MIN_LEN); + /* max frame length and max burst size */ + CAS_WRITE_4(sc, CAS_MAC_MAX_BF, + ((ETHER_MAX_LEN_JUMBO + ETHER_VLAN_ENCAP_LEN) << + CAS_MAC_MAX_BF_FRM_SHFT) | + (0x2000 << CAS_MAC_MAX_BF_BST_SHFT)); + + /* more magic values */ + CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x7); + CAS_WRITE_4(sc, CAS_MAC_JAM_SIZE, 0x4); + CAS_WRITE_4(sc, CAS_MAC_ATTEMPT_LIMIT, 0x10); + CAS_WRITE_4(sc, CAS_MAC_CTRL_TYPE, 0x8088); + + /* random number seed */ + CAS_WRITE_4(sc, CAS_MAC_RANDOM_SEED, + ((laddr[5] << 8) | laddr[4]) & 0x3ff); + + /* secondary MAC addresses: 0:0:0:0:0:0 */ + for (i = CAS_MAC_ADDR3; i <= CAS_MAC_ADDR41; + i += CAS_MAC_ADDR4 - CAS_MAC_ADDR3) + CAS_WRITE_4(sc, i, 0); + + /* MAC control address: 01:80:c2:00:00:01 */ + CAS_WRITE_4(sc, CAS_MAC_ADDR42, 0x0001); + CAS_WRITE_4(sc, CAS_MAC_ADDR43, 0xc200); + CAS_WRITE_4(sc, CAS_MAC_ADDR44, 0x0180); + + /* MAC filter address: 0:0:0:0:0:0 */ + CAS_WRITE_4(sc, CAS_MAC_AFILTER0, 0); + CAS_WRITE_4(sc, CAS_MAC_AFILTER1, 0); + CAS_WRITE_4(sc, CAS_MAC_AFILTER2, 0); + CAS_WRITE_4(sc, CAS_MAC_AFILTER_MASK1_2, 0); + CAS_WRITE_4(sc, CAS_MAC_AFILTER_MASK0, 0); + + /* Zero the hash table. */ + for (i = CAS_MAC_HASH0; i <= CAS_MAC_HASH15; + i += CAS_MAC_HASH1 - CAS_MAC_HASH0) + CAS_WRITE_4(sc, i, 0); + + sc->sc_flags |= CAS_INITED; + } + + /* Counters need to be zeroed. */ + CAS_WRITE_4(sc, CAS_MAC_NORM_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_FIRST_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_EXCESS_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_LATE_COLL_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_DEFER_TMR_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_PEAK_ATTEMPTS, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_FRAME_COUNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_LEN_ERR_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_ALIGN_ERR, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_CRC_ERR_CNT, 0); + CAS_WRITE_4(sc, CAS_MAC_RX_CODE_VIOL, 0); + + /* Set XOFF PAUSE time. */ + CAS_WRITE_4(sc, CAS_MAC_SPC, 0x1BF0 << CAS_MAC_SPC_TIME_SHFT); + + /* Set the station address. */ + CAS_WRITE_4(sc, CAS_MAC_ADDR0, (laddr[4] << 8) | laddr[5]); + CAS_WRITE_4(sc, CAS_MAC_ADDR1, (laddr[2] << 8) | laddr[3]); + CAS_WRITE_4(sc, CAS_MAC_ADDR2, (laddr[0] << 8) | laddr[1]); + + /* Enable MII outputs. */ + CAS_WRITE_4(sc, CAS_MAC_XIF_CONF, CAS_MAC_XIF_CONF_TX_OE); +} + +static void +cas_start(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + + CAS_LOCK(sc); + cas_start_locked(ifp); + CAS_UNLOCK(sc); +} + +static inline void +cas_txkick(struct cas_softc *sc) +{ + + /* + * Update the TX kick register. This register has to point to the + * descriptor after the last valid one and for optimum performance + * should be incremented in multiples of 4 (the DMA engine fetches/ + * updates descriptors in batches of 4). + */ +#ifdef CAS_DEBUG + CTR3(KTR_CAS, "%s: %s: kicking TX %d", + device_get_name(sc->sc_dev), __func__, sc->sc_txnext); +#endif + CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + CAS_WRITE_4(sc, CAS_TX_KICK3, sc->sc_txnext); +} + +static void +cas_start_locked(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + struct mbuf *m; + int kicked, ntx; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != + IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0) + return; + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: %s: txfree %d, txnext %d", + device_get_name(sc->sc_dev), __func__, sc->sc_txfree, + sc->sc_txnext); +#endif + ntx = 0; + kicked = 0; + for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) && sc->sc_txfree > 1;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + if (cas_load_txmbuf(sc, &m) != 0) { + if (m == NULL) + break; + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IFQ_DRV_PREPEND(&ifp->if_snd, m); + break; + } + if ((sc->sc_txnext % 4) == 0) { + cas_txkick(sc); + kicked = 1; + } else + kicked = 0; + ntx++; + BPF_MTAP(ifp, m); + } + + if (ntx > 0) { + if (kicked == 0) + cas_txkick(sc); +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: packets enqueued, OWN on %d", + device_get_name(sc->sc_dev), sc->sc_txnext); +#endif + + /* Set a watchdog timer in case the chip flakes out. */ + sc->sc_wdog_timer = 5; +#ifdef CAS_DEBUG + CTR3(KTR_CAS, "%s: %s: watchdog %d", + device_get_name(sc->sc_dev), __func__, + sc->sc_wdog_timer); +#endif + } +} + +static void +cas_tint(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct cas_txsoft *txs; + int progress; + uint32_t txlast; +#ifdef CAS_DEBUG + int i; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__); +#endif + + /* + * Go through our TX list and free mbufs for those + * frames that have been transmitted. + */ + progress = 0; + CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD); + while ((txs = STAILQ_FIRST(&sc->sc_txdirtyq)) != NULL) { +#ifdef CAS_DEBUG + if ((ifp->if_flags & IFF_DEBUG) != 0) { + printf(" txsoft %p transmit chain:\n", txs); + for (i = txs->txs_firstdesc;; i = CAS_NEXTTX(i)) { + printf("descriptor %d: ", i); + printf("cd_flags: 0x%016llx\t", + (long long)le64toh( + sc->sc_txdescs[i].cd_flags)); + printf("cd_buf_ptr: 0x%016llx\n", + (long long)le64toh( + sc->sc_txdescs[i].cd_buf_ptr)); + if (i == txs->txs_lastdesc) + break; + } + } +#endif + + /* + * In theory, we could harvest some descriptors before + * the ring is empty, but that's a bit complicated. + * + * CAS_TX_COMPn points to the last descriptor + * processed + 1. + */ + txlast = CAS_READ_4(sc, CAS_TX_COMP3); +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: txs->txs_firstdesc = %d, " + "txs->txs_lastdesc = %d, txlast = %d", + __func__, txs->txs_firstdesc, txs->txs_lastdesc, txlast); +#endif + if (txs->txs_firstdesc <= txs->txs_lastdesc) { + if ((txlast >= txs->txs_firstdesc) && + (txlast <= txs->txs_lastdesc)) + break; + } else { + /* Ick -- this command wraps. */ + if ((txlast >= txs->txs_firstdesc) || + (txlast <= txs->txs_lastdesc)) + break; + } + +#ifdef CAS_DEBUG + CTR1(KTR_CAS, "%s: releasing a descriptor", __func__); +#endif + STAILQ_REMOVE_HEAD(&sc->sc_txdirtyq, txs_q); + + sc->sc_txfree += txs->txs_ndescs; + + bus_dmamap_sync(sc->sc_tdmatag, txs->txs_dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_tdmatag, txs->txs_dmamap); + if (txs->txs_mbuf != NULL) { + m_freem(txs->txs_mbuf); + txs->txs_mbuf = NULL; + } + + STAILQ_INSERT_TAIL(&sc->sc_txfreeq, txs, txs_q); + + ifp->if_opackets++; + progress = 1; + } + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: CAS_TX_STATE_MACHINE %x CAS_TX_DESC_BASE %llx " + "CAS_TX_COMP3 %x", + __func__, CAS_READ_4(sc, CAS_TX_STATE_MACHINE), + ((long long)CAS_READ_4(sc, CAS_TX_DESC_BASE_HI3) << 32) | + CAS_READ_4(sc, CAS_TX_DESC_BASE_LO3), + CAS_READ_4(sc, CAS_TX_COMP3)); +#endif + + if (progress) { + if (sc->sc_txfree == CAS_NTXDESC - 1) + sc->sc_txwin = 0; + + /* + * We freed some descriptors, so reset IFF_DRV_OACTIVE + * and restart. + */ + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + if (STAILQ_EMPTY(&sc->sc_txdirtyq)) + sc->sc_wdog_timer = 0; + cas_start_locked(ifp); + } + +#ifdef CAS_DEBUG + CTR3(KTR_CAS, "%s: %s: watchdog %d", + device_get_name(sc->sc_dev), __func__, sc->sc_wdog_timer); +#endif +} + +static void +cas_rint_timeout(void *arg) +{ + struct cas_softc *sc = arg; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + cas_rint(sc); +} + +static void +cas_rint(struct cas_softc *sc) +{ + struct cas_rxdsoft *rxds, *rxds2; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m, *m2; + uint64_t word1, word2, word3, word4; + uint32_t rxhead; + u_int idx, idx2, len, off, skip; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + callout_stop(&sc->sc_rx_ch); + +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: %s", device_get_name(sc->sc_dev), __func__); +#endif + +#define PRINTWORD(n, delimiter) \ + printf("word ## n: 0x%016llx%c", (long long)word ## n, delimiter) + +#define SKIPASSERT(n) \ + KASSERT(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n == 0, \ + ("%s: word ## n not 0", __func__)) + +#define WORDTOH(n) \ + word ## n = le64toh(sc->sc_rxcomps[sc->sc_rxcptr].crc_word ## n) + + /* + * Read the completion head register once. This limits + * how long the following loop can execute. + */ + rxhead = CAS_READ_4(sc, CAS_RX_COMP_HEAD); +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d", + __func__, sc->rxcptr, sc->sc_rxdptr, rxhead); +#endif + skip = 0; + CAS_CDSYNC(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + for (; sc->sc_rxcptr != rxhead; + sc->sc_rxcptr = CAS_NEXTRXCOMP(sc->sc_rxcptr)) { + if (skip != 0) { + SKIPASSERT(1); + SKIPASSERT(2); + SKIPASSERT(3); + + --skip; + goto skip; + } + + WORDTOH(1); + WORDTOH(2); + WORDTOH(3); + WORDTOH(4); + +#ifdef CAS_DEBUG + if ((ifp->if_flags & IFF_DEBUG) != 0) { + printf(" completion %d: ", sc->sc_rxcptr); + PRINTWORD(1, '\t'); + PRINTWORD(2, '\t'); + PRINTWORD(3, '\t'); + PRINTWORD(4, '\n'); + } +#endif + + if (__predict_false( + (word1 & CAS_RC1_TYPE_MASK) == CAS_RC1_TYPE_HW || + (word4 & CAS_RC4_ZERO) != 0)) { + /* + * The descriptor is still marked as owned, although + * it is supposed to have completed. This has been + * observed on some machines. Just exiting here + * might leave the packet sitting around until another + * one arrives to trigger a new interrupt, which is + * generally undesirable, so set up a timeout. + */ + callout_reset(&sc->sc_rx_ch, CAS_RXOWN_TICKS, + cas_rint_timeout, sc); + break; + } + + if (__predict_false( + (word4 & (CAS_RC4_BAD | CAS_RC4_LEN_MMATCH)) != 0)) { + ifp->if_ierrors++; + device_printf(sc->sc_dev, + "receive error: CRC error\n"); + continue; + } + + KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 || + CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0, + ("%s: data and header present", __func__)); + KASSERT((word1 & CAS_RC1_SPLIT_PKT) == 0 || + CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0, + ("%s: split and header present", __func__)); + KASSERT(CAS_GET(word1, CAS_RC1_DATA_SIZE) == 0 || + (word1 & CAS_RC1_RELEASE_HDR) == 0, + ("%s: data present but header release", __func__)); + KASSERT(CAS_GET(word2, CAS_RC2_HDR_SIZE) == 0 || + (word1 & CAS_RC1_RELEASE_DATA) == 0, + ("%s: header present but data release", __func__)); + + if ((len = CAS_GET(word2, CAS_RC2_HDR_SIZE)) != 0) { + idx = CAS_GET(word2, CAS_RC2_HDR_INDEX); + off = CAS_GET(word2, CAS_RC2_HDR_OFF); +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: hdr at idx %d, off %d, len %d", + __func__, idx, off, len); +#endif + rxds = &sc->sc_rxdsoft[idx]; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m != NULL) { + refcount_acquire(&rxds->rxds_refcount); + bus_dmamap_sync(sc->sc_rdmatag, + rxds->rxds_dmamap, BUS_DMASYNC_POSTREAD); + MEXTADD(m, (caddr_t)rxds->rxds_buf + + off * 256 + ETHER_ALIGN, len, cas_free, +#if __FreeBSD_version < 800016 + rxds, +#else + sc, (void *)(uintptr_t)idx, +#endif + M_RDONLY, EXT_NET_DRV); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + m = NULL; + } + } + if (m != NULL) { + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + ifp->if_ipackets++; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + cas_rxcksum(m, CAS_GET(word4, + CAS_RC4_TCP_CSUM)); + /* Pass it on. */ + CAS_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + CAS_LOCK(sc); + } else + ifp->if_ierrors++; + + if ((word1 & CAS_RC1_RELEASE_HDR) != 0 && + refcount_release(&rxds->rxds_refcount) != 0) + cas_add_rxdesc(sc, idx); + } else if ((len = CAS_GET(word1, CAS_RC1_DATA_SIZE)) != 0) { + idx = CAS_GET(word1, CAS_RC1_DATA_INDEX); + off = CAS_GET(word1, CAS_RC1_DATA_OFF); +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: data at idx %d, off %d, len %d", + __func__, idx, off, len); +#endif + rxds = &sc->sc_rxdsoft[idx]; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m != NULL) { + refcount_acquire(&rxds->rxds_refcount); + off += ETHER_ALIGN; + m->m_len = min(CAS_PAGE_SIZE - off, len); + bus_dmamap_sync(sc->sc_rdmatag, + rxds->rxds_dmamap, BUS_DMASYNC_POSTREAD); + MEXTADD(m, (caddr_t)rxds->rxds_buf + off, + m->m_len, cas_free, +#if __FreeBSD_version < 800016 + rxds, +#else + sc, (void *)(uintptr_t)idx, +#endif + M_RDONLY, EXT_NET_DRV); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + m = NULL; + } + } + idx2 = 0; + rxds2 = NULL; + if ((word1 & CAS_RC1_SPLIT_PKT) != 0) { + KASSERT((word1 & CAS_RC1_RELEASE_NEXT) != 0, + ("%s: split but no release next", + __func__)); + + idx2 = CAS_GET(word2, CAS_RC2_NEXT_INDEX); +#ifdef CAS_DEBUG + CTR2(KTR_CAS, "%s: split at idx %d", + __func__, idx2); +#endif + rxds2 = &sc->sc_rxdsoft[idx2]; + MGET(m2, M_DONTWAIT, MT_DATA); + if (m2 != NULL) { + refcount_acquire( + &rxds2->rxds_refcount); + m2->m_len = len - m->m_len; + bus_dmamap_sync(sc->sc_rdmatag, + rxds2->rxds_dmamap, + BUS_DMASYNC_POSTREAD); + MEXTADD(m2, (caddr_t)rxds2->rxds_buf, + m2->m_len, cas_free, +#if __FreeBSD_version < 800016 + rxds2, +#else + sc, (void *)(uintptr_t)idx2, +#endif + M_RDONLY, EXT_NET_DRV); + if ((m2->m_flags & M_EXT) == 0) { + m_freem(m2); + m2 = NULL; + } + } + if (m2 != NULL) + m->m_next = m2; + else { + m_freem(m); + m = NULL; + } + } + if (m != NULL) { + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = len; + ifp->if_ipackets++; + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + cas_rxcksum(m, CAS_GET(word4, + CAS_RC4_TCP_CSUM)); + /* Pass it on. */ + CAS_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + CAS_LOCK(sc); + } else + ifp->if_ierrors++; + + if ((word1 & CAS_RC1_RELEASE_DATA) != 0 && + refcount_release(&rxds->rxds_refcount) != 0) + cas_add_rxdesc(sc, idx); + if ((word1 & CAS_RC1_SPLIT_PKT) != 0 && + refcount_release(&rxds2->rxds_refcount) != 0) + cas_add_rxdesc(sc, idx2); + } + + skip = CAS_GET(word1, CAS_RC1_SKIP); + + skip: + cas_rxcompinit(&sc->sc_rxcomps[sc->sc_rxcptr]); + } + CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, sc->sc_rxcptr); + +#undef PRINTWORD +#undef SKIPASSERT +#undef WORDTOH + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: done sc->sc_rxcptr %d, sc->sc_rxdptr %d, head %d", + __func__, sc->rxcptr, sc->sc_rxdptr, + CAS_READ_4(sc, CAS_RX_COMP_HEAD)); +#endif +} + +static void +cas_free(void *arg1, void *arg2) +{ + struct cas_rxdsoft *rxds; + struct cas_softc *sc; + u_int idx, locked; + +#if __FreeBSD_version < 800016 + rxds = arg2; + sc = rxds->rxds_sc; + idx = rxds->rxds_idx; +#else + sc = arg1; + idx = (uintptr_t)arg2; + rxds = &sc->sc_rxdsoft[idx]; +#endif + if (refcount_release(&rxds->rxds_refcount) == 0) + return; + + /* + * NB: this function can be called via m_freem(9) within + * this driver! + */ + if ((locked = CAS_LOCK_OWNED(sc)) == 0) + CAS_LOCK(sc); + cas_add_rxdesc(sc, idx); + if (locked == 0) + CAS_UNLOCK(sc); +} + +static inline void +cas_add_rxdesc(struct cas_softc *sc, u_int idx) +{ + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + bus_dmamap_sync(sc->sc_rdmatag, sc->sc_rxdsoft[idx].rxds_dmamap, + BUS_DMASYNC_PREREAD); + CAS_UPDATE_RXDESC(sc, sc->sc_rxdptr, idx); + sc->sc_rxdptr = CAS_NEXTRXDESC(sc->sc_rxdptr); + + /* + * Update the RX kick register. This register has to point to the + * descriptor after the last valid one (before the current batch) + * and for optimum performance should be incremented in multiples + * of 4 (the DMA engine fetches/updates descriptors in batches of 4). + */ + if ((sc->sc_rxdptr % 4) == 0) { + CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + CAS_WRITE_4(sc, CAS_RX_KICK, + (sc->sc_rxdptr + CAS_NRXDESC - 4) & CAS_NRXDESC_MASK); + } +} + +static void +cas_eint(struct cas_softc *sc, u_int status) +{ + + sc->sc_ifp->if_ierrors++; + + device_printf(sc->sc_dev, "%s: status 0x%x", __func__, status); + if ((status & CAS_INTR_PCI_ERROR_INT) != 0) { + status = CAS_READ_4(sc, CAS_ERROR_STATUS); + printf(", PCI bus error 0x%x", status); + if ((status & CAS_ERROR_OTHER) != 0) { + status = pci_read_config(sc->sc_dev, PCIR_STATUS, 2); + printf(", PCI status 0x%x", status); + pci_write_config(sc->sc_dev, PCIR_STATUS, status, 2); + } + } + printf("\n"); + + cas_init_locked(sc); + cas_start_locked(sc->sc_ifp); +} + +static void +cas_intr(void *v) +{ + struct cas_softc *sc = v; + uint32_t status, status2; + + status = CAS_READ_4(sc, CAS_STATUS); + if (__predict_false((status & CAS_INTR_SUMMARY) == 0)) + return; + + CAS_LOCK(sc); + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, "%s: %s: cplt %x, status %x", + device_get_name(sc->sc_dev), __func__, + (status >> CAS_STATUS_TX_COMP3_SHIFT), (u_int)status); + + /* + * PCS interrupts must be cleared, otherwise no traffic is passed! + */ + if ((status & CAS_INTR_PCS_INT) != 0) { + status2 = + CAS_READ_4(sc, CAS_PCS_INTR_STATUS) | + CAS_READ_4(sc, CAS_PCS_INTR_STATUS); + if ((status2 & CAS_PCS_INTR_LINK) != 0) + device_printf(sc->sc_dev, + "%s: PCS link status changed\n", __func__); + } + if ((status & CAS_MAC_CTRL_STATUS) != 0) { + status2 = CAS_READ_4(sc, CAS_MAC_CTRL_STATUS); + if ((status2 & CAS_MAC_CTRL_PAUSE) != 0) + device_printf(sc->sc_dev, + "%s: PAUSE received (PAUSE time %d slots)\n", + __func__, + (status2 & CAS_MAC_CTRL_STATUS_PT_MASK) >> + CAS_MAC_CTRL_STATUS_PT_SHFT); + if ((status2 & CAS_MAC_CTRL_PAUSE) != 0) + device_printf(sc->sc_dev, + "%s: transited to PAUSE state\n", __func__); + if ((status2 & CAS_MAC_CTRL_NON_PAUSE) != 0) + device_printf(sc->sc_dev, + "%s: transited to non-PAUSE state\n", __func__); + } + if ((status & CAS_INTR_MIF) != 0) + device_printf(sc->sc_dev, "%s: MIF interrupt\n", __func__); +#endif + + if (__predict_false((status & + (CAS_INTR_TX_TAG_ERR | CAS_INTR_RX_TAG_ERR | + CAS_INTR_RX_LEN_MMATCH | CAS_INTR_PCI_ERROR_INT)) != 0)) { + cas_eint(sc, status); + CAS_UNLOCK(sc); + return; + } + + if (__predict_false(status & CAS_INTR_TX_MAC_INT)) { + status2 = CAS_READ_4(sc, CAS_MAC_TX_STATUS); + if ((status2 & + (CAS_MAC_TX_UNDERRUN | CAS_MAC_TX_MAX_PKT_ERR)) != 0) + sc->sc_ifp->if_oerrors++; + else if ((status2 & ~CAS_MAC_TX_FRAME_XMTD) != 0) + device_printf(sc->sc_dev, + "MAC TX fault, status %x\n", status2); + } + + if (__predict_false(status & CAS_INTR_RX_MAC_INT)) { + status2 = CAS_READ_4(sc, CAS_MAC_RX_STATUS); + if ((status2 & CAS_MAC_RX_OVERFLOW) != 0) + sc->sc_ifp->if_ierrors++; + else if ((status2 & ~CAS_MAC_RX_FRAME_RCVD) != 0) + device_printf(sc->sc_dev, + "MAC RX fault, status %x\n", status2); + } + + if ((status & + (CAS_INTR_RX_DONE | CAS_INTR_RX_BUF_NA | CAS_INTR_RX_COMP_FULL | + CAS_INTR_RX_BUF_AEMPTY | CAS_INTR_RX_COMP_AFULL)) != 0) { + cas_rint(sc); + if (__predict_false((status & + (CAS_INTR_RX_BUF_NA | CAS_INTR_RX_COMP_FULL | + CAS_INTR_RX_BUF_AEMPTY | CAS_INTR_RX_COMP_AFULL)) != 0)) + device_printf(sc->sc_dev, + "RX fault, status %x\n", status); + } + + if ((status & + (CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_DONE)) != 0) + cas_tint(sc); + + CAS_UNLOCK(sc); +} + +static int +cas_watchdog(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + +#ifdef CAS_DEBUG + CTR4(KTR_CAS, + "%s: CAS_RX_CONFIG %x CAS_MAC_RX_STATUS %x CAS_MAC_RX_CONFIG %x", + __func__, CAS_READ_4(sc, CAS_RX_CONFIG), + CAS_READ_4(sc, CAS_MAC_RX_STATUS), + CAS_READ_4(sc, CAS_MAC_RX_CONFIG)); + CTR4(KTR_CAS, + "%s: CAS_TX_CONFIG %x CAS_MAC_TX_STATUS %x CAS_MAC_TX_CONFIG %x", + __func__, CAS_READ_4(sc, CAS_TX_CONFIG), + CAS_READ_4(sc, CAS_MAC_TX_STATUS), + CAS_READ_4(sc, CAS_MAC_TX_CONFIG)); +#endif + + if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0) + return (0); + + if ((sc->sc_flags & CAS_LINK) != 0) + device_printf(sc->sc_dev, "device timeout\n"); + else if (bootverbose) + device_printf(sc->sc_dev, "device timeout (no link)\n"); + ++ifp->if_oerrors; + + /* Try to get more packets going. */ + cas_init_locked(sc); + cas_start_locked(ifp); + return (EJUSTRETURN); +} + +static void +cas_mifinit(struct cas_softc *sc) +{ + + /* Configure the MIF in frame mode. */ + CAS_WRITE_4(sc, CAS_MIF_CONF, + CAS_READ_4(sc, CAS_MIF_CONF) & ~CAS_MIF_CONF_BB_MODE); +} + +/* + * MII interface + * + * The MII interface supports at least three different operating modes: + * + * Bitbang mode is implemented using data, clock and output enable registers. + * + * Frame mode is implemented by loading a complete frame into the frame + * register and polling the valid bit for completion. + * + * Polling mode uses the frame register but completion is indicated by + * an interrupt. + * + */ +static int +cas_mii_readreg(device_t dev, int phy, int reg) +{ + struct cas_softc *sc; + int n; + uint32_t v; + +#ifdef CAS_DEBUG_PHY + printf("%s: phy %d reg %d\n", __func__, phy, reg); +#endif + + sc = device_get_softc(dev); + if (sc->sc_phyad != -1 && phy != sc->sc_phyad) + return (0); + + if ((sc->sc_flags & CAS_SERDES) != 0) { + switch (reg) { + case MII_BMCR: + reg = CAS_PCS_CTRL; + break; + case MII_BMSR: + reg = CAS_PCS_STATUS; + break; + case MII_PHYIDR1: + case MII_PHYIDR2: + return (0); + case MII_ANAR: + reg = CAS_PCS_ANAR; + break; + case MII_ANLPAR: + reg = CAS_PCS_ANLPAR; + break; + case MII_EXTSR: + return (EXTSR_1000XFDX | EXTSR_1000XHDX); + default: + device_printf(sc->sc_dev, + "%s: unhandled register %d\n", __func__, reg); + return (0); + } + return (CAS_READ_4(sc, reg)); + } + + /* Construct the frame command. */ + v = CAS_MIF_FRAME_READ | + (phy << CAS_MIF_FRAME_PHY_SHFT) | + (reg << CAS_MIF_FRAME_REG_SHFT); + + CAS_WRITE_4(sc, CAS_MIF_FRAME, v); + CAS_BARRIER(sc, CAS_MIF_FRAME, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + for (n = 0; n < 100; n++) { + DELAY(1); + v = CAS_READ_4(sc, CAS_MIF_FRAME); + if (v & CAS_MIF_FRAME_TA_LSB) + return (v & CAS_MIF_FRAME_DATA); + } + + device_printf(sc->sc_dev, "%s: timed out\n", __func__); + return (0); +} + +static int +cas_mii_writereg(device_t dev, int phy, int reg, int val) +{ + struct cas_softc *sc; + int n; + uint32_t v; + +#ifdef CAS_DEBUG_PHY + printf("%s: phy %d reg %d val %x\n", phy, reg, val, __func__); +#endif + + sc = device_get_softc(dev); + if (sc->sc_phyad != -1 && phy != sc->sc_phyad) + return (0); + + if ((sc->sc_flags & CAS_SERDES) != 0) { + switch (reg) { + case MII_BMSR: + reg = CAS_PCS_STATUS; + break; + case MII_BMCR: + reg = CAS_PCS_CTRL; + if ((val & CAS_PCS_CTRL_RESET) == 0) + break; + CAS_WRITE_4(sc, CAS_PCS_CTRL, val); + CAS_BARRIER(sc, CAS_PCS_CTRL, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_PCS_CTRL, + CAS_PCS_CTRL_RESET, 0)) + device_printf(sc->sc_dev, + "cannot reset PCS\n"); + /* FALLTHROUGH */ + case MII_ANAR: + CAS_WRITE_4(sc, CAS_PCS_CONF, 0); + CAS_BARRIER(sc, CAS_PCS_CONF, 4, + BUS_SPACE_BARRIER_WRITE); + CAS_WRITE_4(sc, CAS_PCS_ANAR, val); + CAS_WRITE_4(sc, CAS_PCS_SERDES_CTRL, + CAS_PCS_SERDES_CTRL_ESD); + CAS_WRITE_4(sc, CAS_PCS_CONF, + CAS_PCS_CONF_EN); + return (0); + case MII_ANLPAR: + reg = CAS_PCS_ANLPAR; + break; + default: + device_printf(sc->sc_dev, + "%s: unhandled register %d\n", __func__, reg); + return (0); + } + CAS_WRITE_4(sc, reg, val); + return (0); + } + + /* Construct the frame command. */ + v = CAS_MIF_FRAME_WRITE | + (phy << CAS_MIF_FRAME_PHY_SHFT) | + (reg << CAS_MIF_FRAME_REG_SHFT) | + (val & CAS_MIF_FRAME_DATA); + + CAS_WRITE_4(sc, CAS_MIF_FRAME, v); + CAS_BARRIER(sc, CAS_MIF_FRAME, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + for (n = 0; n < 100; n++) { + DELAY(1); + v = CAS_READ_4(sc, CAS_MIF_FRAME); + if (v & CAS_MIF_FRAME_TA_LSB) + return (1); + } + + device_printf(sc->sc_dev, "%s: timed out\n", __func__); + return (0); +} + +static void +cas_mii_statchg(device_t dev) +{ + struct cas_softc *sc; + struct ifnet *ifp; + int gigabit; + uint32_t rxcfg, txcfg, v; + + sc = device_get_softc(dev); + ifp = sc->sc_ifp; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + +#ifdef CAS_DEBUG + if ((ifp->if_flags & IFF_DEBUG) != 0) + device_printf(sc->sc_dev, "%s: status change: PHY = %d\n", + __func__, sc->sc_phyad); +#endif + + if ((sc->sc_mii->mii_media_status & IFM_ACTIVE) != 0 && + IFM_SUBTYPE(sc->sc_mii->mii_media_active) != IFM_NONE) + sc->sc_flags |= CAS_LINK; + else + sc->sc_flags &= ~CAS_LINK; + + switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) { + case IFM_1000_SX: + case IFM_1000_LX: + case IFM_1000_CX: + case IFM_1000_T: + gigabit = 1; + break; + default: + gigabit = 0; + } + + /* + * The configuration done here corresponds to the steps F) and + * G) and as far as enabling of RX and TX MAC goes also step H) + * of the initialization sequence outlined in section 11.2.1 of + * the Cassini+ ASIC Specification. + */ + + rxcfg = CAS_READ_4(sc, CAS_MAC_RX_CONF); + rxcfg &= ~(CAS_MAC_RX_CONF_EN | CAS_MAC_RX_CONF_CARR); + txcfg = CAS_MAC_TX_CONF_EN_IPG0 | CAS_MAC_TX_CONF_NGU | + CAS_MAC_TX_CONF_NGUL; + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) != 0) + txcfg |= CAS_MAC_TX_CONF_ICARR | CAS_MAC_TX_CONF_ICOLLIS; + else if (gigabit != 0) { + rxcfg |= CAS_MAC_RX_CONF_CARR; + txcfg |= CAS_MAC_TX_CONF_CARR; + } + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, 0); + CAS_BARRIER(sc, CAS_MAC_TX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_MAC_TX_CONF, CAS_MAC_TX_CONF_EN, 0)) + device_printf(sc->sc_dev, "cannot disable TX MAC\n"); + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, txcfg); + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, 0); + CAS_BARRIER(sc, CAS_MAC_RX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_MAC_RX_CONF, CAS_MAC_RX_CONF_EN, 0)) + device_printf(sc->sc_dev, "cannot disable RX MAC\n"); + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, rxcfg); + + v = CAS_READ_4(sc, CAS_MAC_CTRL_CONF) & + ~(CAS_MAC_CTRL_CONF_TXP | CAS_MAC_CTRL_CONF_RXP); +#ifdef notyet + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & + IFM_ETH_RXPAUSE) != 0) + v |= CAS_MAC_CTRL_CONF_RXP; + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & + IFM_ETH_TXPAUSE) != 0) + v |= CAS_MAC_CTRL_CONF_TXP; +#endif + CAS_WRITE_4(sc, CAS_MAC_CTRL_CONF, v); + + /* + * All supported chips have a bug causing incorrect checksum + * to be calculated when letting them strip the FCS in half- + * duplex mode. In theory we could disable FCS stripping and + * manually adjust the checksum accordingly. It seems to make + * more sense to optimze for the common case and just disable + * hardware checksumming in half-duplex mode though. + */ + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) { + ifp->if_capenable &= ~IFCAP_HWCSUM; + ifp->if_hwassist = 0; + } else if ((sc->sc_flags & CAS_NO_CSUM) == 0) { + ifp->if_capenable = ifp->if_capabilities; + ifp->if_hwassist = CAS_CSUM_FEATURES; + } + + if (sc->sc_variant == CAS_SATURN) { + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) + /* silicon bug workaround */ + CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x41); + else + CAS_WRITE_4(sc, CAS_MAC_PREAMBLE_LEN, 0x7); + } + + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0 && + gigabit != 0) + CAS_WRITE_4(sc, CAS_MAC_SLOT_TIME, + CAS_MAC_SLOT_TIME_CARR); + else + CAS_WRITE_4(sc, CAS_MAC_SLOT_TIME, + CAS_MAC_SLOT_TIME_NORM); + + /* XIF Configuration */ + v = CAS_MAC_XIF_CONF_TX_OE | CAS_MAC_XIF_CONF_LNKLED; + if ((sc->sc_flags & CAS_SERDES) == 0) { + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) == 0) + v |= CAS_MAC_XIF_CONF_NOECHO; + v |= CAS_MAC_XIF_CONF_BUF_OE; + } + if (gigabit != 0) + v |= CAS_MAC_XIF_CONF_GMII; + if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) & IFM_FDX) != 0) + v |= CAS_MAC_XIF_CONF_FDXLED; + CAS_WRITE_4(sc, CAS_MAC_XIF_CONF, v); + + if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && + (sc->sc_flags & CAS_LINK) != 0) { + CAS_WRITE_4(sc, CAS_MAC_TX_CONF, + txcfg | CAS_MAC_TX_CONF_EN); + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, + rxcfg | CAS_MAC_RX_CONF_EN); + } +} + +static int +cas_mediachange(struct ifnet *ifp) +{ + struct cas_softc *sc = ifp->if_softc; + int error; + + /* XXX add support for serial media. */ + + CAS_LOCK(sc); + error = mii_mediachg(sc->sc_mii); + CAS_UNLOCK(sc); + return (error); +} + +static void +cas_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct cas_softc *sc = ifp->if_softc; + + CAS_LOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0) { + CAS_UNLOCK(sc); + return; + } + + mii_pollstat(sc->sc_mii); + ifmr->ifm_active = sc->sc_mii->mii_media_active; + ifmr->ifm_status = sc->sc_mii->mii_media_status; + CAS_UNLOCK(sc); +} + +static int +cas_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct cas_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int error; + + error = 0; + switch (cmd) { + case SIOCSIFFLAGS: + CAS_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && + ((ifp->if_flags ^ sc->sc_ifflags) & + (IFF_ALLMULTI | IFF_PROMISC)) != 0) + cas_setladrf(sc); + else + cas_init_locked(sc); + } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + cas_stop(ifp); + sc->sc_ifflags = ifp->if_flags; + CAS_UNLOCK(sc); + break; + case SIOCSIFCAP: + CAS_LOCK(sc); + if ((sc->sc_flags & CAS_NO_CSUM) != 0) { + error = EINVAL; + CAS_UNLOCK(sc); + break; + } + ifp->if_capenable = ifr->ifr_reqcap; + if ((ifp->if_capenable & IFCAP_TXCSUM) != 0) + ifp->if_hwassist = CAS_CSUM_FEATURES; + else + ifp->if_hwassist = 0; + CAS_UNLOCK(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + CAS_LOCK(sc); + cas_setladrf(sc); + CAS_UNLOCK(sc); + break; + case SIOCSIFMTU: + if ((ifr->ifr_mtu < ETHERMIN) || + (ifr->ifr_mtu > ETHERMTU_JUMBO)) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, cmd); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +static void +cas_setladrf(struct cas_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ifmultiaddr *inm; + int i; + uint32_t hash[16]; + uint32_t crc, v; + + CAS_LOCK_ASSERT(sc, MA_OWNED); + + /* Get the current RX configuration. */ + v = CAS_READ_4(sc, CAS_MAC_RX_CONF); + + /* + * Turn off promiscuous mode, promiscuous group mode (all multicast), + * and hash filter. Depending on the case, the right bit will be + * enabled. + */ + v &= ~(CAS_MAC_RX_CONF_PROMISC | CAS_MAC_RX_CONF_HFILTER | + CAS_MAC_RX_CONF_PGRP); + + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, v); + CAS_BARRIER(sc, CAS_MAC_RX_CONF, 4, + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); + if (!cas_bitwait(sc, CAS_MAC_RX_CONF, CAS_MAC_RX_CONF_HFILTER, 0)) + device_printf(sc->sc_dev, "cannot disable RX hash filter\n"); + + if ((ifp->if_flags & IFF_PROMISC) != 0) { + v |= CAS_MAC_RX_CONF_PROMISC; + goto chipit; + } + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + v |= CAS_MAC_RX_CONF_PGRP; + goto chipit; + } + + /* + * Set up multicast address filter by passing all multicast + * addresses through a crc generator, and then using the high + * order 8 bits as an index into the 256 bit logical address + * filter. The high order 4 bits selects the word, while the + * other 4 bits select the bit within the word (where bit 0 + * is the MSB). + */ + + /* Clear the hash table. */ + memset(hash, 0, sizeof(hash)); + + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) { + if (inm->ifma_addr->sa_family != AF_LINK) + continue; + crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) + inm->ifma_addr), ETHER_ADDR_LEN); + + /* We just want the 8 most significant bits. */ + crc >>= 24; + + /* Set the corresponding bit in the filter. */ + hash[crc >> 4] |= 1 << (15 - (crc & 15)); + } + IF_ADDR_UNLOCK(ifp); + + v |= CAS_MAC_RX_CONF_HFILTER; + + /* Now load the hash table into the chip (if we are using it). */ + for (i = 0; i < 16; i++) + CAS_WRITE_4(sc, + CAS_MAC_HASH0 + i * (CAS_MAC_HASH1 - CAS_MAC_HASH0), + hash[i]); + + chipit: + CAS_WRITE_4(sc, CAS_MAC_RX_CONF, v); +} + +static int cas_pci_attach(device_t dev); +static int cas_pci_detach(device_t dev); +static int cas_pci_probe(device_t dev); +static int cas_pci_resume(device_t dev); +static int cas_pci_suspend(device_t dev); + +static device_method_t cas_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cas_pci_probe), + DEVMETHOD(device_attach, cas_pci_attach), + DEVMETHOD(device_detach, cas_pci_detach), + DEVMETHOD(device_suspend, cas_pci_suspend), + DEVMETHOD(device_resume, cas_pci_resume), + /* Use the suspend handler here, it is all that is required. */ + DEVMETHOD(device_shutdown, cas_pci_suspend), + + /* bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, cas_mii_readreg), + DEVMETHOD(miibus_writereg, cas_mii_writereg), + DEVMETHOD(miibus_statchg, cas_mii_statchg), + + KOBJMETHOD_END +}; + +static driver_t cas_pci_driver = { + "cas", + cas_pci_methods, + sizeof(struct cas_softc) +}; + +DRIVER_MODULE(cas, pci, cas_pci_driver, cas_devclass, 0, 0); +DRIVER_MODULE(miibus, cas, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(cas, pci, 1, 1, 1); + +static const struct cas_pci_dev { + uint32_t cpd_devid; + uint8_t cpd_revid; + int cpd_variant; + const char *cpd_desc; +} const cas_pci_devlist[] = { + { 0x0035100b, 0x0, CAS_SATURN, "NS DP83065 Saturn Gigabit Ethernet" }, + { 0xabba108e, 0x10, CAS_CASPLUS, "Sun Cassini+ Gigabit Ethernet" }, + { 0xabba108e, 0x0, CAS_CAS, "Sun Cassini Gigabit Ethernet" }, + { 0, 0, 0, NULL } +}; + +static int +cas_pci_probe(device_t dev) +{ + int i; + + for (i = 0; cas_pci_devlist[i].cpd_desc != NULL; i++) { + if (pci_get_devid(dev) == cas_pci_devlist[i].cpd_devid && + pci_get_revid(dev) >= cas_pci_devlist[i].cpd_revid) { + device_set_desc(dev, cas_pci_devlist[i].cpd_desc); + return (BUS_PROBE_DEFAULT); + } + } + + return (ENXIO); +} + +static struct resource_spec cas_pci_res_spec[] = { + { SYS_RES_IRQ, 0, RF_SHAREABLE | RF_ACTIVE }, /* CAS_RES_INTR */ + { SYS_RES_MEMORY, PCIR_BAR(0), RF_ACTIVE }, /* CAS_RES_MEM */ + { -1, 0 } +}; + +static int +cas_pci_attach(device_t dev) +{ + struct cas_softc *sc; + int i; +#if !(defined(__powerpc__) || defined(__sparc64__)) + u_char enaddr[4][ETHER_ADDR_LEN]; + char lma[sizeof("local-mac-address")]; + int found, j; +#endif + + sc = device_get_softc(dev); + sc->sc_variant = CAS_UNKNOWN; + for (i = 0; cas_pci_devlist[i].cpd_desc != NULL; i++) { + if (pci_get_devid(dev) == cas_pci_devlist[i].cpd_devid && + pci_get_revid(dev) >= cas_pci_devlist[i].cpd_revid) { + sc->sc_variant = cas_pci_devlist[i].cpd_variant; + break; + } + } + if (sc->sc_variant == CAS_UNKNOWN) { + device_printf(dev, "unknown adaptor\n"); + return (ENXIO); + } + + pci_enable_busmaster(dev); + + sc->sc_dev = dev; + if (sc->sc_variant == CAS_CAS && pci_get_devid(dev) < 0x02) + /* Hardware checksumming may hang TX. */ + sc->sc_flags |= CAS_NO_CSUM; + if (sc->sc_variant == CAS_CASPLUS || sc->sc_variant == CAS_SATURN) + sc->sc_flags |= CAS_REG_PLUS; + if (sc->sc_variant == CAS_CAS || + (sc->sc_variant == CAS_CASPLUS && pci_get_revid(dev) < 0x11)) + sc->sc_flags |= CAS_TABORT; + if (bootverbose) + device_printf(dev, "flags=0x%x\n", sc->sc_flags); + + if (bus_alloc_resources(dev, cas_pci_res_spec, sc->sc_res)) { + device_printf(dev, "failed to allocate resources\n"); + bus_release_resources(dev, cas_pci_res_spec, sc->sc_res); + return (ENXIO); + } + + CAS_LOCK_INIT(sc, device_get_nameunit(dev)); + +#if defined(__powerpc__) || defined(__sparc64__) + OF_getetheraddr(dev, sc->sc_enaddr); +#else + /* + * Dig out VPD (vital product data) and read the MAX address. + * The VPD resides in the PCI Expansion ROM (PCI FCode) and + * can't be accessed via the PCI capability pointer. + * SUNW,pci-ce and SUNW,pci-qge use the Enhanced VPD format + * described in US Patent 7149820. + */ + +#define PCI_ROMHDR_SIZE 0x1c +#define PCI_ROMHDR_SIG 0x00 +#define PCI_ROMHDR_SIG_MAGIC 0xaa55 /* little endian */ +#define PCI_ROMHDR_PTR_DATA 0x18 +#define PCI_ROM_SIZE 0x18 +#define PCI_ROM_SIG 0x00 +#define PCI_ROM_SIG_MAGIC 0x52494350 /* "PCIR", endian */ + /* reversed */ +#define PCI_ROM_VENDOR 0x04 +#define PCI_ROM_DEVICE 0x06 +#define PCI_ROM_PTR_VPD 0x08 +#define PCI_VPDRES_BYTE0 0x00 +#define PCI_VPDRES_ISLARGE(x) ((x) & 0x80) +#define PCI_VPDRES_LARGE_NAME(x) ((x) & 0x7f) +#define PCI_VPDRES_LARGE_LEN_LSB 0x01 +#define PCI_VPDRES_LARGE_LEN_MSB 0x02 +#define PCI_VPDRES_LARGE_SIZE 0x03 +#define PCI_VPDRES_TYPE_ID_STRING 0x02 /* large */ +#define PCI_VPDRES_TYPE_VPD 0x10 /* large */ +#define PCI_VPD_KEY0 0x00 +#define PCI_VPD_KEY1 0x01 +#define PCI_VPD_LEN 0x02 +#define PCI_VPD_SIZE 0x03 + +#define CAS_ROM_READ_1(sc, offs) \ + CAS_READ_1((sc), CAS_PCI_ROM_OFFSET + (offs)) +#define CAS_ROM_READ_2(sc, offs) \ + CAS_READ_2((sc), CAS_PCI_ROM_OFFSET + (offs)) +#define CAS_ROM_READ_4(sc, offs) \ + CAS_READ_4((sc), CAS_PCI_ROM_OFFSET + (offs)) + + found = 0; + /* Enable PCI Expansion ROM access. */ + CAS_WRITE_4(sc, CAS_BIM_LDEV_OEN, + CAS_BIM_LDEV_OEN_PAD | CAS_BIM_LDEV_OEN_PROM); + + /* Read PCI Expansion ROM header. */ + if (CAS_ROM_READ_2(sc, PCI_ROMHDR_SIG) != PCI_ROMHDR_SIG_MAGIC || + (i = CAS_ROM_READ_2(sc, PCI_ROMHDR_PTR_DATA)) < + PCI_ROMHDR_SIZE) { + device_printf(dev, "unexpected PCI Expansion ROM header\n"); + goto fail_prom; + } + + /* Read PCI Expansion ROM data. */ + if (CAS_ROM_READ_4(sc, i + PCI_ROM_SIG) != PCI_ROM_SIG_MAGIC || + CAS_ROM_READ_2(sc, i + PCI_ROM_VENDOR) != pci_get_vendor(dev) || + CAS_ROM_READ_2(sc, i + PCI_ROM_DEVICE) != pci_get_device(dev) || + (j = CAS_ROM_READ_2(sc, i + PCI_ROM_PTR_VPD)) < + i + PCI_ROM_SIZE) { + device_printf(dev, "unexpected PCI Expansion ROM data\n"); + goto fail_prom; + } + + /* Read PCI VPD. */ + next: + if (PCI_VPDRES_ISLARGE(CAS_ROM_READ_1(sc, + j + PCI_VPDRES_BYTE0)) == 0) { + device_printf(dev, "no large PCI VPD\n"); + goto fail_prom; + } + + i = (CAS_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_MSB) << 8) | + CAS_ROM_READ_1(sc, j + PCI_VPDRES_LARGE_LEN_LSB); + switch (PCI_VPDRES_LARGE_NAME(CAS_ROM_READ_1(sc, + j + PCI_VPDRES_BYTE0))) { + case PCI_VPDRES_TYPE_ID_STRING: + /* Skip identifier string. */ + j += PCI_VPDRES_LARGE_SIZE + i; + goto next; + case PCI_VPDRES_TYPE_VPD: + for (j += PCI_VPDRES_LARGE_SIZE; i > 0; + i -= PCI_VPD_SIZE + CAS_ROM_READ_1(sc, j + PCI_VPD_LEN), + j += PCI_VPD_SIZE + CAS_ROM_READ_1(sc, j + PCI_VPD_LEN)) { + if (CAS_ROM_READ_1(sc, j + PCI_VPD_KEY0) != 'Z') + /* no Enhanced VPD */ + continue; + if (CAS_ROM_READ_1(sc, j + PCI_VPD_SIZE) != 'I') + /* no instance property */ + continue; + if (CAS_ROM_READ_1(sc, j + PCI_VPD_SIZE + 3) != 'B') + /* no byte array */ + continue; + if (CAS_ROM_READ_1(sc, j + PCI_VPD_SIZE + 4) != + ETHER_ADDR_LEN) + continue; + bus_read_region_1(sc->sc_res[CAS_RES_MEM], + CAS_PCI_ROM_OFFSET + j + PCI_VPD_SIZE + 5, + lma, sizeof(lma)); + if (strcmp(lma, "local-mac-address") != 0) + continue; + bus_read_region_1(sc->sc_res[CAS_RES_MEM], + CAS_PCI_ROM_OFFSET + j + PCI_VPD_SIZE + 5 + + sizeof(lma), enaddr[found], + sizeof(enaddr[found])); + if (found++ == 4) + break; + } + break; + default: + device_printf(dev, "unexpected PCI VPD\n"); + goto fail_prom; + } + + fail_prom: + CAS_WRITE_4(sc, CAS_BIM_LDEV_OEN, 0); + + if (found == 0) { + device_printf(dev, "could not determine Ethernet address\n"); + goto fail; + } + i = 0; + if (found > 1 && pci_get_slot(dev) < sizeof(enaddr) / sizeof(*enaddr)) + i = pci_get_slot(dev); + memcpy(sc->sc_enaddr, enaddr[i], ETHER_ADDR_LEN); +#endif + + if (cas_attach(sc) != 0) { + device_printf(dev, "could not be attached\n"); + goto fail; + } + + if (bus_setup_intr(dev, sc->sc_res[CAS_RES_INTR], INTR_TYPE_NET | + INTR_MPSAFE, NULL, cas_intr, sc, &sc->sc_ih) != 0) { + device_printf(dev, "failed to set up interrupt\n"); + cas_detach(sc); + goto fail; + } + return (0); + + fail: + CAS_LOCK_DESTROY(sc); + bus_release_resources(dev, cas_pci_res_spec, sc->sc_res); + return (ENXIO); +} + +static int +cas_pci_detach(device_t dev) +{ + struct cas_softc *sc; + + sc = device_get_softc(dev); + bus_teardown_intr(dev, sc->sc_res[CAS_RES_INTR], sc->sc_ih); + cas_detach(sc); + CAS_LOCK_DESTROY(sc); + bus_release_resources(dev, cas_pci_res_spec, sc->sc_res); + return (0); +} + +static int +cas_pci_suspend(device_t dev) +{ + + cas_suspend(device_get_softc(dev)); + return (0); +} + +static int +cas_pci_resume(device_t dev) +{ + + cas_resume(device_get_softc(dev)); + return (0); +} diff --git a/sys/dev/cas/if_casreg.h b/sys/dev/cas/if_casreg.h new file mode 100644 index 0000000..97250e0 --- /dev/null +++ b/sys/dev/cas/if_casreg.h @@ -0,0 +1,1002 @@ +/*- + * Copyright (C) 2001 Eduardo Horvath. + * Copyright (c) 2008 Marius Strobl <marius@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: gemreg.h,v 1.8 2005/12/11 12:21:26 christos Exp + * from: FreeBSD: if_gemreg.h 174987 2007-12-30 01:32:03Z marius + * + * $FreeBSD$ + */ + +#ifndef _IF_CASREG_H +#define _IF_CASREG_H + +/* + * register definitions for Sun Cassini/Cassini+ and National Semiconductor + * DP83065 Saturn Gigabit Ethernet controllers + */ + +/* global resources */ +#define CAS_CAW 0x0004 /* core arbitration weight */ +#define CAS_INF_BURST 0x0008 /* infinite burst enable */ +#define CAS_STATUS 0x000c /* interrupt status */ +#define CAS_INTMASK 0x0010 /* interrupt mask */ +#define CAS_CLEAR_ALIAS 0x0014 /* clear mask alias */ +#define CAS_STATUS_ALIAS 0x001c /* interrupt status alias */ +#define CAS_ERROR_STATUS 0x1000 /* PCI error status */ +#define CAS_ERROR_MASK 0x1004 /* PCI error mask */ +#define CAS_BIM_CONF 0x1008 /* BIM configuration */ +#define CAS_BIM_DIAG 0x100c /* BIM diagnostic */ +#define CAS_RESET 0x1010 /* software reset */ +#define CAS_BIM_LDEV_OEN 0x1020 /* BIM local device output enable */ +#define CAS_BIM_BUF_ADDR 0x1024 /* BIM buffer address */ +#define CAS_BIM_BUF_DATA_LO 0x1028 /* BIM buffer data low */ +#define CAS_BIM_BUF_DATA_HI 0x102c /* BIM buffer data high */ +#define CAS_BIM_RAM_BIST 0x1030 /* BIM RAM BIST control/status */ +#define CAS_PROBE_MUX_SELECT 0x1034 /* PROBE MUX SELECT */ +#define CAS_INTMASK2 0x1038 /* interrupt mask 2 for INTB */ +#define CAS_STATUS2 0x103c /* interrupt status 2 for INTB */ +#define CAS_CLEAR_ALIAS2 0x1040 /* clear mask alias 2 for INTB */ +#define CAS_STATUS_ALIAS2 0x1044 /* interrupt status alias 2 for INTB */ +#define CAS_INTMASK3 0x1048 /* interrupt mask 3 for INTC */ +#define CAS_STATUS3 0x104c /* interrupt status 3 for INTC */ +#define CAS_CLEAR_ALIAS3 0x1050 /* clear mask alias 3 for INTC */ +#define CAS_STATUS_ALIAS3 0x1054 /* interrupt status alias 3 for INTC */ +#define CAS_INTMASK4 0x1058 /* interrupt mask 4 for INTD */ +#define CAS_STATUS4 0x105c /* interrupt status 4 for INTD */ +#define CAS_CLEAR_ALIAS4 0x1060 /* clear mask alias 4 for INTD */ +#define CAS_STATUS_ALIAS4 0x1064 /* interrupt status alias 4 for INTD */ + +#define CAS_CAW_RX_WGHT_MASK 0x00000003 /* RX DMA factor for... */ +#define CAS_CAW_RX_WGHT_SHFT 0 /* ...weighted round robin */ +#define CAS_CAW_TX_WGHT_MASK 0x0000000c /* RX DMA factor for... */ +#define CAS_CAW_TX_WGHT_SHFT 2 /* ...weighted round robin */ +#define CAS_CAW_RR_DIS 0x00000010 /* weighted round robin dis. */ + +#define CAS_INF_BURST_EN 0x00000001 /* Allow bursts > cachline. */ + +/* + * shared interrupt bits for CAS_STATUS, CAS_INTMASK, CAS_CLEAR_ALIAS and + * CAS_STATUS_ALIAS + * Bits 0-9 of CAS_STATUS auto-clear when read. CAS_CLEAR_ALIAS specifies + * which of bits 0-9 auto-clear when reading CAS_STATUS_ALIAS. + */ +#define CAS_INTR_TX_INT_ME 0x00000001 /* Frame w/ INT_ME set sent. */ +#define CAS_INTR_TX_ALL 0x00000002 /* TX frames trans. to FIFO. */ +#define CAS_INTR_TX_DONE 0x00000004 /* Any TX frame transferred. */ +#define CAS_INTR_TX_TAG_ERR 0x00000008 /* TX FIFO tag corrupted. */ +#define CAS_INTR_RX_DONE 0x00000010 /* >=1 RX frames transferred. */ +#define CAS_INTR_RX_BUF_NA 0x00000020 /* RX buffer not available */ +#define CAS_INTR_RX_TAG_ERR 0x00000040 /* RX FIFO tag corrupted. */ +#define CAS_INTR_RX_COMP_FULL 0x00000080 /* RX completion ring full */ +#define CAS_INTR_RX_BUF_AEMPTY 0x00000100 /* RX desc. ring almost empty */ +#define CAS_INTR_RX_COMP_AFULL 0x00000200 /* RX cmpl. ring almost full */ +#define CAS_INTR_RX_LEN_MMATCH 0x00000400 /* length field mismatch */ +#define CAS_INTR_SUMMARY 0x00001000 /* summary interrupt bit */ +#define CAS_INTR_PCS_INT 0x00002000 /* PCS interrupt */ +#define CAS_INTR_TX_MAC_INT 0x00004000 /* TX MAC interrupt */ +#define CAS_INTR_RX_MAC_INT 0x00008000 /* RX MAC interrupt */ +#define CAS_INTR_MAC_CTRL_INT 0x00010000 /* MAC control interrupt */ +#define CAS_INTR_MIF 0x00020000 /* MIF interrupt */ +#define CAS_INTR_PCI_ERROR_INT 0x00040000 /* PCI error interrupt */ + +#define CAS_STATUS_TX_COMP3_MASK 0xfff80000 /* TX completion 3 */ +#define CAS_STATUS_TX_COMP3_SHFT 19 + +/* CAS_ERROR_STATUS and CAS_ERROR_MASK PCI error bits */ +#define CAS_ERROR_DTRTO 0x00000002 /* delayed trans. timeout */ +#define CAS_ERROR_OTHER 0x00000004 /* errors (see PCIR_STATUS) */ +#define CAS_ERROR_DMAW_ZERO 0x00000008 /* zero count DMA write */ +#define CAS_ERROR_DMAR_ZERO 0x00000010 /* zero count DMA read */ +#define CAS_ERROR_RTRTO 0x00000020 /* 255 retries exceeded */ + +#define CAS_BIM_CONF_BD64_DIS 0x00000004 /* 64-bit mode disable */ +#define CAS_BIM_CONF_M66EN 0x00000008 /* PCI clock is 66MHz (ro). */ +#define CAS_BIM_CONF_BUS32_WIDE 0x00000010 /* PCI bus is 32-bit (ro). */ +#define CAS_BIM_CONF_DPAR_EN 0x00000020 /* parity error intr. enable */ +#define CAS_BIM_CONF_RMA_EN 0x00000040 /* master abort intr. enable */ +#define CAS_BIM_CONF_RTA_EN 0x00000080 /* target abort intr. enable */ +#define CAS_BIM_CONF_DIS_BIM 0x00000200 /* Stop PCI DMA transactions. */ +#define CAS_BIM_CONF_BIM_DIS 0x00000400 /* BIM was stopped (ro). */ +#define CAS_BIM_CONF_BLOCK_PERR 0x00000800 /* Block PERR# to PCI bus. */ + +#define CAS_BIM_DIAG_BRST_SM 0x0000007f /* burst ctrl. state machine */ +#define CAS_BIM_DIAG_MSTR_SM 0x3fffff00 + +#define CAS_RESET_TX 0x00000001 /* Reset TX DMA engine. */ +#define CAS_RESET_RX 0x00000002 /* Reset RX DMA engine. */ +#define CAS_RESET_RSTOUT 0x00000004 /* Force PCI RSTOUT#. */ +#define CAS_RESET_PCS_DIS 0x00000008 /* PCS reset disable */ +#define CAS_RESET_BREQ_SM 0x00007f00 /* breq state machine */ +#define CAS_RESET_PCIARB 0x00070000 /* PCI arbitration state */ +#define CAS_RESET_RDPCI 0x00300000 /* read PCI state */ +#define CAS_RESET_RDARB 0x00c00000 /* read arbitration state */ +#define CAS_RESET_WRPCI 0x06000000 /* write PCI state */ +#define CAS_RESET_WRARB 0x38000000 /* write arbitration state */ + +#define CAS_BIM_LDEV_OEN_PAD 0x00000001 /* addr. bus, RW and OE */ +#define CAS_BIM_LDEV_OEN_PROM 0x00000002 /* PROM chip select */ +#define CAS_BIM_LDEV_OEN_EXT 0x00000004 /* secondary local bus device */ +#define CAS_BIM_LDEV_OEN_SOFT_0 0x00000008 /* soft. progr. ctrl. bit 0 */ +#define CAS_BIM_LDEV_OEN_SOFT_1 0x00000010 /* soft. progr. ctrl. bit 1 */ +#define CAS_BIM_LDEV_OEN_HWRST 0x00000020 /* hw. reset (Cassini+ only) */ + +#define CAS_BIM_BUF_ADDR_INDEX 0x0000003f /* buffer entry index */ +#define CAS_BIM_BUF_ADDR_RDWR 0x00000040 /* 0: read, 1: write access */ + +#define CAS_BIM_RAM_BIST_START 0x00000001 /* Start BIST on read buffer. */ +#define CAS_BIM_RAM_BIST_SUM 0x00000004 /* read buffer pass summary */ +#define CAS_BIM_RAM_BIST_LO 0x00000010 /* read buf. low bank passes */ +#define CAS_BIM_RAM_BIST_HI 0x00000020 /* read buf. high bank passes */ + +#define CAS_PROBE_MUX_SELECT_LO 0x0000000f /* P_A[7:0] */ +#define CAS_PROBE_MUX_SELECT_HI 0x000000f0 /* P_A[15:8] */ +#define CAS_PROBE_MUX_SELECT_SB 0x000000f0 /* txdma_wr address and size */ +#define CAS_PROBE_MUX_SELECT_EN 0xf0000000 /* enable probe on P_A[15:0] */ + +/* + * interrupt bits for CAS_INTMASK[2-4], CAS_STATUS[2-4], CAS_CLEAR_ALIAS[2-4] + * and CAS_STATUS_ALIAS[2-4]. + * CAS_STATUS[2-4] auto-clear when read. CAS_CLEAR_ALIAS[2-4] specifies which + * of bits 0-9 auto-clear when reading the corresponding CAS_STATUS_ALIAS[2-4]. + */ +#define CAS_INTRN_RX_DONE 0x00000001 /* >=1 RX frames transferred. */ +#define CAS_INTRN_RX_COMP_FULL 0x00000002 /* RX completion ring full */ +#define CAS_INTRN_RX_COMP_AFULL 0x00000004 /* RX cmpl. ring almost full */ +#define CAS_INTRN_RX_BUF_NA 0x00000008 /* RX buffer not available */ +#define CAS_INTRN_RX_BUF_AEMPTY 0x00000010 /* RX desc. ring almost empty */ + +/* INTn enable bit for CAS_INTMASK[2-4] */ +#define CAS_INTMASKN_EN 0x00000080 /* INT[B-D] enable */ + +/* TX DMA registers */ +#define CAS_TX_CONF 0x2004 /* TX configuration */ +#define CAS_TX_FIFO_WR 0x2014 /* FIFO write pointer */ +#define CAS_TX_FIFO_SDWR 0x2018 /* FIFO shadow write pointer */ +#define CAS_TX_FIFO_RD 0x201c /* FIFO read pointer */ +#define CAS_TX_FIFO_SDRD 0x2020 /* FIFO shadow read pointer */ +#define CAS_TX_FIFO_PKT_CNT 0x2024 /* FIFO packet counter */ +#define CAS_TX_SM1 0x2028 /* TX state machine 1 */ +#define CAS_TX_SM2 0x202c /* TX state machine 2 */ +#define CAS_TX_DATA_PTR_LO 0x2030 /* TX data pointer low */ +#define CAS_TX_DATA_PTR_HI 0x2034 /* TX data pointer high */ +#define CAS_TX_KICK1 0x2038 /* TX kick 1 */ +#define CAS_TX_KICK2 0x203c /* TX kick 2 */ +#define CAS_TX_KICK3 0x2040 /* TX kick 3 */ +#define CAS_TX_KICK4 0x2044 /* TX kick 4 */ +#define CAS_TX_COMP1 0x2048 /* TX completion 1 */ +#define CAS_TX_COMP2 0x204c /* TX completion 2 */ +#define CAS_TX_COMP3 0x2050 /* TX completion 3 */ +#define CAS_TX_COMP4 0x2054 /* TX completion 4 */ +#define CAS_TX_COMPWB_BASE_LO 0x2058 /* TX completion writeback base low */ +#define CAS_TX_COMPWB_BASE_HI 0x205c /* TX completion writeback base high */ +#define CAS_TX_DESC1_BASE_LO 0x2060 /* TX descriptor ring 1 base low */ +#define CAS_TX_DESC1_BASE_HI 0x2064 /* TX descriptor ring 1 base high */ +#define CAS_TX_DESC2_BASE_LO 0x2068 /* TX descriptor ring 2 base low */ +#define CAS_TX_DESC2_BASE_HI 0x206c /* TX descriptor ring 2 base high */ +#define CAS_TX_DESC3_BASE_LO 0x2070 /* TX descriptor ring 2 base low */ +#define CAS_TX_DESC3_BASE_HI 0x2074 /* TX descriptor ring 2 base high */ +#define CAS_TX_DESC4_BASE_LO 0x2078 /* TX descriptor ring 2 base low */ +#define CAS_TX_DESC4_BASE_HI 0x207c /* TX descriptor ring 2 base high */ +#define CAS_TX_MAXBURST1 0x2080 /* TX MaxBurst 1 */ +#define CAS_TX_MAXBURST2 0x2084 /* TX MaxBurst 2 */ +#define CAS_TX_MAXBURST3 0x2088 /* TX MaxBurst 3 */ +#define CAS_TX_MAXBURST4 0x208c /* TX MaxBurst 4 */ +#define CAS_TX_FIFO_ADDR 0x2104 /* TX FIFO address */ +#define CAS_TX_FIFO_TAG 0x2108 /* TX FIFO tag */ +#define CAS_TX_FIFO_DATA_LO 0x210c /* TX FIFO data low */ +#define CAS_TX_FIFO_DATA_HI_T1 0x2110 /* TX FIFO data highT1 */ +#define CAS_TX_FIFO_DATA_HI_T0 0x2114 /* TX FIFO data highT0 */ +#define CAS_TX_FIFO_SIZE 0x2118 /* TX FIFO size in 64 byte multiples */ +#define CAS_TX_RAM_BIST 0x211c /* TX RAM BIST control/status */ + +#define CAS_TX_CONF_TXDMA_EN 0x00000001 /* TX DMA enable */ +#define CAS_TX_CONF_FIFO_PIO 0x00000002 /* Allow TX FIFO PIO access. */ +#define CAS_TX_CONF_DESC1_MASK 0x0000003c /* TX descriptor ring 1 size */ +#define CAS_TX_CONF_DESC1_SHFT 2 +#define CAS_TX_CONF_DESC2_MASK 0x000003c0 /* TX descriptor ring 2 size */ +#define CAS_TX_CONF_DESC2_SHFT 6 +#define CAS_TX_CONF_DESC3_MASK 0x00003c00 /* TX descriptor ring 3 size */ +#define CAS_TX_CONF_DESC3_SHFT 10 +#define CAS_TX_CONF_DESC4_MASK 0x0003c000 /* TX descriptor ring 4 size */ +#define CAS_TX_CONF_DESC4_SHFT 14 +#define CAS_TX_CONF_PACED 0x00100000 /* ALL intr. on FIFO empty */ +#define CAS_TX_CONF_RDPP_DIS 0x01000000 /* Should always be set. */ +#define CAS_TX_CONF_COMPWB_Q1 0x02000000 /* Completion writeback... */ +#define CAS_TX_CONF_COMPWB_Q2 0x04000000 /* ...happens at the end... */ +#define CAS_TX_CONF_COMPWB_Q3 0x08000000 /* ...of every packet in... */ +#define CAS_TX_CONF_COMPWB_Q4 0x10000000 /* ...queue n. */ +#define CAS_TX_CONF_PICWB_DIS 0x20000000 /* pre-intr. compl. W/B dis. */ +#define CAS_TX_CONF_CTX_MASK 0xc0000000 /* test port selection */ +#define CAS_TX_CONF_CTX_SHFT 30 + +#define CAS_TX_COMPWB_ALIGN 2048 /* TX compl. W/B alignment */ + +#define CAS_TX_DESC_ALIGN 2048 /* TX descriptor alignment */ + +/* descriptor ring size bits for both CAS_TX_CONF and CAS_RX_CONF */ +#define CAS_DESC_32 0x0 /* 32 descriptors */ +#define CAS_DESC_64 0x1 /* 64 descriptors */ +#define CAS_DESC_128 0x2 /* 128 descriptors */ +#define CAS_DESC_256 0x3 /* 256 descriptors */ +#define CAS_DESC_512 0x4 /* 512 descriptors */ +#define CAS_DESC_1K 0x5 /* 1k descriptors */ +#define CAS_DESC_2K 0x6 /* 2k descriptors */ +#define CAS_DESC_4K 0x7 /* 4k descriptors */ +#define CAS_DESC_8K 0x8 /* 8k descriptors */ + +#define CAS_TX_SM1_CHAIN 0x000003ff /* chaining state machine */ +#define CAS_TX_SM1_CKSUM 0x00000c00 /* checksum state machine */ +#define CAS_TX_SM1_TX_FIFO_LOAD 0x0003f000 /* TX FIFO load state machine */ +#define CAS_TX_SM1_TX_FIFO_UNLD 0x003c0000 /* TX FIFO unload state mach. */ +#define CAS_TX_SM1_CACHE_CTRL 0x03c00000 /* cache control state mach. */ +#define CAS_TX_SM1_CBQARB 0x03c00000 /* CBQ arbiter state machine */ + +#define CAS_TX_SM2_COMPWB 0x00000007 /* compl. WB state machine */ +#define CAS_TX_SM2_SUB_LOAD 0x00000038 /* sub load state machine */ +#define CAS_TX_SM2_KICK 0x000000c0 /* kick state machine */ + +#define CAS_TX_RAM_BIST_START 0x00000001 /* Start RAM BIST process. */ +#define CAS_TX_RAM_BIST_SUMMARY 0x00000002 /* All RAM okay */ +#define CAS_TX_RAM_BIST_RAM32B 0x00000004 /* RAM32B okay */ +#define CAS_TX_RAM_BIST_RAM33B 0x00000008 /* RAM33B okay */ +#define CAS_TX_RAM_BIST_RAM32A 0x00000010 /* RAM32A okay */ +#define CAS_TX_RAM_BIST_RAM33A 0x00000020 /* RAM33A okay */ +#define CAS_TX_RAM_BIST_SM 0x000001c0 /* RAM BIST state machine */ + +/* RX DMA registers */ +#define CAS_RX_CONF 0x4000 /* RX configuration */ +#define CAS_RX_PSZ 0x4004 /* RX page size */ +#define CAS_RX_FIFO_WR 0x4008 /* RX FIFO write pointer */ +#define CAS_RX_FIFO_RD 0x400c /* RX FIFO read pointer */ +#define CAS_RX_IPP_WR 0x4010 /* RX IPP FIFO write pointer */ +#define CAS_RX_IPP_SDWR 0x4014 /* RX IPP FIFO shadow write pointer */ +#define CAS_RX_IPP_RD 0x4018 /* RX IPP FIFO read pointer */ +#define CAS_RX_DEBUG 0x401c /* RX debug */ +#define CAS_RX_PTHRS 0x4020 /* RX PAUSE threshold */ +#define CAS_RX_KICK 0x4024 /* RX kick */ +#define CAS_RX_DESC_BASE_LO 0x4028 /* RX descriptor ring base low */ +#define CAS_RX_DESC_BASE_HI 0x402c /* RX descriptor ring base high */ +#define CAS_RX_COMP_BASE_LO 0x4030 /* RX completion ring base low */ +#define CAS_RX_COMP_BASE_HI 0x4034 /* RX completion ring base high */ +#define CAS_RX_COMP 0x4038 /* RX completion */ +#define CAS_RX_COMP_HEAD 0x403c /* RX completion head */ +#define CAS_RX_COMP_TAIL 0x4040 /* RX completion tail */ +#define CAS_RX_BLANK 0x4044 /* RX blanking for ISR read */ +#define CAS_RX_AEMPTY_THRS 0x4048 /* RX almost empty threshold */ +#define CAS_RX_RED 0x4048 /* RX random early detection enable */ +#define CAS_RX_FF 0x4050 /* RX FIFO fullness */ +#define CAS_RX_IPP_PKT_CNT 0x4054 /* RX IPP packet counter */ +#define CAS_RX_WORKING_DMA_LO 0x4058 /* RX working DMA pointer low */ +#define CAS_RX_WORKING_DMA_HI 0x405c /* RX working DMA pointer high */ +#define CAS_RX_BIST 0x4060 /* RX BIST */ +#define CAS_RX_CTRL_FIFO_WR 0x4064 /* RX control FIFO write pointer */ +#define CAS_RX_CTRL_FIFO_RD 0x4068 /* RX control FIFO read pointer */ +#define CAS_RX_BLANK_ALIAS 0x406c /* RX blanking for ISR read alias */ +#define CAS_RX_FIFO_ADDR 0x4080 /* RX FIFO address */ +#define CAS_RX_FIFO_TAG 0x4084 /* RX FIFO tag */ +#define CAS_RX_FIFO_DATA_LO 0x4088 /* RX FIFO data low */ +#define CAS_RX_FIFO_DATA_HI_T0 0x408c /* RX FIFO data highT0 */ +#define CAS_RX_FIFO_DATA_HI_T1 0x4090 /* RX FIFO data highT1 */ +#define CAS_RX_CTRL_FIFO 0x4094 /* RX control FIFO and batching FIFO */ +#define CAS_RX_CTRL_FIFO_LO 0x4098 /* RX control FIFO data low */ +#define CAS_RX_CTRL_FIFO_MD 0x409c /* RX control FIFO data mid */ +#define CAS_RX_CTRL_FIFO_HI 0x4100 /* RX control FIFO data high, flowID */ +#define CAS_RX_IPP_ADDR 0x4104 /* RX IPP FIFO address */ +#define CAS_RX_IPP_TAG 0x4108 /* RX IPP FIFO tag */ +#define CAS_RX_IPP_DATA_LO 0x410c /* RX IPP FIFO data low */ +#define CAS_RX_IPP_DATA_HI_T0 0x4110 /* RX IPP FIFO data highT0 */ +#define CAS_RX_IPP_DATA_HI_T1 0x4114 /* RX IPP FIFO data highT1 */ +#define CAS_RX_HDR_PAGE_LO 0x4118 /* RX header page pointer low */ +#define CAS_RX_HDR_PAGE_HIGH 0x411c /* RX header page pointer high */ +#define CAS_RX_MTU_PAGE_LO 0x4120 /* RX MTU page pointer low */ +#define CAS_RX_MTU_PAGE_HIGH 0x4124 /* RX MTU page pointer high */ +#define CAS_RX_REAS_DMA_ADDR 0x4128 /* RX reassembly DMA table address */ +#define CAS_RX_REAS_DMA_DATA_LO 0x412c /* RX reassembly DMA table data low */ +#define CAS_RX_REAS_DMA_DATA_MD 0x4130 /* RX reassembly DMA table data mid */ +#define CAS_RX_REAS_DMA_DATA_HI 0x4134 /* RX reassembly DMA table data high */ +/* The rest of the RX DMA registers are Cassini+/Saturn only. */ +#define CAS_RX_DESC2_BASE_LO 0x4200 /* RX descriptor ring 2 base low */ +#define CAS_RX_DESC2_BASE_HI 0x4204 /* RX descriptor ring 2 base high */ +#define CAS_RX_COMP2_BASE_LO 0x4208 /* RX completion ring 2 base low */ +#define CAS_RX_COMP2_BASE_HI 0x420c /* RX completion ring 2 base high */ +#define CAS_RX_COMP3_BASE_LO 0x4210 /* RX completion ring 3 base low */ +#define CAS_RX_COMP3_BASE_HI 0x4214 /* RX completion ring 3 base high */ +#define CAS_RX_COMP4_BASE_LO 0x4218 /* RX completion ring 4 base low */ +#define CAS_RX_COMP4_BASE_HI 0x421c /* RX completion ring 4 base high */ +#define CAS_RX_KICK2 0x4220 /* RX kick 2 */ +#define CAS_RX_COMP2 0x4224 /* RX completion 2 */ +#define CAS_RX_COMP_HEAD2 0x4228 /* RX completion head 2 */ +#define CAS_RX_COMP_TAIL2 0x422c /* RX completion tail 2 */ +#define CAS_RX_COMP_HEAD3 0x4230 /* RX completion head 3 */ +#define CAS_RX_COMP_TAIL3 0x4234 /* RX completion tail 3 */ +#define CAS_RX_COMP_HEAD4 0x4238 /* RX completion head 4 */ +#define CAS_RX_COMP_TAIL4 0x423c /* RX completion tail 4 */ +#define CAS_RX_AEMPTY_THRS2 0x4048 /* RX almost empty threshold 2 */ + +#define CAS_RX_CONF_RXDMA_EN 0x00000001 /* RX DMA enable */ +#define CAS_RX_CONF_DESC_MASK 0x0000001e /* RX descriptor ring size */ +#define CAS_RX_CONF_DESC_SHFT 1 +#define CAS_RX_CONF_COMP_MASK 0x000001e0 /* RX complition ring size */ +#define CAS_RX_CONF_COMP_SHFT 5 +#define CAS_RX_CONF_BATCH_DIS 0x00000200 /* descriptor batching dis. */ +#define CAS_RX_CONF_SOFF_MASK 0x00001c00 /* swivel offset */ +#define CAS_RX_CONF_SOFF_SHFT 10 +/* The RX descriptor ring 2 is Cassini+/Saturn only. */ +#define CAS_RX_CONF_DESC2_MASK 0x000f0000 /* RX descriptor ring 2 size */ +#define CAS_RX_CONF_DESC2_SHFT 16 + +#define CAS_RX_CONF_COMP_128 0x0 /* 128 descriptors */ +#define CAS_RX_CONF_COMP_256 0x1 /* 256 descriptors */ +#define CAS_RX_CONF_COMP_512 0x2 /* 512 descriptors */ +#define CAS_RX_CONF_COMP_1K 0x3 /* 1k descriptors */ +#define CAS_RX_CONF_COMP_2K 0x4 /* 2k descriptors */ +#define CAS_RX_CONF_COMP_4K 0x5 /* 4k descriptors */ +#define CAS_RX_CONF_COMP_8K 0x6 /* 8k descriptors */ +#define CAS_RX_CONF_COMP_16K 0x7 /* 16k descriptors */ +#define CAS_RX_CONF_COMP_32K 0x8 /* 32k descriptors */ + +#define CAS_RX_PSZ_MASK 0x00000003 /* RX page size */ +#define CAS_RX_PSZ_SHFT 0 +#define CAS_RX_PSZ_MB_CNT_MASK 0x00007800 /* number of MTU buffers */ +#define CAS_RX_PSZ_MB_CNT_SHFT 11 +#define CAS_RX_PSZ_MB_STRD_MASK 0x18000000 /* MTU buffer stride */ +#define CAS_RX_PSZ_MB_STRD_SHFT 27 +#define CAS_RX_PSZ_MB_OFF_MASK 0xc0000000 /* MTU buffer offset */ +#define CAS_RX_PSZ_MB_OFF_SHFT 30 + +#define CAS_RX_PSZ_2K 0x0 /* page size 2Kbyte */ +#define CAS_RX_PSZ_4K 0x1 /* page size 4Kbyte */ +#define CAS_RX_PSZ_8K 0x2 /* page size 8Kbyte */ +#define CAS_RX_PSZ_16K 0x3 /* page size 16Kbyte*/ + +#define CAS_RX_PSZ_MB_STRD_1K 0x0 /* MTU buffer stride 1Kbyte */ +#define CAS_RX_PSZ_MB_STRD_2K 0x1 /* MTU buffer stride 2Kbyte */ +#define CAS_RX_PSZ_MB_STRD_4K 0x2 /* MTU buffer stride 4Kbyte */ +#define CAS_RX_PSZ_MB_STRD_8K 0x3 /* MTU buffer stride 8Kbyte */ + +#define CAS_RX_PSZ_MB_OFF_0 0x0 /* MTU buf. offset 0 bytes */ +#define CAS_RX_PSZ_MB_OFF_64 0x1 /* MTU buf. offset 64 bytes */ +#define CAS_RX_PSZ_MB_OFF_96 0x2 /* MTU buf. offset 96 bytes */ +#define CAS_RX_PSZ_MB_OFF_128 0x3 /* MTU buf. offset 128 bytes */ + +#define CAS_RX_DESC_ALIGN 8192 /* RX descriptor alignment */ + +#define CAS_RX_COMP_ALIGN 8192 /* RX complition alignment */ + +/* The RX PAUSE thresholds are specified in multiples of 64 bytes. */ +#define CAS_RX_PTHRS_XOFF_MASK 0x000001ff /* XOFF PAUSE */ +#define CAS_RX_PTHRS_XOFF_SHFT 0 +#define CAS_RX_PTHRS_XON_MASK 0x001ff000 /* XON PAUSE */ +#define CAS_RX_PTHRS_XON_SHFT 12 + +/* + * CAS_RX_BLANK and CAS_RX_BLANK_ALIAS bits + * CAS_RX_BLANK is loaded each time CAS_STATUS is read and CAS_RX_BLANK_ALIAS + * is read each time CAS_STATUS_ALIAS is read. The blanking time is specified + * in multiples of 512 core ticks (which runs at 125MHz). + */ +#define CAS_RX_BLANK_PKTS_MASK 0x000001ff /* RX blanking packets */ +#define CAS_RX_BLANK_PKTS_SHFT 0 +#define CAS_RX_BLANK_TIME_MASK 0x3ffff000 /* RX blanking time */ +#define CAS_RX_BLANK_TIME_SHFT 12 + +/* CAS_RX_AEMPTY_THRS and CAS_RX_AEMPTY_THRS2 bits */ +#define CAS_RX_AEMPTY_THRS_MASK 0x00001fff /* RX_BUF_AEMPTY threshold */ +#define CAS_RX_AEMPTY_THRS_SHFT 0 +#define CAS_RX_AEMPTY_COMP_MASK 0x0fffe000 /* RX_COMP_AFULL threshold */ +#define CAS_RX_AEMPTY_COMP_SHFT 13 + +/* The RX random early detection probability is in 12.5% granularity. */ +#define CAS_RX_RED_4K_6K_MASK 0x000000ff /* 4K < FIFO threshold < 6K */ +#define CAS_RX_RED_4K_6K_SHFT 0 +#define CAS_RX_RED_6K_8K_MASK 0x0000ff00 /* 6K < FIFO threshold < 8K */ +#define CAS_RX_RED_6K_8K_SHFT 8 +#define CAS_RX_RED_8K_10K_MASK 0x00ff0000 /* 8K < FIFO threshold < 10K */ +#define CAS_RX_RED_8K_10K_SHFT 16 +#define CAS_RX_RED_10K_12K_MASK 0xff000000 /* 10K < FIFO threshold < 12K */ +#define CAS_RX_RED_10K_12K_SHFT 24 + +/* CAS_RX_FF_IPP_MASK and CAS_RX_FF_FIFO_MASK are in 8 bytes granularity. */ +#define CAS_RX_FF_PKT_MASK 0x000000ff /* # of packets in RX FIFO */ +#define CAS_RX_FF_PKT_SHFT 0 +#define CAS_RX_FF_IPP_MASK 0x0007ff00 /* IPP FIFO level */ +#define CAS_RX_FF_IPP_SHFT 8 +#define CAS_RX_FF_FIFO_MASK 0x3ff80000 /* RX FIFO level */ +#define CAS_RX_FF_FIFO_SHFT 19 + +#define CAS_RX_BIST_START 0x00000001 /* Start BIST process. */ +#define CAS_RX_BIST_SUMMARY 0x00000002 /* All okay */ +#define CAS_RX_BIST_SM 0x00007800 /* BIST state machine */ +#define CAS_RX_BIST_REAS_27 0x00008000 /* Reas 27 okay */ +#define CAS_RX_BIST_REAS_26B 0x00010000 /* Reas 26B okay */ +#define CAS_RX_BIST_REAS_26A 0x00020000 /* Reas 26A okay */ +#define CAS_RX_BIST_CTRL_33 0x00040000 /* Control FIFO 33 okay */ +#define CAS_RX_BIST_CTRL_32 0x00080000 /* Control FIFO 32 okay */ +#define CAS_RX_BIST_IPP_33C 0x00100000 /* IPP 33C okay */ +#define CAS_RX_BIST_IPP_32C 0x00200000 /* IPP 32C okay */ +#define CAS_RX_BIST_IPP_33B 0x00400000 /* IPP 33B okay */ +#define CAS_RX_BIST_IPP_32B 0x00800000 /* IPP 32B okay */ +#define CAS_RX_BIST_IPP_33A 0x01000000 /* IPP 33A okay */ +#define CAS_RX_BIST_IPP_32A 0x02000000 /* IPP 32A okay */ +#define CAS_RX_BIST_33C 0x04000000 /* 33C okay */ +#define CAS_RX_BIST_32C 0x08000000 /* 32C okay */ +#define CAS_RX_BIST_33B 0x10000000 /* 33B okay */ +#define CAS_RX_BIST_32B 0x20000000 /* 32B okay */ +#define CAS_RX_BIST_33A 0x40000000 /* 33A okay */ +#define CAS_RX_BIST_32A 0x80000000 /* 32A okay */ + +#define CAS_RX_REAS_DMA_ADDR_LC 0x0000003f /* reas. table location sel. */ + +/* header parser registers */ +#define CAS_HP_CONF 0x4140 /* HP configuration */ +#define CAS_HP_IR_ADDR 0x4144 /* HP instruction RAM address */ +#define CAS_HP_IR_DATA_LO 0x4148 /* HP instruction RAM data low */ +#define CAS_HP_IR_DATA_MD 0x414c /* HP instruction RAM data mid */ +#define CAS_HP_IR_DATA_HI 0x4150 /* HP instruction RAM data high */ +#define CAS_HP_DR_FDB 0x4154 /* HP data RAM and flow DB address */ +#define CAS_HP_DR_DATA 0x4158 /* HP data RAM data */ +#define CAS_HP_FLOW_DB1 0x415c /* HP flow database 1 */ +#define CAS_HP_FLOW_DB2 0x4160 /* HP flow database 2 */ +#define CAS_HP_FLOW_DB3 0x4164 /* HP flow database 3 */ +#define CAS_HP_FLOW_DB4 0x4168 /* HP flow database 4 */ +#define CAS_HP_FLOW_DB5 0x416c /* HP flow database 5 */ +#define CAS_HP_FLOW_DB6 0x4170 /* HP flow database 6 */ +#define CAS_HP_FLOW_DB7 0x4174 /* HP flow database 7 */ +#define CAS_HP_FLOW_DB8 0x4178 /* HP flow database 8 */ +#define CAS_HP_FLOW_DB9 0x417c /* HP flow database 9 */ +#define CAS_HP_FLOW_DB10 0x4180 /* HP flow database 10 */ +#define CAS_HP_FLOW_DB11 0x4184 /* HP flow database 11 */ +#define CAS_HP_FLOW_DB12 0x4188 /* HP flow database 12 */ +#define CAS_HP_SM 0x418c /* HP state machine */ +#define CAS_HP_STATUS1 0x4190 /* HP status 1 */ +#define CAS_HP_STATUS2 0x4194 /* HP status 2 */ +#define CAS_HP_STATUS3 0x4198 /* HP status 3 */ +#define CAS_HP_RAM_BIST 0x419c /* HP RAM BIST */ + +#define CAS_HP_CONF_PARSE_EN 0x00000001 /* header parsing enable */ +#define CAS_HP_CONF_NCPU_MASK 0x000000fc /* #CPUs (0x0: 64) */ +#define CAS_HP_CONF_NCPU_SHFT 2 +#define CAS_HP_CONF_SINC_DIS 0x00000100 /* SYN inc. seq. number dis. */ +#define CAS_HP_CONF_TPT_MASK 0x000ffe00 /* TCP payload threshold */ +#define CAS_HP_CONF_TPT_SHFT 9 + +#define CAS_HP_DR_FDB_DR_MASK 0x0000001f /* data RAM location sel. */ +#define CAS_HP_DR_FDB_DR_SHFT 0 +#define CAS_HP_DR_FDB_FDB_MASK 0x00003f00 /* flow DB location sel. */ +#define CAS_HP_DR_FDB_FDB_SHFT 8 + +#define CAS_HP_STATUS1_OP_MASK 0x00000007 /* HRP opcode */ +#define CAS_HP_STATUS1_OP_SHFT 0 +#define CAS_HP_STATUS1_LB_MASK 0x000001f8 /* load balancing CPU number */ +#define CAS_HP_STATUS1_LB_SHFT 3 +#define CAS_HP_STATUS1_L3O_MASK 0x0000fe00 /* layer 3 offset */ +#define CAS_HP_STATUS1_L3O_SHFT 9 +#define CAS_HP_STATUS1_SAP_MASK 0xffff0000 /* ethertype */ +#define CAS_HP_STATUS1_SAP_SHFT 16 + +#define CAS_HP_STATUS2_TSZ_MASK 0x0000ffff /* TCP payload size */ +#define CAS_HP_STATUS2_TSZ_SHFT 0 +#define CAS_HP_STATUS2_TO_MASK 0x007f0000 /* TCP payload offset */ +#define CAS_HP_STATUS2_TO_SHFT 16 +#define CAS_HP_STATUS2_FID_MASK 0x1f800000 /* flow ID */ +#define CAS_HP_STATUS2_FID_SHFT 23 +#define CAS_HP_STATUS2_AR2_MASK 0xe0000000 /* accu_R2[6:4] */ +#define CAS_HP_STATUS2_AR2_SHFT 29 + +#define CAS_HP_STATUS3_TCP_NCHK 0x00000001 /* TCP no payload check */ +#define CAS_HP_STATUS3_TCP_CHK 0x00000002 /* TCP payload check */ +#define CAS_HP_STATUS3_SYN_FLAG 0x00000004 /* SYN flag */ +#define CAS_HP_STATUS3_TCP_FLAG 0x00000008 /* TCP flag check */ +#define CAS_HP_STATUS3_CTRL_PF 0x00000010 /* control packet flag */ +#define CAS_HP_STATUS3_NASSIST 0x00000020 /* no assist */ +#define CAS_HP_STATUS3_MASK_PT 0x00000040 /* Mask payload threshold. */ +#define CAS_HP_STATUS3_FRC_TPC 0x00000080 /* Force TCP payload check. */ +#define CAS_HP_STATUS3_MASK_DLZ 0x00000100 /* Mask data length equal 0. */ +#define CAS_HP_STATUS3_FRC_TNPC 0x00000200 /* Force TCP no payload chk. */ +#define CAS_HP_STATUS3_JMBHS_EN 0x00000400 /* jumbo header split enable */ +#define CAS_HP_STATUS3_BWO_REAS 0x00000800 /* batching w/o reassembly */ +#define CAS_HP_STATUS3_FRC_DROP 0x00001000 /* force drop */ +#define CAS_HP_STATUS3_AR1_MASK 0x000fe000 /* accu_R1 */ +#define CAS_HP_STATUS3_AR1_SHFT 13 +#define CAS_HP_STATUS3_CSO_MASK 0x07f00000 /* checksum start offset */ +#define CAS_HP_STATUS3_CSO_SHFT 19 +#define CAS_HP_STATUS3_AR2_MASK 0xf0000000 /* accu_R2[3:0] */ +#define CAS_HP_STATUS3_AR2_SHFT 28 + +#define CAS_HP_RAM_BIST_START 0x00000001 /* Start RAM BIST process. */ +#define CAS_HP_RAM_BIST_SUMMARY 0x00000002 /* all RAM okay */ +#define CAS_HP_RAM_BIST_TCPSEQ 0x00020000 /* TCP seqeunce RAM okay */ +#define CAS_HP_RAM_BIST_FID31 0x00040000 /* flow ID RAM3 bank 1 okay */ +#define CAS_HP_RAM_BIST_FID21 0x00080000 /* flow ID RAM2 bank 1 okay */ +#define CAS_HP_RAM_BIST_FID11 0x00100000 /* flow ID RAM1 bank 1 okay */ +#define CAS_HP_RAM_BIST_FID01 0x00200000 /* flow ID RAM0 bank 1 okay */ +#define CAS_HP_RAM_BIST_FID30 0x00400000 /* flow ID RAM3 bank 0 okay */ +#define CAS_HP_RAM_BIST_FID20 0x00800000 /* flow ID RAM2 bank 0 okay */ +#define CAS_HP_RAM_BIST_FID10 0x01000000 /* flow ID RAM1 bank 0 okay */ +#define CAS_HP_RAM_BIST_FID00 0x02000000 /* flow ID RAM0 bank 0 okay */ +#define CAS_HP_RAM_BIST_AGE1 0x04000000 /* aging RAM1 okay */ +#define CAS_HP_RAM_BIST_AGE0 0x08000000 /* aging RAM0 okay */ +#define CAS_HP_RAM_BIST_IR2 0x10000000 /* instruction RAM2 okay */ +#define CAS_HP_RAM_BIST_IR1 0x20000000 /* instruction RAM1 okay */ +#define CAS_HP_RAM_BIST_IR0 0x40000000 /* instruction RAM0 okay */ +#define CAS_HP_RAM_BIST_DR 0x80000000 /* data RAM okay */ + +/* MAC registers */ +#define CAS_MAC_TXRESET 0x6000 /* TX MAC software reset command */ +#define CAS_MAC_RXRESET 0x6004 /* RX MAC software reset command */ +#define CAS_MAC_SPC 0x6008 /* send PAUSE command */ +#define CAS_MAC_TX_STATUS 0x6010 /* TX MAC status */ +#define CAS_MAC_RX_STATUS 0x6014 /* RX MAC status */ +#define CAS_MAC_CTRL_STATUS 0x6018 /* MAC control status */ +#define CAS_MAC_TX_MASK 0x6020 /* TX MAC mask */ +#define CAS_MAC_RX_MASK 0x6024 /* RX MAC mask */ +#define CAS_MAC_CTRL_MASK 0x6028 /* MAC control mask */ +#define CAS_MAC_TX_CONF 0x6030 /* TX MAC configuration */ +#define CAS_MAC_RX_CONF 0x6034 /* RX MAC configuration */ +#define CAS_MAC_CTRL_CONF 0x6038 /* MAC control configuration */ +#define CAS_MAC_XIF_CONF 0x603c /* XIF configuration */ +#define CAS_MAC_IPG0 0x6040 /* inter packet gap 0 */ +#define CAS_MAC_IPG1 0x6044 /* inter packet gap 1 */ +#define CAS_MAC_IPG2 0x6048 /* inter packet gap 2 */ +#define CAS_MAC_SLOT_TIME 0x604c /* slot time */ +#define CAS_MAC_MIN_FRAME 0x6050 /* minimum frame size */ +#define CAS_MAC_MAX_BF 0x6054 /* maximum bust and frame size */ +#define CAS_MAC_PREAMBLE_LEN 0x6058 /* PA size */ +#define CAS_MAC_JAM_SIZE 0x605c /* jam size */ +#define CAS_MAC_ATTEMPT_LIMIT 0x6060 /* attempt limit */ +#define CAS_MAC_CTRL_TYPE 0x6064 /* MAC control type */ +#define CAS_MAC_ADDR0 0x6080 /* MAC address 0 */ +#define CAS_MAC_ADDR1 0x6084 /* MAC address 1 */ +#define CAS_MAC_ADDR2 0x6088 /* MAC address 2 */ +#define CAS_MAC_ADDR3 0x608c /* MAC address 3 */ +#define CAS_MAC_ADDR4 0x6090 /* MAC address 4 */ +#define CAS_MAC_ADDR5 0x6094 /* MAC address 5 */ +#define CAS_MAC_ADDR6 0x6098 /* MAC address 6 */ +#define CAS_MAC_ADDR7 0x609c /* MAC address 7 */ +#define CAS_MAC_ADDR8 0x60a0 /* MAC address 8 */ +#define CAS_MAC_ADDR9 0x60a4 /* MAC address 9 */ +#define CAS_MAC_ADDR10 0x60a8 /* MAC address 10 */ +#define CAS_MAC_ADDR11 0x60ac /* MAC address 11 */ +#define CAS_MAC_ADDR12 0x60b0 /* MAC address 12 */ +#define CAS_MAC_ADDR13 0x60b4 /* MAC address 13 */ +#define CAS_MAC_ADDR14 0x60b8 /* MAC address 14 */ +#define CAS_MAC_ADDR15 0x60bc /* MAC address 15 */ +#define CAS_MAC_ADDR16 0x60c0 /* MAC address 16 */ +#define CAS_MAC_ADDR17 0x60c4 /* MAC address 17 */ +#define CAS_MAC_ADDR18 0x60c8 /* MAC address 18 */ +#define CAS_MAC_ADDR19 0x60cc /* MAC address 19 */ +#define CAS_MAC_ADDR20 0x60d0 /* MAC address 20 */ +#define CAS_MAC_ADDR21 0x60d4 /* MAC address 21 */ +#define CAS_MAC_ADDR22 0x60d8 /* MAC address 22 */ +#define CAS_MAC_ADDR23 0x60dc /* MAC address 23 */ +#define CAS_MAC_ADDR24 0x60e0 /* MAC address 24 */ +#define CAS_MAC_ADDR25 0x60e4 /* MAC address 25 */ +#define CAS_MAC_ADDR26 0x60e8 /* MAC address 26 */ +#define CAS_MAC_ADDR27 0x60ec /* MAC address 27 */ +#define CAS_MAC_ADDR28 0x60f0 /* MAC address 28 */ +#define CAS_MAC_ADDR29 0x60f4 /* MAC address 29 */ +#define CAS_MAC_ADDR30 0x60f8 /* MAC address 30 */ +#define CAS_MAC_ADDR31 0x60fc /* MAC address 31 */ +#define CAS_MAC_ADDR32 0x6100 /* MAC address 32 */ +#define CAS_MAC_ADDR33 0x6104 /* MAC address 33 */ +#define CAS_MAC_ADDR34 0x6108 /* MAC address 34 */ +#define CAS_MAC_ADDR35 0x610c /* MAC address 35 */ +#define CAS_MAC_ADDR36 0x6110 /* MAC address 36 */ +#define CAS_MAC_ADDR37 0x6114 /* MAC address 37 */ +#define CAS_MAC_ADDR38 0x6118 /* MAC address 38 */ +#define CAS_MAC_ADDR39 0x611c /* MAC address 39 */ +#define CAS_MAC_ADDR40 0x6120 /* MAC address 40 */ +#define CAS_MAC_ADDR41 0x6124 /* MAC address 41 */ +#define CAS_MAC_ADDR42 0x6128 /* MAC address 42 */ +#define CAS_MAC_ADDR43 0x612c /* MAC address 43 */ +#define CAS_MAC_ADDR44 0x6130 /* MAC address 44 */ +#define CAS_MAC_AFILTER0 0x614c /* address filter 0 */ +#define CAS_MAC_AFILTER1 0x6150 /* address filter 1 */ +#define CAS_MAC_AFILTER2 0x6154 /* address filter 2 */ +#define CAS_MAC_AFILTER_MASK1_2 0x6158 /* address filter 2 & 1 mask*/ +#define CAS_MAC_AFILTER_MASK0 0x615c /* address filter 0 mask */ +#define CAS_MAC_HASH0 0x6160 /* hash table 0 */ +#define CAS_MAC_HASH1 0x6164 /* hash table 1 */ +#define CAS_MAC_HASH2 0x6168 /* hash table 2 */ +#define CAS_MAC_HASH3 0x616c /* hash table 3 */ +#define CAS_MAC_HASH4 0x6170 /* hash table 4 */ +#define CAS_MAC_HASH5 0x6174 /* hash table 5 */ +#define CAS_MAC_HASH6 0x6178 /* hash table 6 */ +#define CAS_MAC_HASH7 0x617c /* hash table 7 */ +#define CAS_MAC_HASH8 0x6180 /* hash table 8 */ +#define CAS_MAC_HASH9 0x6184 /* hash table 9 */ +#define CAS_MAC_HASH10 0x6188 /* hash table 10 */ +#define CAS_MAC_HASH11 0x618c /* hash table 11 */ +#define CAS_MAC_HASH12 0x6190 /* hash table 12 */ +#define CAS_MAC_HASH13 0x6194 /* hash table 13 */ +#define CAS_MAC_HASH14 0x6198 /* hash table 14 */ +#define CAS_MAC_HASH15 0x619c /* hash table 15 */ +#define CAS_MAC_NORM_COLL_CNT 0x61a0 /* normal collision counter */ +#define CAS_MAC_FIRST_COLL_CNT 0x61a4 /* 1st attempt suc. collision counter */ +#define CAS_MAC_EXCESS_COLL_CNT 0x61a8 /* excess collision counter */ +#define CAS_MAC_LATE_COLL_CNT 0x61ac /* late collision counter */ +#define CAS_MAC_DEFER_TMR_CNT 0x61b0 /* defer timer */ +#define CAS_MAC_PEAK_ATTEMPTS 0x61b4 /* peak attempts */ +#define CAS_MAC_RX_FRAME_COUNT 0x61b8 /* receive frame counter */ +#define CAS_MAC_RX_LEN_ERR_CNT 0x61bc /* length error counter */ +#define CAS_MAC_RX_ALIGN_ERR 0x61c0 /* alignment error counter */ +#define CAS_MAC_RX_CRC_ERR_CNT 0x61c4 /* FCS error counter */ +#define CAS_MAC_RX_CODE_VIOL 0x61c8 /* RX code violation error counter */ +#define CAS_MAC_RANDOM_SEED 0x61cc /* random number seed */ +#define CAS_MAC_MAC_STATE 0x61d0 /* MAC state machine */ + +#define CAS_MAC_SPC_TIME_MASK 0x0000ffff /* PAUSE time value */ +#define CAS_MAC_SPC_TIME_SHFT 0 +#define CAS_MAC_SPC_SEND 0x00010000 /* Send PAUSE frame. */ + +/* CAS_MAC_TX_STATUS and CAS_MAC_TX_MASK register bits */ +#define CAS_MAC_TX_FRAME_XMTD 0x00000001 /* Frame transmitted. */ +#define CAS_MAC_TX_UNDERRUN 0x00000002 /* TX data starvation */ +#define CAS_MAC_TX_MAX_PKT_ERR 0x00000004 /* frame > CAS_MAC_MAX_FRAME */ +#define CAS_MAC_TX_NCC_EXP 0x00000008 /* normal coll. counter wrap */ +#define CAS_MAC_TX_ECC_EXP 0x00000010 /* excess coll. counter wrap */ +#define CAS_MAC_TX_LCC_EXP 0x00000020 /* late coll. counter wrap */ +#define CAS_MAC_TX_FCC_EXP 0x00000040 /* 1st coll. counter wrap */ +#define CAS_MAC_TX_DEFER_EXP 0x00000080 /* defer timer wrap */ +#define CAS_MAC_TX_PEAK_EXP 0x00000100 /* peak attempts counter wrap */ + +/* CAS_MAC_RX_STATUS and CAS_MAC_RX_MASK register bits */ +#define CAS_MAC_RX_FRAME_RCVD 0x00000001 /* Frame received. */ +#define CAS_MAC_RX_OVERFLOW 0x00000002 /* RX FIFO overflow */ +#define CAS_MAC_RX_FRAME_EXP 0x00000004 /* RX frame counter wrap */ +#define CAS_MAC_RX_ALIGN_EXP 0x00000008 /* alignment error cntr. wrap */ +#define CAS_MAC_RX_CRC_EXP 0x00000010 /* CRC error counter wrap */ +#define CAS_MAC_RX_LEN_EXP 0x00000020 /* length error counter wrap */ +#define CAS_MAC_RX_VIOL_EXP 0x00000040 /* code violation cntr. wrap */ + +/* CAS_MAC_CTRL_STATUS and CAS_MAC_CTRL_MASK register bits */ +#define CAS_MAC_CTRL_PAUSE_RCVD 0x00000001 /* PAUSE received. */ +#define CAS_MAC_CTRL_PAUSE 0x00000002 /* PAUSE state entered. */ +#define CAS_MAC_CTRL_NON_PAUSE 0x00000004 /* PAUSE state left. */ + +#define CAS_MAC_CTRL_STATUS_PT_MASK 0xffff0000 /* PAUSE time */ +#define CAS_MAC_CTRL_STATUS_PT_SHFT 16 + +#define CAS_MAC_TX_CONF_EN 0x00000001 /* TX enable */ +#define CAS_MAC_TX_CONF_ICARR 0x00000002 /* Ignore carrier sense. */ +#define CAS_MAC_TX_CONF_ICOLLIS 0x00000004 /* Ignore collisions. */ +#define CAS_MAC_TX_CONF_EN_IPG0 0x00000008 /* extend RX-to-TX IPG */ +#define CAS_MAC_TX_CONF_NGU 0x00000010 /* Never give up. */ +#define CAS_MAC_TX_CONF_NGUL 0x00000020 /* never give up limit */ +#define CAS_MAC_TX_CONF_NBOFF 0x00000040 /* Disable backoff algorithm. */ +#define CAS_MAC_TX_CONF_SDOWN 0x00000080 /* CSMA/CD slow down */ +#define CAS_MAC_TX_CONF_NO_FCS 0x00000100 /* Don't generate FCS. */ +#define CAS_MAC_TX_CONF_CARR 0x00000200 /* carrier extension enable */ + +#define CAS_MAC_RX_CONF_EN 0x00000001 /* RX enable */ +#define CAS_MAC_RX_CONF_STRPPAD 0x00000002 /* Must not be set. */ +#define CAS_MAC_RX_CONF_STRPFCS 0x00000004 /* Strip FCS bytes. */ +#define CAS_MAC_RX_CONF_PROMISC 0x00000008 /* promiscuous mode enable */ +#define CAS_MAC_RX_CONF_PGRP 0x00000010 /* promiscuous group mode en. */ +#define CAS_MAC_RX_CONF_HFILTER 0x00000020 /* hash filter enable */ +#define CAS_MAC_RX_CONF_AFILTER 0x00000040 /* address filter enable */ +#define CAS_MAC_RX_CONF_DIS_DOE 0x00000080 /* disable discard on error */ +#define CAS_MAC_RX_CONF_CARR 0x00000100 /* carrier extension enable */ + +#define CAS_MAC_CTRL_CONF_TXP 0x00000001 /* send PAUSE enable */ +#define CAS_MAC_CTRL_CONF_RXP 0x00000002 /* receive PAUSE enable */ +#define CAS_MAC_CTRL_CONF_PASSP 0x00000004 /* Pass PAUSE up to RX DMA. */ + +#define CAS_MAC_XIF_CONF_TX_OE 0x00000001 /* MII TX output drivers en. */ +#define CAS_MAC_XIF_CONF_ILBK 0x00000002 /* MII internal loopback en. */ +#define CAS_MAC_XIF_CONF_NOECHO 0x00000004 /* Disable echo. */ +#define CAS_MAC_XIF_CONF_GMII 0x00000008 /* GMII (vs. MII) mode enable */ +#define CAS_MAC_XIF_CONF_BUF_OE 0x00000010 /* MII_BUF_OE enable */ +#define CAS_MAC_XIF_CONF_LNKLED 0x00000020 /* Force LINKLED# active. */ +#define CAS_MAC_XIF_CONF_FDXLED 0x00000040 /* Force FDPLXLED# active. */ + +/* + * The value of CAS_MAC_SLOT_TIME specifies the PAUSE time unit and depends + * on whether carrier extension is enabled. + */ +#define CAS_MAC_SLOT_TIME_CARR 0x200 /* slot time for carr. ext. */ +#define CAS_MAC_SLOT_TIME_NORM 0x40 /* slot time otherwise */ + +#define CAS_MAC_MAX_BF_FRM_MASK 0x00007fff /* maximum frame size */ +#define CAS_MAC_MAX_BF_FRM_SHFT 0 +#define CAS_MAC_MAX_BF_BST_MASK 0x3fff0000 /* maximum burst size */ +#define CAS_MAC_MAX_BF_BST_SHFT 16 + +/* + * MIF registers + * The bit-bang registers use the low bit only. + */ +#define CAS_MIF_BB_CLOCK 0x6200 /* MIF bit-bang clock */ +#define CAS_MIF_BB_DATA 0x6204 /* MIF bit-bang data */ +#define CAS_MIF_BB_OUTPUT_EN 0x6208 /* MIF bit-bang output enable */ +#define CAS_MIF_FRAME 0x620c /* MIF frame/output */ +#define CAS_MIF_CONF 0x6210 /* MIF configuration */ +#define CAS_MIF_MASK 0x6214 /* MIF mask */ +#define CAS_MIF_STATUS 0x6218 /* MIF status */ +#define CAS_MIF_SM 0x621c /* MIF state machine */ + +#define CAS_MIF_FRAME_DATA 0x0000ffff /* instruction payload */ +#define CAS_MIF_FRAME_TA_LSB 0x00010000 /* turn around LSB */ +#define CAS_MIF_FRAME_TA_MSB 0x00020000 /* turn around MSB */ +#define CAS_MIF_FRAME_REG_MASK 0x007c0000 /* register address */ +#define CAS_MIF_FRAME_REG_SHFT 18 +#define CAS_MIF_FRAME_PHY_MASK 0x0f800000 /* PHY address */ +#define CAS_MIF_FRAME_PHY_SHFT 23 +#define CAS_MIF_FRAME_OP_WRITE 0x10000000 /* write opcode */ +#define CAS_MIF_FRAME_OP_READ 0x20000000 /* read opcode */ +#define CAS_MIF_FRAME_OP_MASK \ + (CAS_MIF_FRAME_OP_WRITE | CAS_MIF_FRAME_OP_READ) +#define CAS_MIF_FRAME_ST 0x40000000 /* start of frame */ +#define CAS_MIF_FRAME_ST_MASK 0xc0000000 /* start of frame */ + +#define CAS_MIF_FRAME_READ \ + (CAS_MIF_FRAME_TA_MSB | CAS_MIF_FRAME_OP_READ | CAS_MIF_FRAME_ST) +#define CAS_MIF_FRAME_WRITE \ + (CAS_MIF_FRAME_TA_MSB | CAS_MIF_FRAME_OP_WRITE | CAS_MIF_FRAME_ST) + +#define CAS_MIF_CONF_PHY_SELECT 0x00000001 /* PHY select, 0: MDIO_0 */ +#define CAS_MIF_CONF_POLL_EN 0x00000002 /* polling mechanism enable */ +#define CAS_MIF_CONF_BB_MODE 0x00000004 /* bit-bang mode enable */ +#define CAS_MIF_CONF_PREG_MASK 0x000000f8 /* polled register */ +#define CAS_MIF_CONF_PREG_SHFT 3 +#define CAS_MIF_CONF_MDI0 0x00000100 /* MDIO_0 data/attached */ +#define CAS_MIF_CONF_MDI1 0x00000200 /* MDIO_1 data/attached */ +#define CAS_MIF_CONF_PPHY_MASK 0x00007c00 /* polled PHY */ +#define CAS_MIF_CONF_PPHY_SHFT 10 + +/* CAS_MIF_MASK and CAS_MIF_STATUS bits */ +#define CAS_MIF_POLL_STATUS_MASK 0x0000ffff /* polling status */ +#define CAS_MIF_POLL_STATUS_SHFT 0 +#define CAS_MIF_POLL_DATA_MASK 0xffff0000 /* polling data */ +#define CAS_MIF_POLL_DATA_SHFT 8 + +#define CAS_MIF_SM_CTRL_MASK 0x00000007 /* ctrl. state machine state */ +#define CAS_MIF_SM_CTRL_SHFT 0 +#define CAS_MIF_SM_EXEC_MASK 0x00000060 /* exec. state machine state */ + +/* PCS/Serialink registers */ +#define CAS_PCS_CTRL 0x9000 /* PCS MII control (PCS "BMCR") */ +#define CAS_PCS_STATUS 0x9004 /* PCS MII status (PCS "BMSR") */ +#define CAS_PCS_ANAR 0x9008 /* PCS MII advertisement */ +#define CAS_PCS_ANLPAR 0x900c /* PCS MII link partner ability */ +#define CAS_PCS_CONF 0x9010 /* PCS configuration */ +#define CAS_PCS_SM 0x9014 /* PCS state machine */ +#define CAS_PCS_INTR_STATUS 0x9018 /* PCS interrupt status */ +#define CAS_PCS_DATAPATH 0x9050 /* datapath mode */ +#define CAS_PCS_SERDES_CTRL 0x9054 /* SERDES control */ +#define CAS_PCS_OUTPUT_SELECT 0x9058 /* shared output select */ +#define CAS_PCS_SERDES_STATUS 0x905c /* SERDES state */ +#define CAS_PCS_PKT_CNT 0x9060 /* PCS packet counter */ + +#define CAS_PCS_CTRL_1000M 0x00000040 /* 1000Mbps speed select */ +#define CAS_PCS_CTRL_COLL_TEST 0x00000080 /* collision test */ +#define CAS_PCS_CTRL_FDX 0x00000100 /* full-duplex, always 0 */ +#define CAS_PCS_CTRL_RANEG 0x00000200 /* restart auto-negotiation */ +#define CAS_PCS_CTRL_ISOLATE 0x00000400 /* isolate PHY from MII */ +#define CAS_PCS_CTRL_POWERDOWN 0x00000800 /* power down */ +#define CAS_PCS_CTRL_ANEG_EN 0x00001000 /* auto-negotiation enable */ +#define CAS_PCS_CTRL_10_100M 0x00002000 /* 10/100Mbps speed select */ +#define CAS_PCS_CTRL_RESET 0x00008000 /* Reset PCS. */ + +#define CAS_PCS_STATUS_EXTCAP 0x00000001 /* extended capability */ +#define CAS_PCS_STATUS_JABBER 0x00000002 /* jabber condition detected */ +#define CAS_PCS_STATUS_LINK 0x00000004 /* link status */ +#define CAS_PCS_STATUS_ANEG_ABL 0x00000008 /* auto-negotiation ability */ +#define CAS_PCS_STATUS_REM_FLT 0x00000010 /* remote fault detected */ +#define CAS_PCS_STATUS_ANEG_CPT 0x00000020 /* auto-negotiate complete */ +#define CAS_PCS_STATUS_EXTENDED 0x00000100 /* extended status */ + +/* CAS_PCS_ANAR and CAS_PCS_ANLPAR register bits */ +#define CAS_PCS_ANEG_FDX 0x00000020 /* full-duplex */ +#define CAS_PCS_ANEG_HDX 0x00000040 /* half-duplex */ +#define CAS_PCS_ANEG_PAUSE 0x00000080 /* symmetric PAUSE */ +#define CAS_PCS_ANEG_ASM_DIR 0x00000100 /* asymmetric PAUSE */ +#define CAS_PCS_ANEG_RFLT_FAIL 0x00001000 /* remote fault - fail */ +#define CAS_PCS_ANEG_RFLT_OFF 0x00002000 /* remote fault - off-line */ +#define CAS_PCS_ANEG_RFLT_MASK \ + (CAS_PCS_ANEG_RFLT_FAIL | CAS_PCS_ANEG_RFLT_OFF) +#define CAS_PCS_ANEG_ACK 0x00004000 /* acknowledge */ +#define CAS_PCS_ANEG_NEXT_PAGE 0x00008000 /* next page */ + +#define CAS_PCS_CONF_EN 0x00000001 /* Enable PCS. */ +#define CAS_PCS_CONF_SDO 0x00000002 /* signal detect override */ +#define CAS_PCS_CONF_SDL 0x00000004 /* signal detect active-low */ +#define CAS_PCS_CONF_JS_NORM 0x00000000 /* jitter study - normal op. */ +#define CAS_PCS_CONF_JS_HF 0x00000008 /* jitter study - HF test */ +#define CAS_PCS_CONF_JS_LF 0x00000010 /* jitter study - LF test */ +#define CAS_PCS_CONF_JS_MASK (CAS_PCS_CONF_JS_HF | CAS_PCS_CONF_JS_LF) +#define CAS_PCS_CONF_ANEG_TO 0x00000020 /* auto-neg. timer override */ + +#define CAS_PCS_SM_TX_CTRL_MASK 0x0000000f /* TX control state */ +#define CAS_PCS_SM_TX_CTRL_SHFT 0 +#define CAS_PCS_SM_RX_CTRL_MASK 0x000000f0 /* RX control state */ +#define CAS_PCS_SM_RX_CTRL_SHFT 4 +#define CAS_PCS_SM_WSYNC_MASK 0x00000700 /* word sync. state */ +#define CAS_PCS_SM_WSYNC_SHFT 8 +#define CAS_PCS_SM_SEQ_MASK 0x00001800 /* sequence detection state */ +#define CAS_PCS_SM_SEQ_SHFT 11 +#define CAS_PCS_SM_LINK_UP 0x00016000 +#define CAS_PCS_SM_LINK_MASK 0x0001e000 /* link configuration state */ +#define CAS_PCS_SM_LINK_SHFT 13 +#define CAS_PCS_SM_LOSS_C 0x00100000 /* link-loss due to C codes */ +#define CAS_PCS_SM_LOSS_SYNC 0x00200000 /* link-loss due to sync-loss */ +#define CAS_PCS_SM_LOS 0x00400000 /* loss of signal */ +#define CAS_PCS_SM_NLINK_BREAK 0x01000000 /* no link due to breaklink */ +#define CAS_PCS_SM_NLINK_SERDES 0x02000000 /* no link due to SERDES */ +#define CAS_PCS_SM_NLINK_C 0x04000000 /* no link due to bad C codes */ +#define CAS_PCS_SM_NLINK_SYNC 0x08000000 /* no link due to word sync. */ +#define CAS_PCS_SM_NLINK_WAIT_C 0x10000000 /* no link, waiting for ack. */ +#define CAS_PCS_SM_NLINK_NIDLE 0x20000000 /* no link due to no idle */ + +/* + * CAS_PCS_INTR_STATUS has no corresponding mask register. It can only + * be masked with CAS_INTR_PCS_INT. + */ +#define CAS_PCS_INTR_LINK 0x00000004 /* link status change */ + +#define CAS_PCS_DATAPATH_MII 0x00000001 /* GMII/MII and MAC loopback */ +#define CAS_PCS_DATAPATH_SERDES 0x00000002 /* SERDES via 10-bit */ + +#define CAS_PCS_SERDES_CTRL_LBK 0x00000001 /* loopback at 10-bit enable */ +#define CAS_PCS_SERDES_CTRL_ESD 0x00000002 /* En. sync char. detection. */ +#define CAS_PCS_SERDES_CTRL_LR 0x00000004 /* Lock to reference clock. */ + +#define CAS_PCS_SERDES_STATUS_T 0x00000000 /* Undergoing test. */ +#define CAS_PCS_SERDES_STATUS_L 0x00000001 /* Waiting 500us w/ lockrefn. */ +#define CAS_PCS_SERDES_STATUS_C 0x00000002 /* Waiting for comma detect. */ +#define CAS_PCS_SERDES_STATUS_S 0x00000003 /* Receive data is sync. */ + +#define CAS_PCS_PKT_CNT_TX_MASK 0x000007ff /* TX packets */ +#define CAS_PCS_PKT_CNT_TX_SHFT 0 +#define CAS_PCS_PKT_CNT_RX_MASK 0x07ff0000 /* RX packets */ +#define CAS_PCS_PKT_CNT_RX_SHFT 16 + +/* + * PCI expansion ROM runtime access + * Cassinis and Saturn map a 1MB space for the PCI expansion ROM as the + * second half of the first register bank, although they only support up + * to 64KB ROMs. + */ +#define CAS_PCI_ROM_OFFSET 0x100000 +#define CAS_PCI_ROM_SIZE 0x10000 + +/* secondary local bus device */ +#define CAS_SEC_LBDEV_OFFSET 0x180000 +#define CAS_SEC_LBDE_SIZE 0x7ffff + +/* wired PHY addresses */ +#define CAS_PHYAD_INTERNAL 1 +#define CAS_PHYAD_EXTERNAL 0 + +/* wired RX FIFO size in bytes */ +#define CAS_RX_FIFO_SIZE 16 * 1024 + +/* + * descriptor ring structures + */ +struct cas_desc { + uint64_t cd_flags; + uint64_t cd_buf_ptr; +}; + +/* + * transmit flags + * CAS_TD_CKSUM_START_MASK, CAS_TD_CKSUM_STUFF_MASK, CAS_TD_CKSUM_EN and + * CAS_TD_INT_ME only need to be set in 1st descriptor of a frame. + */ +#define CAS_TD_BUF_LEN_MASK 0x0000000000003fffULL /* buffer length */ +#define CAS_TD_BUF_LEN_SHFT 0 +#define CAS_TD_CKSUM_START_MASK 0x00000000001f8000ULL /* checksum start... */ +#define CAS_TD_CKSUM_START_SHFT 15 /* ...offset */ +#define CAS_TD_CKSUM_STUFF_MASK 0x000000001fe00000ULL /* checksum stuff... */ +#define CAS_TD_CKSUM_STUFF_SHFT 21 /* ...offset */ +#define CAS_TD_CKSUM_EN 0x0000000020000000ULL /* checksum enable */ +#define CAS_TD_END_OF_FRAME 0x0000000040000000ULL /* last desc. of pkt. */ +#define CAS_TD_START_OF_FRAME 0x0000000080000000ULL /* 1st desc. of pkt. */ +#define CAS_TD_INT_ME 0x0000000100000000ULL /* intr. when in FIFO */ +#define CAS_TD_NO_CRC 0x0000000200000000ULL /* Don't insert CRC. */ + +/* receive flags */ +#define CAS_RD_BUF_INDEX_MASK 0x0000000000003fffULL /* data buffer index */ +#define CAS_RD_BUF_INDEX_SHFT 0 + +/* + * receive completion ring structure + */ +struct cas_rx_comp { + uint64_t crc_word1; + uint64_t crc_word2; + uint64_t crc_word3; + uint64_t crc_word4; +}; + +#define CAS_RC1_DATA_SIZE_MASK 0x0000000007ffe000ULL /* pkt. data length */ +#define CAS_RC1_DATA_SIZE_SHFT 13 +#define CAS_RC1_DATA_OFF_MASK 0x000001fff8000000ULL /* data buffer offset */ +#define CAS_RC1_DATA_OFF_SHFT 27 +#define CAS_RC1_DATA_INDEX_MASK 0x007ffe0000000000ULL /* data buffer index */ +#define CAS_RC1_DATA_INDEX_SHFT 41 +#define CAS_RC1_SKIP_MASK 0x0180000000000000ULL /* entries to skip */ +#define CAS_RC1_SKIP_SHFT 55 +#define CAS_RC1_RELEASE_NEXT 0x0200000000000000ULL /* last in reas. buf. */ +#define CAS_RC1_SPLIT_PKT 0x0400000000000000ULL /* used 2 reas. buf. */ +#define CAS_RC1_RELEASE_FLOW 0x0800000000000000ULL /* last pkt. of flow */ +#define CAS_RC1_RELEASE_DATA 0x1000000000000000ULL /* reas. buf. full */ +#define CAS_RC1_RELEASE_HDR 0x2000000000000000ULL /* header buf. full */ +#define CAS_RC1_TYPE_HW 0x0000000000000000ULL /* owned by hardware */ +#define CAS_RC1_TYPE_RSFB 0x4000000000000000ULL /* stale flow buf... */ +#define CAS_RC1_TYPE_RNRP 0x8000000000000000ULL /* non-reas. pkt... */ +#define CAS_RC1_TYPE_RFP 0xc000000000000000ULL /* flow packet... */ +#define CAS_RC1_TYPE_MASK CAS_RC1_TYPE_RFP /* ...release */ +#define CAS_RC1_TYPE_SHFT 62 + +#define CAS_RC2_NEXT_INDEX_MASK 0x00000007ffe00000ULL /* next buf. of pkt. */ +#define CAS_RC2_NEXT_INDEX_SHFT 21 +#define CAS_RC2_HDR_SIZE_MASK 0x00000ff800000000ULL /* header length */ +#define CAS_RC2_HDR_SIZE_SHFT 35 +#define CAS_RC2_HDR_OFF_MASK 0x0003f00000000000ULL /* header buf. offset */ +#define CAS_RC2_HDR_OFF_SHFT 44 +#define CAS_RC2_HDR_INDEX_MASK 0xfffc000000000000ULL /* header buf. index */ +#define CAS_RC2_HDR_INDEX_SHFT 50 + +#define CAS_RC3_SMALL_PKT 0x0000000000000001ULL /* pkt. <= 256 - SOFF */ +#define CAS_RC3_JUMBO_PKT 0x0000000000000002ULL /* pkt. > 1522 bytes */ +#define CAS_RC3_JMBHS_EN 0x0000000000000004ULL /* jmb. hdr. spl. en. */ +#define CAS_RC3_CSO_MASK 0x000000000007f000ULL /* checksum start... */ +#define CAS_RC3_CSO_SHFT 12 /* ...offset */ +#define CAS_RC3_FLOWID_MASK 0x0000000001f80000ULL /* flow ID of pkt. */ +#define CAS_RC3_FLOWID_SHFT 19 +#define CAS_RC3_OP_MASK 0x000000000e000000ULL /* opcode */ +#define CAS_RC3_OP_SHFT 25 +#define CAS_RC3_FRC_FLAG 0x0000000010000000ULL /* op. 2 batch. lkhd. */ +#define CAS_RC3_NASSIST 0x0000000020000000ULL /* no assist */ +#define CAS_RC3_LB_MASK 0x000001f800000000ULL /* load balancing key */ +#define CAS_RC3_LB_SHFT 35 +#define CAS_RC3_L3HO_MASK 0x0000fe0000000000ULL /* layer 3 hdr. off. */ +#define CAS_RC3_L3HO_SHFT 41 +#define CAS_RC3_PLUS_ENC_PKT 0x0000020000000000ULL /* IPsec AH/ESP pkt. */ +#define CAS_RC3_PLUS_L3HO_MASK 0x0000fc0000000000ULL /* layer 3 hdr. off. */ +#define CAS_RC3_PLUS_L3HO_SHFT 42 +#define CAS_RC3_SAP_MASK 0xffff000000000000ULL /* ethertype */ +#define CAS_RC3_SAP_SHFT 48 + +#define CAS_RC4_TCP_CSUM_MASK 0x000000000000ffffULL /* TCP checksum */ +#define CAS_RC4_TCP_CSUM_SHFT 0 +#define CAS_RC4_PKT_LEN_MASK 0x000000003fff0000ULL /* entire pkt. length */ +#define CAS_RC4_PKT_LEN_SHFT 16 +#define CAS_RC4_PAM_MASK 0x00000003c0000000ULL /* mcast. addr. match */ +#define CAS_RC4_PAM_SHFT 30 +#define CAS_RC4_ZERO 0x0000080000000000ULL /* owned by software */ +#define CAS_RC4_HASH_VAL_MASK 0x0ffff00000000000ULL /* mcast. addr. hash */ +#define CAS_RC4_HASH_VAL_SHFT 44 +#define CAS_RC4_HASH_PASS 0x1000000000000000ULL /* passed hash filter */ +#define CAS_RC4_BAD 0x4000000000000000ULL /* CRC error */ +#define CAS_RC4_LEN_MMATCH 0x8000000000000000ULL /* length field mism. */ + +#define CAS_GET(reg, bits) (((reg) & (bits ## _MASK)) >> (bits ## _SHFT)) +#define CAS_SET(val, bits) (((val) << (bits ## _SHFT)) & (bits ## _MASK)) + +#endif diff --git a/sys/dev/cas/if_casvar.h b/sys/dev/cas/if_casvar.h new file mode 100644 index 0000000..0f24cca --- /dev/null +++ b/sys/dev/cas/if_casvar.h @@ -0,0 +1,260 @@ +/*- + * Copyright (C) 2001 Eduardo Horvath. + * Copyright (c) 2008 Marius Strobl <marius@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: gemvar.h,v 1.8 2002/05/15 02:36:12 matt Exp + * from: FreeBSD: if_gemvar.h 177560 2008-03-24 17:23:53Z marius + * + * $FreeBSD$ + */ + +#ifndef _IF_CASVAR_H +#define _IF_CASVAR_H + +/* + * The page size is configurable, but needs to be at least 8k (the + * default) in order to also support jumbo buffers. + */ +#define CAS_PAGE_SIZE 8192 + +/* + * Transmit descriptor ring size - this is arbitrary, but allocate + * enough descriptors for 64 pending transmissions and 16 segments + * per packet. This limit is not actually enforced (packets with + * more segments can be sent, depending on the busdma backend); it + * is however used as an estimate for the TX window size. + */ +#define CAS_NTXSEGS 16 + +#define CAS_TXQUEUELEN 64 +#define CAS_NTXDESC (CAS_TXQUEUELEN * CAS_NTXSEGS) +#define CAS_MAXTXFREE (CAS_NTXDESC - 1) +#define CAS_NTXDESC_MASK (CAS_NTXDESC - 1) +#define CAS_NEXTTX(x) ((x + 1) & CAS_NTXDESC_MASK) + +/* + * Receive completion ring size - we have one completion per + * incoming packet (though the opposite isn't necesarrily true), + * so this logic is a little simpler. + */ +#define CAS_NRXCOMP 4096 +#define CAS_NRXCOMP_MASK (CAS_NRXCOMP - 1) +#define CAS_NEXTRXCOMP(x) ((x + 1) & CAS_NRXCOMP_MASK) + +/* + * Receive descriptor ring sizes - for Cassini+ and Saturn both + * rings must be at least initialized. + */ +#define CAS_NRXDESC 1024 +#define CAS_NRXDESC_MASK (CAS_NRXDESC - 1) +#define CAS_NEXTRXDESC(x) ((x + 1) & CAS_NRXDESC_MASK) +#define CAS_NRXDESC2 32 +#define CAS_NRXDESC2_MASK (CAS_NRXDESC2 - 1) +#define CAS_NEXTRXDESC2(x) ((x + 1) & CAS_NRXDESC2_MASK) + +/* + * How many ticks to wait until to retry on a RX descriptor that is + * still owned by the hardware. + */ +#define CAS_RXOWN_TICKS (hz / 50) + +/* + * Control structures are DMA'd to the chip. We allocate them + * in a single clump that maps to a single DMA segment to make + * several things easier. + */ +struct cas_control_data { + struct cas_desc ccd_txdescs[CAS_NTXDESC]; /* TX descriptors */ + struct cas_rx_comp ccd_rxcomps[CAS_NRXCOMP]; /* RX completions */ + struct cas_desc ccd_rxdescs[CAS_NRXDESC]; /* RX descriptors */ + struct cas_desc ccd_rxdescs2[CAS_NRXDESC2]; /* RX descriptors 2 */ +}; + +#define CAS_CDOFF(x) offsetof(struct cas_control_data, x) +#define CAS_CDTXDOFF(x) CAS_CDOFF(ccd_txdescs[(x)]) +#define CAS_CDRXCOFF(x) CAS_CDOFF(ccd_rxcomps[(x)]) +#define CAS_CDRXDOFF(x) CAS_CDOFF(ccd_rxdescs[(x)]) +#define CAS_CDRXD2OFF(x) CAS_CDOFF(ccd_rxdescs2[(x)]) + +/* + * software state for transmit job mbufs (may be elements of mbuf chains) + */ +struct cas_txsoft { + struct mbuf *txs_mbuf; /* head of our mbuf chain */ + bus_dmamap_t txs_dmamap; /* our DMA map */ + u_int txs_firstdesc; /* first descriptor in packet */ + u_int txs_lastdesc; /* last descriptor in packet */ + u_int txs_ndescs; /* number of descriptors */ + STAILQ_ENTRY(cas_txsoft) txs_q; +}; + +STAILQ_HEAD(cas_txsq, cas_txsoft); + +/* + * software state for receive descriptors + */ +struct cas_rxdsoft { + void *rxds_buf; /* receive buffer */ + bus_dmamap_t rxds_dmamap; /* our DMA map */ + bus_addr_t rxds_paddr; /* physical address of the segment */ +#if __FreeBSD_version < 800016 + struct cas_softc *rxds_sc; /* softc pointer */ + u_int rxds_idx; /* our index */ +#endif + u_int rxds_refcount; /* hardware + mbuf references */ +}; + +/* + * software state per device + */ +struct cas_softc { + struct ifnet *sc_ifp; + struct mtx sc_mtx; + device_t sc_miibus; + struct mii_data *sc_mii; /* MII media control */ + device_t sc_dev; /* generic device information */ + u_char sc_enaddr[ETHER_ADDR_LEN]; + struct callout sc_tick_ch; /* tick callout */ + struct callout sc_rx_ch; /* delayed RX callout */ + u_int sc_wdog_timer; /* watchdog timer */ + + void *sc_ih; + struct resource *sc_res[2]; +#define CAS_RES_INTR 0 +#define CAS_RES_MEM 1 + + bus_dma_tag_t sc_pdmatag; /* parent bus DMA tag */ + bus_dma_tag_t sc_rdmatag; /* RX bus DMA tag */ + bus_dma_tag_t sc_tdmatag; /* TX bus DMA tag */ + bus_dma_tag_t sc_cdmatag; /* control data bus DMA tag */ + bus_dmamap_t sc_dmamap; /* bus DMA handle */ + + u_int sc_phyad; /* PHY to use or -1 for any */ + + u_int sc_variant; +#define CAS_UNKNOWN 0 /* don't know */ +#define CAS_CAS 1 /* Sun Cassini */ +#define CAS_CASPLUS 2 /* Sun Cassini+ */ +#define CAS_SATURN 3 /* National Semiconductor Saturn */ + + u_int sc_flags; +#define CAS_INITED (1 << 0) /* reset persistent regs init'ed */ +#define CAS_NO_CSUM (1 << 1) /* don't use hardware checksumming */ +#define CAS_LINK (1 << 2) /* link is up */ +#define CAS_REG_PLUS (1 << 3) /* has Cassini+ registers */ +#define CAS_SERDES (1 << 4) /* use the SERDES */ +#define CAS_TABORT (1 << 5) /* has target abort issues */ + + bus_dmamap_t sc_cddmamap; /* control data DMA map */ + bus_addr_t sc_cddma; + + /* + * software state for transmit and receive descriptors + */ + struct cas_txsoft sc_txsoft[CAS_TXQUEUELEN]; + struct cas_rxdsoft sc_rxdsoft[CAS_NRXDESC]; + + /* + * control data structures + */ + struct cas_control_data *sc_control_data; +#define sc_txdescs sc_control_data->ccd_txdescs +#define sc_rxcomps sc_control_data->ccd_rxcomps +#define sc_rxdescs sc_control_data->ccd_rxdescs +#define sc_rxdescs2 sc_control_data->ccd_rxdescs2 + + u_int sc_txfree; /* number of free TX descriptors */ + u_int sc_txnext; /* next ready TX descriptor */ + u_int sc_txwin; /* TX desc. since last TX intr. */ + + struct cas_txsq sc_txfreeq; /* free software TX descriptors */ + struct cas_txsq sc_txdirtyq; /* dirty software TX descriptors */ + + u_int sc_rxcptr; /* next ready RX completion */ + u_int sc_rxdptr; /* next ready RX descriptor */ + + int sc_ifflags; +}; + +#define CAS_BARRIER(sc, offs, len, flags) \ + bus_barrier((sc)->sc_res[CAS_RES_MEM], (offs), (len), (flags)) + +#define CAS_READ_N(n, sc, offs) \ + bus_read_ ## n((sc)->sc_res[CAS_RES_MEM], (offs)) +#define CAS_READ_1(sc, offs) CAS_READ_N(1, (sc), (offs)) +#define CAS_READ_2(sc, offs) CAS_READ_N(2, (sc), (offs)) +#define CAS_READ_4(sc, offs) CAS_READ_N(4, (sc), (offs)) + +#define CAS_WRITE_N(n, sc, offs, v) \ + bus_write_ ## n((sc)->sc_res[CAS_RES_MEM], (offs), (v)) +#define CAS_WRITE_1(sc, offs, v) CAS_WRITE_N(1, (sc), (offs), (v)) +#define CAS_WRITE_2(sc, offs, v) CAS_WRITE_N(2, (sc), (offs), (v)) +#define CAS_WRITE_4(sc, offs, v) CAS_WRITE_N(4, (sc), (offs), (v)) + +#define CAS_CDTXDADDR(sc, x) ((sc)->sc_cddma + CAS_CDTXDOFF((x))) +#define CAS_CDRXCADDR(sc, x) ((sc)->sc_cddma + CAS_CDRXCOFF((x))) +#define CAS_CDRXDADDR(sc, x) ((sc)->sc_cddma + CAS_CDRXDOFF((x))) +#define CAS_CDRXD2ADDR(sc, x) ((sc)->sc_cddma + CAS_CDRXD2OFF((x))) + +#define CAS_CDSYNC(sc, ops) \ + bus_dmamap_sync((sc)->sc_cdmatag, (sc)->sc_cddmamap, (ops)); + +#define __CAS_UPDATE_RXDESC(rxd, rxds, s) \ +do { \ + \ + refcount_init(&(rxds)->rxds_refcount, 1); \ + (rxd)->cd_buf_ptr = htole64((rxds)->rxds_paddr); \ + KASSERT((s) < CAS_RD_BUF_INDEX_MASK >> CAS_RD_BUF_INDEX_SHFT, \ + ("%s: RX buffer index too large!", __func__)); \ + (rxd)->cd_flags = \ + htole64((uint64_t)((s) << CAS_RD_BUF_INDEX_SHFT)); \ +} while (0) + +#define CAS_UPDATE_RXDESC(sc, d, s) \ + __CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], \ + &(sc)->sc_rxdsoft[(s)], (s)) + +#if __FreeBSD_version < 800016 +#define CAS_INIT_RXDESC(sc, d, s) \ +do { \ + struct cas_rxdsoft *__rxds = &(sc)->sc_rxdsoft[(s)]; \ + \ + __rxds->rxds_sc = (sc); \ + __rxds->rxds_idx = (s); \ + __CAS_UPDATE_RXDESC(&(sc)->sc_rxdescs[(d)], __rxds, (s)); \ +} while (0) +#else +#define CAS_INIT_RXDESC(sc, d, s) CAS_UPDATE_RXDESC(sc, d, s) +#endif + +#define CAS_LOCK_INIT(_sc, _name) \ + mtx_init(&(_sc)->sc_mtx, _name, MTX_NETWORK_LOCK, MTX_DEF) +#define CAS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define CAS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define CAS_LOCK_ASSERT(_sc, _what) mtx_assert(&(_sc)->sc_mtx, (_what)) +#define CAS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) +#define CAS_LOCK_OWNED(_sc) mtx_owned(&(_sc)->sc_mtx) + +#endif |