diff options
author | phk <phk@FreeBSD.org> | 2003-04-08 19:12:48 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2003-04-08 19:12:48 +0000 |
commit | 7611c6c35178338b16bf2c21cdf3c6eb99ebe843 (patch) | |
tree | d61ae4e76bb0c5f0efd68b79255d318fc7705584 /sys | |
parent | f8e2ba2ce685d4998d1f1f7b216262fbbd536fb2 (diff) | |
download | FreeBSD-src-7611c6c35178338b16bf2c21cdf3c6eb99ebe843.zip FreeBSD-src-7611c6c35178338b16bf2c21cdf3c6eb99ebe843.tar.gz |
Almost the finished article.
Boost sample rate to 1.25 MSPS since that allows us to use a 5Mhz
(/4) or 10Mhz (/8) external clock.
Make the interrupt both MPSAFE and FAST, at 610 interrupts a second,
and a max time to service of 5 msec, we brake for nobody.
Use kernel thread to accumulate into the 25 possible wave signals.
Use #ifdef _KERNEL to let .c file double as .h file defining the ioctls.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/adlink/adlink.c | 299 |
1 files changed, 276 insertions, 23 deletions
diff --git a/sys/dev/adlink/adlink.c b/sys/dev/adlink/adlink.c index aca4cca..2e2d2d4 100644 --- a/sys/dev/adlink/adlink.c +++ b/sys/dev/adlink/adlink.c @@ -29,10 +29,12 @@ * $FreeBSD$ */ +#ifdef _KERNEL #include <sys/param.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <sys/conf.h> #include <sys/bus.h> #include <machine/bus.h> @@ -44,17 +46,54 @@ #include <vm/vm.h> #include <vm/pmap.h> +#endif /* _KERNEL */ + +#include <sys/ioccom.h> + +struct wave { + int index; + int period; + int offset; + int length; + int avg; + off_t mapvir; + int flags; + + int npages; + void **virtual; +}; + +#define ADLINK_SETWAVE _IOWR('A', 232, struct wave) +#define ADLINK_GETWAVE _IOWR('A', 233, struct wave) + +#ifdef _KERNEL + +#define INTPERPAGE (PAGE_SIZE / sizeof(int)) +#define I16PERPAGE (PAGE_SIZE / sizeof(int16_t)) + +/* + * Sample rate + */ +#define SPS 1250000 + /* - * We sample one channel (= 16 bits) at 1 msps giving 2Mbyte/sec, - * 50 pages will give us about 1/10 second buffering. + * We sample one channel (= 16 bits) at 1.25 msps giving 2.5Mbyte/sec, + * 100 pages will give us about 1/6 second buffering. */ -#define NRING 50 +#define NRING 100 -#define IN4(sc, offset) bus_space_read_4(sc->t_io, sc->h_io, offset) +/* + * How many waves are we willing to entertain + */ +#define NWAVE 25 struct info { int nring; off_t o_ring; + + int ngri; + int ppgri; + off_t o_gri; }; struct softc { @@ -64,21 +103,145 @@ struct softc { bus_space_tag_t t0, t1; bus_space_handle_t h0, h1; dev_t dev; + off_t mapvir; + + struct proc *procp; struct info *info; + struct wave *wave[NWAVE]; + int idx; void *ring[NRING]; - vm_paddr_t phys[NRING]; + vm_paddr_t pring[NRING]; int stat[NRING]; + + uint64_t cnt; + + u_char flags[I16PERPAGE]; }; +static void +adlink_wave(struct softc *sc, struct wave *wp, int16_t *sp) +{ + int f, i, k, m, *ip; + + f = 0; + for (i = 0; i < I16PERPAGE; ) { + k = (sc->cnt - wp->offset + i) % wp->period; + if (k >= wp->length) { + i += wp->period - k; + sp += wp->period - k; + continue; + } + m = k % INTPERPAGE; + ip = (int *)(wp->virtual[k / INTPERPAGE]) + m; + while (m < INTPERPAGE && i < I16PERPAGE && k < wp->length) { + if (sc->flags[i] >= wp->index) + *ip += (*sp * 8 - *ip) >> wp->avg; + if (wp->flags & 1) + sc->flags[i] = wp->index; + sp++; + ip++; + m++; + i++; + k++; + } + } +} + +static void +adlink_tickle(struct softc *sc) +{ + + wakeup(sc); + tsleep(&sc->ring, PUSER | PCATCH, "tickle", 1); +} + +static int +adlink_new_wave(struct softc *sc, int index, int period, int offset, int length, int avg, int flags) +{ + struct wave *wp; + int l, i; + void **oldvir, **newvir; + + if (index < 0 || index >= NWAVE) + return (EINVAL); + wp = sc->wave[index]; + if (wp == NULL) { + adlink_tickle(sc); + wp = malloc(sizeof *wp, M_DEVBUF, M_WAITOK | M_ZERO); + } + l = howmany(length, INTPERPAGE); + /* Setting a high average here to neuter the realtime bits */ + wp->avg = 31; + if (wp->npages < l) { + oldvir = wp->virtual; + adlink_tickle(sc); + newvir = malloc(sizeof(void *) * l, M_DEVBUF, M_WAITOK | M_ZERO); + if (wp->npages > 0) { + adlink_tickle(sc); + bcopy(oldvir, newvir, wp->npages * sizeof(void *)); + } + for (i = wp->npages; i < l; i++) { + adlink_tickle(sc); + newvir[i] = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK); + } + wp->virtual = newvir; + wp->npages = l; + wp->mapvir = sc->mapvir; + sc->mapvir += l * PAGE_SIZE; + } else { + oldvir = NULL; + } + wp->index = index; + wp->period = period; + wp->offset = offset; + wp->length = length; + wp->flags = flags; + + for (i = 0; i < l; i++) { + adlink_tickle(sc); + bzero(wp->virtual[i], PAGE_SIZE); + } + wp->avg = avg; + sc->wave[index] = wp; + printf("Wave[%d] {period %d, offset %d, length %d, avg %d, flags %x}\n", + wp->index, wp->period, wp->offset, wp->length, wp->avg, wp->flags); + free(oldvir, M_DEVBUF); + return (0); +} + +static void +adlink_loran(void *arg) +{ + struct softc *sc; + int idx, i; + + sc = arg; + idx = 0; + for (;;) { + while (sc->stat[idx] == 0) + msleep(sc, NULL, PRIBIO, "loran", 1); + memset(sc->flags, NWAVE, sizeof sc->flags); + for (i = 0; i < NWAVE; i++) { + if (sc->wave[i] != NULL) + adlink_wave(sc, sc->wave[i], sc->ring[idx]); + } + sc->cnt += I16PERPAGE; + sc->stat[idx] = 0; + idx++; + idx %= NRING; + } + kthread_exit(0); +} + static int adlink_open(dev_t dev, int oflags, int devtype, struct thread *td) { static int once; struct softc *sc; - int i; + int i, error; uint32_t u; if (once) @@ -88,49 +251,114 @@ adlink_open(dev_t dev, int oflags, int devtype, struct thread *td) sc = dev->si_drv1; sc->info = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK); sc->info->nring = NRING; + sc->info->o_ring = PAGE_SIZE; for (i = 0; i < NRING; i++) { sc->ring[i] = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK); - sc->phys[i] = vtophys(sc->ring[i]); + sc->pring[i] = vtophys(sc->ring[i]); } + error = adlink_new_wave(sc, NWAVE - 1, SPS, 0, SPS, 7, 0); + if (error) + return (error); + + error = kthread_create(adlink_loran, sc, &sc->procp, + 0, 0, "adlink%d", device_get_unit(sc->device)); + if (error) + return (error); + + /* Enable interrupts on write complete */ bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000); + + /* Sample CH0 only */ bus_space_write_4(sc->t1, sc->h1, 0x00, 1); - bus_space_write_4(sc->t1, sc->h1, 0x04, 10); + + /* Divide clock by ten */ + bus_space_write_4(sc->t1, sc->h1, 0x04, 4); + + /* Software trigger mode: software */ bus_space_write_4(sc->t1, sc->h1, 0x08, 0); + + /* Trigger level zero */ bus_space_write_4(sc->t1, sc->h1, 0x0c, 0); + + /* Trigger source CH0 (not used) */ bus_space_write_4(sc->t1, sc->h1, 0x10, 0); + + /* Fifo control/status: flush */ bus_space_write_4(sc->t1, sc->h1, 0x18, 3); + + /* Clock source: external sine */ bus_space_write_4(sc->t1, sc->h1, 0x20, 2); - bus_space_write_4(sc->t0, sc->h0, 0x24, sc->phys[i]); + /* Set up Write DMA */ + bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]); bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE); - u = bus_space_read_4(sc->t0, sc->h0, 0x3c); bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600); + /* Acquisition Enable Register: go! */ bus_space_write_4(sc->t1, sc->h1, 0x1c, 1); return (0); } static int +adlink_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + struct softc *sc; + struct wave *wp; + int i, error; + + sc = dev->si_drv1; + wp = (struct wave *)data; + i = wp->index; + if (i < 0 || i >= NWAVE) + return (EINVAL); + if (cmd == ADLINK_GETWAVE) { + if (sc->wave[i] == NULL) + return (ENOENT); + bcopy(sc->wave[i], wp, sizeof(*wp)); + return (0); + } + if (cmd == ADLINK_SETWAVE) { + error = adlink_new_wave(sc, + i, + wp->period, + wp->offset, + wp->length, + wp->avg, + wp->flags); + if (error) + return (error); + bcopy(sc->wave[i], wp, sizeof(*wp)); + return (0); + } + return (ENOIOCTL); +} + +static int adlink_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { - int i; struct softc *sc; + struct wave *wp; + int i, j; sc = dev->si_drv1; if (nprot != VM_PROT_READ) return (-1); - if (offset == 0) { - *paddr = vtophys(sc->info); + for (i = 0; i < NWAVE; i++) { + if (sc->wave[i] == NULL) + continue; + wp = sc->wave[i]; + if (offset < wp->mapvir) + continue; + j = (offset - wp->mapvir) / PAGE_SIZE; + if (j >= wp->npages) + continue; + *paddr = vtophys(wp->virtual[j]); return (0); } - i = (offset - sc->info->o_ring) / PAGE_SIZE; - if (i >= NRING) - return (-1); - *paddr = vtophys(sc->ring[i]); - return (0); + return (-1); } static void @@ -138,7 +366,7 @@ adlink_intr(void *arg) { struct softc *sc; uint32_t u; - int i; + int i, j; sc = arg; u = bus_space_read_4(sc->t0, sc->h0, 0x38); @@ -146,16 +374,27 @@ adlink_intr(void *arg) return; bus_space_write_4(sc->t0, sc->h0, 0x38, u | 0x003f4000); - sc->stat[sc->idx] = 1; - i = (++sc->idx) % NRING; + j = sc->idx; + sc->stat[j] = 1; + i = (j + 1) % NRING; sc->idx = i; - bus_space_write_4(sc->t0, sc->h0, 0x24, sc->phys[i]); + u = bus_space_read_4(sc->t1, sc->h1, 0x18); + if (u & 1) { + printf("adlink FIFO overrun\n"); + return; + } + bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]); bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE); + wakeup(sc); + if (sc->stat[i]) { + printf("adlink page busy\n"); + } } static struct cdevsw adlink_cdevsw = { .d_open = adlink_open, .d_close = nullclose, + .d_ioctl = adlink_ioctl, .d_mmap = adlink_mmap, .d_name = "adlink", }; @@ -182,6 +421,10 @@ adlink_attach(device_t self) bzero(sc, sizeof *sc); sc->device = self; + /* + * This is the PCI mapped registers of the AMCC 9535 "matchmaker" + * chip. + */ rid = 0x10; sc->r0 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); @@ -191,6 +434,10 @@ adlink_attach(device_t self) sc->h0 = rman_get_bushandle(sc->r0); printf("Res0 %x %x\n", sc->t0, sc->h0); + /* + * This is the PCI mapped registers of the ADC hardware, they + * are described in the manual which comes with the card. + */ rid = 0x14; sc->r1 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); @@ -206,8 +453,13 @@ adlink_attach(device_t self) if (sc->ri == NULL) return (ENODEV); - i = bus_setup_intr(self, sc->ri, INTR_TYPE_MISC, + i = bus_setup_intr(self, sc->ri, INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST, adlink_intr, sc, &sc->intrhand); + if (i) { + printf("adlink: Couldn't get FAST intr\n"); + i = bus_setup_intr(self, sc->ri, INTR_TYPE_MISC, + adlink_intr, sc, &sc->intrhand); + } if (i) return (ENODEV); @@ -236,3 +488,4 @@ static driver_t adlink_driver = { }; DRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); +#endif /* _KERNEL */ |