Date: Mon, 15 Jun 2009 18:22:41 +0000 (UTC) From: Marius Strobl <marius@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r194246 - in head/sys: boot/forth conf dev/cas modules modules/cas sparc64/conf Message-ID: <200906151822.n5FIMfnq097243@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: marius Date: Mon Jun 15 18:22:41 2009 New Revision: 194246 URL: http://svn.freebsd.org/changeset/base/194246 Log: 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 Added: head/sys/dev/cas/ head/sys/dev/cas/if_cas.c (contents, props changed) head/sys/dev/cas/if_casreg.h (contents, props changed) head/sys/dev/cas/if_casvar.h (contents, props changed) head/sys/modules/cas/ head/sys/modules/cas/Makefile (contents, props changed) Modified: head/sys/boot/forth/loader.conf head/sys/conf/NOTES head/sys/conf/files head/sys/modules/Makefile head/sys/sparc64/conf/GENERIC Modified: head/sys/boot/forth/loader.conf ============================================================================== --- head/sys/boot/forth/loader.conf Mon Jun 15 17:14:47 2009 (r194245) +++ head/sys/boot/forth/loader.conf Mon Jun 15 18:22:41 2009 (r194246) @@ -221,6 +221,7 @@ if_axe_load="NO" # ASIX Electronics AX8 if_bce_load="NO" # Broadcom NetXtreme II Gigabit Ethernet if_bfe_load="NO" # Broadcom BCM4401 if_bge_load="NO" # Broadcom BCM570x PCI Gigabit Ethernet +if_cas_load="NO" # Sun Cassini/Cassini+ and NS DP83065 Saturn if_cm_load="NO" # SMC (90c26, 90c56, 90c66) if_cs_load="NO" # Crystal Semiconductor CS8920 if_cue_load="NO" # CATC USB-EL1210A USB Ethernet Modified: head/sys/conf/NOTES ============================================================================== --- head/sys/conf/NOTES Mon Jun 15 17:14:47 2009 (r194245) +++ head/sys/conf/NOTES Mon Jun 15 18:22:41 2009 (r194246) @@ -1768,6 +1768,7 @@ device miibus # BCM570x family of controllers, including the 3Com 3c996-T, # the Netgear GA302T, the SysKonnect SK-9D21 and SK-9D41, and # the embedded gigE NICs on Dell PowerEdge 2550 servers. +# cas: Sun Cassini/Cassini+ and National Semiconductor DP83065 Saturn # cm: Arcnet SMC COM90c26 / SMC COM90c56 # (and SMC COM90c66 in '56 compatibility mode) adapters. # dc: Support for PCI fast ethernet adapters based on the DEC/Intel 21143 @@ -1907,6 +1908,7 @@ device ale # Atheros AR8121/AR8113/AR8 device bce # Broadcom BCM5706/BCM5708 Gigabit Ethernet device bfe # Broadcom BCM440x 10/100 Ethernet device bge # Broadcom BCM570xx Gigabit Ethernet +device cas # Sun Cassini/Cassini+ and NS DP83065 Saturn device cxgb # Chelsio T3 10 Gigabit Ethernet device cxgb_t3fw # Chelsio T3 10 Gigabit Ethernet firmware device dc # DEC/Intel 21143 and various workalikes Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Mon Jun 15 17:14:47 2009 (r194245) +++ head/sys/conf/files Mon Jun 15 18:22:41 2009 (r194246) @@ -738,6 +738,7 @@ dev/bwi/if_bwi_pci.c optional bwi pci dev/cardbus/cardbus.c optional cardbus dev/cardbus/cardbus_cis.c optional cardbus dev/cardbus/cardbus_device.c optional cardbus +dev/cas/if_cas.c optional cas dev/cfi/cfi_core.c optional cfi dev/cfi/cfi_dev.c optional cfi dev/cfi/cfi_disk.c optional cfid Added: head/sys/dev/cas/if_cas.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/cas/if_cas.c Mon Jun 15 18:22:41 2009 (r194246) @@ -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); + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200906151822.n5FIMfnq097243>