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 #include #include #include @@ -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)