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