adlink.c revision 150526
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 150526 2005-09-24 20:46:02Z phk $"); 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 */ 84143844Sphk u_int o_sample; /* 85135482Sphk * offset of ring generation 86135482Sphk * array 87135482Sphk */ 88135482Sphk u_int o_ring; /* offset of ring */ 89113270Sphk}; 90113270Sphk 91143844Sphk#define PAGE0VERSION 20050219 92113270Sphk 93113270Sphk#ifdef _KERNEL 94113270Sphk 95135482Sphkstruct pgstat { 96143844Sphk uint64_t *sample; 97135482Sphk vm_paddr_t phys; 98135482Sphk void *virt; 99135482Sphk struct pgstat *next; 100113094Sphk}; 101113094Sphk 102113094Sphkstruct softc { 103113094Sphk device_t device; 104113094Sphk void *intrhand; 105150526Sphk struct resource *res[3]; 106135482Sphk struct cdev *dev; 107113270Sphk off_t mapvir; 108135482Sphk int error; 109135482Sphk struct page0 *p0; 110135482Sphk u_int nchunks; 111135482Sphk struct pgstat *chunks; 112135482Sphk struct pgstat *next; 113143844Sphk uint64_t sample; 114135482Sphk}; 115113094Sphk 116135482Sphkstatic d_ioctl_t adlink_ioctl; 117135482Sphkstatic d_mmap_t adlink_mmap; 118135482Sphkstatic void adlink_intr(void *arg); 119113270Sphk 120135482Sphkstatic struct cdevsw adlink_cdevsw = { 121135482Sphk .d_version = D_VERSION, 122135482Sphk .d_ioctl = adlink_ioctl, 123135482Sphk .d_mmap = adlink_mmap, 124135482Sphk .d_name = "adlink", 125135482Sphk}; 126113094Sphk 127135482Sphkstatic void 128135482Sphkadlink_intr(void *arg) 129135482Sphk{ 130135482Sphk struct softc *sc; 131135482Sphk struct pgstat *pg; 132135482Sphk uint32_t u; 133113270Sphk 134135482Sphk sc = arg; 135150526Sphk u = bus_read_4(sc->res[0], 0x38); 136135482Sphk if (!(u & 0x00800000)) 137135482Sphk return; 138150526Sphk bus_write_4(sc->res[0], 0x38, u | 0x003f4000); 139113270Sphk 140143844Sphk sc->sample += sc->p0->chunksize / 2; 141135482Sphk pg = sc->next; 142143844Sphk *(pg->sample) = sc->sample; 143113270Sphk 144150526Sphk u = bus_read_4(sc->res[1], 0x18); 145135482Sphk if (u & 1) 146135482Sphk sc->p0->state = EIO; 147113094Sphk 148135482Sphk if (sc->p0->state != STATE_RUN) { 149135482Sphk printf("adlink: stopping %d\n", sc->p0->state); 150135482Sphk return; 151113270Sphk } 152113270Sphk 153135482Sphk pg = pg->next; 154135482Sphk sc->next = pg; 155143844Sphk *(pg->sample) = 0; 156150526Sphk bus_write_4(sc->res[0], 0x24, pg->phys); 157150526Sphk bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 158113270Sphk wakeup(sc); 159113270Sphk} 160113270Sphk 161113094Sphkstatic int 162135482Sphkadlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 163113270Sphk{ 164135482Sphk struct softc *sc; 165135482Sphk vm_offset_t o; 166135482Sphk int i; 167135482Sphk struct pgstat *pg; 168113270Sphk 169135482Sphk sc = dev->si_drv1; 170135482Sphk if (nprot != VM_PROT_READ) 171135482Sphk return (-1); 172135482Sphk if (offset == 0) { 173135482Sphk *paddr = vtophys(sc->p0); 174135482Sphk return (0); 175113270Sphk } 176135482Sphk o = PAGE_SIZE; 177135482Sphk pg = sc->chunks; 178135482Sphk for (i = 0; i < sc->nchunks; i++, pg++) { 179135482Sphk if (offset - o >= sc->p0->chunksize) { 180135482Sphk o += sc->p0->chunksize; 181135482Sphk continue; 182113270Sphk } 183135482Sphk *paddr = pg->phys + (offset - o); 184135482Sphk return (0); 185113270Sphk } 186135482Sphk return (-1); 187113270Sphk} 188113270Sphk 189113270Sphkstatic int 190135482Sphkadlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 191113094Sphk{ 192113094Sphk struct softc *sc; 193113270Sphk int i, error; 194135482Sphk u_int u; 195135482Sphk struct pgstat *pg; 196143844Sphk uint64_t *sample; 197135482Sphk 198113094Sphk sc = dev->si_drv1; 199135482Sphk u = *(u_int*)data; 200135482Sphk error = 0; 201135482Sphk switch (cmd) { 202135482Sphk case ADLINK_SETDIVISOR: 203135482Sphk if (sc->p0->state == STATE_RUN) 204135482Sphk return (EBUSY); 205135482Sphk if (u & 1) 206135482Sphk return (EINVAL); 207135482Sphk sc->p0->divisor = u; 208135482Sphk break; 209135482Sphk case ADLINK_SETCHUNKSIZE: 210135482Sphk if (sc->p0->state != STATE_RESET) 211135482Sphk return (EBUSY); 212135482Sphk if (u % PAGE_SIZE) 213135482Sphk return (EINVAL); 214135482Sphk if (sc->p0->ringsize != 0 && sc->p0->ringsize % u) 215135482Sphk return (EINVAL); 216135482Sphk sc->p0->chunksize = u; 217135482Sphk break; 218135482Sphk case ADLINK_SETRINGSIZE: 219135482Sphk if (sc->p0->state != STATE_RESET) 220135482Sphk return (EBUSY); 221135482Sphk if (u % PAGE_SIZE) 222135482Sphk return (EINVAL); 223135482Sphk if (sc->p0->chunksize != 0 && u % sc->p0->chunksize) 224135482Sphk return (EINVAL); 225135482Sphk sc->p0->ringsize = u; 226135482Sphk break; 227135482Sphk case ADLINK_START: 228135482Sphk if (sc->p0->state == STATE_RUN) 229135482Sphk return (EBUSY); 230135482Sphk if (sc->p0->state == STATE_RESET) { 231135482Sphk 232135482Sphk if (sc->p0->chunksize == 0) 233135482Sphk sc->p0->chunksize = 4 * PAGE_SIZE; 234135482Sphk if (sc->p0->ringsize == 0) 235135482Sphk sc->p0->ringsize = 16 * sc->p0->chunksize; 236135482Sphk if (sc->p0->divisor == 0) 237135482Sphk sc->p0->divisor = 4; 238113270Sphk 239135482Sphk sc->nchunks = sc->p0->ringsize / sc->p0->chunksize; 240143844Sphk if (sc->nchunks * sizeof (*pg->sample) + 241135482Sphk sizeof *sc->p0 > PAGE_SIZE) 242135482Sphk return (EINVAL); 243135482Sphk sc->p0->o_ring = PAGE_SIZE; 244143844Sphk sample = (uint64_t *)(sc->p0 + 1); 245143844Sphk sc->p0->o_sample = 246143844Sphk (uintptr_t)sample - (uintptr_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++) { 251143844Sphk pg->sample = sample; 252143844Sphk *pg->sample = 0; 253143844Sphk sample++; 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++) { 271143844Sphk *pg->sample = 0; 272135482Sphk pg++; 273135482Sphk } 274113270Sphk 275135482Sphk /* Enable interrupts on write complete */ 276150526Sphk bus_write_4(sc->res[0], 0x38, 0x00004000); 277113270Sphk 278135482Sphk /* Sample CH0 only */ 279150526Sphk bus_write_4(sc->res[1], 0x00, 1); 280113270Sphk 281135482Sphk /* Divide clock by four */ 282150526Sphk bus_write_4(sc->res[1], 0x04, sc->p0->divisor); 283113270Sphk 284135482Sphk /* Software trigger mode: software */ 285150526Sphk bus_write_4(sc->res[1], 0x08, 0); 286113270Sphk 287135482Sphk /* Trigger level zero */ 288150526Sphk bus_write_4(sc->res[1], 0x0c, 0); 289113270Sphk 290135482Sphk /* Trigger source CH0 (not used) */ 291150526Sphk bus_write_4(sc->res[1], 0x10, 0); 292113270Sphk 293135482Sphk /* Fifo control/status: flush */ 294150526Sphk bus_write_4(sc->res[1], 0x18, 3); 295113270Sphk 296135482Sphk /* Clock source: external sine */ 297150526Sphk bus_write_4(sc->res[1], 0x20, 2); 298113270Sphk 299135482Sphk /* Chipmunks are go! */ 300135482Sphk sc->p0->state = STATE_RUN; 301113094Sphk 302135482Sphk /* Set up Write DMA */ 303135482Sphk pg = sc->next = sc->chunks; 304143844Sphk *(pg->sample) = 0; 305150526Sphk bus_write_4(sc->res[0], 0x24, pg->phys); 306150526Sphk bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 307150526Sphk u = bus_read_4(sc->res[0], 0x3c); 308150526Sphk bus_write_4(sc->res[0], 0x3c, u | 0x00000600); 309113094Sphk 310135482Sphk /* Acquisition Enable Register: go! */ 311150526Sphk bus_write_4(sc->res[1], 0x1c, 1); 312113094Sphk 313135482Sphk break; 314135482Sphk case ADLINK_STOP: 315135482Sphk if (sc->p0->state == STATE_RESET) 316135482Sphk break; 317135482Sphk sc->p0->state = EINTR; 318143844Sphk while (*(sc->next->sample) == 0) 319135482Sphk tsleep(sc, PUSER | PCATCH, "adstop", 1); 320135482Sphk break; 321135482Sphk#ifdef notyet 322135482Sphk /* 323135482Sphk * I'm not sure we can actually do this. How do we revoke 324135482Sphk * the mmap'ed pages from any process having them mmapped ? 325135482Sphk */ 326135482Sphk case ADLINK_RESET: 327135482Sphk if (sc->p0->state == STATE_RESET) 328135482Sphk break; 329135482Sphk sc->p0->state = EINTR; 330143844Sphk while (*(sc->next->samp) == 0) 331135482Sphk tsleep(sc, PUSER | PCATCH, "adreset", 1); 332135482Sphk /* deallocate ring buffer */ 333135482Sphk break; 334135482Sphk#endif 335135482Sphk default: 336135482Sphk error = ENOIOCTL; 337135482Sphk break; 338113270Sphk } 339135482Sphk return (error); 340113270Sphk} 341113270Sphk 342113094Sphkstatic devclass_t adlink_devclass; 343113094Sphk 344113094Sphkstatic int 345113094Sphkadlink_probe(device_t self) 346113094Sphk{ 347113094Sphk 348113094Sphk if (pci_get_devid(self) != 0x80da10e8) 349113094Sphk return (ENXIO); 350113094Sphk device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps"); 351143168Simp return (BUS_PROBE_DEFAULT); 352113094Sphk} 353113094Sphk 354150526Sphkstatic struct resource_spec adlink_res_spec[] = { 355150526Sphk { SYS_RES_IOPORT, PCIR_BAR(0), RF_ACTIVE}, 356150526Sphk { SYS_RES_IOPORT, PCIR_BAR(1), RF_ACTIVE}, 357150526Sphk { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 358150526Sphk { -1, 0, 0 } 359150526Sphk}; 360150526Sphk 361113094Sphkstatic int 362113094Sphkadlink_attach(device_t self) 363113094Sphk{ 364113094Sphk struct softc *sc; 365150526Sphk int i, error; 366113094Sphk 367113094Sphk sc = device_get_softc(self); 368113094Sphk bzero(sc, sizeof *sc); 369113094Sphk sc->device = self; 370113094Sphk 371150526Sphk error = bus_alloc_resources(self, adlink_res_spec, sc->res); 372150526Sphk if (error) 373150526Sphk return (error); 374113094Sphk 375150526Sphk i = bus_setup_intr(self, sc->res[2], 376135482Sphk INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST, 377113094Sphk adlink_intr, sc, &sc->intrhand); 378113270Sphk if (i) { 379113270Sphk printf("adlink: Couldn't get FAST intr\n"); 380150526Sphk i = bus_setup_intr(self, sc->res[2], 381135482Sphk INTR_MPSAFE | INTR_TYPE_MISC, 382113270Sphk adlink_intr, sc, &sc->intrhand); 383113270Sphk } 384113094Sphk 385150526Sphk if (i) { 386150526Sphk bus_release_resources(self, adlink_res_spec, sc->res); 387113094Sphk return (ENODEV); 388150526Sphk } 389113094Sphk 390135482Sphk sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 391135482Sphk sc->p0->version = PAGE0VERSION; 392135482Sphk sc->p0->state = STATE_RESET; 393135482Sphk 394113094Sphk sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 395113094Sphk UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 396113094Sphk sc->dev->si_drv1 = sc; 397113094Sphk 398113094Sphk return (0); 399113094Sphk} 400113094Sphk 401113094Sphkstatic device_method_t adlink_methods[] = { 402113094Sphk /* Device interface */ 403113094Sphk DEVMETHOD(device_probe, adlink_probe), 404113094Sphk DEVMETHOD(device_attach, adlink_attach), 405113094Sphk DEVMETHOD(device_suspend, bus_generic_suspend), 406113094Sphk DEVMETHOD(device_resume, bus_generic_resume), 407113094Sphk DEVMETHOD(device_shutdown, bus_generic_shutdown), 408113094Sphk {0, 0} 409113094Sphk}; 410113094Sphk 411113094Sphkstatic driver_t adlink_driver = { 412113094Sphk "adlink", 413113094Sphk adlink_methods, 414113094Sphk sizeof(struct softc) 415113094Sphk}; 416113094Sphk 417113094SphkDRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); 418135482Sphk 419113270Sphk#endif /* _KERNEL */ 420