summaryrefslogtreecommitdiffstats
path: root/sys/dev/adlink/adlink.c
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2003-04-08 19:12:48 +0000
committerphk <phk@FreeBSD.org>2003-04-08 19:12:48 +0000
commit7611c6c35178338b16bf2c21cdf3c6eb99ebe843 (patch)
treed61ae4e76bb0c5f0efd68b79255d318fc7705584 /sys/dev/adlink/adlink.c
parentf8e2ba2ce685d4998d1f1f7b216262fbbd536fb2 (diff)
downloadFreeBSD-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/dev/adlink/adlink.c')
-rw-r--r--sys/dev/adlink/adlink.c299
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 */
OpenPOWER on IntegriCloud