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