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/*
  23Driver: dt2811
  24Description: Data Translation DT2811
  25Author: ds
  26Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
  27Status: works
  28
  29Configuration options:
  30  [0] - I/O port base address
  31  [1] - IRQ, although this is currently unused
  32  [2] - A/D reference
  33          0 = signle-ended
  34          1 = differential
  35          2 = pseudo-differential (common reference)
  36  [3] - A/D range
  37          0 = [-5, 5]
  38          1 = [-2.5, 2.5]
  39          2 = [0, 5]
  40  [4] - D/A 0 range (same choices)
  41  [4] - D/A 1 range (same choices)
  42*/
  43
  44#include <linux/module.h>
  45#include "../comedidev.h"
  46
  47static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
  48        4, {
  49                UNI_RANGE(5),
  50                UNI_RANGE(2.5),
  51                UNI_RANGE(1.25),
  52                UNI_RANGE(0.625)
  53        }
  54};
  55
  56static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
  57        4, {
  58                BIP_RANGE(2.5),
  59                BIP_RANGE(1.25),
  60                BIP_RANGE(0.625),
  61                BIP_RANGE(0.3125)
  62        }
  63};
  64
  65static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
  66        4, {
  67                BIP_RANGE(5),
  68                BIP_RANGE(2.5),
  69                BIP_RANGE(1.25),
  70                BIP_RANGE(0.625)
  71        }
  72};
  73
  74static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
  75        4, {
  76                UNI_RANGE(5),
  77                UNI_RANGE(0.5),
  78                UNI_RANGE(0.05),
  79                UNI_RANGE(0.01)
  80        }
  81};
  82
  83static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
  84        4, {
  85                BIP_RANGE(2.5),
  86                BIP_RANGE(0.25),
  87                BIP_RANGE(0.025),
  88                BIP_RANGE(0.005)
  89        }
  90};
  91
  92static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
  93        4, {
  94                BIP_RANGE(5),
  95                BIP_RANGE(0.5),
  96                BIP_RANGE(0.05),
  97                BIP_RANGE(0.01)
  98        }
  99};
 100
 101/*
 102
 103   0x00    ADCSR R/W  A/D Control/Status Register
 104   bit 7 - (R) 1 indicates A/D conversion done
 105   reading ADDAT clears bit
 106   (W) ignored
 107   bit 6 - (R) 1 indicates A/D error
 108   (W) ignored
 109   bit 5 - (R) 1 indicates A/D busy, cleared at end
 110   of conversion
 111   (W) ignored
 112   bit 4 - (R) 0
 113   (W)
 114   bit 3 - (R) 0
 115   bit 2 - (R/W) 1 indicates interrupts enabled
 116   bits 1,0 - (R/W) mode bits
 117   00  single conversion on ADGCR load
 118   01  continuous conversion, internal clock,
 119   (clock enabled on ADGCR load)
 120   10  continuous conversion, internal clock,
 121   external trigger
 122   11  continuous conversion, external clock,
 123   external trigger
 124
 125   0x01    ADGCR R/W A/D Gain/Channel Register
 126   bit 6,7 - (R/W) gain select
 127   00  gain=1, both PGH, PGL models
 128   01  gain=2 PGH, 10 PGL
 129   10  gain=4 PGH, 100 PGL
 130   11  gain=8 PGH, 500 PGL
 131   bit 4,5 - reserved
 132   bit 3-0 - (R/W) channel select
 133   channel number from 0-15
 134
 135   0x02,0x03 (R) ADDAT A/D Data Register
 136   (W) DADAT0 D/A Data Register 0
 137   0x02 low byte
 138   0x03 high byte
 139
 140   0x04,0x05 (W) DADAT0 D/A Data Register 1
 141
 142   0x06 (R) DIO0 Digital Input Port 0
 143   (W) DIO1 Digital Output Port 1
 144
 145   0x07 TMRCTR (R/W) Timer/Counter Register
 146   bits 6,7 - reserved
 147   bits 5-3 - Timer frequency control (mantissa)
 148   543  divisor  freqency (kHz)
 149   000  1        600
 150   001  10       60
 151   010  2        300
 152   011  3        200
 153   100  4        150
 154   101  5        120
 155   110  6        100
 156   111  12       50
 157   bits 2-0 - Timer frequency control (exponent)
 158   210  multiply divisor/divide frequency by
 159   000  1
 160   001  10
 161   010  100
 162   011  1000
 163   100  10000
 164   101  100000
 165   110  1000000
 166   111  10000000
 167
 168 */
 169
 170#define TIMEOUT 10000
 171
 172#define DT2811_ADCSR 0
 173#define DT2811_ADGCR 1
 174#define DT2811_ADDATLO 2
 175#define DT2811_ADDATHI 3
 176#define DT2811_DADAT0LO 2
 177#define DT2811_DADAT0HI 3
 178#define DT2811_DADAT1LO 4
 179#define DT2811_DADAT1HI 5
 180#define DT2811_DIO 6
 181#define DT2811_TMRCTR 7
 182
 183/*
 184 * flags
 185 */
 186
 187/* ADCSR */
 188
 189#define DT2811_ADDONE   0x80
 190#define DT2811_ADERROR  0x40
 191#define DT2811_ADBUSY   0x20
 192#define DT2811_CLRERROR 0x10
 193#define DT2811_INTENB   0x04
 194#define DT2811_ADMODE   0x03
 195
 196struct dt2811_board {
 197        const char *name;
 198        const struct comedi_lrange *bip_5;
 199        const struct comedi_lrange *bip_2_5;
 200        const struct comedi_lrange *unip_5;
 201};
 202
 203enum { card_2811_pgh, card_2811_pgl };
 204
 205struct dt2811_private {
 206        int ntrig;
 207        int curadchan;
 208        enum {
 209                adc_singleended, adc_diff, adc_pseudo_diff
 210        } adc_mux;
 211        enum {
 212                dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
 213        } dac_range[2];
 214        const struct comedi_lrange *range_type_list[2];
 215};
 216
 217static const struct comedi_lrange *dac_range_types[] = {
 218        &range_bipolar5,
 219        &range_bipolar2_5,
 220        &range_unipolar5
 221};
 222
 223static int dt2811_ai_eoc(struct comedi_device *dev,
 224                         struct comedi_subdevice *s,
 225                         struct comedi_insn *insn,
 226                         unsigned long context)
 227{
 228        unsigned int status;
 229
 230        status = inb(dev->iobase + DT2811_ADCSR);
 231        if ((status & DT2811_ADBUSY) == 0)
 232                return 0;
 233        return -EBUSY;
 234}
 235
 236static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
 237                          struct comedi_insn *insn, unsigned int *data)
 238{
 239        int chan = CR_CHAN(insn->chanspec);
 240        int ret;
 241        int i;
 242
 243        for (i = 0; i < insn->n; i++) {
 244                outb(chan, dev->iobase + DT2811_ADGCR);
 245
 246                ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0);
 247                if (ret)
 248                        return ret;
 249
 250                data[i] = inb(dev->iobase + DT2811_ADDATLO);
 251                data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
 252                data[i] &= 0xfff;
 253        }
 254
 255        return i;
 256}
 257
 258static int dt2811_ao_insn_write(struct comedi_device *dev,
 259                                struct comedi_subdevice *s,
 260                                struct comedi_insn *insn,
 261                                unsigned int *data)
 262{
 263        unsigned int chan = CR_CHAN(insn->chanspec);
 264        unsigned int val = s->readback[chan];
 265        int i;
 266
 267        for (i = 0; i < insn->n; i++) {
 268                val = data[i];
 269                outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
 270                outb((val >> 8) & 0xff,
 271                     dev->iobase + DT2811_DADAT0HI + 2 * chan);
 272        }
 273        s->readback[chan] = val;
 274
 275        return insn->n;
 276}
 277
 278static int dt2811_di_insn_bits(struct comedi_device *dev,
 279                               struct comedi_subdevice *s,
 280                               struct comedi_insn *insn, unsigned int *data)
 281{
 282        data[1] = inb(dev->iobase + DT2811_DIO);
 283
 284        return insn->n;
 285}
 286
 287static int dt2811_do_insn_bits(struct comedi_device *dev,
 288                               struct comedi_subdevice *s,
 289                               struct comedi_insn *insn,
 290                               unsigned int *data)
 291{
 292        if (comedi_dio_update_state(s, data))
 293                outb(s->state, dev->iobase + DT2811_DIO);
 294
 295        data[1] = s->state;
 296
 297        return insn->n;
 298}
 299
 300/*
 301  options[0]   Board base address
 302  options[1]   IRQ
 303  options[2]   Input configuration
 304                 0 == single-ended
 305                 1 == differential
 306                 2 == pseudo-differential
 307  options[3]   Analog input range configuration
 308                 0 == bipolar 5  (-5V -- +5V)
 309                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 310                 2 == unipolar 5V  (0V -- +5V)
 311  options[4]   Analog output 0 range configuration
 312                 0 == bipolar 5  (-5V -- +5V)
 313                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 314                 2 == unipolar 5V  (0V -- +5V)
 315  options[5]   Analog output 1 range configuration
 316                 0 == bipolar 5  (-5V -- +5V)
 317                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
 318                 2 == unipolar 5V  (0V -- +5V)
 319*/
 320static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 321{
 322        /* int i; */
 323        const struct dt2811_board *board = dev->board_ptr;
 324        struct dt2811_private *devpriv;
 325        int ret;
 326        struct comedi_subdevice *s;
 327
 328        ret = comedi_request_region(dev, it->options[0], 0x8);
 329        if (ret)
 330                return ret;
 331
 332#if 0
 333        outb(0, dev->iobase + DT2811_ADCSR);
 334        udelay(100);
 335        i = inb(dev->iobase + DT2811_ADDATLO);
 336        i = inb(dev->iobase + DT2811_ADDATHI);
 337#endif
 338
 339        ret = comedi_alloc_subdevices(dev, 4);
 340        if (ret)
 341                return ret;
 342
 343        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 344        if (!devpriv)
 345                return -ENOMEM;
 346
 347        switch (it->options[2]) {
 348        case 0:
 349                devpriv->adc_mux = adc_singleended;
 350                break;
 351        case 1:
 352                devpriv->adc_mux = adc_diff;
 353                break;
 354        case 2:
 355                devpriv->adc_mux = adc_pseudo_diff;
 356                break;
 357        default:
 358                devpriv->adc_mux = adc_singleended;
 359                break;
 360        }
 361        switch (it->options[4]) {
 362        case 0:
 363                devpriv->dac_range[0] = dac_bipolar_5;
 364                break;
 365        case 1:
 366                devpriv->dac_range[0] = dac_bipolar_2_5;
 367                break;
 368        case 2:
 369                devpriv->dac_range[0] = dac_unipolar_5;
 370                break;
 371        default:
 372                devpriv->dac_range[0] = dac_bipolar_5;
 373                break;
 374        }
 375        switch (it->options[5]) {
 376        case 0:
 377                devpriv->dac_range[1] = dac_bipolar_5;
 378                break;
 379        case 1:
 380                devpriv->dac_range[1] = dac_bipolar_2_5;
 381                break;
 382        case 2:
 383                devpriv->dac_range[1] = dac_unipolar_5;
 384                break;
 385        default:
 386                devpriv->dac_range[1] = dac_bipolar_5;
 387                break;
 388        }
 389
 390        s = &dev->subdevices[0];
 391        /* initialize the ADC subdevice */
 392        s->type = COMEDI_SUBD_AI;
 393        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 394        s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
 395        s->insn_read = dt2811_ai_insn;
 396        s->maxdata = 0xfff;
 397        switch (it->options[3]) {
 398        case 0:
 399        default:
 400                s->range_table = board->bip_5;
 401                break;
 402        case 1:
 403                s->range_table = board->bip_2_5;
 404                break;
 405        case 2:
 406                s->range_table = board->unip_5;
 407                break;
 408        }
 409
 410        s = &dev->subdevices[1];
 411        /* ao subdevice */
 412        s->type = COMEDI_SUBD_AO;
 413        s->subdev_flags = SDF_WRITABLE;
 414        s->n_chan = 2;
 415        s->maxdata = 0xfff;
 416        s->range_table_list = devpriv->range_type_list;
 417        devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
 418        devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
 419        s->insn_write = dt2811_ao_insn_write;
 420
 421        ret = comedi_alloc_subdev_readback(s);
 422        if (ret)
 423                return ret;
 424
 425        s = &dev->subdevices[2];
 426        /* di subdevice */
 427        s->type = COMEDI_SUBD_DI;
 428        s->subdev_flags = SDF_READABLE;
 429        s->n_chan = 8;
 430        s->insn_bits = dt2811_di_insn_bits;
 431        s->maxdata = 1;
 432        s->range_table = &range_digital;
 433
 434        s = &dev->subdevices[3];
 435        /* do subdevice */
 436        s->type = COMEDI_SUBD_DO;
 437        s->subdev_flags = SDF_WRITABLE;
 438        s->n_chan = 8;
 439        s->insn_bits = dt2811_do_insn_bits;
 440        s->maxdata = 1;
 441        s->state = 0;
 442        s->range_table = &range_digital;
 443
 444        return 0;
 445}
 446
 447static const struct dt2811_board boardtypes[] = {
 448        {
 449                .name           = "dt2811-pgh",
 450                .bip_5          = &range_dt2811_pgh_ai_5_bipolar,
 451                .bip_2_5        = &range_dt2811_pgh_ai_2_5_bipolar,
 452                .unip_5         = &range_dt2811_pgh_ai_5_unipolar,
 453        }, {
 454                .name           = "dt2811-pgl",
 455                .bip_5          = &range_dt2811_pgl_ai_5_bipolar,
 456                .bip_2_5        = &range_dt2811_pgl_ai_2_5_bipolar,
 457                .unip_5         = &range_dt2811_pgl_ai_5_unipolar,
 458        },
 459};
 460
 461static struct comedi_driver dt2811_driver = {
 462        .driver_name    = "dt2811",
 463        .module         = THIS_MODULE,
 464        .attach         = dt2811_attach,
 465        .detach         = comedi_legacy_detach,
 466        .board_name     = &boardtypes[0].name,
 467        .num_names      = ARRAY_SIZE(boardtypes),
 468        .offset         = sizeof(struct dt2811_board),
 469};
 470module_comedi_driver(dt2811_driver);
 471
 472MODULE_AUTHOR("Comedi http://www.comedi.org");
 473MODULE_DESCRIPTION("Comedi low-level driver");
 474MODULE_LICENSE("GPL");
 475