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