adlink.c revision 126080
1/*-
2 * Copyright (c) 2003 Poul-Henning Kamp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 *    products derived from this software without specific prior written
15 *    permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/adlink/adlink.c 126080 2004-02-21 21:10:55Z phk $");
32
33#ifdef _KERNEL
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/malloc.h>
37#include <sys/kernel.h>
38#include <sys/kthread.h>
39#include <sys/conf.h>
40#include <sys/bus.h>
41#include <machine/bus.h>
42#include <machine/resource.h>
43#include <sys/rman.h>
44#include <dev/pci/pcireg.h>
45#include <dev/pci/pcivar.h>
46#include <pci_if.h>
47#include <vm/vm.h>
48#include <vm/pmap.h>
49
50#endif /* _KERNEL */
51
52#include <sys/ioccom.h>
53
54struct wave {
55	int			index;
56	int			period;
57	int			offset;
58	int			length;
59	int			avg;
60	off_t			mapvir;
61	int			flags;
62
63	int			npages;
64	void			**virtual;
65};
66
67#define ADLINK_SETWAVE		_IOWR('A', 232, struct wave)
68#define ADLINK_GETWAVE		_IOWR('A', 233, struct wave)
69
70#ifdef _KERNEL
71
72#define INTPERPAGE (PAGE_SIZE / sizeof(int))
73#define I16PERPAGE (PAGE_SIZE / sizeof(int16_t))
74
75/*
76 * Sample rate
77 */
78#define SPS	1250000
79
80/*
81 * We sample one channel (= 16 bits) at 1.25 msps giving 2.5Mbyte/sec,
82 * 100 pages will give us about 1/6 second buffering.
83 */
84#define NRING	100
85
86/*
87 * How many waves are we willing to entertain
88 */
89#define NWAVE	25
90
91struct info {
92	int			nring;
93	off_t			o_ring;
94
95	int			ngri;
96	int			ppgri;
97	off_t			o_gri;
98};
99
100struct softc {
101	device_t		device;
102	void			*intrhand;
103	struct resource		*r0, *r1, *ri;
104	bus_space_tag_t		t0, t1;
105	bus_space_handle_t	h0, h1;
106	dev_t			dev;
107	off_t			mapvir;
108
109	struct proc		*procp;
110
111	struct info		*info;
112
113	struct wave		*wave[NWAVE];
114
115	int			idx;
116	void			*ring[NRING];
117	vm_paddr_t		pring[NRING];
118	int			stat[NRING];
119
120	uint64_t		cnt;
121
122	u_char			flags[I16PERPAGE];
123};
124
125static void
126adlink_wave(struct softc *sc, struct wave *wp, int16_t *sp)
127{
128	int f, i, k, m, *ip;
129
130	f = 0;
131	for (i = 0; i < I16PERPAGE; ) {
132		k = (sc->cnt - wp->offset + i) % wp->period;
133		if (k >= wp->length) {
134			i += wp->period - k;
135			sp += wp->period - k;
136			continue;
137		}
138		m = k % INTPERPAGE;
139		ip = (int *)(wp->virtual[k / INTPERPAGE]) + m;
140		while (m < INTPERPAGE && i < I16PERPAGE && k < wp->length) {
141			if (sc->flags[i] >= wp->index)
142				*ip += (*sp * 8 - *ip) >> wp->avg;
143			if (wp->flags & 1)
144				sc->flags[i] = wp->index;
145			sp++;
146			ip++;
147			m++;
148			i++;
149			k++;
150		}
151	}
152}
153
154static void
155adlink_tickle(struct softc *sc)
156{
157
158	wakeup(sc);
159	tsleep(&sc->ring, PUSER | PCATCH, "tickle", 1);
160}
161
162static int
163adlink_new_wave(struct softc *sc, int index, int period, int offset, int length, int avg, int flags)
164{
165	struct wave *wp;
166	int l, i;
167	void **oldvir, **newvir;
168
169	if (index < 0 || index >= NWAVE)
170		return (EINVAL);
171	wp = sc->wave[index];
172	if (wp == NULL) {
173		adlink_tickle(sc);
174		wp = malloc(sizeof *wp, M_DEVBUF, M_WAITOK | M_ZERO);
175	}
176	l = howmany(length, INTPERPAGE);
177	/* Setting a high average here to neuter the realtime bits */
178	wp->avg = 31;
179	if (wp->npages < l) {
180		oldvir = wp->virtual;
181		adlink_tickle(sc);
182		newvir = malloc(sizeof(void *) * l, M_DEVBUF, M_WAITOK | M_ZERO);
183		if (wp->npages > 0) {
184			adlink_tickle(sc);
185			bcopy(oldvir, newvir, wp->npages * sizeof(void *));
186		}
187		for (i = wp->npages; i < l; i++) {
188			adlink_tickle(sc);
189			newvir[i] = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
190		}
191		wp->virtual = newvir;
192		wp->npages = l;
193		wp->mapvir = sc->mapvir;
194		sc->mapvir += l * PAGE_SIZE;
195	} else {
196		oldvir = NULL;
197	}
198	wp->index = index;
199	wp->period = period;
200	wp->offset = offset;
201	wp->length = length;
202	wp->flags = flags;
203
204	for (i = 0; i < l; i++) {
205		adlink_tickle(sc);
206		bzero(wp->virtual[i], PAGE_SIZE);
207	}
208	wp->avg = avg;
209	sc->wave[index] = wp;
210	printf("Wave[%d] {period %d, offset %d, length %d, avg %d, flags %x}\n",
211	    wp->index, wp->period, wp->offset, wp->length, wp->avg, wp->flags);
212	free(oldvir, M_DEVBUF);
213	return (0);
214}
215
216static void
217adlink_loran(void *arg)
218{
219	struct softc *sc;
220	int idx, i;
221
222	sc = arg;
223	idx = 0;
224	for (;;) {
225		while (sc->stat[idx] == 0)
226			msleep(sc, NULL, PRIBIO, "loran", 1);
227		memset(sc->flags, NWAVE, sizeof sc->flags);
228		for (i = 0; i < NWAVE; i++) {
229			if (sc->wave[i] != NULL)
230				adlink_wave(sc, sc->wave[i], sc->ring[idx]);
231		}
232		sc->cnt += I16PERPAGE;
233		sc->stat[idx] = 0;
234		idx++;
235		idx %= NRING;
236	}
237	kthread_exit(0);
238}
239
240static int
241adlink_open(dev_t dev, int oflags, int devtype, struct thread *td)
242{
243	static int once;
244	struct softc *sc;
245	int i, error;
246	uint32_t u;
247
248	if (once)
249		return (0);
250	once = 1;
251
252	sc = dev->si_drv1;
253	sc->info = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK);
254	sc->info->nring = NRING;
255
256	sc->info->o_ring = PAGE_SIZE;
257	for (i = 0; i < NRING; i++) {
258		sc->ring[i] = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK);
259		sc->pring[i] = vtophys(sc->ring[i]);
260	}
261
262	error = adlink_new_wave(sc, NWAVE - 1, SPS, 0, SPS, 7, 0);
263	if (error)
264		return (error);
265
266	error = kthread_create(adlink_loran, sc, &sc->procp,
267	    0, 0, "adlink%d", device_get_unit(sc->device));
268	if (error)
269		return (error);
270
271	/* Enable interrupts on write complete */
272	bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000);
273
274	/* Sample CH0 only */
275	bus_space_write_4(sc->t1, sc->h1, 0x00, 1);
276
277	/* Divide clock by four */
278	bus_space_write_4(sc->t1, sc->h1, 0x04, 4);
279
280	/* Software trigger mode: software */
281	bus_space_write_4(sc->t1, sc->h1, 0x08, 0);
282
283	/* Trigger level zero */
284	bus_space_write_4(sc->t1, sc->h1, 0x0c, 0);
285
286	/* Trigger source CH0 (not used) */
287	bus_space_write_4(sc->t1, sc->h1, 0x10, 0);
288
289	/* Fifo control/status: flush */
290	bus_space_write_4(sc->t1, sc->h1, 0x18, 3);
291
292	/* Clock source: external sine */
293	bus_space_write_4(sc->t1, sc->h1, 0x20, 2);
294
295	/* Set up Write DMA */
296	bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]);
297	bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE);
298	u = bus_space_read_4(sc->t0, sc->h0, 0x3c);
299	bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600);
300
301	/* Acquisition Enable Register: go! */
302	bus_space_write_4(sc->t1, sc->h1, 0x1c, 1);
303	return (0);
304}
305
306static int
307adlink_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
308{
309	struct softc *sc;
310	struct wave *wp;
311	int i, error;
312
313	sc = dev->si_drv1;
314	wp = (struct wave *)data;
315	i = wp->index;
316	if (i < 0 || i >= NWAVE)
317		return (EINVAL);
318	if (cmd == ADLINK_GETWAVE) {
319		if (sc->wave[i] == NULL)
320			return (ENOENT);
321		bcopy(sc->wave[i], wp, sizeof(*wp));
322		return (0);
323	}
324	if (cmd == ADLINK_SETWAVE) {
325		error = adlink_new_wave(sc,
326			i,
327			wp->period,
328			wp->offset,
329			wp->length,
330			wp->avg,
331			wp->flags);
332		if (error)
333			return (error);
334		bcopy(sc->wave[i], wp, sizeof(*wp));
335		return (0);
336	}
337	return (ENOIOCTL);
338}
339
340static int
341adlink_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot)
342{
343	struct softc *sc;
344	struct wave *wp;
345	int i, j;
346
347	sc = dev->si_drv1;
348	if (nprot != VM_PROT_READ)
349		return (-1);
350	for (i = 0; i < NWAVE; i++) {
351		if (sc->wave[i] == NULL)
352			continue;
353		wp = sc->wave[i];
354		if (offset < wp->mapvir)
355			continue;
356		j = (offset - wp->mapvir) / PAGE_SIZE;
357		if (j >= wp->npages)
358			continue;
359		*paddr = vtophys(wp->virtual[j]);
360		return (0);
361	}
362	return (-1);
363}
364
365static void
366adlink_intr(void *arg)
367{
368	struct softc *sc;
369	uint32_t u;
370	int i, j;
371
372	sc = arg;
373	u = bus_space_read_4(sc->t0, sc->h0, 0x38);
374	if (!(u & 0x00800000))
375		return;
376	bus_space_write_4(sc->t0, sc->h0, 0x38, u | 0x003f4000);
377
378	j = sc->idx;
379	sc->stat[j] = 1;
380	i = (j + 1) % NRING;
381	sc->idx = i;
382	u = bus_space_read_4(sc->t1, sc->h1, 0x18);
383	if (u & 1) {
384		printf("adlink FIFO overrun\n");
385		return;
386	}
387	bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]);
388	bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE);
389	wakeup(sc);
390	if (sc->stat[i]) {
391		printf("adlink page busy\n");
392	}
393}
394
395static struct cdevsw adlink_cdevsw = {
396	.d_version =	D_VERSION,
397	.d_flags =	D_NEEDGIANT,
398	.d_open =	adlink_open,
399	.d_ioctl =	adlink_ioctl,
400	.d_mmap =	adlink_mmap,
401	.d_name =	"adlink",
402};
403
404static devclass_t adlink_devclass;
405
406static int
407adlink_probe(device_t self)
408{
409
410	if (pci_get_devid(self) != 0x80da10e8)
411		return (ENXIO);
412	device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps");
413	return (0);
414}
415
416static int
417adlink_attach(device_t self)
418{
419	struct softc *sc;
420	int rid, i;
421
422	sc = device_get_softc(self);
423	bzero(sc, sizeof *sc);
424	sc->device = self;
425
426	/*
427	 * This is the PCI mapped registers of the AMCC 9535 "matchmaker"
428	 * chip.
429	 */
430	rid = 0x10;
431	sc->r0 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid,
432	    0, ~0, 1, RF_ACTIVE);
433	if (sc->r0 == NULL)
434		return(ENODEV);
435	sc->t0 = rman_get_bustag(sc->r0);
436	sc->h0 = rman_get_bushandle(sc->r0);
437	printf("Res0 %x %x\n", sc->t0, sc->h0);
438
439	/*
440	 * This is the PCI mapped registers of the ADC hardware, they
441	 * are described in the manual which comes with the card.
442	 */
443	rid = 0x14;
444	sc->r1 =  bus_alloc_resource(self, SYS_RES_IOPORT, &rid,
445            0, ~0, 1, RF_ACTIVE);
446	if (sc->r1 == NULL)
447		return(ENODEV);
448	sc->t1 = rman_get_bustag(sc->r1);
449	sc->h1 = rman_get_bushandle(sc->r1);
450	printf("Res1 %x %x\n", sc->t1, sc->h1);
451
452	rid = 0x0;
453	sc->ri =  bus_alloc_resource(self, SYS_RES_IRQ, &rid,
454            0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
455	if (sc->ri == NULL)
456		return (ENODEV);
457
458	i = bus_setup_intr(self, sc->ri, INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST,
459	    adlink_intr, sc, &sc->intrhand);
460	if (i) {
461		printf("adlink: Couldn't get FAST intr\n");
462		i = bus_setup_intr(self, sc->ri, INTR_TYPE_MISC,
463		    adlink_intr, sc, &sc->intrhand);
464	}
465
466	if (i)
467		return (ENODEV);
468
469	sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self),
470	    UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self));
471	sc->dev->si_drv1 = sc;
472
473	return (0);
474}
475
476static device_method_t adlink_methods[] = {
477	/* Device interface */
478	DEVMETHOD(device_probe,		adlink_probe),
479	DEVMETHOD(device_attach,	adlink_attach),
480	DEVMETHOD(device_suspend,	bus_generic_suspend),
481	DEVMETHOD(device_resume,	bus_generic_resume),
482	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
483	{0, 0}
484};
485
486static driver_t adlink_driver = {
487	"adlink",
488	adlink_methods,
489	sizeof(struct softc)
490};
491
492DRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0);
493#endif /* _KERNEL */
494