Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 20 Sep 2009 12:08:30 +0000 (UTC)
From:      Marius Strobl <marius@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org
Subject:   svn commit: r197343 - in stable/7/sys: . boot/forth conf contrib/pf dev/cas modules modules/cas sparc64/conf
Message-ID:  <200909201208.n8KC8U0i055798@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: marius
Date: Sun Sep 20 12:08:29 2009
New Revision: 197343
URL: http://svn.freebsd.org/changeset/base/197343

Log:
  MFC: 194246, 194904, 194973
  
  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.

Added:
  stable/7/sys/dev/cas/
     - copied from r194246, head/sys/dev/cas/
  stable/7/sys/modules/cas/
     - copied from r194246, head/sys/modules/cas/
Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/boot/forth/loader.conf
  stable/7/sys/conf/NOTES
  stable/7/sys/conf/files
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/cas/if_cas.c
  stable/7/sys/dev/cas/if_casvar.h
  stable/7/sys/modules/Makefile
  stable/7/sys/sparc64/conf/GENERIC

Modified: stable/7/sys/boot/forth/loader.conf
==============================================================================
--- stable/7/sys/boot/forth/loader.conf	Sun Sep 20 11:33:39 2009	(r197342)
+++ stable/7/sys/boot/forth/loader.conf	Sun Sep 20 12:08:29 2009	(r197343)
@@ -220,6 +220,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: stable/7/sys/conf/NOTES
==============================================================================
--- stable/7/sys/conf/NOTES	Sun Sep 20 11:33:39 2009	(r197342)
+++ stable/7/sys/conf/NOTES	Sun Sep 20 12:08:29 2009	(r197343)
@@ -1733,6 +1733,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.
 # cnw:  Xircom CNW/Netware Airsurfer PC Card adapter
@@ -1875,6 +1876,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		dc		# DEC/Intel 21143 and various workalikes
 device		et		# Agere ET1310 10/100/Gigabit Ethernet

Modified: stable/7/sys/conf/files
==============================================================================
--- stable/7/sys/conf/files	Sun Sep 20 11:33:39 2009	(r197342)
+++ stable/7/sys/conf/files	Sun Sep 20 12:08:29 2009	(r197343)
@@ -671,6 +671,7 @@ dev/buslogic/bt_pci.c		optional bt 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/ciss/ciss.c			optional ciss
 dev/cm/smc90cx6.c		optional cm
 dev/cmx/cmx.c			optional cmx

Modified: stable/7/sys/dev/cas/if_cas.c
==============================================================================
--- head/sys/dev/cas/if_cas.c	Mon Jun 15 18:22:41 2009	(r194246)
+++ stable/7/sys/dev/cas/if_cas.c	Sun Sep 20 12:08:29 2009	(r197343)
@@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/rman.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
-#include <sys/rman.h>
+#include <sys/taskqueue.h>
 
 #include <net/bpf.h>
 #include <net/ethernet.h>
@@ -135,7 +135,8 @@ static void	cas_free(void *arg1, void* a
 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_intr(void *v);
+static void	cas_intr_task(void *arg, int pending __unused);
 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);
@@ -159,13 +160,13 @@ static void	cas_rxdma_callback(void *xsc
 		    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 void	cas_tx_task(void *arg, int pending __unused);
 static inline void cas_txkick(struct cas_softc *sc);
-static int	cas_watchdog(struct cas_softc *sc);
+static void	cas_watchdog(struct cas_softc *sc);
 
 static devclass_t cas_devclass;
 
@@ -201,7 +202,19 @@ cas_attach(struct cas_softc *sc)
 	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);
+	callout_init(&sc->sc_rx_ch, 1);
+	/* Create local taskq. */
+	TASK_INIT(&sc->sc_intr_task, 0, cas_intr_task, sc);
+	TASK_INIT(&sc->sc_tx_task, 1, cas_tx_task, ifp);
+	sc->sc_tq = taskqueue_create_fast("cas_taskq", M_WAITOK,
+	    taskqueue_thread_enqueue, &sc->sc_tq);
+	if (sc->sc_tq == NULL) {
+		device_printf(sc->sc_dev, "could not create taskqueue\n");
+		error = ENXIO;
+		goto fail_ifnet;
+	}
+	taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq",
+	    device_get_nameunit(sc->sc_dev));
 
 	/* Make sure the chip is stopped. */
 	cas_reset(sc);
@@ -211,7 +224,7 @@ cas_attach(struct cas_softc *sc)
 	    BUS_SPACE_MAXSIZE, 0, BUS_SPACE_MAXSIZE, 0, NULL, NULL,
 	    &sc->sc_pdmatag);
 	if (error != 0)
-		goto fail_ifnet;
+		goto fail_taskq;
 
 	error = bus_dma_tag_create(sc->sc_pdmatag, 1, 0,
 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
@@ -422,6 +435,8 @@ cas_attach(struct cas_softc *sc)
 	bus_dma_tag_destroy(sc->sc_rdmatag);
  fail_ptag:
 	bus_dma_tag_destroy(sc->sc_pdmatag);
+ fail_taskq:
+	taskqueue_free(sc->sc_tq);
  fail_ifnet:
 	if_free(ifp);
 	return (error);
@@ -433,13 +448,16 @@ cas_detach(struct cas_softc *sc)
 	struct ifnet *ifp = sc->sc_ifp;
 	int i;
 
+	ether_ifdetach(ifp);
 	CAS_LOCK(sc);
 	cas_stop(ifp);
 	CAS_UNLOCK(sc);
 	callout_drain(&sc->sc_tick_ch);
 	callout_drain(&sc->sc_rx_ch);
-	ether_ifdetach(ifp);
+	taskqueue_drain(sc->sc_tq, &sc->sc_intr_task);
+	taskqueue_drain(sc->sc_tq, &sc->sc_tx_task);
 	if_free(ifp);
+	taskqueue_free(sc->sc_tq);
 	device_delete_child(sc->sc_dev, sc->sc_miibus);
 
 	for (i = 0; i < CAS_NRXDESC; i++)
@@ -586,12 +604,11 @@ static void
 cas_tick(void *arg)
 {
 	struct cas_softc *sc = arg;
-	struct ifnet *ifp;
+	struct ifnet *ifp = sc->sc_ifp;
 	uint32_t v;
 
 	CAS_LOCK_ASSERT(sc, MA_OWNED);
 
-	ifp = sc->sc_ifp;
 	/*
 	 * Unload collision and error counters.
 	 */
@@ -622,8 +639,10 @@ cas_tick(void *arg)
 
 	mii_tick(sc->sc_mii);
 
-	if (cas_watchdog(sc) == EJUSTRETURN)
-		return;
+	if (sc->sc_txfree != CAS_MAXTXFREE)
+		cas_tint(sc);
+
+	cas_watchdog(sc);
 
 	callout_reset(&sc->sc_tick_ch, hz, cas_tick, sc);
 }
@@ -915,6 +934,9 @@ cas_init_locked(struct cas_softc *sc)
 
 	CAS_LOCK_ASSERT(sc, MA_OWNED);
 
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+		return;
+
 #ifdef CAS_DEBUG
 	CTR2(KTR_CAS, "%s: %s: calling stop", device_get_name(sc->sc_dev),
 	    __func__);
@@ -994,7 +1016,7 @@ cas_init_locked(struct cas_softc *sc)
 
 	/* 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_TX_INT_ME | 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 |
@@ -1003,6 +1025,8 @@ cas_init_locked(struct cas_softc *sc)
 	    | CAS_INTR_PCS_INT | CAS_INTR_MIF
 #endif
 	    ));
+	/* Don't clear top level interrupts when CAS_STATUS_ALIAS is read. */
+	CAS_WRITE_4(sc, CAS_CLEAR_ALIAS, 0);
 	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));
@@ -1240,7 +1264,7 @@ cas_load_txmbuf(struct cas_softc *sc, st
 	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) {
+	if (sc->sc_txwin += nsegs > CAS_MAXTXFREE * 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);
@@ -1351,13 +1375,12 @@ cas_init_regs(struct cas_softc *sc)
 }
 
 static void
-cas_start(struct ifnet *ifp)
+cas_tx_task(void *arg, int pending __unused)
 {
-	struct cas_softc *sc = ifp->if_softc;
+	struct ifnet *ifp;
 
-	CAS_LOCK(sc);
-	cas_start_locked(ifp);
-	CAS_UNLOCK(sc);
+	ifp = (struct ifnet *)arg;
+	cas_start(ifp);
 }
 
 static inline void
@@ -1379,17 +1402,22 @@ cas_txkick(struct cas_softc *sc)
 }
 
 static void
-cas_start_locked(struct ifnet *ifp)
+cas_start(struct ifnet *ifp)
 {
 	struct cas_softc *sc = ifp->if_softc;
 	struct mbuf *m;
 	int kicked, ntx;
 
-	CAS_LOCK_ASSERT(sc, MA_OWNED);
+	CAS_LOCK(sc);
 
 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
-	    IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0)
+	    IFF_DRV_RUNNING || (sc->sc_flags & CAS_LINK) == 0) {
+		CAS_UNLOCK(sc);
 		return;
+	}
+
+	if (sc->sc_txfree < CAS_MAXTXFREE / 4)
+		cas_tint(sc);
 
 #ifdef CAS_DEBUG
 	CTR4(KTR_CAS, "%s: %s: txfree %d, txnext %d",
@@ -1434,6 +1462,8 @@ cas_start_locked(struct ifnet *ifp)
 		    sc->sc_wdog_timer);
 #endif
 	}
+
+	CAS_UNLOCK(sc);
 }
 
 static void
@@ -1530,17 +1560,10 @@ cas_tint(struct cas_softc *sc)
 #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.
-		 */
+		/* We freed some descriptors, so reset IFF_DRV_OACTIVE. */
 		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
@@ -1554,7 +1577,7 @@ cas_rint_timeout(void *arg)
 {
 	struct cas_softc *sc = arg;
 
-	CAS_LOCK_ASSERT(sc, MA_OWNED);
+	CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
 
 	cas_rint(sc);
 }
@@ -1569,7 +1592,7 @@ cas_rint(struct cas_softc *sc)
 	uint32_t rxhead;
 	u_int idx, idx2, len, off, skip;
 
-	CAS_LOCK_ASSERT(sc, MA_OWNED);
+	CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
 
 	callout_stop(&sc->sc_rx_ch);
 
@@ -1674,14 +1697,16 @@ cas_rint(struct cas_softc *sc)
 				refcount_acquire(&rxds->rxds_refcount);
 				bus_dmamap_sync(sc->sc_rdmatag,
 				    rxds->rxds_dmamap, BUS_DMASYNC_POSTREAD);
+#if __FreeBSD_version < 800016
 				MEXTADD(m, (caddr_t)rxds->rxds_buf +
 				    off * 256 + ETHER_ALIGN, len, cas_free,
-#if __FreeBSD_version < 800016
-				    rxds,
+				    rxds, M_RDONLY, EXT_NET_DRV);
 #else
+				MEXTADD(m, (caddr_t)rxds->rxds_buf +
+				    off * 256 + ETHER_ALIGN, len, cas_free,
 				    sc, (void *)(uintptr_t)idx,
-#endif
 				    M_RDONLY, EXT_NET_DRV);
+#endif
 				if ((m->m_flags & M_EXT) == 0) {
 					m_freem(m);
 					m = NULL;
@@ -1695,9 +1720,7 @@ cas_rint(struct cas_softc *sc)
 					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++;
 
@@ -1719,14 +1742,16 @@ cas_rint(struct cas_softc *sc)
 				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,
+				MEXTADD(m, (caddr_t)rxds->rxds_buf + off,
+				    m->m_len, cas_free, rxds, M_RDONLY,
+				    EXT_NET_DRV);
 #else
-				    sc, (void *)(uintptr_t)idx,
+				MEXTADD(m, (caddr_t)rxds->rxds_buf + off,
+				    m->m_len, cas_free, sc,
+				    (void *)(uintptr_t)idx, M_RDONLY,
+				    EXT_NET_DRV);
 #endif
-				    M_RDONLY, EXT_NET_DRV);
 				if ((m->m_flags & M_EXT) == 0) {
 					m_freem(m);
 					m = NULL;
@@ -1753,14 +1778,16 @@ cas_rint(struct cas_softc *sc)
 					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,
+					MEXTADD(m2, (caddr_t)rxds2->rxds_buf,
+					    m2->m_len, cas_free, rxds2,
+					    M_RDONLY, EXT_NET_DRV);
 #else
+					MEXTADD(m2, (caddr_t)rxds2->rxds_buf,
+					    m2->m_len, cas_free,
 					    sc, (void *)(uintptr_t)idx2,
-#endif
 					    M_RDONLY, EXT_NET_DRV);
+#endif
 					if ((m2->m_flags & M_EXT) == 0) {
 						m_freem(m2);
 						m2 = NULL;
@@ -1781,9 +1808,7 @@ cas_rint(struct cas_softc *sc)
 					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++;
 
@@ -1799,6 +1824,8 @@ cas_rint(struct cas_softc *sc)
 
  skip:
 		cas_rxcompinit(&sc->sc_rxcomps[sc->sc_rxcptr]);
+		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+			break;
 	}
 	CAS_CDSYNC(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
 	CAS_WRITE_4(sc, CAS_RX_COMP_TAIL, sc->sc_rxcptr);
@@ -1819,7 +1846,7 @@ cas_free(void *arg1, void *arg2)
 {
 	struct cas_rxdsoft *rxds;
 	struct cas_softc *sc;
-	u_int idx, locked;
+	u_int idx;
 
 #if __FreeBSD_version < 800016
 	rxds = arg2;
@@ -1837,18 +1864,17 @@ cas_free(void *arg1, void *arg2)
 	 * 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)
 {
+	u_int locked;
 
-	CAS_LOCK_ASSERT(sc, MA_OWNED);
+	if ((locked = CAS_LOCK_OWNED(sc)) == 0)
+		CAS_LOCK(sc);
 
 	bus_dmamap_sync(sc->sc_rdmatag, sc->sc_rxdsoft[idx].rxds_dmamap,
 	    BUS_DMASYNC_PREREAD);
@@ -1866,13 +1892,19 @@ cas_add_rxdesc(struct cas_softc *sc, u_i
 		CAS_WRITE_4(sc, CAS_RX_KICK,
 		    (sc->sc_rxdptr + CAS_NRXDESC - 4) & CAS_NRXDESC_MASK);
 	}
+
+	if (locked == 0)
+		CAS_UNLOCK(sc);
 }
 
 static void
 cas_eint(struct cas_softc *sc, u_int status)
 {
+	struct ifnet *ifp = sc->sc_ifp;
+
+	CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
 
-	sc->sc_ifp->if_ierrors++;
+	ifp->if_ierrors++;
 
 	device_printf(sc->sc_dev, "%s: status 0x%x", __func__, status);
 	if ((status & CAS_INTR_PCI_ERROR_INT) != 0) {
@@ -1886,21 +1918,43 @@ cas_eint(struct cas_softc *sc, u_int sta
 	}
 	printf("\n");
 
-	cas_init_locked(sc);
-	cas_start_locked(sc->sc_ifp);
+	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+	cas_init(sc);
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
 }
 
-static void
+static int
 cas_intr(void *v)
 {
 	struct cas_softc *sc = v;
+
+	if (__predict_false((CAS_READ_4(sc, CAS_STATUS_ALIAS) &
+	    CAS_INTR_SUMMARY) == 0))
+		return (FILTER_STRAY);
+
+	/* Disable interrupts. */
+	CAS_WRITE_4(sc, CAS_INTMASK, 0xffffffff);
+	taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
+
+	return (FILTER_HANDLED);
+}
+
+static void
+cas_intr_task(void *arg, int pending __unused)
+{
+	struct cas_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
 	uint32_t status, status2;
 
-	status = CAS_READ_4(sc, CAS_STATUS);
-	if (__predict_false((status & CAS_INTR_SUMMARY) == 0))
+	CAS_LOCK_ASSERT(sc, MA_NOTOWNED);
+
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
 		return;
 
-	CAS_LOCK(sc);
+	status = CAS_READ_4(sc, CAS_STATUS);
+	if (__predict_false((status & CAS_INTR_SUMMARY) == 0))
+		goto done;
 
 #ifdef CAS_DEBUG
 	CTR4(KTR_CAS, "%s: %s: cplt %x, status %x",
@@ -1941,7 +1995,6 @@ cas_intr(void *v)
 	    (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;
 	}
 
@@ -1968,21 +2021,48 @@ cas_intr(void *v)
 	    (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);
+#ifdef CAS_DEBUG
 		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);
+#endif
 	}
 
 	if ((status &
-	    (CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_DONE)) != 0)
+	    (CAS_INTR_TX_INT_ME | CAS_INTR_TX_ALL | CAS_INTR_TX_DONE)) != 0) {
+		CAS_LOCK(sc);
 		cas_tint(sc);
+		CAS_UNLOCK(sc);
+	}
 
-	CAS_UNLOCK(sc);
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		return;
+	else if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
+
+	status = CAS_READ_4(sc, CAS_STATUS_ALIAS);
+	if (__predict_false((status & CAS_INTR_SUMMARY) != 0)) {
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_intr_task);
+		return;
+	}
+
+ done:
+	/* Re-enable interrupts. */
+	CAS_WRITE_4(sc, CAS_INTMASK,
+	    ~(CAS_INTR_TX_INT_ME | 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
+	));
 }
 
-static int
+static void
 cas_watchdog(struct cas_softc *sc)
 {
 	struct ifnet *ifp = sc->sc_ifp;
@@ -2003,7 +2083,7 @@ cas_watchdog(struct cas_softc *sc)
 #endif
 
 	if (sc->sc_wdog_timer == 0 || --sc->sc_wdog_timer != 0)
-		return (0);
+		return;
 
 	if ((sc->sc_flags & CAS_LINK) != 0)
 		device_printf(sc->sc_dev, "device timeout\n");
@@ -2012,9 +2092,10 @@ cas_watchdog(struct cas_softc *sc)
 	++ifp->if_oerrors;
 
 	/* Try to get more packets going. */
+	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 	cas_init_locked(sc);
-	cas_start_locked(ifp);
-	return (EJUSTRETURN);
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		taskqueue_enqueue(sc->sc_tq, &sc->sc_tx_task);
 }
 
 static void
@@ -2378,7 +2459,8 @@ cas_ioctl(struct ifnet *ifp, u_long cmd,
 	case SIOCADDMULTI:
 	case SIOCDELMULTI:
 		CAS_LOCK(sc);
-		cas_setladrf(sc);
+		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+			cas_setladrf(sc);
 		CAS_UNLOCK(sc);
 		break;
 	case SIOCSIFMTU:
@@ -2729,7 +2811,7 @@ cas_pci_attach(device_t dev)
 	}
 
 	if (bus_setup_intr(dev, sc->sc_res[CAS_RES_INTR], INTR_TYPE_NET |
-	    INTR_MPSAFE, NULL, cas_intr, sc, &sc->sc_ih) != 0) {
+	    INTR_MPSAFE, cas_intr, NULL, sc, &sc->sc_ih) != 0) {
 		device_printf(dev, "failed to set up interrupt\n");
 		cas_detach(sc);
 		goto fail;

Modified: stable/7/sys/dev/cas/if_casvar.h
==============================================================================
--- head/sys/dev/cas/if_casvar.h	Mon Jun 15 18:22:41 2009	(r194246)
+++ stable/7/sys/dev/cas/if_casvar.h	Sun Sep 20 12:08:29 2009	(r197343)
@@ -138,6 +138,9 @@ struct cas_softc {
 	u_char		sc_enaddr[ETHER_ADDR_LEN];
 	struct callout	sc_tick_ch;	/* tick callout */
 	struct callout	sc_rx_ch;	/* delayed RX callout */
+	struct task	sc_intr_task;
+	struct task	sc_tx_task;
+	struct taskqueue	*sc_tq;
 	u_int		sc_wdog_timer;	/* watchdog timer */
 
 	void		*sc_ih;

Modified: stable/7/sys/modules/Makefile
==============================================================================
--- stable/7/sys/modules/Makefile	Sun Sep 20 11:33:39 2009	(r197342)
+++ stable/7/sys/modules/Makefile	Sun Sep 20 12:08:29 2009	(r197343)
@@ -46,6 +46,7 @@ SUBDIR=	${_3dfx} \
 	${_canbepm} \
 	${_canbus} \
 	${_cardbus} \
+	cas \
 	${_cbb} \
 	cd9660 \
 	cd9660_iconv \

Modified: stable/7/sys/sparc64/conf/GENERIC
==============================================================================
--- stable/7/sys/sparc64/conf/GENERIC	Sun Sep 20 11:33:39 2009	(r197342)
+++ stable/7/sys/sparc64/conf/GENERIC	Sun Sep 20 12:08:29 2009	(r197343)
@@ -164,6 +164,7 @@ device		txp		# 3Com 3cR990 (``Typhoon'')
 device		miibus		# MII bus support
 #device		bfe		# Broadcom BCM440x 10/100 Ethernet
 device		bge		# Broadcom BCM570xx Gigabit Ethernet
+device		cas		# Sun Cassini/Cassini+ and NS DP83065 Saturn
 device		dc		# DEC/Intel 21143 and various workalikes
 device		fxp		# Intel EtherExpress PRO/100B (82557, 82558)
 device		gem		# Sun GEM/Sun ERI/Apple GMAC



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200909201208.n8KC8U0i055798>