adlink.c revision 130026
1113094Sphk/*- 2113094Sphk * Copyright (c) 2003 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. 28113094Sphk */ 29113094Sphk 30119418Sobrien#include <sys/cdefs.h> 31119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/adlink/adlink.c 130026 2004-06-03 06:10:02Z phk $"); 32119418Sobrien 33113270Sphk#ifdef _KERNEL 34113094Sphk#include <sys/param.h> 35113094Sphk#include <sys/systm.h> 36113094Sphk#include <sys/malloc.h> 37113094Sphk#include <sys/kernel.h> 38130026Sphk#include <sys/module.h> 39113270Sphk#include <sys/kthread.h> 40113094Sphk#include <sys/conf.h> 41113094Sphk#include <sys/bus.h> 42113094Sphk#include <machine/bus.h> 43113094Sphk#include <machine/resource.h> 44113094Sphk#include <sys/rman.h> 45119274Simp#include <dev/pci/pcireg.h> 46119274Simp#include <dev/pci/pcivar.h> 47113094Sphk#include <pci_if.h> 48113094Sphk#include <vm/vm.h> 49113094Sphk#include <vm/pmap.h> 50113094Sphk 51113270Sphk#endif /* _KERNEL */ 52113270Sphk 53113270Sphk#include <sys/ioccom.h> 54113270Sphk 55113270Sphkstruct wave { 56113270Sphk int index; 57113270Sphk int period; 58113270Sphk int offset; 59113270Sphk int length; 60113270Sphk int avg; 61113270Sphk off_t mapvir; 62113270Sphk int flags; 63113270Sphk 64113270Sphk int npages; 65113270Sphk void **virtual; 66113270Sphk}; 67113270Sphk 68113270Sphk#define ADLINK_SETWAVE _IOWR('A', 232, struct wave) 69113270Sphk#define ADLINK_GETWAVE _IOWR('A', 233, struct wave) 70113270Sphk 71113270Sphk#ifdef _KERNEL 72113270Sphk 73113270Sphk#define INTPERPAGE (PAGE_SIZE / sizeof(int)) 74113270Sphk#define I16PERPAGE (PAGE_SIZE / sizeof(int16_t)) 75113270Sphk 76113094Sphk/* 77113270Sphk * Sample rate 78113094Sphk */ 79113270Sphk#define SPS 1250000 80113094Sphk 81113270Sphk/* 82113270Sphk * We sample one channel (= 16 bits) at 1.25 msps giving 2.5Mbyte/sec, 83113270Sphk * 100 pages will give us about 1/6 second buffering. 84113270Sphk */ 85113270Sphk#define NRING 100 86113094Sphk 87113270Sphk/* 88113270Sphk * How many waves are we willing to entertain 89113270Sphk */ 90113270Sphk#define NWAVE 25 91113270Sphk 92113094Sphkstruct info { 93113094Sphk int nring; 94113094Sphk off_t o_ring; 95113270Sphk 96113270Sphk int ngri; 97113270Sphk int ppgri; 98113270Sphk off_t o_gri; 99113094Sphk}; 100113094Sphk 101113094Sphkstruct softc { 102113094Sphk device_t device; 103113094Sphk void *intrhand; 104113094Sphk struct resource *r0, *r1, *ri; 105113094Sphk bus_space_tag_t t0, t1; 106113094Sphk bus_space_handle_t h0, h1; 107113094Sphk dev_t dev; 108113270Sphk off_t mapvir; 109113094Sphk 110113270Sphk struct proc *procp; 111113270Sphk 112113094Sphk struct info *info; 113113094Sphk 114113270Sphk struct wave *wave[NWAVE]; 115113270Sphk 116113094Sphk int idx; 117113094Sphk void *ring[NRING]; 118113270Sphk vm_paddr_t pring[NRING]; 119113094Sphk int stat[NRING]; 120113270Sphk 121113270Sphk uint64_t cnt; 122113270Sphk 123113270Sphk u_char flags[I16PERPAGE]; 124113094Sphk}; 125113094Sphk 126113270Sphkstatic void 127113270Sphkadlink_wave(struct softc *sc, struct wave *wp, int16_t *sp) 128113270Sphk{ 129113270Sphk int f, i, k, m, *ip; 130113270Sphk 131113270Sphk f = 0; 132113270Sphk for (i = 0; i < I16PERPAGE; ) { 133113270Sphk k = (sc->cnt - wp->offset + i) % wp->period; 134113270Sphk if (k >= wp->length) { 135113270Sphk i += wp->period - k; 136113270Sphk sp += wp->period - k; 137113270Sphk continue; 138113270Sphk } 139113270Sphk m = k % INTPERPAGE; 140113270Sphk ip = (int *)(wp->virtual[k / INTPERPAGE]) + m; 141113270Sphk while (m < INTPERPAGE && i < I16PERPAGE && k < wp->length) { 142113270Sphk if (sc->flags[i] >= wp->index) 143113270Sphk *ip += (*sp * 8 - *ip) >> wp->avg; 144113270Sphk if (wp->flags & 1) 145113270Sphk sc->flags[i] = wp->index; 146113270Sphk sp++; 147113270Sphk ip++; 148113270Sphk m++; 149113270Sphk i++; 150113270Sphk k++; 151113270Sphk } 152113270Sphk } 153113270Sphk} 154113270Sphk 155113270Sphkstatic void 156113270Sphkadlink_tickle(struct softc *sc) 157113270Sphk{ 158113270Sphk 159113270Sphk wakeup(sc); 160113270Sphk tsleep(&sc->ring, PUSER | PCATCH, "tickle", 1); 161113270Sphk} 162113270Sphk 163113094Sphkstatic int 164113270Sphkadlink_new_wave(struct softc *sc, int index, int period, int offset, int length, int avg, int flags) 165113270Sphk{ 166113270Sphk struct wave *wp; 167113270Sphk int l, i; 168113270Sphk void **oldvir, **newvir; 169113270Sphk 170113270Sphk if (index < 0 || index >= NWAVE) 171113270Sphk return (EINVAL); 172113270Sphk wp = sc->wave[index]; 173113270Sphk if (wp == NULL) { 174113270Sphk adlink_tickle(sc); 175113270Sphk wp = malloc(sizeof *wp, M_DEVBUF, M_WAITOK | M_ZERO); 176113270Sphk } 177113270Sphk l = howmany(length, INTPERPAGE); 178113270Sphk /* Setting a high average here to neuter the realtime bits */ 179113270Sphk wp->avg = 31; 180113270Sphk if (wp->npages < l) { 181113270Sphk oldvir = wp->virtual; 182113270Sphk adlink_tickle(sc); 183113270Sphk newvir = malloc(sizeof(void *) * l, M_DEVBUF, M_WAITOK | M_ZERO); 184113270Sphk if (wp->npages > 0) { 185113270Sphk adlink_tickle(sc); 186113270Sphk bcopy(oldvir, newvir, wp->npages * sizeof(void *)); 187113270Sphk } 188113270Sphk for (i = wp->npages; i < l; i++) { 189113270Sphk adlink_tickle(sc); 190113270Sphk newvir[i] = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK); 191113270Sphk } 192113270Sphk wp->virtual = newvir; 193113270Sphk wp->npages = l; 194113270Sphk wp->mapvir = sc->mapvir; 195113270Sphk sc->mapvir += l * PAGE_SIZE; 196113270Sphk } else { 197113270Sphk oldvir = NULL; 198113270Sphk } 199113270Sphk wp->index = index; 200113270Sphk wp->period = period; 201113270Sphk wp->offset = offset; 202113270Sphk wp->length = length; 203113270Sphk wp->flags = flags; 204113270Sphk 205113270Sphk for (i = 0; i < l; i++) { 206113270Sphk adlink_tickle(sc); 207113270Sphk bzero(wp->virtual[i], PAGE_SIZE); 208113270Sphk } 209113270Sphk wp->avg = avg; 210113270Sphk sc->wave[index] = wp; 211113270Sphk printf("Wave[%d] {period %d, offset %d, length %d, avg %d, flags %x}\n", 212113270Sphk wp->index, wp->period, wp->offset, wp->length, wp->avg, wp->flags); 213113270Sphk free(oldvir, M_DEVBUF); 214113270Sphk return (0); 215113270Sphk} 216113270Sphk 217113270Sphkstatic void 218113270Sphkadlink_loran(void *arg) 219113270Sphk{ 220113270Sphk struct softc *sc; 221113270Sphk int idx, i; 222113270Sphk 223113270Sphk sc = arg; 224113270Sphk idx = 0; 225126673Sjhb mtx_lock(&Giant); 226113270Sphk for (;;) { 227113270Sphk while (sc->stat[idx] == 0) 228113270Sphk msleep(sc, NULL, PRIBIO, "loran", 1); 229113270Sphk memset(sc->flags, NWAVE, sizeof sc->flags); 230113270Sphk for (i = 0; i < NWAVE; i++) { 231113270Sphk if (sc->wave[i] != NULL) 232113270Sphk adlink_wave(sc, sc->wave[i], sc->ring[idx]); 233113270Sphk } 234113270Sphk sc->cnt += I16PERPAGE; 235113270Sphk sc->stat[idx] = 0; 236113270Sphk idx++; 237113270Sphk idx %= NRING; 238113270Sphk } 239126673Sjhb mtx_unlock(&Giant); 240113270Sphk kthread_exit(0); 241113270Sphk} 242113270Sphk 243113270Sphkstatic int 244113094Sphkadlink_open(dev_t dev, int oflags, int devtype, struct thread *td) 245113094Sphk{ 246113094Sphk static int once; 247113094Sphk struct softc *sc; 248113270Sphk int i, error; 249113094Sphk uint32_t u; 250113094Sphk 251113094Sphk if (once) 252113094Sphk return (0); 253113094Sphk once = 1; 254113094Sphk 255113094Sphk sc = dev->si_drv1; 256113094Sphk sc->info = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK); 257113094Sphk sc->info->nring = NRING; 258113270Sphk 259113094Sphk sc->info->o_ring = PAGE_SIZE; 260113094Sphk for (i = 0; i < NRING; i++) { 261113094Sphk sc->ring[i] = malloc(PAGE_SIZE, M_DEVBUF, M_ZERO | M_WAITOK); 262113270Sphk sc->pring[i] = vtophys(sc->ring[i]); 263113094Sphk } 264113094Sphk 265113270Sphk error = adlink_new_wave(sc, NWAVE - 1, SPS, 0, SPS, 7, 0); 266113270Sphk if (error) 267113270Sphk return (error); 268113270Sphk 269113270Sphk error = kthread_create(adlink_loran, sc, &sc->procp, 270113270Sphk 0, 0, "adlink%d", device_get_unit(sc->device)); 271113270Sphk if (error) 272113270Sphk return (error); 273113270Sphk 274113270Sphk /* Enable interrupts on write complete */ 275113094Sphk bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000); 276113270Sphk 277113270Sphk /* Sample CH0 only */ 278113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x00, 1); 279113270Sphk 280119762Sphk /* Divide clock by four */ 281113270Sphk bus_space_write_4(sc->t1, sc->h1, 0x04, 4); 282113270Sphk 283113270Sphk /* Software trigger mode: software */ 284113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x08, 0); 285113270Sphk 286113270Sphk /* Trigger level zero */ 287113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x0c, 0); 288113270Sphk 289113270Sphk /* Trigger source CH0 (not used) */ 290113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x10, 0); 291113270Sphk 292113270Sphk /* Fifo control/status: flush */ 293113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x18, 3); 294113270Sphk 295113270Sphk /* Clock source: external sine */ 296113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x20, 2); 297113094Sphk 298113270Sphk /* Set up Write DMA */ 299113270Sphk bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]); 300113094Sphk bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE); 301113094Sphk u = bus_space_read_4(sc->t0, sc->h0, 0x3c); 302113094Sphk bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600); 303113094Sphk 304113270Sphk /* Acquisition Enable Register: go! */ 305113094Sphk bus_space_write_4(sc->t1, sc->h1, 0x1c, 1); 306113094Sphk return (0); 307113094Sphk} 308113094Sphk 309113094Sphkstatic int 310113270Sphkadlink_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 311113270Sphk{ 312113270Sphk struct softc *sc; 313113270Sphk struct wave *wp; 314113270Sphk int i, error; 315113270Sphk 316113270Sphk sc = dev->si_drv1; 317113270Sphk wp = (struct wave *)data; 318113270Sphk i = wp->index; 319113270Sphk if (i < 0 || i >= NWAVE) 320113270Sphk return (EINVAL); 321113270Sphk if (cmd == ADLINK_GETWAVE) { 322113270Sphk if (sc->wave[i] == NULL) 323113270Sphk return (ENOENT); 324113270Sphk bcopy(sc->wave[i], wp, sizeof(*wp)); 325113270Sphk return (0); 326113270Sphk } 327113270Sphk if (cmd == ADLINK_SETWAVE) { 328113270Sphk error = adlink_new_wave(sc, 329113270Sphk i, 330113270Sphk wp->period, 331113270Sphk wp->offset, 332113270Sphk wp->length, 333113270Sphk wp->avg, 334113270Sphk wp->flags); 335113270Sphk if (error) 336113270Sphk return (error); 337113270Sphk bcopy(sc->wave[i], wp, sizeof(*wp)); 338113270Sphk return (0); 339113270Sphk } 340113270Sphk return (ENOIOCTL); 341113270Sphk} 342113270Sphk 343113270Sphkstatic int 344113094Sphkadlink_mmap(dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 345113094Sphk{ 346113094Sphk struct softc *sc; 347113270Sphk struct wave *wp; 348113270Sphk int i, j; 349113094Sphk 350113094Sphk sc = dev->si_drv1; 351113094Sphk if (nprot != VM_PROT_READ) 352113094Sphk return (-1); 353113270Sphk for (i = 0; i < NWAVE; i++) { 354113270Sphk if (sc->wave[i] == NULL) 355113270Sphk continue; 356113270Sphk wp = sc->wave[i]; 357113270Sphk if (offset < wp->mapvir) 358113270Sphk continue; 359113270Sphk j = (offset - wp->mapvir) / PAGE_SIZE; 360113270Sphk if (j >= wp->npages) 361113270Sphk continue; 362113270Sphk *paddr = vtophys(wp->virtual[j]); 363113094Sphk return (0); 364113094Sphk } 365113270Sphk return (-1); 366113094Sphk} 367113094Sphk 368113094Sphkstatic void 369113094Sphkadlink_intr(void *arg) 370113094Sphk{ 371113094Sphk struct softc *sc; 372113094Sphk uint32_t u; 373113270Sphk int i, j; 374113094Sphk 375113094Sphk sc = arg; 376113094Sphk u = bus_space_read_4(sc->t0, sc->h0, 0x38); 377113094Sphk if (!(u & 0x00800000)) 378113094Sphk return; 379113094Sphk bus_space_write_4(sc->t0, sc->h0, 0x38, u | 0x003f4000); 380113094Sphk 381113270Sphk j = sc->idx; 382113270Sphk sc->stat[j] = 1; 383113270Sphk i = (j + 1) % NRING; 384113094Sphk sc->idx = i; 385113270Sphk u = bus_space_read_4(sc->t1, sc->h1, 0x18); 386113270Sphk if (u & 1) { 387113270Sphk printf("adlink FIFO overrun\n"); 388113270Sphk return; 389113270Sphk } 390113270Sphk bus_space_write_4(sc->t0, sc->h0, 0x24, sc->pring[i]); 391113094Sphk bus_space_write_4(sc->t0, sc->h0, 0x28, PAGE_SIZE); 392113270Sphk wakeup(sc); 393113270Sphk if (sc->stat[i]) { 394113270Sphk printf("adlink page busy\n"); 395113270Sphk } 396113094Sphk} 397113094Sphk 398113094Sphkstatic struct cdevsw adlink_cdevsw = { 399126080Sphk .d_version = D_VERSION, 400126080Sphk .d_flags = D_NEEDGIANT, 401113094Sphk .d_open = adlink_open, 402113270Sphk .d_ioctl = adlink_ioctl, 403113094Sphk .d_mmap = adlink_mmap, 404113094Sphk .d_name = "adlink", 405113094Sphk}; 406113094Sphk 407113094Sphkstatic devclass_t adlink_devclass; 408113094Sphk 409113094Sphkstatic int 410113094Sphkadlink_probe(device_t self) 411113094Sphk{ 412113094Sphk 413113094Sphk if (pci_get_devid(self) != 0x80da10e8) 414113094Sphk return (ENXIO); 415113094Sphk device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps"); 416113094Sphk return (0); 417113094Sphk} 418113094Sphk 419113094Sphkstatic int 420113094Sphkadlink_attach(device_t self) 421113094Sphk{ 422113094Sphk struct softc *sc; 423113094Sphk int rid, i; 424113094Sphk 425113094Sphk sc = device_get_softc(self); 426113094Sphk bzero(sc, sizeof *sc); 427113094Sphk sc->device = self; 428113094Sphk 429113270Sphk /* 430113270Sphk * This is the PCI mapped registers of the AMCC 9535 "matchmaker" 431113270Sphk * chip. 432113270Sphk */ 433113094Sphk rid = 0x10; 434127135Snjl sc->r0 = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, RF_ACTIVE); 435113094Sphk if (sc->r0 == NULL) 436113094Sphk return(ENODEV); 437113094Sphk sc->t0 = rman_get_bustag(sc->r0); 438113094Sphk sc->h0 = rman_get_bushandle(sc->r0); 439113094Sphk printf("Res0 %x %x\n", sc->t0, sc->h0); 440113094Sphk 441113270Sphk /* 442113270Sphk * This is the PCI mapped registers of the ADC hardware, they 443113270Sphk * are described in the manual which comes with the card. 444113270Sphk */ 445113094Sphk rid = 0x14; 446127135Snjl sc->r1 = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid, RF_ACTIVE); 447113094Sphk if (sc->r1 == NULL) 448113094Sphk return(ENODEV); 449113094Sphk sc->t1 = rman_get_bustag(sc->r1); 450113094Sphk sc->h1 = rman_get_bushandle(sc->r1); 451113094Sphk printf("Res1 %x %x\n", sc->t1, sc->h1); 452113094Sphk 453113094Sphk rid = 0x0; 454127135Snjl sc->ri = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, 455127135Snjl RF_ACTIVE | RF_SHAREABLE); 456113094Sphk if (sc->ri == NULL) 457113094Sphk return (ENODEV); 458113094Sphk 459113270Sphk i = bus_setup_intr(self, sc->ri, INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST, 460113094Sphk adlink_intr, sc, &sc->intrhand); 461113270Sphk if (i) { 462113270Sphk printf("adlink: Couldn't get FAST intr\n"); 463113270Sphk i = bus_setup_intr(self, sc->ri, INTR_TYPE_MISC, 464113270Sphk adlink_intr, sc, &sc->intrhand); 465113270Sphk } 466113094Sphk 467113094Sphk if (i) 468113094Sphk return (ENODEV); 469113094Sphk 470113094Sphk sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 471113094Sphk UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 472113094Sphk sc->dev->si_drv1 = sc; 473113094Sphk 474113094Sphk return (0); 475113094Sphk} 476113094Sphk 477113094Sphkstatic device_method_t adlink_methods[] = { 478113094Sphk /* Device interface */ 479113094Sphk DEVMETHOD(device_probe, adlink_probe), 480113094Sphk DEVMETHOD(device_attach, adlink_attach), 481113094Sphk DEVMETHOD(device_suspend, bus_generic_suspend), 482113094Sphk DEVMETHOD(device_resume, bus_generic_resume), 483113094Sphk DEVMETHOD(device_shutdown, bus_generic_shutdown), 484113094Sphk {0, 0} 485113094Sphk}; 486113094Sphk 487113094Sphkstatic driver_t adlink_driver = { 488113094Sphk "adlink", 489113094Sphk adlink_methods, 490113094Sphk sizeof(struct softc) 491113094Sphk}; 492113094Sphk 493113094SphkDRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); 494113270Sphk#endif /* _KERNEL */ 495