linux/drivers/staging/comedi/drivers/dt2801.c
<<
>>
Prefs
   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 "../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 */
  92static 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
 101static 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 */
 112static 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
 121static 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
 130struct 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 */
 144static 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
 211struct 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 */
 227static 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
 245static 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
 262static 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
 281static 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
 295static 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
 315static 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
 331static 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
 377static 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
 399static 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
 407static 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
 414static 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
 429static 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
 446static 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
 469static 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
 485static 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
 508static 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 */
 534static 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
 563havetype:
 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;
 631out:
 632        return ret;
 633}
 634
 635static struct comedi_driver dt2801_driver = {
 636        .driver_name    = "dt2801",
 637        .module         = THIS_MODULE,
 638        .attach         = dt2801_attach,
 639        .detach         = comedi_legacy_detach,
 640};
 641module_comedi_driver(dt2801_driver);
 642
 643MODULE_AUTHOR("Comedi http://www.comedi.org");
 644MODULE_DESCRIPTION("Comedi low-level driver");
 645MODULE_LICENSE("GPL");
 646