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