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