adlink.c revision 143844
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 143844 2005-03-19 12:55:46Z 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; 105113094Sphk struct resource *r0, *r1, *ri; 106113094Sphk bus_space_tag_t t0, t1; 107113094Sphk bus_space_handle_t h0, h1; 108135482Sphk struct cdev *dev; 109113270Sphk off_t mapvir; 110135482Sphk int error; 111135482Sphk struct page0 *p0; 112135482Sphk u_int nchunks; 113135482Sphk struct pgstat *chunks; 114135482Sphk struct pgstat *next; 115143844Sphk uint64_t sample; 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 142143844Sphk sc->sample += sc->p0->chunksize / 2; 143135482Sphk pg = sc->next; 144143844Sphk *(pg->sample) = sc->sample; 145113270Sphk 146135482Sphk u = bus_space_read_4(sc->t1, sc->h1, 0x18); 147135482Sphk if (u & 1) 148135482Sphk sc->p0->state = EIO; 149113094Sphk 150135482Sphk if (sc->p0->state != STATE_RUN) { 151135482Sphk printf("adlink: stopping %d\n", sc->p0->state); 152135482Sphk return; 153113270Sphk } 154113270Sphk 155135482Sphk pg = pg->next; 156135482Sphk sc->next = pg; 157143844Sphk *(pg->sample) = 0; 158135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys); 159135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize); 160113270Sphk wakeup(sc); 161113270Sphk} 162113270Sphk 163113094Sphkstatic int 164135482Sphkadlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 165113270Sphk{ 166135482Sphk struct softc *sc; 167135482Sphk vm_offset_t o; 168135482Sphk int i; 169135482Sphk struct pgstat *pg; 170113270Sphk 171135482Sphk sc = dev->si_drv1; 172135482Sphk if (nprot != VM_PROT_READ) 173135482Sphk return (-1); 174135482Sphk if (offset == 0) { 175135482Sphk *paddr = vtophys(sc->p0); 176135482Sphk return (0); 177113270Sphk } 178135482Sphk o = PAGE_SIZE; 179135482Sphk pg = sc->chunks; 180135482Sphk for (i = 0; i < sc->nchunks; i++, pg++) { 181135482Sphk if (offset - o >= sc->p0->chunksize) { 182135482Sphk o += sc->p0->chunksize; 183135482Sphk continue; 184113270Sphk } 185135482Sphk *paddr = pg->phys + (offset - o); 186135482Sphk return (0); 187113270Sphk } 188135482Sphk return (-1); 189113270Sphk} 190113270Sphk 191113270Sphkstatic int 192135482Sphkadlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 193113094Sphk{ 194113094Sphk struct softc *sc; 195113270Sphk int i, error; 196135482Sphk u_int u; 197135482Sphk struct pgstat *pg; 198143844Sphk uint64_t *sample; 199135482Sphk 200113094Sphk sc = dev->si_drv1; 201135482Sphk u = *(u_int*)data; 202135482Sphk error = 0; 203135482Sphk switch (cmd) { 204135482Sphk case ADLINK_SETDIVISOR: 205135482Sphk if (sc->p0->state == STATE_RUN) 206135482Sphk return (EBUSY); 207135482Sphk if (u & 1) 208135482Sphk return (EINVAL); 209135482Sphk sc->p0->divisor = u; 210135482Sphk break; 211135482Sphk case ADLINK_SETCHUNKSIZE: 212135482Sphk if (sc->p0->state != STATE_RESET) 213135482Sphk return (EBUSY); 214135482Sphk if (u % PAGE_SIZE) 215135482Sphk return (EINVAL); 216135482Sphk if (sc->p0->ringsize != 0 && sc->p0->ringsize % u) 217135482Sphk return (EINVAL); 218135482Sphk sc->p0->chunksize = u; 219135482Sphk break; 220135482Sphk case ADLINK_SETRINGSIZE: 221135482Sphk if (sc->p0->state != STATE_RESET) 222135482Sphk return (EBUSY); 223135482Sphk if (u % PAGE_SIZE) 224135482Sphk return (EINVAL); 225135482Sphk if (sc->p0->chunksize != 0 && u % sc->p0->chunksize) 226135482Sphk return (EINVAL); 227135482Sphk sc->p0->ringsize = u; 228135482Sphk break; 229135482Sphk case ADLINK_START: 230135482Sphk if (sc->p0->state == STATE_RUN) 231135482Sphk return (EBUSY); 232135482Sphk if (sc->p0->state == STATE_RESET) { 233135482Sphk 234135482Sphk if (sc->p0->chunksize == 0) 235135482Sphk sc->p0->chunksize = 4 * PAGE_SIZE; 236135482Sphk if (sc->p0->ringsize == 0) 237135482Sphk sc->p0->ringsize = 16 * sc->p0->chunksize; 238135482Sphk if (sc->p0->divisor == 0) 239135482Sphk sc->p0->divisor = 4; 240113270Sphk 241135482Sphk sc->nchunks = sc->p0->ringsize / sc->p0->chunksize; 242143844Sphk if (sc->nchunks * sizeof (*pg->sample) + 243135482Sphk sizeof *sc->p0 > PAGE_SIZE) 244135482Sphk return (EINVAL); 245135482Sphk sc->p0->o_ring = PAGE_SIZE; 246143844Sphk sample = (uint64_t *)(sc->p0 + 1); 247143844Sphk sc->p0->o_sample = 248143844Sphk (uintptr_t)sample - (uintptr_t)(sc->p0); 249135482Sphk pg = malloc(sizeof *pg * sc->nchunks, 250135482Sphk M_DEVBUF, M_WAITOK | M_ZERO); 251135482Sphk sc->chunks = pg; 252135482Sphk for (i = 0; i < sc->nchunks; i++) { 253143844Sphk pg->sample = sample; 254143844Sphk *pg->sample = 0; 255143844Sphk sample++; 256135482Sphk pg->virt = contigmalloc(sc->p0->chunksize, 257135482Sphk M_DEVBUF, M_WAITOK, 258135482Sphk 0ul, 0xfffffffful, 259135482Sphk PAGE_SIZE, 0); 260135482Sphk pg->phys = vtophys(pg->virt); 261135482Sphk if (i == sc->nchunks - 1) 262135482Sphk pg->next = sc->chunks; 263135482Sphk else 264135482Sphk pg->next = pg + 1; 265135482Sphk pg++; 266135482Sphk } 267135482Sphk sc->next = sc->chunks; 268135482Sphk } 269113094Sphk 270135482Sphk /* Reset generation numbers */ 271135482Sphk pg = sc->chunks; 272135482Sphk for (i = 0; i < sc->nchunks; i++) { 273143844Sphk *pg->sample = 0; 274135482Sphk pg++; 275135482Sphk } 276113270Sphk 277135482Sphk /* Enable interrupts on write complete */ 278135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000); 279113270Sphk 280135482Sphk /* Sample CH0 only */ 281135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x00, 1); 282113270Sphk 283135482Sphk /* Divide clock by four */ 284135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x04, sc->p0->divisor); 285113270Sphk 286135482Sphk /* Software trigger mode: software */ 287135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x08, 0); 288113270Sphk 289135482Sphk /* Trigger level zero */ 290135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x0c, 0); 291113270Sphk 292135482Sphk /* Trigger source CH0 (not used) */ 293135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x10, 0); 294113270Sphk 295135482Sphk /* Fifo control/status: flush */ 296135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x18, 3); 297113270Sphk 298135482Sphk /* Clock source: external sine */ 299135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x20, 2); 300113270Sphk 301135482Sphk /* Chipmunks are go! */ 302135482Sphk sc->p0->state = STATE_RUN; 303113094Sphk 304135482Sphk /* Set up Write DMA */ 305135482Sphk pg = sc->next = sc->chunks; 306143844Sphk *(pg->sample) = 0; 307135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys); 308135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize); 309135482Sphk u = bus_space_read_4(sc->t0, sc->h0, 0x3c); 310135482Sphk bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600); 311113094Sphk 312135482Sphk /* Acquisition Enable Register: go! */ 313135482Sphk bus_space_write_4(sc->t1, sc->h1, 0x1c, 1); 314113094Sphk 315135482Sphk break; 316135482Sphk case ADLINK_STOP: 317135482Sphk if (sc->p0->state == STATE_RESET) 318135482Sphk break; 319135482Sphk sc->p0->state = EINTR; 320143844Sphk while (*(sc->next->sample) == 0) 321135482Sphk tsleep(sc, PUSER | PCATCH, "adstop", 1); 322135482Sphk break; 323135482Sphk#ifdef notyet 324135482Sphk /* 325135482Sphk * I'm not sure we can actually do this. How do we revoke 326135482Sphk * the mmap'ed pages from any process having them mmapped ? 327135482Sphk */ 328135482Sphk case ADLINK_RESET: 329135482Sphk if (sc->p0->state == STATE_RESET) 330135482Sphk break; 331135482Sphk sc->p0->state = EINTR; 332143844Sphk while (*(sc->next->samp) == 0) 333135482Sphk tsleep(sc, PUSER | PCATCH, "adreset", 1); 334135482Sphk /* deallocate ring buffer */ 335135482Sphk break; 336135482Sphk#endif 337135482Sphk default: 338135482Sphk error = ENOIOCTL; 339135482Sphk break; 340113270Sphk } 341135482Sphk return (error); 342113270Sphk} 343113270Sphk 344113094Sphkstatic devclass_t adlink_devclass; 345113094Sphk 346113094Sphkstatic int 347113094Sphkadlink_probe(device_t self) 348113094Sphk{ 349113094Sphk 350113094Sphk if (pci_get_devid(self) != 0x80da10e8) 351113094Sphk return (ENXIO); 352113094Sphk device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps"); 353143168Simp return (BUS_PROBE_DEFAULT); 354113094Sphk} 355113094Sphk 356113094Sphkstatic int 357113094Sphkadlink_attach(device_t self) 358113094Sphk{ 359113094Sphk struct softc *sc; 360113094Sphk int rid, i; 361113094Sphk 362113094Sphk sc = device_get_softc(self); 363113094Sphk bzero(sc, sizeof *sc); 364113094Sphk sc->device = self; 365113094Sphk 366113270Sphk /* 367113270Sphk * This is the PCI mapped registers of the AMCC 9535 "matchmaker" 368113270Sphk * chip. 369113270Sphk */ 370113094Sphk rid = 0x10; 371135482Sphk sc->r0 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 372135482Sphk 0, ~0, 1, RF_ACTIVE); 373113094Sphk if (sc->r0 == NULL) 374113094Sphk return(ENODEV); 375113094Sphk sc->t0 = rman_get_bustag(sc->r0); 376113094Sphk sc->h0 = rman_get_bushandle(sc->r0); 377113094Sphk printf("Res0 %x %x\n", sc->t0, sc->h0); 378113094Sphk 379113270Sphk /* 380113270Sphk * This is the PCI mapped registers of the ADC hardware, they 381113270Sphk * are described in the manual which comes with the card. 382113270Sphk */ 383113094Sphk rid = 0x14; 384135482Sphk sc->r1 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 385135482Sphk 0, ~0, 1, RF_ACTIVE); 386113094Sphk if (sc->r1 == NULL) 387113094Sphk return(ENODEV); 388113094Sphk sc->t1 = rman_get_bustag(sc->r1); 389113094Sphk sc->h1 = rman_get_bushandle(sc->r1); 390113094Sphk printf("Res1 %x %x\n", sc->t1, sc->h1); 391113094Sphk 392113094Sphk rid = 0x0; 393135482Sphk sc->ri = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 394135482Sphk 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 395113094Sphk if (sc->ri == NULL) 396113094Sphk return (ENODEV); 397113094Sphk 398135482Sphk i = bus_setup_intr(self, sc->ri, 399135482Sphk INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST, 400113094Sphk adlink_intr, sc, &sc->intrhand); 401113270Sphk if (i) { 402113270Sphk printf("adlink: Couldn't get FAST intr\n"); 403135482Sphk i = bus_setup_intr(self, sc->ri, 404135482Sphk INTR_MPSAFE | INTR_TYPE_MISC, 405113270Sphk adlink_intr, sc, &sc->intrhand); 406113270Sphk } 407113094Sphk 408113094Sphk if (i) 409113094Sphk return (ENODEV); 410113094Sphk 411135482Sphk sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 412135482Sphk sc->p0->version = PAGE0VERSION; 413135482Sphk sc->p0->state = STATE_RESET; 414135482Sphk 415113094Sphk sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 416113094Sphk UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 417113094Sphk sc->dev->si_drv1 = sc; 418113094Sphk 419113094Sphk return (0); 420113094Sphk} 421113094Sphk 422113094Sphkstatic device_method_t adlink_methods[] = { 423113094Sphk /* Device interface */ 424113094Sphk DEVMETHOD(device_probe, adlink_probe), 425113094Sphk DEVMETHOD(device_attach, adlink_attach), 426113094Sphk DEVMETHOD(device_suspend, bus_generic_suspend), 427113094Sphk DEVMETHOD(device_resume, bus_generic_resume), 428113094Sphk DEVMETHOD(device_shutdown, bus_generic_shutdown), 429113094Sphk {0, 0} 430113094Sphk}; 431113094Sphk 432113094Sphkstatic driver_t adlink_driver = { 433113094Sphk "adlink", 434113094Sphk adlink_methods, 435113094Sphk sizeof(struct softc) 436113094Sphk}; 437113094Sphk 438113094SphkDRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); 439135482Sphk 440113270Sphk#endif /* _KERNEL */ 441