1 /*- 2 * Copyright (c) 2003-2004 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The names of the authors may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * This is a device driver or the Adlink 9812 and 9810 ADC cards, mainly 30 * intended to support Software Defined Radio reception of timesignals 31 * in the VLF band. See http://phk.freebsd.dk/loran-c 32 * 33 * The driver is configured with ioctls which define a ringbuffer with 34 * a given number of chunks in it. The a control structure and the 35 * ringbuffer can then be mmap(2)'ed into userland and the application 36 * can operate on the data directly. 37 * 38 * Tested with 10MHz external clock, divisor of 2 (ie: 5MHz sampling), 39 * One channel active (ie: 2 bytes per sample = 10MB/sec) on a 660MHz 40 * Celeron PC. 41 * 42 */ 43 44 #ifdef _KERNEL 45 #include <sys/cdefs.h> 46 __FBSDID("$FreeBSD$"); 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/malloc.h> 51 #include <sys/kernel.h> 52 #include <sys/module.h> 53 #include <sys/kthread.h> 54 #include <sys/conf.h> 55 #include <sys/bus.h> 56 #include <machine/bus.h> 57 #include <machine/resource.h> 58 #include <sys/rman.h> 59 #include <dev/pci/pcireg.h> 60 #include <dev/pci/pcivar.h> 61 #include <pci_if.h> 62 #include <vm/vm.h> 63 #include <vm/pmap.h> 64 65 #endif /* _KERNEL */ 66 67 #include <sys/ioccom.h> 68 69 #define ADLINK_SETDIVISOR _IOWR('A', 255, u_int) /* divisor */ 70 #define ADLINK_SETCHUNKSIZE _IOWR('A', 254, u_int) /* bytes */ 71 #define ADLINK_SETRINGSIZE _IOWR('A', 253, u_int) /* bytes */ 72 #define ADLINK_START _IOWR('A', 252, u_int) /* dummy */ 73 #define ADLINK_STOP _IOWR('A', 251, u_int) /* dummy */ 74 #define ADLINK_RESET _IOWR('A', 250, u_int) /* dummy */ 75 76 struct page0 { 77 u_int version; 78 int state; 79 # define STATE_RESET -1 80 # define STATE_RUN 0 81 u_int divisor; /* int */ 82 u_int chunksize; /* bytes */ 83 u_int ringsize; /* chunks */ 84 u_int o_ringgen; /* 85 * offset of ring generation 86 * array 87 */ 88 u_int o_ring; /* offset of ring */ 89 }; 90 91 #define PAGE0VERSION 20031021 92 93 #ifdef _KERNEL 94 95 struct pgstat { 96 u_int *genp; 97 u_int gen; 98 vm_paddr_t phys; 99 void *virt; 100 struct pgstat *next; 101 }; 102 103 struct softc { 104 device_t device; 105 void *intrhand; 106 struct resource *r0, *r1, *ri; 107 bus_space_tag_t t0, t1; 108 bus_space_handle_t h0, h1; 109 struct cdev *dev; 110 off_t mapvir; 111 int error; 112 struct page0 *p0; 113 u_int nchunks; 114 struct pgstat *chunks; 115 struct pgstat *next; 116 }; 117 118 static d_ioctl_t adlink_ioctl; 119 static d_mmap_t adlink_mmap; 120 static void adlink_intr(void *arg); 121 122 static struct cdevsw adlink_cdevsw = { 123 .d_version = D_VERSION, 124 .d_ioctl = adlink_ioctl, 125 .d_mmap = adlink_mmap, 126 .d_name = "adlink", 127 }; 128 129 static void 130 adlink_intr(void *arg) 131 { 132 struct softc *sc; 133 struct pgstat *pg; 134 uint32_t u; 135 136 sc = arg; 137 u = bus_space_read_4(sc->t0, sc->h0, 0x38); 138 if (!(u & 0x00800000)) 139 return; 140 bus_space_write_4(sc->t0, sc->h0, 0x38, u | 0x003f4000); 141 142 pg = sc->next; 143 *(pg->genp) = ++pg->gen; 144 145 u = bus_space_read_4(sc->t1, sc->h1, 0x18); 146 if (u & 1) 147 sc->p0->state = EIO; 148 149 if (sc->p0->state != STATE_RUN) { 150 printf("adlink: stopping %d\n", sc->p0->state); 151 return; 152 } 153 154 pg = pg->next; 155 sc->next = pg; 156 *(pg->genp) = 0; 157 bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys); 158 bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize); 159 wakeup(sc); 160 } 161 162 static int 163 adlink_mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) 164 { 165 struct softc *sc; 166 vm_offset_t o; 167 int i; 168 struct pgstat *pg; 169 170 sc = dev->si_drv1; 171 if (nprot != VM_PROT_READ) 172 return (-1); 173 if (offset == 0) { 174 *paddr = vtophys(sc->p0); 175 return (0); 176 } 177 o = PAGE_SIZE; 178 pg = sc->chunks; 179 for (i = 0; i < sc->nchunks; i++, pg++) { 180 if (offset - o >= sc->p0->chunksize) { 181 o += sc->p0->chunksize; 182 continue; 183 } 184 *paddr = pg->phys + (offset - o); 185 return (0); 186 } 187 return (-1); 188 } 189 190 static int 191 adlink_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) 192 { 193 struct softc *sc; 194 int i, error; 195 u_int u; 196 struct pgstat *pg; 197 u_int *genp; 198 199 sc = dev->si_drv1; 200 u = *(u_int*)data; 201 error = 0; 202 switch (cmd) { 203 case ADLINK_SETDIVISOR: 204 if (sc->p0->state == STATE_RUN) 205 return (EBUSY); 206 if (u & 1) 207 return (EINVAL); 208 sc->p0->divisor = u; 209 break; 210 case ADLINK_SETCHUNKSIZE: 211 if (sc->p0->state != STATE_RESET) 212 return (EBUSY); 213 if (u % PAGE_SIZE) 214 return (EINVAL); 215 if (sc->p0->ringsize != 0 && sc->p0->ringsize % u) 216 return (EINVAL); 217 sc->p0->chunksize = u; 218 break; 219 case ADLINK_SETRINGSIZE: 220 if (sc->p0->state != STATE_RESET) 221 return (EBUSY); 222 if (u % PAGE_SIZE) 223 return (EINVAL); 224 if (sc->p0->chunksize != 0 && u % sc->p0->chunksize) 225 return (EINVAL); 226 sc->p0->ringsize = u; 227 break; 228 case ADLINK_START: 229 if (sc->p0->state == STATE_RUN) 230 return (EBUSY); 231 if (sc->p0->state == STATE_RESET) { 232 233 if (sc->p0->chunksize == 0) 234 sc->p0->chunksize = 4 * PAGE_SIZE; 235 if (sc->p0->ringsize == 0) 236 sc->p0->ringsize = 16 * sc->p0->chunksize; 237 if (sc->p0->divisor == 0) 238 sc->p0->divisor = 4; 239 240 sc->nchunks = sc->p0->ringsize / sc->p0->chunksize; 241 if (sc->nchunks * sizeof (*pg->genp) + 242 sizeof *sc->p0 > PAGE_SIZE) 243 return (EINVAL); 244 sc->p0->o_ring = PAGE_SIZE; 245 genp = (u_int *)(sc->p0 + 1); 246 sc->p0->o_ringgen = (intptr_t)genp - (intptr_t)(sc->p0); 247 pg = malloc(sizeof *pg * sc->nchunks, 248 M_DEVBUF, M_WAITOK | M_ZERO); 249 sc->chunks = pg; 250 for (i = 0; i < sc->nchunks; i++) { 251 pg->genp = genp; 252 *pg->genp = 1; 253 genp++; 254 pg->virt = contigmalloc(sc->p0->chunksize, 255 M_DEVBUF, M_WAITOK, 256 0ul, 0xfffffffful, 257 PAGE_SIZE, 0); 258 pg->phys = vtophys(pg->virt); 259 if (i == sc->nchunks - 1) 260 pg->next = sc->chunks; 261 else 262 pg->next = pg + 1; 263 pg++; 264 } 265 sc->next = sc->chunks; 266 } 267 268 /* Reset generation numbers */ 269 pg = sc->chunks; 270 for (i = 0; i < sc->nchunks; i++) { 271 *pg->genp = 0; 272 pg->gen = 0; 273 pg++; 274 } 275 276 /* Enable interrupts on write complete */ 277 bus_space_write_4(sc->t0, sc->h0, 0x38, 0x00004000); 278 279 /* Sample CH0 only */ 280 bus_space_write_4(sc->t1, sc->h1, 0x00, 1); 281 282 /* Divide clock by four */ 283 bus_space_write_4(sc->t1, sc->h1, 0x04, sc->p0->divisor); 284 285 /* Software trigger mode: software */ 286 bus_space_write_4(sc->t1, sc->h1, 0x08, 0); 287 288 /* Trigger level zero */ 289 bus_space_write_4(sc->t1, sc->h1, 0x0c, 0); 290 291 /* Trigger source CH0 (not used) */ 292 bus_space_write_4(sc->t1, sc->h1, 0x10, 0); 293 294 /* Fifo control/status: flush */ 295 bus_space_write_4(sc->t1, sc->h1, 0x18, 3); 296 297 /* Clock source: external sine */ 298 bus_space_write_4(sc->t1, sc->h1, 0x20, 2); 299 300 /* Chipmunks are go! */ 301 sc->p0->state = STATE_RUN; 302 303 /* Set up Write DMA */ 304 pg = sc->next = sc->chunks; 305 *(pg->genp) = 0; 306 bus_space_write_4(sc->t0, sc->h0, 0x24, pg->phys); 307 bus_space_write_4(sc->t0, sc->h0, 0x28, sc->p0->chunksize); 308 u = bus_space_read_4(sc->t0, sc->h0, 0x3c); 309 bus_space_write_4(sc->t0, sc->h0, 0x3c, u | 0x00000600); 310 311 /* Acquisition Enable Register: go! */ 312 bus_space_write_4(sc->t1, sc->h1, 0x1c, 1); 313 314 break; 315 case ADLINK_STOP: 316 if (sc->p0->state == STATE_RESET) 317 break; 318 sc->p0->state = EINTR; 319 while (*(sc->next->genp) == 0) 320 tsleep(sc, PUSER | PCATCH, "adstop", 1); 321 break; 322 #ifdef notyet 323 /* 324 * I'm not sure we can actually do this. How do we revoke 325 * the mmap'ed pages from any process having them mmapped ? 326 */ 327 case ADLINK_RESET: 328 if (sc->p0->state == STATE_RESET) 329 break; 330 sc->p0->state = EINTR; 331 while (*(sc->next->genp) == 0) 332 tsleep(sc, PUSER | PCATCH, "adreset", 1); 333 /* deallocate ring buffer */ 334 break; 335 #endif 336 default: 337 error = ENOIOCTL; 338 break; 339 } 340 return (error); 341 } 342 343 static devclass_t adlink_devclass; 344 345 static int 346 adlink_probe(device_t self) 347 { 348 349 if (pci_get_devid(self) != 0x80da10e8) 350 return (ENXIO); 351 device_set_desc(self, "Adlink PCI-9812 4 ch 12 bit 20 msps"); 352 return (0); 353 } 354 355 static int 356 adlink_attach(device_t self) 357 { 358 struct softc *sc; 359 int rid, i; 360 361 sc = device_get_softc(self); 362 bzero(sc, sizeof *sc); 363 sc->device = self; 364 365 /* 366 * This is the PCI mapped registers of the AMCC 9535 "matchmaker" 367 * chip. 368 */ 369 rid = 0x10; 370 sc->r0 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 371 0, ~0, 1, RF_ACTIVE); 372 if (sc->r0 == NULL) 373 return(ENODEV); 374 sc->t0 = rman_get_bustag(sc->r0); 375 sc->h0 = rman_get_bushandle(sc->r0); 376 printf("Res0 %x %x\n", sc->t0, sc->h0); 377 378 /* 379 * This is the PCI mapped registers of the ADC hardware, they 380 * are described in the manual which comes with the card. 381 */ 382 rid = 0x14; 383 sc->r1 = bus_alloc_resource(self, SYS_RES_IOPORT, &rid, 384 0, ~0, 1, RF_ACTIVE); 385 if (sc->r1 == NULL) 386 return(ENODEV); 387 sc->t1 = rman_get_bustag(sc->r1); 388 sc->h1 = rman_get_bushandle(sc->r1); 389 printf("Res1 %x %x\n", sc->t1, sc->h1); 390 391 rid = 0x0; 392 sc->ri = bus_alloc_resource(self, SYS_RES_IRQ, &rid, 393 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 394 if (sc->ri == NULL) 395 return (ENODEV); 396 397 i = bus_setup_intr(self, sc->ri, 398 INTR_MPSAFE | INTR_TYPE_MISC | INTR_FAST, 399 adlink_intr, sc, &sc->intrhand); 400 if (i) { 401 printf("adlink: Couldn't get FAST intr\n"); 402 i = bus_setup_intr(self, sc->ri, 403 INTR_MPSAFE | INTR_TYPE_MISC, 404 adlink_intr, sc, &sc->intrhand); 405 } 406 407 if (i) 408 return (ENODEV); 409 410 sc->p0 = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO); 411 sc->p0->version = PAGE0VERSION; 412 sc->p0->state = STATE_RESET; 413 414 sc->dev = make_dev(&adlink_cdevsw, device_get_unit(self), 415 UID_ROOT, GID_WHEEL, 0444, "adlink%d", device_get_unit(self)); 416 sc->dev->si_drv1 = sc; 417 418 return (0); 419 } 420 421 static device_method_t adlink_methods[] = { 422 /* Device interface */ 423 DEVMETHOD(device_probe, adlink_probe), 424 DEVMETHOD(device_attach, adlink_attach), 425 DEVMETHOD(device_suspend, bus_generic_suspend), 426 DEVMETHOD(device_resume, bus_generic_resume), 427 DEVMETHOD(device_shutdown, bus_generic_shutdown), 428 {0, 0} 429 }; 430 431 static driver_t adlink_driver = { 432 "adlink", 433 adlink_methods, 434 sizeof(struct softc) 435 }; 436 437 DRIVER_MODULE(adlink, pci, adlink_driver, adlink_devclass, 0, 0); 438 439 #endif /* _KERNEL */ 440