1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * comedi/drivers/dt2801.c 4 * Device Driver for DataTranslation DT2801 5 * 6 */ 7 /* 8 * Driver: dt2801 9 * Description: Data Translation DT2801 series and DT01-EZ 10 * Author: ds 11 * Status: works 12 * Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A, 13 * DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ 14 * 15 * This driver can autoprobe the type of board. 16 * 17 * Configuration options: 18 * [0] - I/O port base address 19 * [1] - unused 20 * [2] - A/D reference 0=differential, 1=single-ended 21 * [3] - A/D range 22 * 0 = [-10, 10] 23 * 1 = [0,10] 24 * [4] - D/A 0 range 25 * 0 = [-10, 10] 26 * 1 = [-5,5] 27 * 2 = [-2.5,2.5] 28 * 3 = [0,10] 29 * 4 = [0,5] 30 * [5] - D/A 1 range (same choices) 31 */ 32 33 #include <linux/module.h> 34 #include <linux/comedi/comedidev.h> 35 #include <linux/delay.h> 36 37 #define DT2801_TIMEOUT 1000 38 39 /* Hardware Configuration */ 40 /* ====================== */ 41 42 #define DT2801_MAX_DMA_SIZE (64 * 1024) 43 44 /* define's */ 45 /* ====================== */ 46 47 /* Commands */ 48 #define DT_C_RESET 0x0 49 #define DT_C_CLEAR_ERR 0x1 50 #define DT_C_READ_ERRREG 0x2 51 #define DT_C_SET_CLOCK 0x3 52 53 #define DT_C_TEST 0xb 54 #define DT_C_STOP 0xf 55 56 #define DT_C_SET_DIGIN 0x4 57 #define DT_C_SET_DIGOUT 0x5 58 #define DT_C_READ_DIG 0x6 59 #define DT_C_WRITE_DIG 0x7 60 61 #define DT_C_WRITE_DAIM 0x8 62 #define DT_C_SET_DA 0x9 63 #define DT_C_WRITE_DA 0xa 64 65 #define DT_C_READ_ADIM 0xc 66 #define DT_C_SET_AD 0xd 67 #define DT_C_READ_AD 0xe 68 69 /* 70 * Command modifiers (only used with read/write), EXTTRIG can be 71 * used with some other commands. 72 */ 73 #define DT_MOD_DMA BIT(4) 74 #define DT_MOD_CONT BIT(5) 75 #define DT_MOD_EXTCLK BIT(6) 76 #define DT_MOD_EXTTRIG BIT(7) 77 78 /* Bits in status register */ 79 #define DT_S_DATA_OUT_READY BIT(0) 80 #define DT_S_DATA_IN_FULL BIT(1) 81 #define DT_S_READY BIT(2) 82 #define DT_S_COMMAND BIT(3) 83 #define DT_S_COMPOSITE_ERROR BIT(7) 84 85 /* registers */ 86 #define DT2801_DATA 0 87 #define DT2801_STATUS 1 88 #define DT2801_CMD 1 89 90 #if 0 91 /* ignore 'defined but not used' warning */ 92 static const struct comedi_lrange range_dt2801_ai_pgh_bipolar = { 93 4, { 94 BIP_RANGE(10), 95 BIP_RANGE(5), 96 BIP_RANGE(2.5), 97 BIP_RANGE(1.25) 98 } 99 }; 100 #endif 101 static const struct comedi_lrange range_dt2801_ai_pgl_bipolar = { 102 4, { 103 BIP_RANGE(10), 104 BIP_RANGE(1), 105 BIP_RANGE(0.1), 106 BIP_RANGE(0.02) 107 } 108 }; 109 110 #if 0 111 /* ignore 'defined but not used' warning */ 112 static const struct comedi_lrange range_dt2801_ai_pgh_unipolar = { 113 4, { 114 UNI_RANGE(10), 115 UNI_RANGE(5), 116 UNI_RANGE(2.5), 117 UNI_RANGE(1.25) 118 } 119 }; 120 #endif 121 static const struct comedi_lrange range_dt2801_ai_pgl_unipolar = { 122 4, { 123 UNI_RANGE(10), 124 UNI_RANGE(1), 125 UNI_RANGE(0.1), 126 UNI_RANGE(0.02) 127 } 128 }; 129 130 struct dt2801_board { 131 const char *name; 132 int boardcode; 133 int ad_diff; 134 int ad_chan; 135 int adbits; 136 int adrangetype; 137 int dabits; 138 }; 139 140 /* 141 * Typeid's for the different boards of the DT2801-series 142 * (taken from the test-software, that comes with the board) 143 */ 144 static const struct dt2801_board boardtypes[] = { 145 { 146 .name = "dt2801", 147 .boardcode = 0x09, 148 .ad_diff = 2, 149 .ad_chan = 16, 150 .adbits = 12, 151 .adrangetype = 0, 152 .dabits = 12}, 153 { 154 .name = "dt2801-a", 155 .boardcode = 0x52, 156 .ad_diff = 2, 157 .ad_chan = 16, 158 .adbits = 12, 159 .adrangetype = 0, 160 .dabits = 12}, 161 { 162 .name = "dt2801/5716a", 163 .boardcode = 0x82, 164 .ad_diff = 1, 165 .ad_chan = 16, 166 .adbits = 16, 167 .adrangetype = 1, 168 .dabits = 12}, 169 { 170 .name = "dt2805", 171 .boardcode = 0x12, 172 .ad_diff = 1, 173 .ad_chan = 16, 174 .adbits = 12, 175 .adrangetype = 0, 176 .dabits = 12}, 177 { 178 .name = "dt2805/5716a", 179 .boardcode = 0x92, 180 .ad_diff = 1, 181 .ad_chan = 16, 182 .adbits = 16, 183 .adrangetype = 1, 184 .dabits = 12}, 185 { 186 .name = "dt2808", 187 .boardcode = 0x20, 188 .ad_diff = 0, 189 .ad_chan = 16, 190 .adbits = 12, 191 .adrangetype = 2, 192 .dabits = 8}, 193 { 194 .name = "dt2818", 195 .boardcode = 0xa2, 196 .ad_diff = 0, 197 .ad_chan = 4, 198 .adbits = 12, 199 .adrangetype = 0, 200 .dabits = 12}, 201 { 202 .name = "dt2809", 203 .boardcode = 0xb0, 204 .ad_diff = 0, 205 .ad_chan = 8, 206 .adbits = 12, 207 .adrangetype = 1, 208 .dabits = 12}, 209 }; 210 211 struct dt2801_private { 212 const struct comedi_lrange *dac_range_types[2]; 213 }; 214 215 /* 216 * These are the low-level routines: 217 * writecommand: write a command to the board 218 * writedata: write data byte 219 * readdata: read data byte 220 */ 221 222 /* 223 * Only checks DataOutReady-flag, not the Ready-flag as it is done 224 * in the examples of the manual. I don't see why this should be 225 * necessary. 226 */ dt2801_readdata(struct comedi_device * dev,int * data)227 static int dt2801_readdata(struct comedi_device *dev, int *data) 228 { 229 int stat = 0; 230 int timeout = DT2801_TIMEOUT; 231 232 do { 233 stat = inb_p(dev->iobase + DT2801_STATUS); 234 if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY)) 235 return stat; 236 if (stat & DT_S_DATA_OUT_READY) { 237 *data = inb_p(dev->iobase + DT2801_DATA); 238 return 0; 239 } 240 } while (--timeout > 0); 241 242 return -ETIME; 243 } 244 dt2801_readdata2(struct comedi_device * dev,int * data)245 static int dt2801_readdata2(struct comedi_device *dev, int *data) 246 { 247 int lb = 0; 248 int hb = 0; 249 int ret; 250 251 ret = dt2801_readdata(dev, &lb); 252 if (ret) 253 return ret; 254 ret = dt2801_readdata(dev, &hb); 255 if (ret) 256 return ret; 257 258 *data = (hb << 8) + lb; 259 return 0; 260 } 261 dt2801_writedata(struct comedi_device * dev,unsigned int data)262 static int dt2801_writedata(struct comedi_device *dev, unsigned int data) 263 { 264 int stat = 0; 265 int timeout = DT2801_TIMEOUT; 266 267 do { 268 stat = inb_p(dev->iobase + DT2801_STATUS); 269 270 if (stat & DT_S_COMPOSITE_ERROR) 271 return stat; 272 if (!(stat & DT_S_DATA_IN_FULL)) { 273 outb_p(data & 0xff, dev->iobase + DT2801_DATA); 274 return 0; 275 } 276 } while (--timeout > 0); 277 278 return -ETIME; 279 } 280 dt2801_writedata2(struct comedi_device * dev,unsigned int data)281 static int dt2801_writedata2(struct comedi_device *dev, unsigned int data) 282 { 283 int ret; 284 285 ret = dt2801_writedata(dev, data & 0xff); 286 if (ret < 0) 287 return ret; 288 ret = dt2801_writedata(dev, data >> 8); 289 if (ret < 0) 290 return ret; 291 292 return 0; 293 } 294 dt2801_wait_for_ready(struct comedi_device * dev)295 static int dt2801_wait_for_ready(struct comedi_device *dev) 296 { 297 int timeout = DT2801_TIMEOUT; 298 int stat; 299 300 stat = inb_p(dev->iobase + DT2801_STATUS); 301 if (stat & DT_S_READY) 302 return 0; 303 do { 304 stat = inb_p(dev->iobase + DT2801_STATUS); 305 306 if (stat & DT_S_COMPOSITE_ERROR) 307 return stat; 308 if (stat & DT_S_READY) 309 return 0; 310 } while (--timeout > 0); 311 312 return -ETIME; 313 } 314 dt2801_writecmd(struct comedi_device * dev,int command)315 static void dt2801_writecmd(struct comedi_device *dev, int command) 316 { 317 int stat; 318 319 dt2801_wait_for_ready(dev); 320 321 stat = inb_p(dev->iobase + DT2801_STATUS); 322 if (stat & DT_S_COMPOSITE_ERROR) { 323 dev_dbg(dev->class_dev, 324 "composite-error in %s, ignoring\n", __func__); 325 } 326 if (!(stat & DT_S_READY)) 327 dev_dbg(dev->class_dev, "!ready in %s, ignoring\n", __func__); 328 outb_p(command, dev->iobase + DT2801_CMD); 329 } 330 dt2801_reset(struct comedi_device * dev)331 static int dt2801_reset(struct comedi_device *dev) 332 { 333 int board_code = 0; 334 unsigned int stat; 335 int timeout; 336 337 /* pull random data from data port */ 338 inb_p(dev->iobase + DT2801_DATA); 339 inb_p(dev->iobase + DT2801_DATA); 340 inb_p(dev->iobase + DT2801_DATA); 341 inb_p(dev->iobase + DT2801_DATA); 342 343 /* dt2801_writecmd(dev,DT_C_STOP); */ 344 outb_p(DT_C_STOP, dev->iobase + DT2801_CMD); 345 346 /* dt2801_wait_for_ready(dev); */ 347 usleep_range(100, 200); 348 timeout = 10000; 349 do { 350 stat = inb_p(dev->iobase + DT2801_STATUS); 351 if (stat & DT_S_READY) 352 break; 353 } while (timeout--); 354 if (!timeout) 355 dev_dbg(dev->class_dev, "timeout 1 status=0x%02x\n", stat); 356 357 /* dt2801_readdata(dev,&board_code); */ 358 359 outb_p(DT_C_RESET, dev->iobase + DT2801_CMD); 360 /* dt2801_writecmd(dev,DT_C_RESET); */ 361 362 usleep_range(100, 200); 363 timeout = 10000; 364 do { 365 stat = inb_p(dev->iobase + DT2801_STATUS); 366 if (stat & DT_S_READY) 367 break; 368 } while (timeout--); 369 if (!timeout) 370 dev_dbg(dev->class_dev, "timeout 2 status=0x%02x\n", stat); 371 372 dt2801_readdata(dev, &board_code); 373 374 return board_code; 375 } 376 probe_number_of_ai_chans(struct comedi_device * dev)377 static int probe_number_of_ai_chans(struct comedi_device *dev) 378 { 379 int n_chans; 380 int stat; 381 int data; 382 383 for (n_chans = 0; n_chans < 16; n_chans++) { 384 dt2801_writecmd(dev, DT_C_READ_ADIM); 385 dt2801_writedata(dev, 0); 386 dt2801_writedata(dev, n_chans); 387 stat = dt2801_readdata2(dev, &data); 388 389 if (stat) 390 break; 391 } 392 393 dt2801_reset(dev); 394 dt2801_reset(dev); 395 396 return n_chans; 397 } 398 399 static const struct comedi_lrange *dac_range_table[] = { 400 &range_bipolar10, 401 &range_bipolar5, 402 &range_bipolar2_5, 403 &range_unipolar10, 404 &range_unipolar5 405 }; 406 dac_range_lkup(int opt)407 static const struct comedi_lrange *dac_range_lkup(int opt) 408 { 409 if (opt < 0 || opt >= 5) 410 return &range_unknown; 411 return dac_range_table[opt]; 412 } 413 ai_range_lkup(int type,int opt)414 static const struct comedi_lrange *ai_range_lkup(int type, int opt) 415 { 416 switch (type) { 417 case 0: 418 return (opt) ? 419 &range_dt2801_ai_pgl_unipolar : 420 &range_dt2801_ai_pgl_bipolar; 421 case 1: 422 return (opt) ? &range_unipolar10 : &range_bipolar10; 423 case 2: 424 return &range_unipolar5; 425 } 426 return &range_unknown; 427 } 428 dt2801_error(struct comedi_device * dev,int stat)429 static int dt2801_error(struct comedi_device *dev, int stat) 430 { 431 if (stat < 0) { 432 if (stat == -ETIME) 433 dev_dbg(dev->class_dev, "timeout\n"); 434 else 435 dev_dbg(dev->class_dev, "error %d\n", stat); 436 return stat; 437 } 438 dev_dbg(dev->class_dev, "error status 0x%02x, resetting...\n", stat); 439 440 dt2801_reset(dev); 441 dt2801_reset(dev); 442 443 return -EIO; 444 } 445 dt2801_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)446 static int dt2801_ai_insn_read(struct comedi_device *dev, 447 struct comedi_subdevice *s, 448 struct comedi_insn *insn, unsigned int *data) 449 { 450 int d; 451 int stat; 452 int i; 453 454 for (i = 0; i < insn->n; i++) { 455 dt2801_writecmd(dev, DT_C_READ_ADIM); 456 dt2801_writedata(dev, CR_RANGE(insn->chanspec)); 457 dt2801_writedata(dev, CR_CHAN(insn->chanspec)); 458 stat = dt2801_readdata2(dev, &d); 459 460 if (stat != 0) 461 return dt2801_error(dev, stat); 462 463 data[i] = d; 464 } 465 466 return i; 467 } 468 dt2801_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)469 static int dt2801_ao_insn_write(struct comedi_device *dev, 470 struct comedi_subdevice *s, 471 struct comedi_insn *insn, 472 unsigned int *data) 473 { 474 unsigned int chan = CR_CHAN(insn->chanspec); 475 476 dt2801_writecmd(dev, DT_C_WRITE_DAIM); 477 dt2801_writedata(dev, chan); 478 dt2801_writedata2(dev, data[0]); 479 480 s->readback[chan] = data[0]; 481 482 return 1; 483 } 484 dt2801_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)485 static int dt2801_dio_insn_bits(struct comedi_device *dev, 486 struct comedi_subdevice *s, 487 struct comedi_insn *insn, 488 unsigned int *data) 489 { 490 int which = (s == &dev->subdevices[3]) ? 1 : 0; 491 unsigned int val = 0; 492 493 if (comedi_dio_update_state(s, data)) { 494 dt2801_writecmd(dev, DT_C_WRITE_DIG); 495 dt2801_writedata(dev, which); 496 dt2801_writedata(dev, s->state); 497 } 498 499 dt2801_writecmd(dev, DT_C_READ_DIG); 500 dt2801_writedata(dev, which); 501 dt2801_readdata(dev, &val); 502 503 data[1] = val; 504 505 return insn->n; 506 } 507 dt2801_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)508 static int dt2801_dio_insn_config(struct comedi_device *dev, 509 struct comedi_subdevice *s, 510 struct comedi_insn *insn, 511 unsigned int *data) 512 { 513 int ret; 514 515 ret = comedi_dio_insn_config(dev, s, insn, data, 0xff); 516 if (ret) 517 return ret; 518 519 dt2801_writecmd(dev, s->io_bits ? DT_C_SET_DIGOUT : DT_C_SET_DIGIN); 520 dt2801_writedata(dev, (s == &dev->subdevices[3]) ? 1 : 0); 521 522 return insn->n; 523 } 524 525 /* 526 * options: 527 * [0] - i/o base 528 * [1] - unused 529 * [2] - a/d 0=differential, 1=single-ended 530 * [3] - a/d range 0=[-10,10], 1=[0,10] 531 * [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] 532 * [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5] 533 */ dt2801_attach(struct comedi_device * dev,struct comedi_devconfig * it)534 static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it) 535 { 536 const struct dt2801_board *board; 537 struct dt2801_private *devpriv; 538 struct comedi_subdevice *s; 539 int board_code, type; 540 int ret = 0; 541 int n_ai_chans; 542 543 ret = comedi_request_region(dev, it->options[0], 0x2); 544 if (ret) 545 return ret; 546 547 /* do some checking */ 548 549 board_code = dt2801_reset(dev); 550 551 /* heh. if it didn't work, try it again. */ 552 if (!board_code) 553 board_code = dt2801_reset(dev); 554 555 for (type = 0; type < ARRAY_SIZE(boardtypes); type++) { 556 if (boardtypes[type].boardcode == board_code) 557 goto havetype; 558 } 559 dev_dbg(dev->class_dev, 560 "unrecognized board code=0x%02x, contact author\n", board_code); 561 type = 0; 562 563 havetype: 564 dev->board_ptr = boardtypes + type; 565 board = dev->board_ptr; 566 567 n_ai_chans = probe_number_of_ai_chans(dev); 568 569 ret = comedi_alloc_subdevices(dev, 4); 570 if (ret) 571 goto out; 572 573 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 574 if (!devpriv) 575 return -ENOMEM; 576 577 dev->board_name = board->name; 578 579 s = &dev->subdevices[0]; 580 /* ai subdevice */ 581 s->type = COMEDI_SUBD_AI; 582 s->subdev_flags = SDF_READABLE | SDF_GROUND; 583 #if 1 584 s->n_chan = n_ai_chans; 585 #else 586 if (it->options[2]) 587 s->n_chan = board->ad_chan; 588 else 589 s->n_chan = board->ad_chan / 2; 590 #endif 591 s->maxdata = (1 << board->adbits) - 1; 592 s->range_table = ai_range_lkup(board->adrangetype, it->options[3]); 593 s->insn_read = dt2801_ai_insn_read; 594 595 s = &dev->subdevices[1]; 596 /* ao subdevice */ 597 s->type = COMEDI_SUBD_AO; 598 s->subdev_flags = SDF_WRITABLE; 599 s->n_chan = 2; 600 s->maxdata = (1 << board->dabits) - 1; 601 s->range_table_list = devpriv->dac_range_types; 602 devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]); 603 devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]); 604 s->insn_write = dt2801_ao_insn_write; 605 606 ret = comedi_alloc_subdev_readback(s); 607 if (ret) 608 return ret; 609 610 s = &dev->subdevices[2]; 611 /* 1st digital subdevice */ 612 s->type = COMEDI_SUBD_DIO; 613 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 614 s->n_chan = 8; 615 s->maxdata = 1; 616 s->range_table = &range_digital; 617 s->insn_bits = dt2801_dio_insn_bits; 618 s->insn_config = dt2801_dio_insn_config; 619 620 s = &dev->subdevices[3]; 621 /* 2nd digital subdevice */ 622 s->type = COMEDI_SUBD_DIO; 623 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 624 s->n_chan = 8; 625 s->maxdata = 1; 626 s->range_table = &range_digital; 627 s->insn_bits = dt2801_dio_insn_bits; 628 s->insn_config = dt2801_dio_insn_config; 629 630 ret = 0; 631 out: 632 return ret; 633 } 634 635 static struct comedi_driver dt2801_driver = { 636 .driver_name = "dt2801", 637 .module = THIS_MODULE, 638 .attach = dt2801_attach, 639 .detach = comedi_legacy_detach, 640 }; 641 module_comedi_driver(dt2801_driver); 642 643 MODULE_AUTHOR("Comedi https://www.comedi.org"); 644 MODULE_DESCRIPTION("Comedi low-level driver"); 645 MODULE_LICENSE("GPL"); 646