Index: hifn7751.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/hifn/hifn7751.c,v
retrieving revision 1.34
diff -U5 -r1.34 hifn7751.c
--- hifn7751.c	5 Jun 2005 22:57:32 -0000	1.34
+++ hifn7751.c	6 Jan 2006 16:51:14 -0000
@@ -45,10 +45,11 @@
 
 /*
  * Driver for various Hifn encryption processors.
  */
 #include "opt_hifn.h"
+#include "opt_device_polling.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
 #include <sys/errno.h>
@@ -129,29 +130,40 @@
 static	int hifn_sramsize(struct hifn_softc *);
 static	int hifn_dramsize(struct hifn_softc *);
 static	int hifn_ramtype(struct hifn_softc *);
 static	void hifn_sessions(struct hifn_softc *);
 static	void hifn_intr(void *);
+static  int hifn_handle_state(struct hifn_softc *, u_int32_t);
+static  int hifn_handle_rings(struct hifn_softc *, int32_t);
 static	u_int hifn_write_command(struct hifn_command *, u_int8_t *);
 static	u_int32_t hifn_next_signature(u_int32_t a, u_int cnt);
 static	int hifn_newsession(void *, u_int32_t *, struct cryptoini *);
 static	int hifn_freesession(void *, u_int64_t);
 static	int hifn_process(void *, struct cryptop *, int);
 static	void hifn_callback(struct hifn_softc *, struct hifn_command *, u_int8_t *);
+static  void hifn_wakeup_crypto(struct hifn_softc *);
 static	int hifn_crypto(struct hifn_softc *, struct hifn_command *, struct cryptop *, int);
 static	int hifn_readramaddr(struct hifn_softc *, int, u_int8_t *);
 static	int hifn_writeramaddr(struct hifn_softc *, int, u_int8_t *);
 static	int hifn_dmamap_load_src(struct hifn_softc *, struct hifn_command *);
 static	int hifn_dmamap_load_dst(struct hifn_softc *, struct hifn_command *);
 static	int hifn_init_pubrng(struct hifn_softc *);
 static	void hifn_rng(void *);
 static	void hifn_tick(void *);
 static	void hifn_abort(struct hifn_softc *);
 static	void hifn_alloc_slot(struct hifn_softc *, int *, int *, int *, int *);
-
 static	void hifn_write_reg_0(struct hifn_softc *, bus_size_t, u_int32_t);
 static	void hifn_write_reg_1(struct hifn_softc *, bus_size_t, u_int32_t);
+static  void hifn_sysctlattach(struct hifn_softc *);
+static  void hifn_update_intr(struct hifn_softc *);
+
+/* Polling support for the hifn driver */
+#ifdef DEVICE_POLLING
+static int hifn_sysctl_polling(SYSCTL_HANDLER_ARGS);
+static int hifn_set_polling(struct hifn_softc *sc, int val);
+static poll_handler_t hifn_poll;
+#endif 
 
 static __inline u_int32_t
 READ_REG_0(struct hifn_softc *sc, bus_size_t reg)
 {
     u_int32_t v = bus_space_read_4(sc->sc_st0, sc->sc_sh0, reg);
@@ -602,10 +614,12 @@
 		hifn_init_pubrng(sc);
 
 	callout_init(&sc->sc_tickto, CALLOUT_MPSAFE);
 	callout_reset(&sc->sc_tickto, hz, hifn_tick, sc);
 
+	hifn_sysctlattach(sc);
+
 	return (0);
 
 fail_intr:
 	bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
 fail_intr2:
@@ -627,20 +641,38 @@
 fail_pci:
 	mtx_destroy(&sc->sc_mtx);
 	return (ENXIO);
 }
 
+static void 
+hifn_sysctlattach(struct hifn_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
+	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
+
+#ifdef DEVICE_POLLING
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"polling", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		hifn_sysctl_polling, "I", "polling enabled for device");
+#endif
+}
+
 /*
  * Detach an interface that successfully probed.
  */
 static int 
 hifn_detach(device_t dev)
 {
 	struct hifn_softc *sc = device_get_softc(dev);
 
 	KASSERT(sc != NULL, ("hifn_detach: null software carrier!"));
 
+#ifdef DEVICE_POLLING
+	/* disable polling if necessary */
+	hifn_set_polling(sc, 0);
+#endif 
+
 	/* disable interrupts */
 	WRITE_REG_1(sc, HIFN_1_DMA_IER, 0);
 
 	/*XXX other resources */
 	callout_stop(&sc->sc_tickto);
@@ -720,10 +752,14 @@
 
         /* reinitialize interface if necessary */
         if (ifp->if_flags & IFF_UP)
                 rl_init(sc);
 #endif
+
+	/* This disables interrupts (if polling) */
+	hifn_update_intr(sc);
+	
 	sc->sc_suspended = 0;
 
 	return (0);
 }
 
@@ -788,11 +824,11 @@
 
 	/* Enable public key engine, if available */
 	if (sc->sc_flags & HIFN_HAS_PUBLIC) {
 		WRITE_REG_1(sc, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE);
 		sc->sc_dmaier |= HIFN_DMAIER_PUBDONE;
-		WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+		hifn_update_intr(sc);
 	}
 
 	return (0);
 }
 
@@ -1156,11 +1192,11 @@
 	    HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER |
 	    HIFN_DMAIER_S_ABORT | HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT |
 	    ((sc->sc_flags & HIFN_IS_7811) ?
 		HIFN_DMAIER_ILLW | HIFN_DMAIER_ILLR : 0);
 	sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
-	WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+	hifn_update_intr(sc);
 
 
 	if (sc->sc_flags & HIFN_IS_7956) {
 		u_int32_t pll;
 
@@ -1196,10 +1232,23 @@
 	    HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE | HIFN_DMACNFG_LAST |
 	    ((HIFN_POLL_FREQUENCY << 16 ) & HIFN_DMACNFG_POLLFREQ) |
 	    ((HIFN_POLL_SCALAR << 8) & HIFN_DMACNFG_POLLINVAL));
 }
 
+static void
+hifn_update_intr(struct hifn_softc *sc)
+{
+	if(sc->sc_polling)
+		WRITE_REG_1(sc, HIFN_1_DMA_IER, 
+			HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT | 
+			HIFN_DMAIER_S_ABORT | HIFN_DMAIER_C_ABORT | 
+			HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER | 
+			HIFN_DMAIER_ILLW | HIFN_DMAIER_ILLR);
+	else
+		WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+}
+
 /*
  * The maximum number of sessions supported by the card
  * is dependent on the amount of context ram, which
  * encryption algorithms are enabled, and how compression
  * is configured.  This should be configured before this
@@ -2006,11 +2055,11 @@
 	 * interrupt salvages us from), unless there is more than one command
 	 * in the queue.
 	 */
 	if (dma->cmdu > 1) {
 		sc->sc_dmaier |= HIFN_DMAIER_C_WAIT;
-		WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+		hifn_update_intr(sc);
 	}
 
 	hifnstats.hst_ipackets++;
 	hifnstats.hst_ibytes += cmd->src_mapsize;
 
@@ -2137,35 +2186,54 @@
 
 static void 
 hifn_intr(void *arg)
 {
 	struct hifn_softc *sc = arg;
-	struct hifn_dma *dma;
-	u_int32_t dmacsr, restart;
-	int i, u;
+	u_int32_t dmacsr;
 
 	dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR);
 
 	/* Nothing in the DMA unit interrupted */
 	if ((dmacsr & sc->sc_dmaier) == 0)
 		return;
 
 	HIFN_LOCK(sc);
 
-	dma = sc->sc_dma;
-
 #ifdef HIFN_DEBUG
 	if (hifn_debug) {
+		struct hifn_dma *dma = sc->sc_dma;
 		device_printf(sc->sc_dev,
 		    "irq: stat %08x ien %08x damier %08x i %d/%d/%d/%d k %d/%d/%d/%d u %d/%d/%d/%d\n",
 		    dmacsr, READ_REG_1(sc, HIFN_1_DMA_IER), sc->sc_dmaier,
 		    dma->cmdi, dma->srci, dma->dsti, dma->resi,
 		    dma->cmdk, dma->srck, dma->dstk, dma->resk,
 		    dma->cmdu, dma->srcu, dma->dstu, dma->resu);
 	}
 #endif
 
+	if (hifn_handle_state (sc, dmacsr) == 0) {
+
+		/* Process all results */
+		hifn_handle_rings (sc, 0);
+	}
+
+	HIFN_UNLOCK(sc);
+
+	hifn_wakeup_crypto (sc);
+}
+
+/* 
+ * Processes any device state interrupts. 
+ * Called from interrupt/polling handlers.
+ */
+static int
+hifn_handle_state(struct hifn_softc *sc, u_int32_t dmacsr)
+{
+	int restart;
+
+	/* Called from within the HIFN device lock */
+
 	WRITE_REG_1(sc, HIFN_1_DMA_CSR, dmacsr & sc->sc_dmaier);
 
 	if ((sc->sc_flags & HIFN_HAS_PUBLIC) &&
 	    (dmacsr & HIFN_DMACSR_PUBDONE))
 		WRITE_REG_1(sc, HIFN_1_PUB_STATUS,
@@ -2187,26 +2255,46 @@
 	if (restart) {
 		device_printf(sc->sc_dev, "abort, resetting.\n");
 		hifnstats.hst_abort++;
 		hifn_abort(sc);
 		HIFN_UNLOCK(sc);
-		return;
+		return (-1);
 	}
 
-	if ((dmacsr & HIFN_DMACSR_C_WAIT) && (dma->cmdu == 0)) {
+	if ((dmacsr & HIFN_DMACSR_C_WAIT) && (sc->sc_dma->cmdu == 0)) {
 		/*
 		 * If no slots to process and we receive a "waiting on
 		 * command" interrupt, we disable the "waiting on command"
 		 * (by clearing it).
 		 */
 		sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
-		WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
+		hifn_update_intr(sc);
 	}
 
-	/* clear the rings */
-	i = dma->resk; u = dma->resu;
-	while (u != 0) {
+	return (0);
+}
+
+/* 
+ * Processes any result data in the descriptor rings.
+ * Called from interrupt/polling handlers.
+ */
+static int
+hifn_handle_rings (struct hifn_softc *sc, int32_t count)
+{
+	struct hifn_dma *dma = sc->sc_dma;	
+	int i, u, leave;
+
+	/* Called from within the hifn device lock */
+
+	/* 0 or less means process all */
+	if (count <= 0)
+		count = INT32_MAX;
+
+	/* process rings */
+	i = dma->resk; u = dma->resu; 
+	leave = u > count ? u - count : 0;
+	while (u != leave) {
 		HIFN_RESR_SYNC(sc, i,
 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 		if (dma->resr[i].l & htole32(HIFN_D_VALID)) {
 			HIFN_RESR_SYNC(sc, i,
 			    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -2237,11 +2325,12 @@
 			i = 0;
 	}
 	dma->resk = i; dma->resu = u;
 
 	i = dma->srck; u = dma->srcu;
-	while (u != 0) {
+	leave = u > count ? u - count : 0;
+	while (u != leave) {
 		if (i == HIFN_D_SRC_RSIZE)
 			i = 0;
 		HIFN_SRCR_SYNC(sc, i,
 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 		if (dma->srcr[i].l & htole32(HIFN_D_VALID)) {
@@ -2252,11 +2341,12 @@
 		i++, u--;
 	}
 	dma->srck = i; dma->srcu = u;
 
 	i = dma->cmdk; u = dma->cmdu;
-	while (u != 0) {
+	leave = u > count ? u - count : 0;
+	while (u != leave) {
 		HIFN_CMDR_SYNC(sc, i,
 		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 		if (dma->cmdr[i].l & htole32(HIFN_D_VALID)) {
 			HIFN_CMDR_SYNC(sc, i,
 			    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
@@ -2268,27 +2358,106 @@
 		}
 		if (++i == (HIFN_D_CMD_RSIZE + 1))
 			i = 0;
 	}
 	dma->cmdk = i; dma->cmdu = u;
+	
+	return (0);
+}
 
-	HIFN_UNLOCK(sc);
+/* 
+ * Wakes up crypto subsystem if we have results. 
+ * Called from interrupt/polling handlers.
+ */
+static void
+hifn_wakeup_crypto (struct hifn_softc *sc)
+{
+	/* Called from outside device lock */
 
 	if (sc->sc_needwakeup) {		/* XXX check high watermark */
 		int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ);
 #ifdef HIFN_DEBUG
 		if (hifn_debug)
-			device_printf(sc->sc_dev,
-				"wakeup crypto (%x) u %d/%d/%d/%d\n",
-				sc->sc_needwakeup,
-				dma->cmdu, dma->srcu, dma->dstu, dma->resu);
+			device_printf(sc->sc_dev, "wakeup crypto (%x) u %d/%d/%d/%d\n",
+				sc->sc_needwakeup, sc->sc_dma->cmdu, sc->sc_dma->srcu, 
+				sc->sc_dma->dstu, sc->sc_dma->resu);
 #endif
 		sc->sc_needwakeup &= ~wakeup;
 		crypto_unblock(sc->sc_cid, wakeup);
 	}
 }
 
+#ifdef DEVICE_POLLING
+
+static int 
+hifn_sysctl_polling(SYSCTL_HANDLER_ARGS)
+{
+	struct hifn_softc *sc = arg1;
+	int val = sc->sc_polling;
+	int error;
+
+	error = sysctl_handle_int(oidp, &val, sizeof(int), req);
+	if (error || !req->newptr)
+		return (error);
+	if (val != 0 && val != 1)
+		return (EINVAL);
+	return hifn_set_polling (sc, val);
+}
+
+static int 
+hifn_set_polling(struct hifn_softc *sc, int val)
+{
+	int error = 0;
+
+	if (val == sc->sc_polling)
+		return (0);
+		
+	device_printf(sc->sc_dev, "polling %s\n", 
+		      val ? "enabled" : "disabled");
+
+	if (val)
+		error = device_poll_register(hifn_poll, sc, 
+					     device_get_name(sc->sc_dev));
+	else 
+		error = device_poll_deregister(sc, device_get_name(sc->sc_dev));
+
+	if (error)
+		return error;
+
+	HIFN_LOCK(sc);
+	sc->sc_polling = val;
+	hifn_update_intr(sc);
+	HIFN_UNLOCK(sc);
+
+	return (0);
+}
+
+static void 
+hifn_poll(void *arg, enum poll_cmd cmd, int count)
+{
+	struct hifn_softc *sc = arg;
+	u_int32_t dmacsr;
+
+        dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR);
+
+	HIFN_LOCK(sc);
+
+	/* Only do status maintenance when asked */
+	if (cmd != POLL_AND_CHECK_STATUS || 
+            hifn_handle_state(sc, dmacsr) == 0) {
+
+		/* Process |count| results */
+		hifn_handle_rings(sc, count);
+	}
+
+	HIFN_UNLOCK(sc);
+
+	hifn_wakeup_crypto(sc);
+}
+
+#endif /* DEVICE_POLLING */
+
 /*
  * Allocate a new 'session' and return an encoded session id.  'sidp'
  * contains our registration id, and should contain an encoded session
  * id on successful allocation.
  */
Index: hifn7751var.h
===================================================================
RCS file: /home/ncvs/src/sys/dev/hifn/hifn7751var.h,v
retrieving revision 1.7
diff -U5 -r1.7 hifn7751var.h
--- hifn7751var.h	19 Jan 2005 17:03:35 -0000	1.7
+++ hifn7751var.h	6 Jan 2006 16:51:14 -0000
@@ -180,10 +180,11 @@
 	int			sc_r_busy;	/* result ring busy */
 	int			sc_active;	/* for initial countdown */
 	int			sc_needwakeup;	/* ops q'd wating on resources */
 	int			sc_curbatch;	/* # ops submitted w/o int */
 	int			sc_suspended;
+	int			sc_polling; 	/* device polling enabled */	
 };
 
 #define	HIFN_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
 #define	HIFN_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
 
