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