adlink.c revision 166901
117651Speter/*-
2250261Sdelphij * Copyright (c) 2003-2004 Poul-Henning Kamp
3131380Stjr * All rights reserved.
417651Speter *
517651Speter * Redistribution and use in source and binary forms, with or without
617651Speter * modification, are permitted provided that the following conditions
717651Speter * are met:
817651Speter * 1. Redistributions of source code must retain the above copyright
9131380Stjr *    notice, this list of conditions and the following disclaimer.
1042468Speter * 2. Redistributions in binary form must reproduce the above copyright
1133904Ssteve *    notice, this list of conditions and the following disclaimer in the
12250261Sdelphij *    documentation and/or other materials provided with the distribution.
1317651Speter * 3. The names of the authors may not be used to endorse or promote
1417651Speter *    products derived from this software without specific prior written
1517651Speter *    permission.
1617651Speter *
1717651Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1817651Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1917651Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20131380Stjr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21131380Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22131380Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23131380Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24131380Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25131380Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26131380Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27131380Stjr * SUCH DAMAGE.
28131380Stjr *
29131380Stjr * This is a device driver or the Adlink 9812 and 9810 ADC cards, mainly
30131380Stjr * intended to support Software Defined Radio reception of timesignals
31131380Stjr * in the VLF band.  See http://phk.freebsd.dk/loran-c
32206924Sdelphij *
33131380Stjr * The driver is configured with ioctls which define a ringbuffer with
34131380Stjr * a given number of chunks in it.  The a control structure and the
35131380Stjr * ringbuffer can then be mmap(2)'ed into userland and the application
36131380Stjr * can operate on the data directly.
37131380Stjr *
38131380Stjr * Tested with 10MHz external clock, divisor of 2 (ie: 5MHz sampling),
39131380Stjr * One channel active (ie: 2 bytes per sample = 10MB/sec) on a 660MHz
40131380Stjr * Celeron PC.
41131380Stjr *
42131380Stjr */
43131380Stjr
44131380Stjr#ifdef _KERNEL
45131380Stjr#include <sys/cdefs.h>
46131380Stjr__FBSDID("$FreeBSD: head/sys/dev/adlink/adlink.c 166901 2007-02-23 12:19:07Z piso $");
47131380Stjr
48131380Stjr#include <sys/param.h>
49131380Stjr#include <sys/systm.h>
50131380Stjr#include <sys/malloc.h>
51131380Stjr#include <sys/kernel.h>
52131380Stjr#include <sys/module.h>
53205471Sdelphij#include <sys/kthread.h>
54131380Stjr#include <sys/conf.h>
55131380Stjr#include <sys/bus.h>
56131380Stjr#include <machine/bus.h>
57131380Stjr#include <machine/resource.h>
58131380Stjr#include <sys/rman.h>
59131380Stjr#include <dev/pci/pcireg.h>
60131380Stjr#include <dev/pci/pcivar.h>
6117651Speter#include <pci_if.h>
6217651Speter#include <vm/vm.h>
63131380Stjr#include <vm/pmap.h>
64131380Stjr
65250261Sdelphij#endif /* _KERNEL */
66131380Stjr
6717651Speter#include <sys/ioccom.h>
6817651Speter
69131380Stjr#define ADLINK_SETDIVISOR	_IOWR('A', 255, u_int)	/* divisor */
70131380Stjr#define ADLINK_SETCHUNKSIZE	_IOWR('A', 254, u_int)	/* bytes */
71131380Stjr#define ADLINK_SETRINGSIZE	_IOWR('A', 253, u_int)	/* bytes */
72131380Stjr#define ADLINK_START		_IOWR('A', 252, u_int)	/* dummy */
73131380Stjr#define ADLINK_STOP		_IOWR('A', 251, u_int)	/* dummy */
7417651Speter#define ADLINK_RESET		_IOWR('A', 250, u_int)	/* dummy */
75131380Stjr
76131380Stjrstruct page0 {
77131380Stjr	u_int			version;
78131380Stjr	int			state;
79131380Stjr#	  define STATE_RESET	-1
80131380Stjr#	  define STATE_RUN	0
81131380Stjr	u_int			divisor;	/* int */
82131380Stjr	u_int			chunksize;	/* bytes */
83131380Stjr	u_int			ringsize;	/* chunks */
84131380Stjr	u_int			o_sample;	/*
85131380Stjr						 * offset of ring generation
86131380Stjr						 * array
8717651Speter						 */
88131380Stjr	u_int			o_ring;		/* offset of ring */
89131380Stjr};
90131380Stjr
91131380Stjr#define PAGE0VERSION	20050219
9217651Speter
93131380Stjr#ifdef _KERNEL
94131380Stjr
95131380Stjrstruct pgstat {
96131380Stjr	uint64_t		*sample;
97131380Stjr	vm_paddr_t		phys;
9817651Speter	void			*virt;
99131380Stjr	struct pgstat		*next;
100131380Stjr};
101131380Stjr
102131380Stjrstruct softc {
103131380Stjr	device_t		device;
104131380Stjr	void			*intrhand;
10517651Speter	struct resource		*res[3];
106131380Stjr	struct cdev		*dev;
107131380Stjr	off_t			mapvir;
108131380Stjr	int			error;
109131380Stjr	struct page0		*p0;
110131380Stjr	u_int			nchunks;
11117651Speter	struct pgstat		*chunks;
112131380Stjr	struct pgstat		*next;
113131380Stjr	uint64_t		sample;
114131380Stjr};
115131380Stjr
116131380Stjrstatic d_ioctl_t adlink_ioctl;
117146081Skientzlestatic d_mmap_t	adlink_mmap;
118205471Sdelphijstatic int adlink_intr(void *arg);
119205471Sdelphij
120205471Sdelphijstatic struct cdevsw adlink_cdevsw = {
121205471Sdelphij	.d_version =	D_VERSION,
122205471Sdelphij	.d_ioctl =	adlink_ioctl,
123146081Skientzle	.d_mmap =	adlink_mmap,
124146081Skientzle	.d_name =	"adlink",
125146081Skientzle};
126205471Sdelphij
127131380Stjrstatic int
128131380Stjradlink_intr(void *arg)
12917651Speter{
130131380Stjr	struct softc *sc;
131131380Stjr	struct pgstat *pg;
132131380Stjr	uint32_t u;
133131380Stjr
134131380Stjr	sc = arg;
135131380Stjr	u = bus_read_4(sc->res[0], 0x38);
136131380Stjr	if (!(u & 0x00800000))
137147790Scperciva		return; // XXX - FILTER_STRAY?
138131380Stjr	bus_write_4(sc->res[0], 0x38, u | 0x003f4000);
13917651Speter
140131380Stjr	sc->sample += sc->p0->chunksize / 2;
141131380Stjr	pg = sc->next;
142131380Stjr	*(pg->sample) = sc->sample;
143131380Stjr
14417651Speter	u = bus_read_4(sc->res[1], 0x18);
145131380Stjr	if (u & 1)
146131380Stjr		sc->p0->state = EIO;
147131380Stjr
14817651Speter	if (sc->p0->state != STATE_RUN) {
149131380Stjr		printf("adlink: stopping %d\n", sc->p0->state);
150131380Stjr		return; // XXX - FILTER_STRAY?
151131380Stjr	}
152131380Stjr
153131380Stjr	pg = pg->next;
154131380Stjr	sc->next = pg;
155131380Stjr	*(pg->sample) = 0;
15617651Speter	bus_write_4(sc->res[0], 0x24, pg->phys);
157131380Stjr	bus_write_4(sc->res[0], 0x28, sc->p0->chunksize);
158131380Stjr	wakeup(sc);
159131380Stjr	return (FILTER_HANDLED);
160131380Stjr}
161131380Stjr
16217651Speterstatic int
163131380Stjradlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
164131380Stjr{
165131380Stjr	struct softc *sc;
166131380Stjr	vm_offset_t o;
16717651Speter	int i;
168131380Stjr	struct pgstat *pg;
169205471Sdelphij
170205471Sdelphij	sc = dev->si_drv1;
171205471Sdelphij	if (nprot != VM_PROT_READ)
172205471Sdelphij		return (-1);
17317651Speter	if (offset == 0) {
174131380Stjr		*paddr = vtophys(sc->p0);
175131380Stjr		return (0);
176131380Stjr	}
177131380Stjr	o = PAGE_SIZE;
178131380Stjr	pg = sc->chunks;
17917651Speter	for (i = 0; i < sc->nchunks; i++, pg++) {
180131380Stjr		if (offset - o >= sc->p0->chunksize) {
181131380Stjr			o += sc->p0->chunksize;
182131380Stjr			continue;
183131380Stjr		}
184131380Stjr		*paddr = pg->phys + (offset - o);
185131380Stjr		return (0);
186131380Stjr	}
187131380Stjr	return (-1);
188131380Stjr}
189131380Stjr
190131380Stjrstatic int
191131380Stjradlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
192131380Stjr{
193131380Stjr	struct softc *sc;
194131380Stjr	int i, error;
195131380Stjr	u_int u;
196131380Stjr	struct pgstat *pg;
197131380Stjr	uint64_t *sample;
19817651Speter
199131380Stjr	sc = dev->si_drv1;
200131380Stjr	u = *(u_int*)data;
201131380Stjr	error = 0;
202131380Stjr	switch (cmd) {
203131380Stjr	case ADLINK_SETDIVISOR:
204131380Stjr		if (sc->p0->state == STATE_RUN)
205131380Stjr			return (EBUSY);
206131380Stjr		if (u & 1)
207131380Stjr			return (EINVAL);
208131380Stjr		sc->p0->divisor = u;
20917651Speter		break;
210131380Stjr	case ADLINK_SETCHUNKSIZE:
211250261Sdelphij		if (sc->p0->state != STATE_RESET)
212250261Sdelphij			return (EBUSY);
213131380Stjr		if (u % PAGE_SIZE)
21417651Speter			return (EINVAL);
215131380Stjr		if (sc->p0->ringsize != 0 && sc->p0->ringsize % u)
216131380Stjr			return (EINVAL);
217131380Stjr		sc->p0->chunksize = u;
218205471Sdelphij		break;
219131380Stjr	case ADLINK_SETRINGSIZE:
220205471Sdelphij		if (sc->p0->state != STATE_RESET)
221205471Sdelphij			return (EBUSY);
22217651Speter		if (u % PAGE_SIZE)
223131380Stjr			return (EINVAL);
224205471Sdelphij		if (sc->p0->chunksize != 0 && u % sc->p0->chunksize)
225205471Sdelphij			return (EINVAL);
226131380Stjr		sc->p0->ringsize = u;
227131380Stjr		break;
228205471Sdelphij	case ADLINK_START:
229205471Sdelphij		if (sc->p0->state == STATE_RUN)
230131380Stjr			return (EBUSY);
23117651Speter		if (sc->p0->state == STATE_RESET) {
232131380Stjr
233131380Stjr			if (sc->p0->chunksize == 0)
234131380Stjr				sc->p0->chunksize = 4 * PAGE_SIZE;
235157046Sdes			if (sc->p0->ringsize == 0)
236131380Stjr				sc->p0->ringsize = 16 * sc->p0->chunksize;
237131380Stjr			if (sc->p0->divisor == 0)
238205471Sdelphij				sc->p0->divisor = 4;
239131380Stjr
24017651Speter			sc->nchunks = sc->p0->ringsize / sc->p0->chunksize;
241131380Stjr			if (sc->nchunks * sizeof (*pg->sample) +
242131380Stjr			    sizeof *sc->p0 > PAGE_SIZE)
243131380Stjr				return (EINVAL);
244131380Stjr			sc->p0->o_ring = PAGE_SIZE;
245131380Stjr			sample = (uint64_t *)(sc->p0 + 1);
246131380Stjr			sc->p0->o_sample =
247131380Stjr			    (uintptr_t)sample - (uintptr_t)(sc->p0);
24817651Speter			pg = malloc(sizeof *pg * sc->nchunks,
24942468Speter			    M_DEVBUF, M_WAITOK | M_ZERO);
250131380Stjr			sc->chunks = pg;
25117651Speter			for (i = 0; i < sc->nchunks; i++) {
252131380Stjr				pg->sample = sample;
253131380Stjr				*pg->sample = 0;
254131380Stjr				sample++;
255131380Stjr				pg->virt = contigmalloc(sc->p0->chunksize,
256131380Stjr				    M_DEVBUF, M_WAITOK,
257131380Stjr				    0ul, 0xfffffffful,
25817651Speter				    PAGE_SIZE, 0);
259131380Stjr				pg->phys = vtophys(pg->virt);
260131380Stjr				if (i == sc->nchunks - 1)
261131380Stjr					pg->next = sc->chunks;
262131380Stjr				else
263131380Stjr					pg->next = pg + 1;
26417651Speter				pg++;
265131380Stjr			}
266157046Sdes			sc->next = sc->chunks;
26717651Speter		}
268131380Stjr
269131380Stjr		/* Reset generation numbers */
270131380Stjr		pg = sc->chunks;
271131380Stjr		for (i = 0; i < sc->nchunks; i++) {
272131380Stjr			*pg->sample = 0;
273131380Stjr			pg++;
274131380Stjr		}
275131380Stjr
276131380Stjr		/* Enable interrupts on write complete */
27717651Speter		bus_write_4(sc->res[0], 0x38, 0x00004000);
278131380Stjr
279131380Stjr		/* Sample CH0 only */
280250261Sdelphij		bus_write_4(sc->res[1], 0x00, 1);
281250261Sdelphij
282131380Stjr		/* Divide clock by four */
28317651Speter		bus_write_4(sc->res[1], 0x04, sc->p0->divisor);
284131380Stjr
285131380Stjr		/* Software trigger mode: software */
286131380Stjr		bus_write_4(sc->res[1], 0x08, 0);
287131380Stjr
288131380Stjr		/* Trigger level zero */
289131380Stjr		bus_write_4(sc->res[1], 0x0c, 0);
29017651Speter
29117651Speter		/* Trigger source CH0 (not used) */
292237410Sdelphij		bus_write_4(sc->res[1], 0x10, 0);
293237410Sdelphij
294237410Sdelphij		/* Fifo control/status: flush */
295237410Sdelphij		bus_write_4(sc->res[1], 0x18, 3);
296237410Sdelphij
297237410Sdelphij		/* Clock source: external sine */
298237410Sdelphij		bus_write_4(sc->res[1], 0x20, 2);
299237410Sdelphij
30033904Ssteve		/* Chipmunks are go! */
30117651Speter		sc->p0->state = STATE_RUN;
302131380Stjr
303131380Stjr		/* Set up Write DMA */
304131380Stjr		pg = sc->next = sc->chunks;
305131380Stjr		*(pg->sample) = 0;
30617651Speter		bus_write_4(sc->res[0], 0x24, pg->phys);
307		bus_write_4(sc->res[0], 0x28, sc->p0->chunksize);
308		u = bus_read_4(sc->res[0], 0x3c);
309		bus_write_4(sc->res[0], 0x3c, u | 0x00000600);
310
311		/* Acquisition Enable Register: go! */
312		bus_write_4(sc->res[1], 0x1c, 1);
313
314		break;
315	case ADLINK_STOP:
316		if (sc->p0->state == STATE_RESET)
317			break;
318		sc->p0->state = EINTR;
319		while (*(sc->next->sample) == 0)
320			tsleep(sc, PUSER | PCATCH, "adstop", 1);
321		break;
322#ifdef notyet
323	/*
324	 * I'm not sure we can actually do this.  How do we revoke
325	 * the mmap'ed pages from any process having them mmapped ?
326	 */
327	case ADLINK_RESET:
328		if (sc->p0->state == STATE_RESET)
329			break;
330		sc->p0->state = EINTR;
331		while (*(sc->next->samp) == 0)
332			tsleep(sc, PUSER | PCATCH, "adreset", 1);
333		/* deallocate ring buffer */
334		break;
335#endif
336	default:
337		error = ENOIOCTL;
338		break;
339	}
340	return (error);
341}
342
343static devclass_t adlink_devclass;
344
345static int
346adlink_probe(device_t self)
347{
348
349	if (pci_get_devid(self) != 0x80da10e8)
350		return (ENXIO);
351	device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps");
352	return (BUS_PROBE_DEFAULT);
353}
354
355static struct resource_spec adlink_res_spec[] = {
356	{ SYS_RES_IOPORT,	PCIR_BAR(0),	RF_ACTIVE},
357	{ SYS_RES_IOPORT,	PCIR_BAR(1),	RF_ACTIVE},
358	{ SYS_RES_IRQ,		0,		RF_ACTIVE | RF_SHAREABLE},
359	{ -1, 0, 0 }
360};
361
362static int
363adlink_attach(device_t self)
364{
365	struct softc *sc;
366	int i, error;
367
368	sc = device_get_softc(self);
369	bzero(sc, sizeof *sc);
370	sc->device = self;
371
372	error = bus_alloc_resources(self, adlink_res_spec, sc->res);
373	if (error)
374		return (error);
375
376	/* XXX why do we need INTR_MPSAFE if INTR_FAST was declared too?!?!? */
377	i = bus_setup_intr(self, sc->res[2],
378	    INTR_MPSAFE | INTR_TYPE_MISC,
379	    adlink_intr, NULL, sc, &sc->intrhand);
380	if (i) {
381		printf("adlink: Couldn't get FAST intr\n");
382		i = bus_setup_intr(self, sc->res[2],
383		    INTR_MPSAFE | INTR_TYPE_MISC,
384		    NULL, (driver_intr_t *)adlink_intr, sc, &sc->intrhand);
385	}
386
387	if (i) {
388		bus_release_resources(self, adlink_res_spec, sc->res);
389		return (ENODEV);
390	}
391
392	sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
393	sc->p0->version = PAGE0VERSION;
394	sc->p0->state = STATE_RESET;
395
396	sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self),
397	    UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self));
398	sc->dev->si_drv1 = sc;
399
400	return (0);
401}
402
403static device_method_t adlink_methods[] = {
404	/* Device interface */
405	DEVMETHOD(device_probe,		adlink_probe),
406	DEVMETHOD(device_attach,	adlink_attach),
407	DEVMETHOD(device_suspend,	bus_generic_suspend),
408	DEVMETHOD(device_resume,	bus_generic_resume),
409	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
410	{0, 0}
411};
412
413static driver_t adlink_driver = {
414	"adlink",
415	adlink_methods,
416	sizeof(struct softc)
417};
418
419DRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0);
420
421#endif /* _KERNEL */
422