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/* 23Driver: dt2811 24Description: Data Translation DT2811 25Author: ds 26Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh) 27Status: works 28 29Configuration options: 30 [0] - I/O port base address 31 [1] - IRQ, although this is currently unused 32 [2] - A/D reference 33 0 = signle-ended 34 1 = differential 35 2 = pseudo-differential (common reference) 36 [3] - A/D range 37 0 = [-5, 5] 38 1 = [-2.5, 2.5] 39 2 = [0, 5] 40 [4] - D/A 0 range (same choices) 41 [4] - D/A 1 range (same choices) 42*/ 43 44#include <linux/module.h> 45#include "../comedidev.h" 46 47static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 48 4, { 49 UNI_RANGE(5), 50 UNI_RANGE(2.5), 51 UNI_RANGE(1.25), 52 UNI_RANGE(0.625) 53 } 54}; 55 56static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 57 4, { 58 BIP_RANGE(2.5), 59 BIP_RANGE(1.25), 60 BIP_RANGE(0.625), 61 BIP_RANGE(0.3125) 62 } 63}; 64 65static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 66 4, { 67 BIP_RANGE(5), 68 BIP_RANGE(2.5), 69 BIP_RANGE(1.25), 70 BIP_RANGE(0.625) 71 } 72}; 73 74static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 75 4, { 76 UNI_RANGE(5), 77 UNI_RANGE(0.5), 78 UNI_RANGE(0.05), 79 UNI_RANGE(0.01) 80 } 81}; 82 83static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 84 4, { 85 BIP_RANGE(2.5), 86 BIP_RANGE(0.25), 87 BIP_RANGE(0.025), 88 BIP_RANGE(0.005) 89 } 90}; 91 92static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 93 4, { 94 BIP_RANGE(5), 95 BIP_RANGE(0.5), 96 BIP_RANGE(0.05), 97 BIP_RANGE(0.01) 98 } 99}; 100 101/* 102 103 0x00 ADCSR R/W A/D Control/Status Register 104 bit 7 - (R) 1 indicates A/D conversion done 105 reading ADDAT clears bit 106 (W) ignored 107 bit 6 - (R) 1 indicates A/D error 108 (W) ignored 109 bit 5 - (R) 1 indicates A/D busy, cleared at end 110 of conversion 111 (W) ignored 112 bit 4 - (R) 0 113 (W) 114 bit 3 - (R) 0 115 bit 2 - (R/W) 1 indicates interrupts enabled 116 bits 1,0 - (R/W) mode bits 117 00 single conversion on ADGCR load 118 01 continuous conversion, internal clock, 119 (clock enabled on ADGCR load) 120 10 continuous conversion, internal clock, 121 external trigger 122 11 continuous conversion, external clock, 123 external trigger 124 125 0x01 ADGCR R/W A/D Gain/Channel Register 126 bit 6,7 - (R/W) gain select 127 00 gain=1, both PGH, PGL models 128 01 gain=2 PGH, 10 PGL 129 10 gain=4 PGH, 100 PGL 130 11 gain=8 PGH, 500 PGL 131 bit 4,5 - reserved 132 bit 3-0 - (R/W) channel select 133 channel number from 0-15 134 135 0x02,0x03 (R) ADDAT A/D Data Register 136 (W) DADAT0 D/A Data Register 0 137 0x02 low byte 138 0x03 high byte 139 140 0x04,0x05 (W) DADAT0 D/A Data Register 1 141 142 0x06 (R) DIO0 Digital Input Port 0 143 (W) DIO1 Digital Output Port 1 144 145 0x07 TMRCTR (R/W) Timer/Counter Register 146 bits 6,7 - reserved 147 bits 5-3 - Timer frequency control (mantissa) 148 543 divisor freqency (kHz) 149 000 1 600 150 001 10 60 151 010 2 300 152 011 3 200 153 100 4 150 154 101 5 120 155 110 6 100 156 111 12 50 157 bits 2-0 - Timer frequency control (exponent) 158 210 multiply divisor/divide frequency by 159 000 1 160 001 10 161 010 100 162 011 1000 163 100 10000 164 101 100000 165 110 1000000 166 111 10000000 167 168 */ 169 170#define TIMEOUT 10000 171 172#define DT2811_ADCSR 0 173#define DT2811_ADGCR 1 174#define DT2811_ADDATLO 2 175#define DT2811_ADDATHI 3 176#define DT2811_DADAT0LO 2 177#define DT2811_DADAT0HI 3 178#define DT2811_DADAT1LO 4 179#define DT2811_DADAT1HI 5 180#define DT2811_DIO 6 181#define DT2811_TMRCTR 7 182 183/* 184 * flags 185 */ 186 187/* ADCSR */ 188 189#define DT2811_ADDONE 0x80 190#define DT2811_ADERROR 0x40 191#define DT2811_ADBUSY 0x20 192#define DT2811_CLRERROR 0x10 193#define DT2811_INTENB 0x04 194#define DT2811_ADMODE 0x03 195 196struct dt2811_board { 197 198 const char *name; 199 const struct comedi_lrange *bip_5; 200 const struct comedi_lrange *bip_2_5; 201 const struct comedi_lrange *unip_5; 202}; 203 204enum { card_2811_pgh, card_2811_pgl }; 205 206struct dt2811_private { 207 int ntrig; 208 int curadchan; 209 enum { 210 adc_singleended, adc_diff, adc_pseudo_diff 211 } adc_mux; 212 enum { 213 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 214 } dac_range[2]; 215 const struct comedi_lrange *range_type_list[2]; 216}; 217 218static const struct comedi_lrange *dac_range_types[] = { 219 &range_bipolar5, 220 &range_bipolar2_5, 221 &range_unipolar5 222}; 223 224static int dt2811_ai_eoc(struct comedi_device *dev, 225 struct comedi_subdevice *s, 226 struct comedi_insn *insn, 227 unsigned long context) 228{ 229 unsigned int status; 230 231 status = inb(dev->iobase + DT2811_ADCSR); 232 if ((status & DT2811_ADBUSY) == 0) 233 return 0; 234 return -EBUSY; 235} 236 237static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 238 struct comedi_insn *insn, unsigned int *data) 239{ 240 int chan = CR_CHAN(insn->chanspec); 241 int ret; 242 int i; 243 244 for (i = 0; i < insn->n; i++) { 245 outb(chan, dev->iobase + DT2811_ADGCR); 246 247 ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0); 248 if (ret) 249 return ret; 250 251 data[i] = inb(dev->iobase + DT2811_ADDATLO); 252 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; 253 data[i] &= 0xfff; 254 } 255 256 return i; 257} 258 259static int dt2811_ao_insn_write(struct comedi_device *dev, 260 struct comedi_subdevice *s, 261 struct comedi_insn *insn, 262 unsigned int *data) 263{ 264 unsigned int chan = CR_CHAN(insn->chanspec); 265 unsigned int val = s->readback[chan]; 266 int i; 267 268 for (i = 0; i < insn->n; i++) { 269 val = data[i]; 270 outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); 271 outb((val >> 8) & 0xff, 272 dev->iobase + DT2811_DADAT0HI + 2 * chan); 273 } 274 s->readback[chan] = val; 275 276 return insn->n; 277} 278 279static int dt2811_di_insn_bits(struct comedi_device *dev, 280 struct comedi_subdevice *s, 281 struct comedi_insn *insn, unsigned int *data) 282{ 283 data[1] = inb(dev->iobase + DT2811_DIO); 284 285 return insn->n; 286} 287 288static int dt2811_do_insn_bits(struct comedi_device *dev, 289 struct comedi_subdevice *s, 290 struct comedi_insn *insn, 291 unsigned int *data) 292{ 293 if (comedi_dio_update_state(s, data)) 294 outb(s->state, dev->iobase + DT2811_DIO); 295 296 data[1] = s->state; 297 298 return insn->n; 299} 300 301/* 302 options[0] Board base address 303 options[1] IRQ 304 options[2] Input configuration 305 0 == single-ended 306 1 == differential 307 2 == pseudo-differential 308 options[3] Analog input range configuration 309 0 == bipolar 5 (-5V -- +5V) 310 1 == bipolar 2.5V (-2.5V -- +2.5V) 311 2 == unipolar 5V (0V -- +5V) 312 options[4] Analog output 0 range configuration 313 0 == bipolar 5 (-5V -- +5V) 314 1 == bipolar 2.5V (-2.5V -- +2.5V) 315 2 == unipolar 5V (0V -- +5V) 316 options[5] Analog output 1 range configuration 317 0 == bipolar 5 (-5V -- +5V) 318 1 == bipolar 2.5V (-2.5V -- +2.5V) 319 2 == unipolar 5V (0V -- +5V) 320*/ 321static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) 322{ 323 /* int i; */ 324 const struct dt2811_board *board = dev->board_ptr; 325 struct dt2811_private *devpriv; 326 int ret; 327 struct comedi_subdevice *s; 328 329 ret = comedi_request_region(dev, it->options[0], 0x8); 330 if (ret) 331 return ret; 332 333#if 0 334 outb(0, dev->iobase + DT2811_ADCSR); 335 udelay(100); 336 i = inb(dev->iobase + DT2811_ADDATLO); 337 i = inb(dev->iobase + DT2811_ADDATHI); 338#endif 339 340 ret = comedi_alloc_subdevices(dev, 4); 341 if (ret) 342 return ret; 343 344 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 345 if (!devpriv) 346 return -ENOMEM; 347 348 switch (it->options[2]) { 349 case 0: 350 devpriv->adc_mux = adc_singleended; 351 break; 352 case 1: 353 devpriv->adc_mux = adc_diff; 354 break; 355 case 2: 356 devpriv->adc_mux = adc_pseudo_diff; 357 break; 358 default: 359 devpriv->adc_mux = adc_singleended; 360 break; 361 } 362 switch (it->options[4]) { 363 case 0: 364 devpriv->dac_range[0] = dac_bipolar_5; 365 break; 366 case 1: 367 devpriv->dac_range[0] = dac_bipolar_2_5; 368 break; 369 case 2: 370 devpriv->dac_range[0] = dac_unipolar_5; 371 break; 372 default: 373 devpriv->dac_range[0] = dac_bipolar_5; 374 break; 375 } 376 switch (it->options[5]) { 377 case 0: 378 devpriv->dac_range[1] = dac_bipolar_5; 379 break; 380 case 1: 381 devpriv->dac_range[1] = dac_bipolar_2_5; 382 break; 383 case 2: 384 devpriv->dac_range[1] = dac_unipolar_5; 385 break; 386 default: 387 devpriv->dac_range[1] = dac_bipolar_5; 388 break; 389 } 390 391 s = &dev->subdevices[0]; 392 /* initialize the ADC subdevice */ 393 s->type = COMEDI_SUBD_AI; 394 s->subdev_flags = SDF_READABLE | SDF_GROUND; 395 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; 396 s->insn_read = dt2811_ai_insn; 397 s->maxdata = 0xfff; 398 switch (it->options[3]) { 399 case 0: 400 default: 401 s->range_table = board->bip_5; 402 break; 403 case 1: 404 s->range_table = board->bip_2_5; 405 break; 406 case 2: 407 s->range_table = board->unip_5; 408 break; 409 } 410 411 s = &dev->subdevices[1]; 412 /* ao subdevice */ 413 s->type = COMEDI_SUBD_AO; 414 s->subdev_flags = SDF_WRITABLE; 415 s->n_chan = 2; 416 s->maxdata = 0xfff; 417 s->range_table_list = devpriv->range_type_list; 418 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; 419 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; 420 s->insn_write = dt2811_ao_insn_write; 421 s->insn_read = comedi_readback_insn_read; 422 423 ret = comedi_alloc_subdev_readback(s); 424 if (ret) 425 return ret; 426 427 s = &dev->subdevices[2]; 428 /* di subdevice */ 429 s->type = COMEDI_SUBD_DI; 430 s->subdev_flags = SDF_READABLE; 431 s->n_chan = 8; 432 s->insn_bits = dt2811_di_insn_bits; 433 s->maxdata = 1; 434 s->range_table = &range_digital; 435 436 s = &dev->subdevices[3]; 437 /* do subdevice */ 438 s->type = COMEDI_SUBD_DO; 439 s->subdev_flags = SDF_WRITABLE; 440 s->n_chan = 8; 441 s->insn_bits = dt2811_do_insn_bits; 442 s->maxdata = 1; 443 s->state = 0; 444 s->range_table = &range_digital; 445 446 return 0; 447} 448 449static const struct dt2811_board boardtypes[] = { 450 { 451 .name = "dt2811-pgh", 452 .bip_5 = &range_dt2811_pgh_ai_5_bipolar, 453 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar, 454 .unip_5 = &range_dt2811_pgh_ai_5_unipolar, 455 }, { 456 .name = "dt2811-pgl", 457 .bip_5 = &range_dt2811_pgl_ai_5_bipolar, 458 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar, 459 .unip_5 = &range_dt2811_pgl_ai_5_unipolar, 460 }, 461}; 462 463static struct comedi_driver dt2811_driver = { 464 .driver_name = "dt2811", 465 .module = THIS_MODULE, 466 .attach = dt2811_attach, 467 .detach = comedi_legacy_detach, 468 .board_name = &boardtypes[0].name, 469 .num_names = ARRAY_SIZE(boardtypes), 470 .offset = sizeof(struct dt2811_board), 471}; 472module_comedi_driver(dt2811_driver); 473 474MODULE_AUTHOR("Comedi http://www.comedi.org"); 475MODULE_DESCRIPTION("Comedi low-level driver"); 476MODULE_LICENSE("GPL"); 477