adlink.c revision 143168
1113094Sphk/*-
2135482Sphk * Copyright (c) 2003-2004 Poul-Henning Kamp
3113094Sphk * All rights reserved.
4113094Sphk *
5113094Sphk * Redistribution and use in source and binary forms, with or without
6113094Sphk * modification, are permitted provided that the following conditions
7113094Sphk * are met:
8113094Sphk * 1. Redistributions of source code must retain the above copyright
9113094Sphk *    notice, this list of conditions and the following disclaimer.
10113094Sphk * 2. Redistributions in binary form must reproduce the above copyright
11113094Sphk *    notice, this list of conditions and the following disclaimer in the
12113094Sphk *    documentation and/or other materials provided with the distribution.
13113094Sphk * 3. The names of the authors may not be used to endorse or promote
14113094Sphk *    products derived from this software without specific prior written
15113094Sphk *    permission.
16113094Sphk *
17113094Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18113094Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19113094Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20113094Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21113094Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22113094Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23113094Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24113094Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25113094Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26113094Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27113094Sphk * SUCH DAMAGE.
28135482Sphk *
29135482Sphk * This is a device driver or the Adlink 9812 and 9810 ADC cards, mainly
30135482Sphk * intended to support Software Defined Radio reception of timesignals
31135482Sphk * in the VLF band.  See http://phk.freebsd.dk/loran-c
32135482Sphk *
33135482Sphk * The driver is configured with ioctls which define a ringbuffer with
34135482Sphk * a given number of chunks in it.  The a control structure and the
35135482Sphk * ringbuffer can then be mmap(2)'ed into userland and the application
36135482Sphk * can operate on the data directly.
37135482Sphk *
38135482Sphk * Tested with 10MHz external clock, divisor of 2 (ie: 5MHz sampling),
39135482Sphk * One channel active (ie: 2 bytes per sample = 10MB/sec) on a 660MHz
40135482Sphk * Celeron PC.
41135482Sphk *
42113094Sphk */
43113094Sphk
44135482Sphk#ifdef _KERNEL
45119418Sobrien#include <sys/cdefs.h>
46119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/adlink/adlink.c 143168 2005-03-06 06:55:11Z imp $");
47119418Sobrien
48113094Sphk#include <sys/param.h>
49113094Sphk#include <sys/systm.h>
50113094Sphk#include <sys/malloc.h>
51113094Sphk#include <sys/kernel.h>
52130026Sphk#include <sys/module.h>
53113270Sphk#include <sys/kthread.h>
54113094Sphk#include <sys/conf.h>
55113094Sphk#include <sys/bus.h>
56113094Sphk#include <machine/bus.h>
57113094Sphk#include <machine/resource.h>
58113094Sphk#include <sys/rman.h>
59119274Simp#include <dev/pci/pcireg.h>
60119274Simp#include <dev/pci/pcivar.h>
61113094Sphk#include <pci_if.h>
62113094Sphk#include <vm/vm.h>
63113094Sphk#include <vm/pmap.h>
64113094Sphk
65113270Sphk#endif /* _KERNEL */
66113270Sphk
67113270Sphk#include <sys/ioccom.h>
68113270Sphk
69135482Sphk#define ADLINK_SETDIVISOR	_IOWR('A', 255, u_int)	/* divisor */
70135482Sphk#define ADLINK_SETCHUNKSIZE	_IOWR('A', 254, u_int)	/* bytes */
71135482Sphk#define ADLINK_SETRINGSIZE	_IOWR('A', 253, u_int)	/* bytes */
72135482Sphk#define ADLINK_START		_IOWR('A', 252, u_int)	/* dummy */
73135482Sphk#define ADLINK_STOP		_IOWR('A', 251, u_int)	/* dummy */
74135482Sphk#define ADLINK_RESET		_IOWR('A', 250, u_int)	/* dummy */
75113270Sphk
76135482Sphkstruct page0 {
77135482Sphk	u_int			version;
78135482Sphk	int			state;
79135482Sphk#	  define STATE_RESET	-1
80135482Sphk#	  define STATE_RUN	0
81135482Sphk	u_int			divisor;	/* int */
82135482Sphk	u_int			chunksize;	/* bytes */
83135482Sphk	u_int			ringsize;	/* chunks */
84135482Sphk	u_int			o_ringgen;	/*
85135482Sphk						 * offset of ring generation
86135482Sphk						 * array
87135482Sphk						 */
88135482Sphk	u_int			o_ring;		/* offset of ring */
89113270Sphk};
90113270Sphk
91135482Sphk#define PAGE0VERSION	20031021
92113270Sphk
93113270Sphk#ifdef _KERNEL
94113270Sphk
95135482Sphkstruct pgstat {
96135482Sphk	u_int			*genp;
97135482Sphk	u_int			gen;
98135482Sphk	vm_paddr_t		phys;
99135482Sphk	void			*virt;
100135482Sphk	struct pgstat		*next;
101113094Sphk};
102113094Sphk
103113094Sphkstruct softc {
104113094Sphk	device_t		device;
105113094Sphk	void			*intrhand;
106113094Sphk	struct resource		*r0, *r1, *ri;
107113094Sphk	bus_space_tag_t		t0, t1;
108113094Sphk	bus_space_handle_t	h0, h1;
109135482Sphk	struct cdev		*dev;
110113270Sphk	off_t			mapvir;
111135482Sphk	int			error;
112135482Sphk	struct page0		*p0;
113135482Sphk	u_int			nchunks;
114135482Sphk	struct pgstat		*chunks;
115135482Sphk	struct pgstat		*next;
116135482Sphk};
117113094Sphk
118135482Sphkstatic d_ioctl_t adlink_ioctl;
119135482Sphkstatic d_mmap_t	adlink_mmap;
120135482Sphkstatic void adlink_intr(void *arg);
121113270Sphk
122135482Sphkstatic struct cdevsw adlink_cdevsw = {
123135482Sphk	.d_version =	D_VERSION,
124135482Sphk	.d_ioctl =	adlink_ioctl,
125135482Sphk	.d_mmap =	adlink_mmap,
126135482Sphk	.d_name =	"adlink",
127135482Sphk};
128113094Sphk
129135482Sphkstatic void
130135482Sphkadlink_intr(void *arg)
131135482Sphk{
132135482Sphk	struct softc *sc;
133135482Sphk	struct pgstat *pg;
134135482Sphk	uint32_t u;
135113270Sphk
136135482Sphk	sc = arg;
137135482Sphk	u = bus_space_read_4(sc->t0, sc->h0, 0x38);
138135482Sphk	if (!(u & 0x00800000))
139135482Sphk		return;
140135482Sphk	bus_space_write_4(sc->t0, sc->h0, 0x38, u | 0x003f4000);
141113270Sphk
142135482Sphk	pg = sc->next;
143135482Sphk	*(pg->genp) = ++pg->gen;
144113270Sphk
145135482Sphk	u = bus_space_read_4(sc->t1, sc->h1, 0x18);
146135482Sphk	if (u & 1)
147135482Sphk		sc->p0->state = EIO;
148113094Sphk
149135482Sphk	if (sc->p0->state != STATE_RUN) {
150135482Sphk		printf("adlink: stopping %d\n", sc->p0->state);
151135482Sphk		return;
152113270Sphk	}
153113270Sphk
154135482Sphk	pg = pg->next;
155135482Sphk	sc->next = pg;
156135482Sphk	*(pg->genp) = 0;
157135482Sphk	bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys);
158135482Sphk	bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize);
159113270Sphk	wakeup(sc);
160113270Sphk}
161113270Sphk
162113094Sphkstatic int
163135482Sphkadlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
164113270Sphk{
165135482Sphk	struct softc *sc;
166135482Sphk	vm_offset_t o;
167135482Sphk	int i;
168135482Sphk	struct pgstat *pg;
169113270Sphk
170135482Sphk	sc = dev->si_drv1;
171135482Sphk	if (nprot != VM_PROT_READ)
172135482Sphk		return (-1);
173135482Sphk	if (offset == 0) {
174135482Sphk		*paddr = vtophys(sc->p0);
175135482Sphk		return (0);
176113270Sphk	}
177135482Sphk	o = PAGE_SIZE;
178135482Sphk	pg = sc->chunks;
179135482Sphk	for (i = 0; i < sc->nchunks; i++, pg++) {
180135482Sphk		if (offset - o >= sc->p0->chunksize) {
181135482Sphk			o += sc->p0->chunksize;
182135482Sphk			continue;
183113270Sphk		}
184135482Sphk		*paddr = pg->phys + (offset - o);
185135482Sphk		return (0);
186113270Sphk	}
187135482Sphk	return (-1);
188113270Sphk}
189113270Sphk
190113270Sphkstatic int
191135482Sphkadlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
192113094Sphk{
193113094Sphk	struct softc *sc;
194113270Sphk	int i, error;
195135482Sphk	u_int u;
196135482Sphk	struct pgstat *pg;
197135482Sphk	u_int *genp;
198135482Sphk
199113094Sphk	sc = dev->si_drv1;
200135482Sphk	u = *(u_int*)data;
201135482Sphk	error = 0;
202135482Sphk	switch (cmd) {
203135482Sphk	case ADLINK_SETDIVISOR:
204135482Sphk		if (sc->p0->state == STATE_RUN)
205135482Sphk			return (EBUSY);
206135482Sphk		if (u & 1)
207135482Sphk			return (EINVAL);
208135482Sphk		sc->p0->divisor = u;
209135482Sphk		break;
210135482Sphk	case ADLINK_SETCHUNKSIZE:
211135482Sphk		if (sc->p0->state != STATE_RESET)
212135482Sphk			return (EBUSY);
213135482Sphk		if (u % PAGE_SIZE)
214135482Sphk			return (EINVAL);
215135482Sphk		if (sc->p0->ringsize != 0 && sc->p0->ringsize % u)
216135482Sphk			return (EINVAL);
217135482Sphk		sc->p0->chunksize = u;
218135482Sphk		break;
219135482Sphk	case ADLINK_SETRINGSIZE:
220135482Sphk		if (sc->p0->state != STATE_RESET)
221135482Sphk			return (EBUSY);
222135482Sphk		if (u % PAGE_SIZE)
223135482Sphk			return (EINVAL);
224135482Sphk		if (sc->p0->chunksize != 0 && u % sc->p0->chunksize)
225135482Sphk			return (EINVAL);
226135482Sphk		sc->p0->ringsize = u;
227135482Sphk		break;
228135482Sphk	case ADLINK_START:
229135482Sphk		if (sc->p0->state == STATE_RUN)
230135482Sphk			return (EBUSY);
231135482Sphk		if (sc->p0->state == STATE_RESET) {
232135482Sphk
233135482Sphk			if (sc->p0->chunksize == 0)
234135482Sphk				sc->p0->chunksize = 4 * PAGE_SIZE;
235135482Sphk			if (sc->p0->ringsize == 0)
236135482Sphk				sc->p0->ringsize = 16 * sc->p0->chunksize;
237135482Sphk			if (sc->p0->divisor == 0)
238135482Sphk				sc->p0->divisor = 4;
239113270Sphk
240135482Sphk			sc->nchunks = sc->p0->ringsize / sc->p0->chunksize;
241135482Sphk			if (sc->nchunks * sizeof (*pg->genp) +
242135482Sphk			    sizeof *sc->p0 > PAGE_SIZE)
243135482Sphk				return (EINVAL);
244135482Sphk			sc->p0->o_ring = PAGE_SIZE;
245135482Sphk			genp = (u_int *)(sc->p0 + 1);
246135482Sphk			sc->p0->o_ringgen = (intptr_t)genp - (intptr_t)(sc->p0);
247135482Sphk			pg = malloc(sizeof *pg * sc->nchunks,
248135482Sphk			    M_DEVBUF, M_WAITOK | M_ZERO);
249135482Sphk			sc->chunks = pg;
250135482Sphk			for (i = 0; i < sc->nchunks; i++) {
251135482Sphk				pg->genp = genp;
252135482Sphk				*pg->genp = 1;
253135482Sphk				genp++;
254135482Sphk				pg->virt = contigmalloc(sc->p0->chunksize,
255135482Sphk				    M_DEVBUF, M_WAITOK,
256135482Sphk				    0ul, 0xfffffffful,
257135482Sphk				    PAGE_SIZE, 0);
258135482Sphk				pg->phys = vtophys(pg->virt);
259135482Sphk				if (i == sc->nchunks - 1)
260135482Sphk					pg->next = sc->chunks;
261135482Sphk				else
262135482Sphk					pg->next = pg + 1;
263135482Sphk				pg++;
264135482Sphk			}
265135482Sphk			sc->next = sc->chunks;
266135482Sphk		}
267113094Sphk
268135482Sphk		/* Reset generation numbers */
269135482Sphk		pg = sc->chunks;
270135482Sphk		for (i = 0; i < sc->nchunks; i++) {
271135482Sphk			*pg->genp = 0;
272135482Sphk			pg->gen = 0;
273135482Sphk			pg++;
274135482Sphk		}
275113270Sphk
276135482Sphk		/* Enable interrupts on write complete */
277135482Sphk		bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000);
278113270Sphk
279135482Sphk		/* Sample CH0 only */
280135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x00, 1);
281113270Sphk
282135482Sphk		/* Divide clock by four */
283135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x04, sc->p0->divisor);
284113270Sphk
285135482Sphk		/* Software trigger mode: software */
286135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x08, 0);
287113270Sphk
288135482Sphk		/* Trigger level zero */
289135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x0c, 0);
290113270Sphk
291135482Sphk		/* Trigger source CH0 (not used) */
292135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x10, 0);
293113270Sphk
294135482Sphk		/* Fifo control/status: flush */
295135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x18, 3);
296113270Sphk
297135482Sphk		/* Clock source: external sine */
298135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x20, 2);
299113270Sphk
300135482Sphk		/* Chipmunks are go! */
301135482Sphk		sc->p0->state = STATE_RUN;
302113094Sphk
303135482Sphk		/* Set up Write DMA */
304135482Sphk		pg = sc->next = sc->chunks;
305135482Sphk		*(pg->genp) = 0;
306135482Sphk		bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys);
307135482Sphk		bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize);
308135482Sphk		u = bus_space_read_4(sc->t0, sc->h0, 0x3c);
309135482Sphk		bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600);
310113094Sphk
311135482Sphk		/* Acquisition Enable Register: go! */
312135482Sphk		bus_space_write_4(sc->t1, sc->h1, 0x1c, 1);
313113094Sphk
314135482Sphk		break;
315135482Sphk	case ADLINK_STOP:
316135482Sphk		if (sc->p0->state == STATE_RESET)
317135482Sphk			break;
318135482Sphk		sc->p0->state = EINTR;
319135482Sphk		while (*(sc->next->genp) == 0)
320135482Sphk			tsleep(sc, PUSER | PCATCH, "adstop", 1);
321135482Sphk		break;
322135482Sphk#ifdef notyet
323135482Sphk	/*
324135482Sphk	 * I'm not sure we can actually do this.  How do we revoke
325135482Sphk	 * the mmap'ed pages from any process having them mmapped ?
326135482Sphk	 */
327135482Sphk	case ADLINK_RESET:
328135482Sphk		if (sc->p0->state == STATE_RESET)
329135482Sphk			break;
330135482Sphk		sc->p0->state = EINTR;
331135482Sphk		while (*(sc->next->genp) == 0)
332135482Sphk			tsleep(sc, PUSER | PCATCH, "adreset", 1);
333135482Sphk		/* deallocate ring buffer */
334135482Sphk		break;
335135482Sphk#endif
336135482Sphk	default:
337135482Sphk		error = ENOIOCTL;
338135482Sphk		break;
339113270Sphk	}
340135482Sphk	return (error);
341113270Sphk}
342113270Sphk
343113094Sphkstatic devclass_t adlink_devclass;
344113094Sphk
345113094Sphkstatic int
346113094Sphkadlink_probe(device_t self)
347113094Sphk{
348113094Sphk
349113094Sphk	if (pci_get_devid(self) != 0x80da10e8)
350113094Sphk		return (ENXIO);
351113094Sphk	device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps");
352143168Simp	return (BUS_PROBE_DEFAULT);
353113094Sphk}
354113094Sphk
355113094Sphkstatic int
356113094Sphkadlink_attach(device_t self)
357113094Sphk{
358113094Sphk	struct softc *sc;
359113094Sphk	int rid, i;
360113094Sphk
361113094Sphk	sc = device_get_softc(self);
362113094Sphk	bzero(sc, sizeof *sc);
363113094Sphk	sc->device = self;
364113094Sphk
365113270Sphk	/*
366113270Sphk	 * This is the PCI mapped registers of the AMCC 9535 "matchmaker"
367113270Sphk	 * chip.
368113270Sphk	 */
369113094Sphk	rid = 0x10;
370135482Sphk	sc->r0 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid,
371135482Sphk	    0, ~0, 1, RF_ACTIVE);
372113094Sphk	if (sc->r0 == NULL)
373113094Sphk		return(ENODEV);
374113094Sphk	sc->t0 = rman_get_bustag(sc->r0);
375113094Sphk	sc->h0 = rman_get_bushandle(sc->r0);
376113094Sphk	printf("Res0 %x %x\n", sc->t0, sc->h0);
377113094Sphk
378113270Sphk	/*
379113270Sphk	 * This is the PCI mapped registers of the ADC hardware, they
380113270Sphk	 * are described in the manual which comes with the card.
381113270Sphk	 */
382113094Sphk	rid = 0x14;
383135482Sphk	sc->r1 =  bus_alloc_resource(self, SYS_RES_IOPORT, &rid,
384135482Sphk            0, ~0, 1, RF_ACTIVE);
385113094Sphk	if (sc->r1 == NULL)
386113094Sphk		return(ENODEV);
387113094Sphk	sc->t1 = rman_get_bustag(sc->r1);
388113094Sphk	sc->h1 = rman_get_bushandle(sc->r1);
389113094Sphk	printf("Res1 %x %x\n", sc->t1, sc->h1);
390113094Sphk
391113094Sphk	rid = 0x0;
392135482Sphk	sc->ri =  bus_alloc_resource(self, SYS_RES_IRQ, &rid,
393135482Sphk            0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
394113094Sphk	if (sc->ri == NULL)
395113094Sphk		return (ENODEV);
396113094Sphk
397135482Sphk	i = bus_setup_intr(self, sc->ri,
398135482Sphk	    INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST,
399113094Sphk	    adlink_intr, sc, &sc->intrhand);
400113270Sphk	if (i) {
401113270Sphk		printf("adlink: Couldn't get FAST intr\n");
402135482Sphk		i = bus_setup_intr(self, sc->ri,
403135482Sphk		    INTR_MPSAFE | INTR_TYPE_MISC,
404113270Sphk		    adlink_intr, sc, &sc->intrhand);
405113270Sphk	}
406113094Sphk
407113094Sphk	if (i)
408113094Sphk		return (ENODEV);
409113094Sphk
410135482Sphk	sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
411135482Sphk	sc->p0->version = PAGE0VERSION;
412135482Sphk	sc->p0->state = STATE_RESET;
413135482Sphk
414113094Sphk	sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self),
415113094Sphk	    UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self));
416113094Sphk	sc->dev->si_drv1 = sc;
417113094Sphk
418113094Sphk	return (0);
419113094Sphk}
420113094Sphk
421113094Sphkstatic device_method_t adlink_methods[] = {
422113094Sphk	/* Device interface */
423113094Sphk	DEVMETHOD(device_probe,		adlink_probe),
424113094Sphk	DEVMETHOD(device_attach,	adlink_attach),
425113094Sphk	DEVMETHOD(device_suspend,	bus_generic_suspend),
426113094Sphk	DEVMETHOD(device_resume,	bus_generic_resume),
427113094Sphk	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
428113094Sphk	{0, 0}
429113094Sphk};
430113094Sphk
431113094Sphkstatic driver_t adlink_driver = {
432113094Sphk	"adlink",
433113094Sphk	adlink_methods,
434113094Sphk	sizeof(struct softc)
435113094Sphk};
436113094Sphk
437113094SphkDRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0);
438135482Sphk
439113270Sphk#endif /* _KERNEL */
440