adlink.c revision 171717
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 2003-2004 Poul-Henning Kamp 31556Srgrimes * All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 3. The names of the authors may not be used to endorse or promote 141556Srgrimes * products derived from this software without specific prior written 151556Srgrimes * permission. 161556Srgrimes * 171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271556Srgrimes * SUCH DAMAGE. 28127499Sgad * 29127499Sgad * This is a device driver or the Adlink 9812 and 9810 ADC cards, mainly 30127499Sgad * intended to support Software Defined Radio reception of timesignals 31127499Sgad * in the VLF band. See http://phk.freebsd.dk/loran-c 32127499Sgad * 33127499Sgad * The driver is configured with ioctls which define a ringbuffer with 34127499Sgad * a given number of chunks in it. The a control structure and the 351556Srgrimes * ringbuffer can then be mmap(2)'ed into userland and the application 361556Srgrimes * can operate on the data directly. 371556Srgrimes * 3890143Smarkm * Tested with 10MHz external clock, divisor of 2 (ie: 5MHz sampling), 391556Srgrimes * One channel active (ie: 2 bytes per sample = 10MB/sec) on a 660MHz 401556Srgrimes * Celeron PC. 411556Srgrimes * 421556Srgrimes */ 4390143Smarkm 441556Srgrimes#ifdef _KERNEL 4536049Scharnier#include <sys/cdefs.h> 4690143Smarkm__FBSDID("$FreeBSD: head/sys/dev/adlink/adlink.c 171717 2007-08-04 17:43:11Z kib $"); 4736049Scharnier 48110391Scharnier#include <sys/param.h> 4999110Sobrien#include <sys/systm.h> 5099110Sobrien#include <sys/malloc.h> 511556Srgrimes#include <sys/kernel.h> 521556Srgrimes#include <sys/module.h> 53127546Sgad#include <sys/kthread.h> 543296Sdg#include <sys/conf.h> 551556Srgrimes#include <sys/bus.h> 561556Srgrimes#include <machine/bus.h> 571556Srgrimes#include <machine/resource.h> 581556Srgrimes#include <sys/rman.h> 591556Srgrimes#include <dev/pci/pcireg.h> 601556Srgrimes#include <dev/pci/pcivar.h> 61127149Sgad#include <pci_if.h> 621556Srgrimes#include <vm/vm.h> 63127499Sgad#include <vm/pmap.h> 641556Srgrimes 6513514Smpp#endif /* _KERNEL */ 6673367Sache 671556Srgrimes#include <sys/ioccom.h> 6890143Smarkm 691556Srgrimes#define ADLINK_SETDIVISOR _IOWR('A', 255, u_int) /* divisor */ 701556Srgrimes#define ADLINK_SETCHUNKSIZE _IOWR('A', 254, u_int) /* bytes */ 711556Srgrimes#define ADLINK_SETRINGSIZE _IOWR('A', 253, u_int) /* bytes */ 721556Srgrimes#define ADLINK_START _IOWR('A', 252, u_int) /* dummy */ 731556Srgrimes#define ADLINK_STOP _IOWR('A', 251, u_int) /* dummy */ 741556Srgrimes#define ADLINK_RESET _IOWR('A', 250, u_int) /* dummy */ 751556Srgrimes 76127499Sgadstruct page0 { 77127499Sgad u_int version; 7866377Sbrian int state; 79127537Sgad# define STATE_RESET -1 80127555Sgad# define STATE_RUN 0 81127537Sgad u_int divisor; /* int */ 82127537Sgad u_int chunksize; /* bytes */ 83127555Sgad u_int ringsize; /* chunks */ 84127537Sgad u_int o_sample; /* 85127537Sgad * offset of ring generation 86127537Sgad * array 87129914Sgad */ 88129914Sgad u_int o_ring; /* offset of ring */ 89129914Sgad}; 90129914Sgad 91129914Sgad#define PAGE0VERSION 20050219 92129914Sgad 93127537Sgad#ifdef _KERNEL 94127537Sgad 95127537Sgadstruct pgstat { 96127537Sgad uint64_t *sample; 97127537Sgad vm_paddr_t phys; 98127537Sgad void *virt; 99127537Sgad struct pgstat *next; 100127537Sgad}; 10190143Smarkm 1021556Srgrimesstruct softc { 103127537Sgad device_t device; 104127537Sgad void *intrhand; 105127537Sgad struct resource *res[3]; 106127537Sgad struct cdev *dev; 107127537Sgad off_t mapvir; 108127537Sgad int error; 109127537Sgad struct page0 *p0; 1101556Srgrimes u_int nchunks; 111127537Sgad struct pgstat *chunks; 11297966Sjmallett struct pgstat *next; 113127499Sgad uint64_t sample; 114127537Sgad}; 115127499Sgad 116127499Sgadstatic d_ioctl_t adlink_ioctl; 117127499Sgadstatic d_mmap_t adlink_mmap; 118127499Sgadstatic int adlink_intr(void *arg); 119127499Sgad 120127499Sgadstatic struct cdevsw adlink_cdevsw = { 121127499Sgad .d_version = D_VERSION, 122127499Sgad .d_flags = D_NEEDGIANT, 123127499Sgad .d_ioctl = adlink_ioctl, 124127499Sgad .d_mmap = adlink_mmap, 125127499Sgad .d_name = "adlink", 126127499Sgad}; 127127499Sgad 128127823Sgadstatic int 129127499Sgadadlink_intr(void *arg) 130127499Sgad{ 131127499Sgad struct softc *sc; 132127499Sgad struct pgstat *pg; 133127499Sgad uint32_t u; 134127499Sgad 135127499Sgad sc = arg; 136127536Sgad u = bus_read_4(sc->res[0], 0x38); 137127499Sgad if (!(u & 0x00800000)) 138127598Sgad return; // XXX - FILTER_STRAY? 139127598Sgad bus_write_4(sc->res[0], 0x38, u | 0x003f4000); 140127536Sgad 141127499Sgad sc->sample += sc->p0->chunksize / 2; 142127499Sgad pg = sc->next; 143129914Sgad *(pg->sample) = sc->sample; 144127536Sgad 145127536Sgad u = bus_read_4(sc->res[1], 0x18); 146127536Sgad if (u & 1) 147127536Sgad sc->p0->state = EIO; 148127536Sgad 149127499Sgad if (sc->p0->state != STATE_RUN) { 15097875Sjmallett printf("adlink: stopping %d\n", sc->p0->state); 151129635Sgad return; // XXX - FILTER_STRAY? 152127538Sgad } 153127538Sgad 15490143Smarkm pg = pg->next; 15597875Sjmallett sc->next = pg; 15697875Sjmallett *(pg->sample) = 0; 157127538Sgad bus_write_4(sc->res[0], 0x24, pg->phys); 158127538Sgad bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 159105831Srwatson wakeup(sc); 1601556Srgrimes return (FILTER_HANDLED); 161127843Sgad} 16298494Ssobomax 1631556Srgrimesstatic int 16490110Simpadlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 1651556Srgrimes{ 166127499Sgad struct softc *sc; 167127499Sgad vm_offset_t o; 1681556Srgrimes int i; 1691556Srgrimes struct pgstat *pg; 1701556Srgrimes 171129914Sgad sc = dev->si_drv1; 172127539Sgad if (nprot != VM_PROT_READ) 173127499Sgad return (-1); 174129914Sgad if (offset == 0) { 175127499Sgad *paddr = vtophys(sc->p0); 17690143Smarkm return (0); 1771556Srgrimes } 17811809Sache o = PAGE_SIZE; 179127542Sgad pg = sc->chunks; 18011809Sache for (i = 0; i < sc->nchunks; i++, pg++) { 18197804Stjr if (offset - o >= sc->p0->chunksize) { 18297804Stjr o += sc->p0->chunksize; 18397804Stjr continue; 1841556Srgrimes } 1851556Srgrimes *paddr = pg->phys + (offset - o); 1861556Srgrimes return (0); 1871556Srgrimes } 1881556Srgrimes return (-1); 1891556Srgrimes} 1901556Srgrimes 19198494Ssobomaxstatic int 192129914Sgadadlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 193129914Sgad{ 19498494Ssobomax struct softc *sc; 195129914Sgad int i, error; 196129914Sgad u_int u; 1971556Srgrimes struct pgstat *pg; 198127542Sgad uint64_t *sample; 199127542Sgad 200127542Sgad sc = dev->si_drv1; 201127499Sgad u = *(u_int*)data; 202127499Sgad error = 0; 203127499Sgad switch (cmd) { 204127499Sgad case ADLINK_SETDIVISOR: 205127499Sgad if (sc->p0->state == STATE_RUN) 206127499Sgad return (EBUSY); 207127499Sgad if (u & 1) 20889909Sru return (EINVAL); 20998494Ssobomax sc->p0->divisor = u; 2101556Srgrimes break; 211127499Sgad case ADLINK_SETCHUNKSIZE: 212127499Sgad if (sc->p0->state != STATE_RESET) 213127499Sgad return (EBUSY); 214127499Sgad if (u % PAGE_SIZE) 215127499Sgad return (EINVAL); 216127499Sgad if (sc->p0->ringsize != 0 && sc->p0->ringsize % u) 217127499Sgad return (EINVAL); 218127499Sgad sc->p0->chunksize = u; 219127499Sgad break; 2201556Srgrimes case ADLINK_SETRINGSIZE: 221127499Sgad if (sc->p0->state != STATE_RESET) 2221556Srgrimes return (EBUSY); 2231556Srgrimes if (u % PAGE_SIZE) 22419068Speter return (EINVAL); 22519068Speter if (sc->p0->chunksize != 0 && u % sc->p0->chunksize) 22619068Speter return (EINVAL); 22719068Speter sc->p0->ringsize = u; 22819068Speter break; 22919068Speter case ADLINK_START: 2301556Srgrimes if (sc->p0->state == STATE_RUN) 2311556Srgrimes return (EBUSY); 2321556Srgrimes if (sc->p0->state == STATE_RESET) { 233127506Sgad 234127506Sgad if (sc->p0->chunksize == 0) 235127506Sgad sc->p0->chunksize = 4 * PAGE_SIZE; 236127542Sgad if (sc->p0->ringsize == 0) 237127506Sgad sc->p0->ringsize = 16 * sc->p0->chunksize; 238127506Sgad if (sc->p0->divisor == 0) 239127499Sgad sc->p0->divisor = 4; 240127499Sgad 241127499Sgad sc->nchunks = sc->p0->ringsize / sc->p0->chunksize; 242127499Sgad if (sc->nchunks * sizeof (*pg->sample) + 243127499Sgad sizeof *sc->p0 > PAGE_SIZE) 244127542Sgad return (EINVAL); 245127499Sgad sc->p0->o_ring = PAGE_SIZE; 246127597Sgad sample = (uint64_t *)(sc->p0 + 1); 247127542Sgad sc->p0->o_sample = 248127542Sgad (uintptr_t)sample - (uintptr_t)(sc->p0); 249127542Sgad pg = malloc(sizeof *pg * sc->nchunks, 250127542Sgad M_DEVBUF, M_WAITOK | M_ZERO); 251127499Sgad sc->chunks = pg; 252127499Sgad for (i = 0; i < sc->nchunks; i++) { 253127499Sgad pg->sample = sample; 254127499Sgad *pg->sample = 0; 255127499Sgad sample++; 256127542Sgad pg->virt = contigmalloc(sc->p0->chunksize, 2571556Srgrimes M_DEVBUF, M_WAITOK, 258127499Sgad 0ul, 0xfffffffful, 259116265Sscottl PAGE_SIZE, 0); 260126127Sdeischen pg->phys = vtophys(pg->virt); 261116265Sscottl if (i == sc->nchunks - 1) 2621556Srgrimes pg->next = sc->chunks; 2631556Srgrimes else 2641556Srgrimes pg->next = pg + 1; 2651556Srgrimes pg++; 266109502Sjmallett } 26790143Smarkm sc->next = sc->chunks; 2681556Srgrimes } 2691556Srgrimes 2701556Srgrimes /* Reset generation numbers */ 2711556Srgrimes pg = sc->chunks; 2721556Srgrimes for (i = 0; i < sc->nchunks; i++) { 2731556Srgrimes *pg->sample = 0; 274109502Sjmallett pg++; 27590143Smarkm } 2761556Srgrimes 2771556Srgrimes /* Enable interrupts on write complete */ 2781556Srgrimes bus_write_4(sc->res[0], 0x38, 0x00004000); 2791556Srgrimes 28037317Sphk /* Sample CH0 only */ 2811556Srgrimes bus_write_4(sc->res[1], 0x00, 1); 2821556Srgrimes 2831556Srgrimes /* Divide clock by four */ 2841556Srgrimes bus_write_4(sc->res[1], 0x04, sc->p0->divisor); 2851556Srgrimes 2861556Srgrimes /* Software trigger mode: software */ 28737317Sphk bus_write_4(sc->res[1], 0x08, 0); 2881556Srgrimes 2891556Srgrimes /* Trigger level zero */ 290109502Sjmallett bus_write_4(sc->res[1], 0x0c, 0); 291109502Sjmallett 292109502Sjmallett /* Trigger source CH0 (not used) */ 2931556Srgrimes bus_write_4(sc->res[1], 0x10, 0); 29490143Smarkm 2951556Srgrimes /* Fifo control/status: flush */ 2961556Srgrimes bus_write_4(sc->res[1], 0x18, 3); 297109502Sjmallett 29890143Smarkm /* Clock source: external sine */ 2991556Srgrimes bus_write_4(sc->res[1], 0x20, 2); 3001556Srgrimes 301127499Sgad /* Chipmunks are go! */ 302127499Sgad sc->p0->state = STATE_RUN; 303127499Sgad 304127499Sgad /* Set up Write DMA */ 305127499Sgad pg = sc->next = sc->chunks; 306127499Sgad *(pg->sample) = 0; 307127499Sgad bus_write_4(sc->res[0], 0x24, pg->phys); 308127499Sgad bus_write_4(sc->res[0], 0x28, sc->p0->chunksize); 3091556Srgrimes u = bus_read_4(sc->res[0], 0x3c); 310127499Sgad bus_write_4(sc->res[0], 0x3c, u | 0x00000600); 311127499Sgad 312127597Sgad /* Acquisition Enable Register: go! */ 313127542Sgad bus_write_4(sc->res[1], 0x1c, 1); 314127542Sgad 315127542Sgad break; 316127542Sgad case ADLINK_STOP: 317127542Sgad if (sc->p0->state == STATE_RESET) 318127542Sgad break; 319127499Sgad sc->p0->state = EINTR; 320127499Sgad while (*(sc->next->sample) == 0) 321127499Sgad tsleep(sc, PUSER | PCATCH, "adstop", 1); 322127499Sgad break; 323127499Sgad#ifdef notyet 3241556Srgrimes /* 3251556Srgrimes * I'm not sure we can actually do this. How do we revoke 3261556Srgrimes * the mmap'ed pages from any process having them mmapped ? 3271556Srgrimes */ 3281556Srgrimes case ADLINK_RESET: 3291556Srgrimes if (sc->p0->state == STATE_RESET) 330127499Sgad break; 331127499Sgad sc->p0->state = EINTR; 332127597Sgad while (*(sc->next->samp) == 0) 333127542Sgad tsleep(sc, PUSER | PCATCH, "adreset", 1); 334127542Sgad /* deallocate ring buffer */ 335127542Sgad break; 336127542Sgad#endif 337127542Sgad default: 338127499Sgad error = ENOIOCTL; 339127499Sgad break; 340127499Sgad } 341127499Sgad return (error); 342127499Sgad} 3431556Srgrimes 3441556Srgrimesstatic devclass_t adlink_devclass; 3451556Srgrimes 3461556Srgrimesstatic int 347127499Sgadadlink_probe(device_t self) 348127499Sgad{ 349127499Sgad 350127499Sgad if (pci_get_devid(self) != 0x80da10e8) 3511556Srgrimes return (ENXIO); 35213020Speter device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps"); 353127499Sgad return (BUS_PROBE_DEFAULT); 354127499Sgad} 355127499Sgad 356127499Sgadstatic struct resource_spec adlink_res_spec[] = { 35713020Speter { SYS_RES_IOPORT, PCIR_BAR(0), RF_ACTIVE}, 3581556Srgrimes { SYS_RES_IOPORT, PCIR_BAR(1), RF_ACTIVE}, 359109502Sjmallett { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE}, 3601556Srgrimes { -1, 0, 0 } 36190143Smarkm}; 3621556Srgrimes 3631556Srgrimesstatic int 3641556Srgrimesadlink_attach(device_t self) 365109502Sjmallett{ 3661556Srgrimes struct softc *sc; 36790143Smarkm int i, error; 3681556Srgrimes 3691556Srgrimes sc = device_get_softc(self); 3701556Srgrimes bzero(sc, sizeof *sc); 3711556Srgrimes sc->device = self; 3721556Srgrimes 3731556Srgrimes error = bus_alloc_resources(self, adlink_res_spec, sc->res); 3741556Srgrimes if (error) 3751556Srgrimes return (error); 3761556Srgrimes 377127499Sgad /* XXX why do we need INTR_MPSAFE if INTR_FAST was declared too?!?!? */ 378127499Sgad i = bus_setup_intr(self, sc->res[2], 379127499Sgad INTR_MPSAFE | INTR_TYPE_MISC, 380127499Sgad adlink_intr, NULL, sc, &sc->intrhand); 381127499Sgad if (i) { 382127499Sgad printf("adlink: Couldn't get FAST intr\n"); 383127499Sgad i = bus_setup_intr(self, sc->res[2], 384127499Sgad INTR_MPSAFE | INTR_TYPE_MISC, 385127499Sgad NULL, (driver_intr_t *)adlink_intr, sc, &sc->intrhand); 386127499Sgad } 387127499Sgad 388127499Sgad if (i) { 389127499Sgad bus_release_resources(self, adlink_res_spec, sc->res); 390127499Sgad return (ENODEV); 3911556Srgrimes } 392127499Sgad 3931556Srgrimes sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 39486922Sgreen sc->p0->version = PAGE0VERSION; 395109502Sjmallett sc->p0->state = STATE_RESET; 39686922Sgreen 39786922Sgreen sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 3981556Srgrimes UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 3991556Srgrimes sc->dev->si_drv1 = sc; 4001556Srgrimes 4011556Srgrimes return (0); 4021556Srgrimes} 4031556Srgrimes 404129914Sgadstatic device_method_t adlink_methods[] = { 405129914Sgad /* Device interface */ 406129914Sgad DEVMETHOD(device_probe, adlink_probe), 407129914Sgad DEVMETHOD(device_attach, adlink_attach), 408129914Sgad DEVMETHOD(device_suspend, bus_generic_suspend), 409129914Sgad DEVMETHOD(device_resume, bus_generic_resume), 410129914Sgad DEVMETHOD(device_shutdown, bus_generic_shutdown), 411129914Sgad {0, 0} 412129914Sgad}; 413129914Sgad 414129914Sgadstatic driver_t adlink_driver = { 415129914Sgad "adlink", 416129914Sgad adlink_methods, 417129914Sgad sizeof(struct softc) 418129914Sgad}; 419129914Sgad 420127499SgadDRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); 421127542Sgad 422127542Sgad#endif /* _KERNEL */ 423127499Sgad