Date: Tue, 28 Mar 2006 19:54:11 GMT From: Marcel Moolenaar <marcel@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 94202 for review Message-ID: <200603281954.k2SJsB7q056281@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=94202 Change 94202 by marcel@marcel_nfs on 2006/03/28 19:53:55 Implement fine-grained interrupt handling: o When a subordinate driver sets up an interrupt handler, the scc(4) driver calls back for interrupt source-specific handlers. When a subordinate driver has at least one interrupt source-specific handler, it's generic handler (i.e. the one passed to BUS_SETUP_INTR) is nullified. o On interrupt, scc(4) queries the hardware for all pending interrupt conditions for all channels. Interrupts are handled in priority order across channels. Interrupt conditions are cleared for a particular source and channel if the subordinate driver has an interrupt source-specific handler and it indicated to have cleared the condition. Afterwards, all channels with pending interrupt conditions will have the generic interrupt handler of the subordinate driver called or otherwise the condition is cleared by scc(4). Affected files ... .. //depot/projects/uart/dev/scc/scc_bfe.h#10 edit .. //depot/projects/uart/dev/scc/scc_bus.h#5 edit .. //depot/projects/uart/dev/scc/scc_core.c#11 edit .. //depot/projects/uart/dev/scc/scc_dev_sab82532.c#5 edit .. //depot/projects/uart/dev/scc/scc_dev_z8530.c#5 edit .. //depot/projects/uart/dev/scc/scc_if.m#4 edit Differences ... ==== //depot/projects/uart/dev/scc/scc_bfe.h#10 (text+ko) ==== @@ -29,6 +29,8 @@ #ifndef _DEV_SCC_BFE_H_ #define _DEV_SCC_BFE_H_ +#include <sys/serial.h> + /* * Bus access structure. This structure holds the minimum information needed * to access the SCC. The rclk field, although not important to actually @@ -59,6 +61,7 @@ */ #define SCC_NMODES 3 +#define SCC_ISRCCNT 5 struct scc_chan; @@ -74,13 +77,8 @@ int m_fastintr:1; driver_intr_t *ih; - driver_intr_t *ih_overrun; - driver_intr_t *ih_break; - driver_intr_t *ih_rxready; - driver_intr_t *ih_sigchg; - driver_intr_t *ih_txidle; + serdev_intr_t *ih_src[SCC_ISRCCNT]; void *ih_arg; - void *softih; }; struct scc_chan { @@ -89,7 +87,9 @@ struct scc_mode ch_mode[SCC_NMODES]; - u_int ch_nr; + uint32_t ch_nr; + uint32_t ch_ipend; + uint32_t ch_hwsig; }; /* ==== //depot/projects/uart/dev/scc/scc_bus.h#5 (text+ko) ==== @@ -29,6 +29,7 @@ #ifndef _DEV_SCC_BUS_H_ #define _DEV_SCC_BUS_H_ +#include <sys/serial.h> #include <serdev_if.h> #define SCC_IVAR_CHANNEL 0 ==== //depot/projects/uart/dev/scc/scc_core.c#11 (text+ko) ==== @@ -45,47 +45,99 @@ #include "scc_if.h" +#define SCC_ITRACE + devclass_t scc_devclass; char scc_driver_name[] = "scc"; MALLOC_DEFINE(M_SCC, "SCC", "SCC driver"); +#ifdef SCC_ITRACE +#define SCC_ITRACE_BUFSZ 128 +static uint32_t scc_itrace_entry[SCC_ITRACE_BUFSZ]; +static uint32_t scc_itrace_count[SCC_ITRACE_BUFSZ]; +static int scc_itrace_head = 0; + static void +scc_itrace_log(struct scc_softc *sc, int ch, int ipend) +{ + uint32_t entry; + int unit; + + unit = device_get_unit(sc->sc_dev); + entry = ipend | (ch << 8) | unit; + + if (scc_itrace_entry[scc_itrace_head] == entry) { + scc_itrace_count[scc_itrace_head]++; + } else { + scc_itrace_head = (scc_itrace_head + 1) % SCC_ITRACE_BUFSZ; + scc_itrace_entry[scc_itrace_head] = entry; + scc_itrace_count[scc_itrace_head] = 1; + } +} + +void scc_itrace_dump(void); +void +scc_itrace_dump() +{ + int idx; + + idx = scc_itrace_head; + do { + if (scc_itrace_entry[idx] != 0) + printf("%06x %u\n", scc_itrace_entry[idx], + scc_itrace_count[idx]); + idx = (idx == 0) ? SCC_ITRACE_BUFSZ - 1 : idx - 1; + } while (idx != scc_itrace_head); +} +#endif /* SCC_ITRACE */ + +static void scc_bfe_intr(void *arg) { struct scc_softc *sc = arg; struct scc_chan *ch; struct scc_class *cl; struct scc_mode *m; - int c; - - if (sc->sc_leaving) - return; + int c, i, ipend, isrc; cl = sc->sc_class; - for (c = 0; c < cl->cl_channels; c++) { - ch = &sc->sc_chan[c]; - m = &ch->ch_mode[0]; - if (!m->m_attached) - continue; - if (m->ih != NULL) - (*m->ih)(m->ih_arg); - } - -#if 0 while (!sc->sc_leaving && (ipend = SCC_IPEND(sc)) != 0) { - if (ipend & SER_INT_OVERRUN) - uart_intr_overrun(sc); - if (ipend & SER_INT_BREAK) - uart_intr_break(sc); - if (ipend & SER_INT_RXREADY) - uart_intr_rxready(sc); - if (ipend & SER_INT_SIGCHG) - uart_intr_sigchg(sc); - if (ipend & SER_INT_TXIDLE) - uart_intr_txidle(sc); +#ifdef SCC_ITRACE + for (c = 0; c < cl->cl_channels; c++) { + ch = &sc->sc_chan[c]; + if (ch->ch_ipend) + scc_itrace_log(sc, ch->ch_nr, ch->ch_ipend); + } +#endif + i = 0, isrc = SER_INT_OVERRUN; + while (ipend) { + while (i < SCC_ISRCCNT && !(ipend & isrc)) + i++, isrc <<= 1; + KASSERT(i < SCC_ISRCCNT, ("%s", __func__)); + ipend &= ~isrc; + for (c = 0; c < cl->cl_channels; c++) { + ch = &sc->sc_chan[c]; + if (!(ch->ch_ipend & isrc)) + continue; + m = &ch->ch_mode[0]; + if (m->ih_src[i] == NULL) + continue; + if ((*m->ih_src[i])(m->ih_arg)) + ch->ch_ipend &= ~isrc; + } + } + for (c = 0; c < cl->cl_channels; c++) { + ch = &sc->sc_chan[c]; + if (!ch->ch_ipend) + continue; + m = &ch->ch_mode[0]; + if (m->ih != NULL) + (*m->ih)(m->ih_arg); + else + SCC_ICLEAR(sc, ch); + } } -#endif } int @@ -99,7 +151,7 @@ const char *sep; bus_space_handle_t bh; u_long base, size, start; - int c, error, intr, mode, reset; + int c, error, mode, reset; /* * The sc_class field defines the type of SCC we're going to work @@ -118,13 +170,6 @@ size = abs(cl->cl_range); - /* - * Protect ourselves against interrupts while we're not completely - * finished attaching and initializing. We don't expect interrupts - * until after SCC_ATTACH() though. - */ - sc->sc_leaving = 1; - mtx_init(&sc->sc_hwmtx, "scc_hwmtx", NULL, MTX_SPIN); /* @@ -201,11 +246,38 @@ goto fail; /* + * Setup our interrupt handler. Make it FAST under the assumption + * that our children's are fast as well. We make it MPSAFE as soon + * as a child sets up a MPSAFE interrupt handler. + * Of course, if we can't setup a fast handler, we make it MPSAFE + * right away. + */ + if (sc->sc_ires != NULL) { + error = bus_setup_intr(dev, sc->sc_ires, + INTR_TYPE_TTY | INTR_FAST, scc_bfe_intr, sc, + &sc->sc_icookie); + if (error) { + error = bus_setup_intr(dev, sc->sc_ires, + INTR_TYPE_TTY | INTR_MPSAFE, scc_bfe_intr, sc, + &sc->sc_icookie); + } else + sc->sc_fastintr = 1; + + if (error) { + device_printf(dev, "could not activate interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, + sc->sc_ires); + sc->sc_ires = NULL; + } + } + if (sc->sc_ires == NULL) { + /* XXX no interrupt resource. Force polled mode. */ + sc->sc_polled = 1; + } + + /* * Attach all child devices that were probed successfully. - * Keep track of whether we can setup a fast interrupt - * handler ourselves. */ - intr = INTR_MPSAFE; for (c = 0; c < cl->cl_channels; c++) { ch = &sc->sc_chan[c]; for (mode = 0; mode < SCC_NMODES; mode++) { @@ -216,39 +288,9 @@ if (error) continue; m->m_attached = 1; - if (m->m_hasintr && !m->m_fastintr) - intr = INTR_MPSAFE; } } - /* - * Setup our interrupt handler. Make it FAST if all our - * children are fast as well. Otherwise make it MPSAFE. - */ - if (sc->sc_ires != NULL) { - error = BUS_SETUP_INTR(device_get_parent(dev), dev, - sc->sc_ires, INTR_TYPE_TTY | intr, scc_bfe_intr, - sc, &sc->sc_icookie); - if (error && intr == INTR_FAST) { - intr = INTR_MPSAFE; - error = BUS_SETUP_INTR(device_get_parent(dev), dev, - sc->sc_ires, INTR_TYPE_TTY | intr, scc_bfe_intr, - sc, &sc->sc_icookie); - } - if (error) { - device_printf(dev, "could not activate interrupt\n"); - bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irid, - sc->sc_ires); - sc->sc_ires = NULL; - } - else - sc->sc_fastintr = (intr == INTR_FAST) ? 1 : 0; - } - if (sc->sc_ires == NULL) { - /* XXX no interrupt resource. Force polled mode. */ - sc->sc_polled = 1; - } - if (bootverbose && (sc->sc_fastintr || sc->sc_polled)) { sep = ""; device_print_prettyname(dev); @@ -263,9 +305,6 @@ printf("\n"); } - sc->sc_leaving = 0; - scc_bfe_intr(sc); - return (0); fail: @@ -451,6 +490,8 @@ void (*ihand)(void *), void *arg, void **cookiep) { struct scc_mode *m; + struct scc_softc *sc; + int i, isrc; if (device_get_parent(child) != dev) return (EINVAL); @@ -459,17 +500,30 @@ if ((flags & (INTR_FAST|INTR_MPSAFE)) == 0) return (EINVAL); + sc = device_get_softc(dev); + if (sc->sc_polled) + return (ENXIO); + + if (sc->sc_fastintr && !(flags & INTR_FAST)) { + sc->sc_fastintr = 0; + bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie); + bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_TTY | INTR_MPSAFE, + scc_bfe_intr, sc, &sc->sc_icookie); + } + m = device_get_ivars(child); m->m_hasintr = 1; m->m_fastintr = (flags & INTR_FAST) ? 1 : 0; m->ih = ihand; m->ih_arg = arg; - m->ih_break = SERDEV_IHAND(child, SER_INT_BREAK); - m->ih_overrun = SERDEV_IHAND(child, SER_INT_OVERRUN); - m->ih_rxready = SERDEV_IHAND(child, SER_INT_RXREADY); - m->ih_sigchg = SERDEV_IHAND(child, SER_INT_SIGCHG); - m->ih_txidle = SERDEV_IHAND(child, SER_INT_TXIDLE); + i = 0, isrc = SER_INT_OVERRUN; + while (i < SCC_ISRCCNT) { + m->ih_src[i] = SERDEV_IHAND(child, isrc); + if (m->ih_src[i] != NULL) + m->ih = NULL; + i++, isrc <<= 1; + } return (0); } @@ -478,6 +532,7 @@ void *cookie) { struct scc_mode *m; + int i; if (device_get_parent(child) != dev) return (EINVAL); @@ -490,10 +545,7 @@ m->m_fastintr = 0; m->ih = NULL; m->ih_arg = NULL; - m->ih_break = NULL; - m->ih_overrun = NULL; - m->ih_rxready = NULL; - m->ih_sigchg = NULL; - m->ih_txidle = NULL; + for (i = 0; i < SCC_ISRCCNT; i++) + m->ih_src[i] = NULL; return (0); } ==== //depot/projects/uart/dev/scc/scc_dev_sab82532.c#5 (text+ko) ==== @@ -45,11 +45,13 @@ #define DEFAULT_RCLK 29491200 static int sab82532_bfe_attach(struct scc_softc *, int); +static int sab82532_bfe_iclear(struct scc_softc *, struct scc_chan *); static int sab82532_bfe_ipend(struct scc_softc *); static int sab82532_bfe_probe(struct scc_softc *); static kobj_method_t sab82532_methods[] = { KOBJMETHOD(scc_attach, sab82532_bfe_attach), + KOBJMETHOD(scc_iclear, sab82532_bfe_iclear), KOBJMETHOD(scc_ipend, sab82532_bfe_ipend), KOBJMETHOD(scc_probe, sab82532_bfe_probe), { 0, 0 } @@ -77,37 +79,51 @@ } static int +sab82532_bfe_iclear(struct scc_softc *sc, struct scc_chan *ch) +{ + + return (0); +} + +static int sab82532_bfe_ipend(struct scc_softc *sc) { struct scc_bas *bas; + struct scc_chan *ch; int ipend; + int c, ofs; uint8_t isr0, isr1; bas = &sc->sc_bas; - mtx_lock_spin(&sc->sc_hwmtx); - isr0 = scc_getreg(bas, SAB_ISR0); - isr1 = scc_getreg(bas, SAB_ISR1); - scc_barrier(bas); - if (isr0 & SAB_ISR0_TIME) { - while (scc_getreg(bas, SAB_STAR) & SAB_STAR_CEC) - ; - scc_setreg(bas, SAB_CMDR, SAB_CMDR_RFRD); + ipend = 0; + for (c = 0; c < SAB_NCHAN; c++) { + ch = &sc->sc_chan[c]; + ofs = c * SAB_CHANLEN; + mtx_lock_spin(&sc->sc_hwmtx); + isr0 = scc_getreg(bas, ofs + SAB_ISR0); + isr1 = scc_getreg(bas, ofs + SAB_ISR1); scc_barrier(bas); + if (isr0 & SAB_ISR0_TIME) { + while (scc_getreg(bas, ofs + SAB_STAR) & SAB_STAR_CEC) + ; + scc_setreg(bas, ofs + SAB_CMDR, SAB_CMDR_RFRD); + scc_barrier(bas); + } + mtx_unlock_spin(&sc->sc_hwmtx); + + ch->ch_ipend = 0; + if (isr1 & SAB_ISR1_BRKT) + ch->ch_ipend |= SER_INT_BREAK; + if (isr0 & SAB_ISR0_RFO) + ch->ch_ipend |= SER_INT_OVERRUN; + if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF)) + ch->ch_ipend |= SER_INT_RXREADY; + if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC)) + ch->ch_ipend |= SER_INT_SIGCHG; + if (isr1 & SAB_ISR1_ALLS) + ch->ch_ipend |= SER_INT_TXIDLE; + ipend |= ch->ch_ipend; } - mtx_unlock_spin(&sc->sc_hwmtx); - - ipend = 0; - if (isr1 & SAB_ISR1_BRKT) - ipend |= SER_INT_BREAK; - if (isr0 & SAB_ISR0_RFO) - ipend |= SER_INT_OVERRUN; - if (isr0 & (SAB_ISR0_TCD|SAB_ISR0_RPF)) - ipend |= SER_INT_RXREADY; - if ((isr0 & SAB_ISR0_CDSC) || (isr1 & SAB_ISR1_CSC)) - ipend |= SER_INT_SIGCHG; - if (isr1 & SAB_ISR1_ALLS) - ipend |= SER_INT_TXIDLE; - return (ipend); } ==== //depot/projects/uart/dev/scc/scc_dev_z8530.c#5 (text+ko) ==== @@ -45,11 +45,13 @@ #define DEFAULT_RCLK 307200 static int z8530_bfe_attach(struct scc_softc *, int); +static int z8530_bfe_iclear(struct scc_softc *, struct scc_chan *); static int z8530_bfe_ipend(struct scc_softc *); static int z8530_bfe_probe(struct scc_softc *); static kobj_method_t z8530_methods[] = { KOBJMETHOD(scc_attach, z8530_bfe_attach), + KOBJMETHOD(scc_iclear, z8530_bfe_iclear), KOBJMETHOD(scc_ipend, z8530_bfe_ipend), KOBJMETHOD(scc_probe, z8530_bfe_probe), { 0, 0 } @@ -96,12 +98,98 @@ } static int +z8530_bfe_iclear(struct scc_softc *sc, struct scc_chan *ch) +{ + struct scc_bas *bas; + int c; + + bas = &sc->sc_bas; + c = (ch->ch_nr == 1) ? CHAN_A : CHAN_B; + mtx_lock_spin(&sc->sc_hwmtx); + if (ch->ch_ipend & SER_INT_TXIDLE) { + scc_setreg(bas, c + REG_CTRL, CR_RSTTXI); + scc_barrier(bas); + } + if (ch->ch_ipend & SER_INT_RXREADY) { + scc_getreg(bas, c + REG_DATA); + scc_barrier(bas); + } + if (ch->ch_ipend & SER_INT_SIGCHG) { + scc_setreg(bas, c + REG_CTRL, CR_RSTXSI); + scc_barrier(bas); + } + if (ch->ch_ipend & (SER_INT_OVERRUN|SER_INT_BREAK)) + scc_setreg(bas, c + REG_CTRL, CR_RSTERR); + mtx_unlock_spin(&sc->sc_hwmtx); + return (0); +} + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int z8530_bfe_ipend(struct scc_softc *sc) { struct scc_bas *bas; + struct scc_chan *ch[2]; + uint32_t sig; + uint8_t bes, ip, src; bas = &sc->sc_bas; - return (0); + ch[0] = &sc->sc_chan[0]; + ch[1] = &sc->sc_chan[1]; + ch[0]->ch_ipend = 0; + ch[1]->ch_ipend = 0; + + mtx_lock_spin(&sc->sc_hwmtx); + ip = scc_getmreg(bas, CHAN_A, RR_IP); + if (ip & IP_RIA) + ch[0]->ch_ipend |= SER_INT_RXREADY; + if (ip & IP_RIB) + ch[1]->ch_ipend |= SER_INT_RXREADY; + if (ip & IP_TIA) + ch[0]->ch_ipend |= SER_INT_TXIDLE; + if (ip & IP_TIB) + ch[1]->ch_ipend |= SER_INT_TXIDLE; + if (ip & IP_SIA) { + bes = scc_getreg(bas, CHAN_A + RR_BES); + if (bes & BES_BRK) + ch[0]->ch_ipend |= SER_INT_BREAK; + sig = ch[0]->ch_hwsig; + SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS); + SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD); + SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR); + if (sig & SER_MASK_DELTA) { + ch[0]->ch_hwsig = sig; + ch[0]->ch_ipend |= SER_INT_SIGCHG; + } + src = scc_getmreg(bas, CHAN_A, RR_SRC); + if (src & SRC_OVR) + ch[0]->ch_ipend |= SER_INT_OVERRUN; + } + if (ip & IP_SIB) { + bes = scc_getreg(bas, CHAN_B + RR_BES); + if (bes & BES_BRK) + ch[1]->ch_ipend |= SER_INT_BREAK; + sig = ch[1]->ch_hwsig; + SIGCHG(bes & BES_CTS, sig, SER_CTS, SER_DCTS); + SIGCHG(bes & BES_DCD, sig, SER_DCD, SER_DDCD); + SIGCHG(bes & BES_SYNC, sig, SER_DSR, SER_DDSR); + if (sig & SER_MASK_DELTA) { + ch[1]->ch_hwsig = sig; + ch[1]->ch_ipend |= SER_INT_SIGCHG; + } + src = scc_getmreg(bas, CHAN_B, RR_SRC); + if (src & SRC_OVR) + ch[1]->ch_ipend |= SER_INT_OVERRUN; + } + mtx_unlock_spin(&sc->sc_hwmtx); + + return (ch[0]->ch_ipend | ch[1]->ch_ipend); } static int ==== //depot/projects/uart/dev/scc/scc_if.m#4 (text+ko) ==== @@ -49,6 +49,11 @@ int reset; }; +METHOD void iclear { + struct scc_softc *this; + struct scc_chan *chan; +}; + # ipend() - query SCC for pending interrupts. # When an interrupt is signalled, the handler will call this method to find # out which of the interrupt sources needs attention. The handler will use
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200603281954.k2SJsB7q056281>