dt2811.c revision 25985edcedea6396277003854657b5f3cb31a628
1/* 2 comedi/drivers/dt2811.c 3 Hardware driver for Data Translation DT2811 4 5 COMEDI - Linux Control and Measurement Device Interface 6 History: 7 Base Version - David A. Schleef <ds@schleef.org> 8 December 1998 - Updated to work. David does not have a DT2811 9 board any longer so this was suffering from bitrot. 10 Updated performed by ... 11 12 This program is free software; you can redistribute it and/or modify 13 it under the terms of the GNU General Public License as published by 14 the Free Software Foundation; either version 2 of the License, or 15 (at your option) any later version. 16 17 This program is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program; if not, write to the Free Software 24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 25 */ 26/* 27Driver: dt2811 28Description: Data Translation DT2811 29Author: ds 30Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh) 31Status: works 32 33Configuration options: 34 [0] - I/O port base address 35 [1] - IRQ, although this is currently unused 36 [2] - A/D reference 37 0 = signle-ended 38 1 = differential 39 2 = pseudo-differential (common reference) 40 [3] - A/D range 41 0 = [-5, 5] 42 1 = [-2.5, 2.5] 43 2 = [0, 5] 44 [4] - D/A 0 range (same choices) 45 [4] - D/A 1 range (same choices) 46*/ 47 48#include <linux/interrupt.h> 49#include "../comedidev.h" 50 51#include <linux/ioport.h> 52 53static const char *driver_name = "dt2811"; 54 55static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 56 4, { 57 RANGE(0, 5), 58 RANGE(0, 2.5), 59 RANGE(0, 1.25), 60 RANGE(0, 0.625) 61 } 62}; 63 64static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 65 4, { 66 RANGE(-2.5, 2.5), 67 RANGE(-1.25, 1.25), 68 RANGE(-0.625, 0.625), 69 RANGE(-0.3125, 0.3125) 70 } 71}; 72 73static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 74 4, { 75 RANGE(-5, 5), 76 RANGE(-2.5, 2.5), 77 RANGE(-1.25, 1.25), 78 RANGE(-0.625, 0.625) 79 } 80}; 81 82static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 83 4, { 84 RANGE(0, 5), 85 RANGE(0, 0.5), 86 RANGE(0, 0.05), 87 RANGE(0, 0.01) 88 } 89}; 90 91static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 92 4, { 93 RANGE(-2.5, 2.5), 94 RANGE(-0.25, 0.25), 95 RANGE(-0.025, 0.025), 96 RANGE(-0.005, 0.005) 97 } 98}; 99 100static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 101 4, { 102 RANGE(-5, 5), 103 RANGE(-0.5, 0.5), 104 RANGE(-0.05, 0.05), 105 RANGE(-0.01, 0.01) 106 } 107}; 108 109/* 110 111 0x00 ADCSR R/W A/D Control/Status Register 112 bit 7 - (R) 1 indicates A/D conversion done 113 reading ADDAT clears bit 114 (W) ignored 115 bit 6 - (R) 1 indicates A/D error 116 (W) ignored 117 bit 5 - (R) 1 indicates A/D busy, cleared at end 118 of conversion 119 (W) ignored 120 bit 4 - (R) 0 121 (W) 122 bit 3 - (R) 0 123 bit 2 - (R/W) 1 indicates interrupts enabled 124 bits 1,0 - (R/W) mode bits 125 00 single conversion on ADGCR load 126 01 continuous conversion, internal clock, 127 (clock enabled on ADGCR load) 128 10 continuous conversion, internal clock, 129 external trigger 130 11 continuous conversion, external clock, 131 external trigger 132 133 0x01 ADGCR R/W A/D Gain/Channel Register 134 bit 6,7 - (R/W) gain select 135 00 gain=1, both PGH, PGL models 136 01 gain=2 PGH, 10 PGL 137 10 gain=4 PGH, 100 PGL 138 11 gain=8 PGH, 500 PGL 139 bit 4,5 - reserved 140 bit 3-0 - (R/W) channel select 141 channel number from 0-15 142 143 0x02,0x03 (R) ADDAT A/D Data Register 144 (W) DADAT0 D/A Data Register 0 145 0x02 low byte 146 0x03 high byte 147 148 0x04,0x05 (W) DADAT0 D/A Data Register 1 149 150 0x06 (R) DIO0 Digital Input Port 0 151 (W) DIO1 Digital Output Port 1 152 153 0x07 TMRCTR (R/W) Timer/Counter Register 154 bits 6,7 - reserved 155 bits 5-3 - Timer frequency control (mantissa) 156 543 divisor freqency (kHz) 157 000 1 600 158 001 10 60 159 010 2 300 160 011 3 200 161 100 4 150 162 101 5 120 163 110 6 100 164 111 12 50 165 bits 2-0 - Timer frequency control (exponent) 166 210 multiply divisor/divide frequency by 167 000 1 168 001 10 169 010 100 170 011 1000 171 100 10000 172 101 100000 173 110 1000000 174 111 10000000 175 176 */ 177 178#define TIMEOUT 10000 179 180#define DT2811_SIZE 8 181 182#define DT2811_ADCSR 0 183#define DT2811_ADGCR 1 184#define DT2811_ADDATLO 2 185#define DT2811_ADDATHI 3 186#define DT2811_DADAT0LO 2 187#define DT2811_DADAT0HI 3 188#define DT2811_DADAT1LO 4 189#define DT2811_DADAT1HI 5 190#define DT2811_DIO 6 191#define DT2811_TMRCTR 7 192 193/* 194 * flags 195 */ 196 197/* ADCSR */ 198 199#define DT2811_ADDONE 0x80 200#define DT2811_ADERROR 0x40 201#define DT2811_ADBUSY 0x20 202#define DT2811_CLRERROR 0x10 203#define DT2811_INTENB 0x04 204#define DT2811_ADMODE 0x03 205 206struct dt2811_board { 207 208 const char *name; 209 const struct comedi_lrange *bip_5; 210 const struct comedi_lrange *bip_2_5; 211 const struct comedi_lrange *unip_5; 212}; 213 214static const struct dt2811_board boardtypes[] = { 215 {"dt2811-pgh", 216 &range_dt2811_pgh_ai_5_bipolar, 217 &range_dt2811_pgh_ai_2_5_bipolar, 218 &range_dt2811_pgh_ai_5_unipolar, 219 }, 220 {"dt2811-pgl", 221 &range_dt2811_pgl_ai_5_bipolar, 222 &range_dt2811_pgl_ai_2_5_bipolar, 223 &range_dt2811_pgl_ai_5_unipolar, 224 }, 225}; 226 227#define this_board ((const struct dt2811_board *)dev->board_ptr) 228 229static int dt2811_attach(struct comedi_device *dev, 230 struct comedi_devconfig *it); 231static int dt2811_detach(struct comedi_device *dev); 232static struct comedi_driver driver_dt2811 = { 233 .driver_name = "dt2811", 234 .module = THIS_MODULE, 235 .attach = dt2811_attach, 236 .detach = dt2811_detach, 237 .board_name = &boardtypes[0].name, 238 .num_names = ARRAY_SIZE(boardtypes), 239 .offset = sizeof(struct dt2811_board), 240}; 241 242static int __init driver_dt2811_init_module(void) 243{ 244 return comedi_driver_register(&driver_dt2811); 245} 246 247static void __exit driver_dt2811_cleanup_module(void) 248{ 249 comedi_driver_unregister(&driver_dt2811); 250} 251 252module_init(driver_dt2811_init_module); 253module_exit(driver_dt2811_cleanup_module); 254 255static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 256 struct comedi_insn *insn, unsigned int *data); 257static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, 258 struct comedi_insn *insn, unsigned int *data); 259static int dt2811_ao_insn_read(struct comedi_device *dev, 260 struct comedi_subdevice *s, 261 struct comedi_insn *insn, unsigned int *data); 262static int dt2811_di_insn_bits(struct comedi_device *dev, 263 struct comedi_subdevice *s, 264 struct comedi_insn *insn, unsigned int *data); 265static int dt2811_do_insn_bits(struct comedi_device *dev, 266 struct comedi_subdevice *s, 267 struct comedi_insn *insn, unsigned int *data); 268 269enum { card_2811_pgh, card_2811_pgl }; 270 271struct dt2811_private { 272 int ntrig; 273 int curadchan; 274 enum { 275 adc_singleended, adc_diff, adc_pseudo_diff 276 } adc_mux; 277 enum { 278 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 279 } dac_range[2]; 280 const struct comedi_lrange *range_type_list[2]; 281 unsigned int ao_readback[2]; 282}; 283 284#define devpriv ((struct dt2811_private *)dev->private) 285 286static const struct comedi_lrange *dac_range_types[] = { 287 &range_bipolar5, 288 &range_bipolar2_5, 289 &range_unipolar5 290}; 291 292#define DT2811_TIMEOUT 5 293 294#if 0 295static irqreturn_t dt2811_interrupt(int irq, void *d) 296{ 297 int lo, hi; 298 int data; 299 struct comedi_device *dev = d; 300 301 if (!dev->attached) { 302 comedi_error(dev, "spurious interrupt"); 303 return IRQ_HANDLED; 304 } 305 306 lo = inb(dev->iobase + DT2811_ADDATLO); 307 hi = inb(dev->iobase + DT2811_ADDATHI); 308 309 data = lo + (hi << 8); 310 311 if (!(--devpriv->ntrig)) { 312 /* how to turn off acquisition */ 313 s->async->events |= COMEDI_SB_EOA; 314 } 315 comedi_event(dev, s); 316 return IRQ_HANDLED; 317} 318#endif 319 320/* 321 options[0] Board base address 322 options[1] IRQ 323 options[2] Input configuration 324 0 == single-ended 325 1 == differential 326 2 == pseudo-differential 327 options[3] Analog input range configuration 328 0 == bipolar 5 (-5V -- +5V) 329 1 == bipolar 2.5V (-2.5V -- +2.5V) 330 2 == unipolar 5V (0V -- +5V) 331 options[4] Analog output 0 range configuration 332 0 == bipolar 5 (-5V -- +5V) 333 1 == bipolar 2.5V (-2.5V -- +2.5V) 334 2 == unipolar 5V (0V -- +5V) 335 options[5] Analog output 1 range configuration 336 0 == bipolar 5 (-5V -- +5V) 337 1 == bipolar 2.5V (-2.5V -- +2.5V) 338 2 == unipolar 5V (0V -- +5V) 339*/ 340 341static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) 342{ 343 /* int i, irq; */ 344 /* unsigned long irqs; */ 345 /* long flags; */ 346 347 int ret; 348 struct comedi_subdevice *s; 349 unsigned long iobase; 350 351 iobase = it->options[0]; 352 353 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase); 354 355 if (!request_region(iobase, DT2811_SIZE, driver_name)) { 356 printk(KERN_ERR "I/O port conflict\n"); 357 return -EIO; 358 } 359 360 dev->iobase = iobase; 361 dev->board_name = this_board->name; 362 363#if 0 364 outb(0, dev->iobase + DT2811_ADCSR); 365 udelay(100); 366 i = inb(dev->iobase + DT2811_ADDATLO); 367 i = inb(dev->iobase + DT2811_ADDATHI); 368#endif 369 370#if 0 371 irq = it->options[1]; 372 if (irq < 0) { 373 save_flags(flags); 374 sti(); 375 irqs = probe_irq_on(); 376 377 outb(DT2811_CLRERROR | DT2811_INTENB, 378 dev->iobase + DT2811_ADCSR); 379 outb(0, dev->iobase + DT2811_ADGCR); 380 381 udelay(100); 382 383 irq = probe_irq_off(irqs); 384 restore_flags(flags); 385 386 /*outb(DT2811_CLRERROR|DT2811_INTENB, 387 dev->iobase+DT2811_ADCSR);*/ 388 389 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) 390 printk(KERN_ERR "error probing irq (bad)\n"); 391 dev->irq = 0; 392 if (irq > 0) { 393 i = inb(dev->iobase + DT2811_ADDATLO); 394 i = inb(dev->iobase + DT2811_ADDATHI); 395 printk(KERN_INFO "(irq = %d)\n", irq); 396 ret = request_irq(irq, dt2811_interrupt, 0, 397 driver_name, dev); 398 if (ret < 0) 399 return -EIO; 400 dev->irq = irq; 401 } else if (irq == 0) { 402 printk(KERN_INFO "(no irq)\n"); 403 } else { 404 printk(KERN_ERR "( multiple irq's -- this is bad! )\n"); 405 } 406 } 407#endif 408 409 ret = alloc_subdevices(dev, 4); 410 if (ret < 0) 411 return ret; 412 413 ret = alloc_private(dev, sizeof(struct dt2811_private)); 414 if (ret < 0) 415 return ret; 416 417 switch (it->options[2]) { 418 case 0: 419 devpriv->adc_mux = adc_singleended; 420 break; 421 case 1: 422 devpriv->adc_mux = adc_diff; 423 break; 424 case 2: 425 devpriv->adc_mux = adc_pseudo_diff; 426 break; 427 default: 428 devpriv->adc_mux = adc_singleended; 429 break; 430 } 431 switch (it->options[4]) { 432 case 0: 433 devpriv->dac_range[0] = dac_bipolar_5; 434 break; 435 case 1: 436 devpriv->dac_range[0] = dac_bipolar_2_5; 437 break; 438 case 2: 439 devpriv->dac_range[0] = dac_unipolar_5; 440 break; 441 default: 442 devpriv->dac_range[0] = dac_bipolar_5; 443 break; 444 } 445 switch (it->options[5]) { 446 case 0: 447 devpriv->dac_range[1] = dac_bipolar_5; 448 break; 449 case 1: 450 devpriv->dac_range[1] = dac_bipolar_2_5; 451 break; 452 case 2: 453 devpriv->dac_range[1] = dac_unipolar_5; 454 break; 455 default: 456 devpriv->dac_range[1] = dac_bipolar_5; 457 break; 458 } 459 460 s = dev->subdevices + 0; 461 /* initialize the ADC subdevice */ 462 s->type = COMEDI_SUBD_AI; 463 s->subdev_flags = SDF_READABLE | SDF_GROUND; 464 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; 465 s->insn_read = dt2811_ai_insn; 466 s->maxdata = 0xfff; 467 switch (it->options[3]) { 468 case 0: 469 default: 470 s->range_table = this_board->bip_5; 471 break; 472 case 1: 473 s->range_table = this_board->bip_2_5; 474 break; 475 case 2: 476 s->range_table = this_board->unip_5; 477 break; 478 } 479 480 s = dev->subdevices + 1; 481 /* ao subdevice */ 482 s->type = COMEDI_SUBD_AO; 483 s->subdev_flags = SDF_WRITABLE; 484 s->n_chan = 2; 485 s->insn_write = dt2811_ao_insn; 486 s->insn_read = dt2811_ao_insn_read; 487 s->maxdata = 0xfff; 488 s->range_table_list = devpriv->range_type_list; 489 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; 490 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; 491 492 s = dev->subdevices + 2; 493 /* di subdevice */ 494 s->type = COMEDI_SUBD_DI; 495 s->subdev_flags = SDF_READABLE; 496 s->n_chan = 8; 497 s->insn_bits = dt2811_di_insn_bits; 498 s->maxdata = 1; 499 s->range_table = &range_digital; 500 501 s = dev->subdevices + 3; 502 /* do subdevice */ 503 s->type = COMEDI_SUBD_DO; 504 s->subdev_flags = SDF_WRITABLE; 505 s->n_chan = 8; 506 s->insn_bits = dt2811_do_insn_bits; 507 s->maxdata = 1; 508 s->state = 0; 509 s->range_table = &range_digital; 510 511 return 0; 512} 513 514static int dt2811_detach(struct comedi_device *dev) 515{ 516 printk(KERN_INFO "comedi%d: dt2811: remove\n", dev->minor); 517 518 if (dev->irq) 519 free_irq(dev->irq, dev); 520 if (dev->iobase) 521 release_region(dev->iobase, DT2811_SIZE); 522 523 return 0; 524} 525 526static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 527 struct comedi_insn *insn, unsigned int *data) 528{ 529 int chan = CR_CHAN(insn->chanspec); 530 int timeout = DT2811_TIMEOUT; 531 int i; 532 533 for (i = 0; i < insn->n; i++) { 534 outb(chan, dev->iobase + DT2811_ADGCR); 535 536 while (timeout 537 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY) 538 timeout--; 539 if (!timeout) 540 return -ETIME; 541 542 data[i] = inb(dev->iobase + DT2811_ADDATLO); 543 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; 544 data[i] &= 0xfff; 545 } 546 547 return i; 548} 549 550#if 0 551/* Wow. This is code from the Comedi stone age. But it hasn't been 552 * replaced, so I'll let it stay. */ 553int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig) 554{ 555 struct comedi_device *dev = comedi_devices + minor; 556 557 if (adtrig->n < 1) 558 return 0; 559 dev->curadchan = adtrig->chan; 560 switch (dev->i_admode) { 561 case COMEDI_MDEMAND: 562 dev->ntrig = adtrig->n - 1; 563 /* not necessary */ 564 /*printk("dt2811: AD soft trigger\n"); */ 565 /*outb(DT2811_CLRERROR|DT2811_INTENB, 566 dev->iobase+DT2811_ADCSR); */ 567 outb(dev->curadchan, dev->iobase + DT2811_ADGCR); 568 do_gettimeofday(&trigtime); 569 break; 570 case COMEDI_MCONTS: 571 dev->ntrig = adtrig->n; 572 break; 573 } 574 575 return 0; 576} 577#endif 578 579static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, 580 struct comedi_insn *insn, unsigned int *data) 581{ 582 int i; 583 int chan; 584 585 chan = CR_CHAN(insn->chanspec); 586 587 for (i = 0; i < insn->n; i++) { 588 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); 589 outb((data[i] >> 8) & 0xff, 590 dev->iobase + DT2811_DADAT0HI + 2 * chan); 591 devpriv->ao_readback[chan] = data[i]; 592 } 593 594 return i; 595} 596 597static int dt2811_ao_insn_read(struct comedi_device *dev, 598 struct comedi_subdevice *s, 599 struct comedi_insn *insn, unsigned int *data) 600{ 601 int i; 602 int chan; 603 604 chan = CR_CHAN(insn->chanspec); 605 606 for (i = 0; i < insn->n; i++) 607 data[i] = devpriv->ao_readback[chan]; 608 609 return i; 610} 611 612static int dt2811_di_insn_bits(struct comedi_device *dev, 613 struct comedi_subdevice *s, 614 struct comedi_insn *insn, unsigned int *data) 615{ 616 if (insn->n != 2) 617 return -EINVAL; 618 619 data[1] = inb(dev->iobase + DT2811_DIO); 620 621 return 2; 622} 623 624static int dt2811_do_insn_bits(struct comedi_device *dev, 625 struct comedi_subdevice *s, 626 struct comedi_insn *insn, unsigned int *data) 627{ 628 if (insn->n != 2) 629 return -EINVAL; 630 631 s->state &= ~data[0]; 632 s->state |= data[0] & data[1]; 633 outb(s->state, dev->iobase + DT2811_DIO); 634 635 data[1] = s->state; 636 637 return 2; 638} 639 640MODULE_AUTHOR("Comedi http://www.comedi.org"); 641MODULE_DESCRIPTION("Comedi low-level driver"); 642MODULE_LICENSE("GPL"); 643