svn commit: r194246 - in head/sys: boot/forth conf dev/cas modules
modules/cas sparc64/conf
Marius Strobl
marius at FreeBSD.org
Mon Jun 15 18:22:42 UTC 2009
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 at 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 ***
More information about the svn-src-all
mailing list