dt2811.c revision 3b9fdcd5e85104e622c0ec5f626c81b831ddfae2
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 242COMEDI_INITCLEANUP(driver_dt2811); 243 244static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 245 struct comedi_insn *insn, unsigned int *data); 246static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, 247 struct comedi_insn *insn, unsigned int *data); 248static int dt2811_ao_insn_read(struct comedi_device *dev, 249 struct comedi_subdevice *s, 250 struct comedi_insn *insn, unsigned int *data); 251static int dt2811_di_insn_bits(struct comedi_device *dev, 252 struct comedi_subdevice *s, 253 struct comedi_insn *insn, unsigned int *data); 254static int dt2811_do_insn_bits(struct comedi_device *dev, 255 struct comedi_subdevice *s, 256 struct comedi_insn *insn, unsigned int *data); 257 258enum { card_2811_pgh, card_2811_pgl }; 259 260struct dt2811_private { 261 int ntrig; 262 int curadchan; 263 enum { 264 adc_singleended, adc_diff, adc_pseudo_diff 265 } adc_mux; 266 enum { 267 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 268 } dac_range[2]; 269 const struct comedi_lrange *range_type_list[2]; 270 unsigned int ao_readback[2]; 271}; 272 273#define devpriv ((struct dt2811_private *)dev->private) 274 275static const struct comedi_lrange *dac_range_types[] = { 276 &range_bipolar5, 277 &range_bipolar2_5, 278 &range_unipolar5 279}; 280 281#define DT2811_TIMEOUT 5 282 283#if 0 284static irqreturn_t dt2811_interrupt(int irq, void *d) 285{ 286 int lo, hi; 287 int data; 288 struct comedi_device *dev = d; 289 290 if (!dev->attached) { 291 comedi_error(dev, "spurious interrupt"); 292 return IRQ_HANDLED; 293 } 294 295 lo = inb(dev->iobase + DT2811_ADDATLO); 296 hi = inb(dev->iobase + DT2811_ADDATHI); 297 298 data = lo + (hi << 8); 299 300 if (!(--devpriv->ntrig)) { 301 /* how to turn off acquisition */ 302 s->async->events |= COMEDI_SB_EOA; 303 } 304 comedi_event(dev, s); 305 return IRQ_HANDLED; 306} 307#endif 308 309/* 310 options[0] Board base address 311 options[1] IRQ 312 options[2] Input configuration 313 0 == single-ended 314 1 == differential 315 2 == pseudo-differential 316 options[3] Analog input range configuration 317 0 == bipolar 5 (-5V -- +5V) 318 1 == bipolar 2.5V (-2.5V -- +2.5V) 319 2 == unipolar 5V (0V -- +5V) 320 options[4] Analog output 0 range configuration 321 0 == bipolar 5 (-5V -- +5V) 322 1 == bipolar 2.5V (-2.5V -- +2.5V) 323 2 == unipolar 5V (0V -- +5V) 324 options[5] Analog output 1 range configuration 325 0 == bipolar 5 (-5V -- +5V) 326 1 == bipolar 2.5V (-2.5V -- +2.5V) 327 2 == unipolar 5V (0V -- +5V) 328*/ 329 330static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) 331{ 332 /* int i, irq; */ 333 /* unsigned long irqs; */ 334 /* long flags; */ 335 336 int ret; 337 struct comedi_subdevice *s; 338 unsigned long iobase; 339 340 iobase = it->options[0]; 341 342 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase); 343 344 if (!request_region(iobase, DT2811_SIZE, driver_name)) { 345 printk(KERN_ERR "I/O port conflict\n"); 346 return -EIO; 347 } 348 349 dev->iobase = iobase; 350 dev->board_name = this_board->name; 351 352#if 0 353 outb(0, dev->iobase + DT2811_ADCSR); 354 udelay(100); 355 i = inb(dev->iobase + DT2811_ADDATLO); 356 i = inb(dev->iobase + DT2811_ADDATHI); 357#endif 358 359#if 0 360 irq = it->options[1]; 361 if (irq < 0) { 362 save_flags(flags); 363 sti(); 364 irqs = probe_irq_on(); 365 366 outb(DT2811_CLRERROR | DT2811_INTENB, 367 dev->iobase + DT2811_ADCSR); 368 outb(0, dev->iobase + DT2811_ADGCR); 369 370 udelay(100); 371 372 irq = probe_irq_off(irqs); 373 restore_flags(flags); 374 375 /*outb(DT2811_CLRERROR|DT2811_INTENB, 376 dev->iobase+DT2811_ADCSR);*/ 377 378 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) 379 printk(KERN_ERR "error probing irq (bad)\n"); 380 dev->irq = 0; 381 if (irq > 0) { 382 i = inb(dev->iobase + DT2811_ADDATLO); 383 i = inb(dev->iobase + DT2811_ADDATHI); 384 printk(KERN_INFO "(irq = %d)\n", irq); 385 ret = request_irq(irq, dt2811_interrupt, 0, 386 driver_name, dev); 387 if (ret < 0) 388 return -EIO; 389 dev->irq = irq; 390 } else if (irq == 0) { 391 printk(KERN_INFO "(no irq)\n"); 392 } else { 393 printk(KERN_ERR "( multiple irq's -- this is bad! )\n"); 394 } 395 } 396#endif 397 398 ret = alloc_subdevices(dev, 4); 399 if (ret < 0) 400 return ret; 401 402 ret = alloc_private(dev, sizeof(struct dt2811_private)); 403 if (ret < 0) 404 return ret; 405 406 switch (it->options[2]) { 407 case 0: 408 devpriv->adc_mux = adc_singleended; 409 break; 410 case 1: 411 devpriv->adc_mux = adc_diff; 412 break; 413 case 2: 414 devpriv->adc_mux = adc_pseudo_diff; 415 break; 416 default: 417 devpriv->adc_mux = adc_singleended; 418 break; 419 } 420 switch (it->options[4]) { 421 case 0: 422 devpriv->dac_range[0] = dac_bipolar_5; 423 break; 424 case 1: 425 devpriv->dac_range[0] = dac_bipolar_2_5; 426 break; 427 case 2: 428 devpriv->dac_range[0] = dac_unipolar_5; 429 break; 430 default: 431 devpriv->dac_range[0] = dac_bipolar_5; 432 break; 433 } 434 switch (it->options[5]) { 435 case 0: 436 devpriv->dac_range[1] = dac_bipolar_5; 437 break; 438 case 1: 439 devpriv->dac_range[1] = dac_bipolar_2_5; 440 break; 441 case 2: 442 devpriv->dac_range[1] = dac_unipolar_5; 443 break; 444 default: 445 devpriv->dac_range[1] = dac_bipolar_5; 446 break; 447 } 448 449 s = dev->subdevices + 0; 450 /* initialize the ADC subdevice */ 451 s->type = COMEDI_SUBD_AI; 452 s->subdev_flags = SDF_READABLE | SDF_GROUND; 453 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; 454 s->insn_read = dt2811_ai_insn; 455 s->maxdata = 0xfff; 456 switch (it->options[3]) { 457 case 0: 458 default: 459 s->range_table = this_board->bip_5; 460 break; 461 case 1: 462 s->range_table = this_board->bip_2_5; 463 break; 464 case 2: 465 s->range_table = this_board->unip_5; 466 break; 467 } 468 469 s = dev->subdevices + 1; 470 /* ao subdevice */ 471 s->type = COMEDI_SUBD_AO; 472 s->subdev_flags = SDF_WRITABLE; 473 s->n_chan = 2; 474 s->insn_write = dt2811_ao_insn; 475 s->insn_read = dt2811_ao_insn_read; 476 s->maxdata = 0xfff; 477 s->range_table_list = devpriv->range_type_list; 478 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; 479 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; 480 481 s = dev->subdevices + 2; 482 /* di subdevice */ 483 s->type = COMEDI_SUBD_DI; 484 s->subdev_flags = SDF_READABLE; 485 s->n_chan = 8; 486 s->insn_bits = dt2811_di_insn_bits; 487 s->maxdata = 1; 488 s->range_table = &range_digital; 489 490 s = dev->subdevices + 3; 491 /* do subdevice */ 492 s->type = COMEDI_SUBD_DO; 493 s->subdev_flags = SDF_WRITABLE; 494 s->n_chan = 8; 495 s->insn_bits = dt2811_do_insn_bits; 496 s->maxdata = 1; 497 s->state = 0; 498 s->range_table = &range_digital; 499 500 return 0; 501} 502 503static int dt2811_detach(struct comedi_device *dev) 504{ 505 printk(KERN_INFO "comedi%d: dt2811: remove\n", dev->minor); 506 507 if (dev->irq) 508 free_irq(dev->irq, dev); 509 if (dev->iobase) 510 release_region(dev->iobase, DT2811_SIZE); 511 512 return 0; 513} 514 515static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 516 struct comedi_insn *insn, unsigned int *data) 517{ 518 int chan = CR_CHAN(insn->chanspec); 519 int timeout = DT2811_TIMEOUT; 520 int i; 521 522 for (i = 0; i < insn->n; i++) { 523 outb(chan, dev->iobase + DT2811_ADGCR); 524 525 while (timeout 526 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY) 527 timeout--; 528 if (!timeout) 529 return -ETIME; 530 531 data[i] = inb(dev->iobase + DT2811_ADDATLO); 532 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; 533 data[i] &= 0xfff; 534 } 535 536 return i; 537} 538 539#if 0 540/* Wow. This is code from the Comedi stone age. But it hasn't been 541 * replaced, so I'll let it stay. */ 542int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig) 543{ 544 struct comedi_device *dev = comedi_devices + minor; 545 546 if (adtrig->n < 1) 547 return 0; 548 dev->curadchan = adtrig->chan; 549 switch (dev->i_admode) { 550 case COMEDI_MDEMAND: 551 dev->ntrig = adtrig->n - 1; 552 /* not neccessary */ 553 /*printk("dt2811: AD soft trigger\n"); */ 554 /*outb(DT2811_CLRERROR|DT2811_INTENB, 555 dev->iobase+DT2811_ADCSR); */ 556 outb(dev->curadchan, dev->iobase + DT2811_ADGCR); 557 do_gettimeofday(&trigtime); 558 break; 559 case COMEDI_MCONTS: 560 dev->ntrig = adtrig->n; 561 break; 562 } 563 564 return 0; 565} 566#endif 567 568static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s, 569 struct comedi_insn *insn, unsigned int *data) 570{ 571 int i; 572 int chan; 573 574 chan = CR_CHAN(insn->chanspec); 575 576 for (i = 0; i < insn->n; i++) { 577 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); 578 outb((data[i] >> 8) & 0xff, 579 dev->iobase + DT2811_DADAT0HI + 2 * chan); 580 devpriv->ao_readback[chan] = data[i]; 581 } 582 583 return i; 584} 585 586static int dt2811_ao_insn_read(struct comedi_device *dev, 587 struct comedi_subdevice *s, 588 struct comedi_insn *insn, unsigned int *data) 589{ 590 int i; 591 int chan; 592 593 chan = CR_CHAN(insn->chanspec); 594 595 for (i = 0; i < insn->n; i++) 596 data[i] = devpriv->ao_readback[chan]; 597 598 return i; 599} 600 601static int dt2811_di_insn_bits(struct comedi_device *dev, 602 struct comedi_subdevice *s, 603 struct comedi_insn *insn, unsigned int *data) 604{ 605 if (insn->n != 2) 606 return -EINVAL; 607 608 data[1] = inb(dev->iobase + DT2811_DIO); 609 610 return 2; 611} 612 613static int dt2811_do_insn_bits(struct comedi_device *dev, 614 struct comedi_subdevice *s, 615 struct comedi_insn *insn, unsigned int *data) 616{ 617 if (insn->n != 2) 618 return -EINVAL; 619 620 s->state &= ~data[0]; 621 s->state |= data[0] & data[1]; 622 outb(s->state, dev->iobase + DT2811_DIO); 623 624 data[1] = s->state; 625 626 return 2; 627} 628