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
 214#define this_board ((const struct dt2811_board *)dev->board_ptr)
 215
 216enum { card_2811_pgh, card_2811_pgl };
 217
 218struct dt2811_private {
 219        int ntrig;
 220        int curadchan;
 221        enum {
 222                adc_singleended, adc_diff, adc_pseudo_diff
 223        } adc_mux;
 224        enum {
 225                dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
 226        } dac_range[2];
 227        const struct comedi_lrange *range_type_list[2];
 228        unsigned int ao_readback[2];
 229};
 230
 231#define devpriv ((struct dt2811_private *)dev->private)
 232
 233static const struct comedi_lrange *dac_range_types[] = {
 234        &range_bipolar5,
 235        &range_bipolar2_5,
 236        &range_unipolar5
 237};
 238
 239#define DT2811_TIMEOUT 5
 240
 241#if 0
 242static irqreturn_t dt2811_interrupt(int irq, void *d)
 243{
 244        int lo, hi;
 245        int data;
 246        struct comedi_device *dev = d;
 247
 248        if (!dev->attached) {
 249                comedi_error(dev, "spurious interrupt");
 250                return IRQ_HANDLED;
 251        }
 252
 253        lo = inb(dev->iobase + DT2811_ADDATLO);
 254        hi = inb(dev->iobase + DT2811_ADDATHI);
 255
 256        data = lo + (hi << 8);
 257
 258        if (!(--devpriv->ntrig)) {
 259                /* how to turn off acquisition */
 260                s->async->events |= COMEDI_SB_EOA;
 261        }
 262        comedi_event(dev, s);
 263        return IRQ_HANDLED;
 264}
 265#endif
 266
 267static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
 268                          struct comedi_insn *insn, unsigned int *data)
 269{
 270        int chan = CR_CHAN(insn->chanspec);
 271        int timeout = DT2811_TIMEOUT;
 272        int i;
 273
 274        for (i = 0; i < insn->n; i++) {
 275                outb(chan, dev->iobase + DT2811_ADGCR);
 276
 277                while (timeout
 278                       && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
 279                        timeout--;
 280                if (!timeout)
 281                        return -ETIME;
 282
 283                data[i] = inb(dev->iobase + DT2811_ADDATLO);
 284                data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
 285                data[i] &= 0xfff;
 286        }
 287
 288        return i;
 289}
 290
 291#if 0
 292/* Wow.  This is code from the Comedi stone age.  But it hasn't been
 293 * replaced, so I'll let it stay. */
 294int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
 295{
 296        struct comedi_device *dev = comedi_devices + minor;
 297
 298        if (adtrig->n < 1)
 299                return 0;
 300        dev->curadchan = adtrig->chan;
 301        switch (dev->i_admode) {
 302        case COMEDI_MDEMAND:
 303                dev->ntrig = adtrig->n - 1;
 304                /* not necessary */
 305                /*printk("dt2811: AD soft trigger\n"); */
 306                /*outb(DT2811_CLRERROR|DT2811_INTENB,
 307                        dev->iobase+DT2811_ADCSR); */
 308                outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
 309                do_gettimeofday(&trigtime);
 310                break;
 311        case COMEDI_MCONTS:
 312                dev->ntrig = adtrig->n;
 313                break;
 314        }
 315
 316        return 0;
 317}
 318#endif
 319
 320static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
 321                          struct comedi_insn *insn, unsigned int *data)
 322{
 323        int i;
 324        int chan;
 325
 326        chan = CR_CHAN(insn->chanspec);
 327
 328        for (i = 0; i < insn->n; i++) {
 329                outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
 330                outb((data[i] >> 8) & 0xff,
 331                     dev->iobase + DT2811_DADAT0HI + 2 * chan);
 332                devpriv->ao_readback[chan] = data[i];
 333        }
 334
 335        return i;
 336}
 337
 338static int dt2811_ao_insn_read(struct comedi_device *dev,
 339                               struct comedi_subdevice *s,
 340                               struct comedi_insn *insn, unsigned int *data)
 341{
 342        int i;
 343        int chan;
 344
 345        chan = CR_CHAN(insn->chanspec);
 346
 347        for (i = 0; i < insn->n; i++)
 348                data[i] = devpriv->ao_readback[chan];
 349
 350        return i;
 351}
 352
 353static int dt2811_di_insn_bits(struct comedi_device *dev,
 354                               struct comedi_subdevice *s,
 355                               struct comedi_insn *insn, unsigned int *data)
 356{
 357        if (insn->n != 2)
 358                return -EINVAL;
 359
 360        data[1] = inb(dev->iobase + DT2811_DIO);
 361
 362        return 2;
 363}
 364
 365static int dt2811_do_insn_bits(struct comedi_device *dev,
 366                               struct comedi_subdevice *s,
 367                               struct comedi_insn *insn, unsigned int *data)
 368{
 369        if (insn->n != 2)
 370                return -EINVAL;
 371
 372        s->state &= ~data[0];
 373        s->state |= data[0] & data[1];
 374        outb(s->state, dev->iobase + DT2811_DIO);
 375
 376        data[1] = s->state;
 377
 378        return 2;
 379}
 380
 381/*
 382  options[0]   Board base address
 383  options[1]   IRQ
 384  options[2]   Input configuration
 385                 0 == single-ended
 386                 1 == differential
 387                 2 == pseudo-differential
 388  options[3]   Analog input range configuration
 389                 0 == bipolar 5  (-5V -- +5V)
 390                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 391                 2 == unipolar 5V  (0V -- +5V)
 392  options[4]   Analog output 0 range configuration
 393                 0 == bipolar 5  (-5V -- +5V)
 394                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 395                 2 == unipolar 5V  (0V -- +5V)
 396  options[5]   Analog output 1 range configuration
 397                 0 == bipolar 5  (-5V -- +5V)
 398                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 399                 2 == unipolar 5V  (0V -- +5V)
 400*/
 401static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 402{
 403        /* int i, irq; */
 404        /* unsigned long irqs; */
 405        /* long flags; */
 406
 407        int ret;
 408        struct comedi_subdevice *s;
 409        unsigned long iobase;
 410
 411        iobase = it->options[0];
 412
 413        printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
 414
 415        if (!request_region(iobase, DT2811_SIZE, driver_name)) {
 416                printk(KERN_ERR "I/O port conflict\n");
 417                return -EIO;
 418        }
 419
 420        dev->iobase = iobase;
 421        dev->board_name = this_board->name;
 422
 423#if 0
 424        outb(0, dev->iobase + DT2811_ADCSR);
 425        udelay(100);
 426        i = inb(dev->iobase + DT2811_ADDATLO);
 427        i = inb(dev->iobase + DT2811_ADDATHI);
 428#endif
 429
 430#if 0
 431        irq = it->options[1];
 432        if (irq < 0) {
 433                save_flags(flags);
 434                sti();
 435                irqs = probe_irq_on();
 436
 437                outb(DT2811_CLRERROR | DT2811_INTENB,
 438                     dev->iobase + DT2811_ADCSR);
 439                outb(0, dev->iobase + DT2811_ADGCR);
 440
 441                udelay(100);
 442
 443                irq = probe_irq_off(irqs);
 444                restore_flags(flags);
 445
 446                /*outb(DT2811_CLRERROR|DT2811_INTENB,
 447                        dev->iobase+DT2811_ADCSR);*/
 448
 449                if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
 450                        printk(KERN_ERR "error probing irq (bad)\n");
 451                dev->irq = 0;
 452                if (irq > 0) {
 453                        i = inb(dev->iobase + DT2811_ADDATLO);
 454                        i = inb(dev->iobase + DT2811_ADDATHI);
 455                        printk(KERN_INFO "(irq = %d)\n", irq);
 456                        ret = request_irq(irq, dt2811_interrupt, 0,
 457                                          driver_name, dev);
 458                        if (ret < 0)
 459                                return -EIO;
 460                        dev->irq = irq;
 461                } else if (irq == 0) {
 462                        printk(KERN_INFO "(no irq)\n");
 463                } else {
 464                        printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
 465                }
 466        }
 467#endif
 468
 469        ret = alloc_subdevices(dev, 4);
 470        if (ret < 0)
 471                return ret;
 472
 473        ret = alloc_private(dev, sizeof(struct dt2811_private));
 474        if (ret < 0)
 475                return ret;
 476
 477        switch (it->options[2]) {
 478        case 0:
 479                devpriv->adc_mux = adc_singleended;
 480                break;
 481        case 1:
 482                devpriv->adc_mux = adc_diff;
 483                break;
 484        case 2:
 485                devpriv->adc_mux = adc_pseudo_diff;
 486                break;
 487        default:
 488                devpriv->adc_mux = adc_singleended;
 489                break;
 490        }
 491        switch (it->options[4]) {
 492        case 0:
 493                devpriv->dac_range[0] = dac_bipolar_5;
 494                break;
 495        case 1:
 496                devpriv->dac_range[0] = dac_bipolar_2_5;
 497                break;
 498        case 2:
 499                devpriv->dac_range[0] = dac_unipolar_5;
 500                break;
 501        default:
 502                devpriv->dac_range[0] = dac_bipolar_5;
 503                break;
 504        }
 505        switch (it->options[5]) {
 506        case 0:
 507                devpriv->dac_range[1] = dac_bipolar_5;
 508                break;
 509        case 1:
 510                devpriv->dac_range[1] = dac_bipolar_2_5;
 511                break;
 512        case 2:
 513                devpriv->dac_range[1] = dac_unipolar_5;
 514                break;
 515        default:
 516                devpriv->dac_range[1] = dac_bipolar_5;
 517                break;
 518        }
 519
 520        s = dev->subdevices + 0;
 521        /* initialize the ADC subdevice */
 522        s->type = COMEDI_SUBD_AI;
 523        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 524        s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
 525        s->insn_read = dt2811_ai_insn;
 526        s->maxdata = 0xfff;
 527        switch (it->options[3]) {
 528        case 0:
 529        default:
 530                s->range_table = this_board->bip_5;
 531                break;
 532        case 1:
 533                s->range_table = this_board->bip_2_5;
 534                break;
 535        case 2:
 536                s->range_table = this_board->unip_5;
 537                break;
 538        }
 539
 540        s = dev->subdevices + 1;
 541        /* ao subdevice */
 542        s->type = COMEDI_SUBD_AO;
 543        s->subdev_flags = SDF_WRITABLE;
 544        s->n_chan = 2;
 545        s->insn_write = dt2811_ao_insn;
 546        s->insn_read = dt2811_ao_insn_read;
 547        s->maxdata = 0xfff;
 548        s->range_table_list = devpriv->range_type_list;
 549        devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
 550        devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
 551
 552        s = dev->subdevices + 2;
 553        /* di subdevice */
 554        s->type = COMEDI_SUBD_DI;
 555        s->subdev_flags = SDF_READABLE;
 556        s->n_chan = 8;
 557        s->insn_bits = dt2811_di_insn_bits;
 558        s->maxdata = 1;
 559        s->range_table = &range_digital;
 560
 561        s = dev->subdevices + 3;
 562        /* do subdevice */
 563        s->type = COMEDI_SUBD_DO;
 564        s->subdev_flags = SDF_WRITABLE;
 565        s->n_chan = 8;
 566        s->insn_bits = dt2811_do_insn_bits;
 567        s->maxdata = 1;
 568        s->state = 0;
 569        s->range_table = &range_digital;
 570
 571        return 0;
 572}
 573
 574static void dt2811_detach(struct comedi_device *dev)
 575{
 576        if (dev->irq)
 577                free_irq(dev->irq, dev);
 578        if (dev->iobase)
 579                release_region(dev->iobase, DT2811_SIZE);
 580}
 581
 582static const struct dt2811_board boardtypes[] = {
 583        {
 584                .name           = "dt2811-pgh",
 585                .bip_5          = &range_dt2811_pgh_ai_5_bipolar,
 586                .bip_2_5        = &range_dt2811_pgh_ai_2_5_bipolar,
 587                .unip_5         = &range_dt2811_pgh_ai_5_unipolar,
 588        }, {
 589                .name           = "dt2811-pgl",
 590                .bip_5          = &range_dt2811_pgl_ai_5_bipolar,
 591                .bip_2_5        = &range_dt2811_pgl_ai_2_5_bipolar,
 592                .unip_5         = &range_dt2811_pgl_ai_5_unipolar,
 593        },
 594};
 595
 596static struct comedi_driver dt2811_driver = {
 597        .driver_name    = "dt2811",
 598        .module         = THIS_MODULE,
 599        .attach         = dt2811_attach,
 600        .detach         = dt2811_detach,
 601        .board_name     = &boardtypes[0].name,
 602        .num_names      = ARRAY_SIZE(boardtypes),
 603        .offset         = sizeof(struct dt2811_board),
 604};
 605module_comedi_driver(dt2811_driver);
 606
 607MODULE_AUTHOR("Comedi http://www.comedi.org");
 608MODULE_DESCRIPTION("Comedi low-level driver");
 609MODULE_LICENSE("GPL");
 610