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