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