linux/drivers/staging/comedi/drivers/adq12b.c
<<
>>
Prefs
   1/*
   2 * adq12b.c
   3 * Driver for MicroAxial ADQ12-B data acquisition and control card
   4 * written by jeremy theler <thelerg@ib.cnea.gov.ar>
   5 *      instituto balseiro
   6 *      commission nacional de energia atomica
   7 *      universidad nacional de cuyo
   8 *      argentina
   9 *
  10 * COMEDI - Linux Control and Measurement Device Interface
  11 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License as published by
  15 * the Free Software Foundation; either version 2 of the License, or
  16 * (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 */
  23
  24/*
  25 * Driver: adq12b
  26 * Description: Driver for MicroAxial ADQ12-B data acquisition and control card
  27 * Devices: [MicroAxial] ADQ12-B (adq12b)
  28 * Author: jeremy theler <thelerg@ib.cnea.gov.ar>
  29 * Updated: Thu, 21 Feb 2008 02:56:27 -0300
  30 * Status: works
  31 *
  32 * Configuration options:
  33 *   [0] - I/O base address (set with hardware jumpers)
  34 *              address         jumper JADR
  35 *              0x300           1 (factory default)
  36 *              0x320           2
  37 *              0x340           3
  38 *              0x360           4
  39 *              0x380           5
  40 *              0x3A0           6
  41 *   [1] - Analog Input unipolar/bipolar selection
  42 *              selection       option  JUB
  43 *              bipolar         0       2-3 (factory default)
  44 *              unipolar        1       1-2
  45 *   [2] - Analog Input single-ended/differential selection
  46 *              selection       option  JCHA    JCHB
  47 *              single-ended    0       1-2     1-2 (factory default)
  48 *              differential    1       2-3     2-3
  49 *
  50 * Driver for the acquisition card ADQ12-B (without any add-on).
  51 *
  52 * - Analog input is subdevice 0 (16 channels single-ended or 8 differential)
  53 * - Digital input is subdevice 1 (5 channels)
  54 * - Digital output is subdevice 1 (8 channels)
  55 * - The PACER is not supported in this version
  56 */
  57
  58#include <linux/module.h>
  59#include <linux/delay.h>
  60
  61#include "../comedidev.h"
  62
  63/* address scheme (page 2.17 of the manual) */
  64#define ADQ12B_CTREG            0x00
  65#define ADQ12B_CTREG_MSKP       BIT(7)  /* enable pacer interrupt */
  66#define ADQ12B_CTREG_GTP        BIT(6)  /* enable pacer */
  67#define ADQ12B_CTREG_RANGE(x)   ((x) << 4)
  68#define ADQ12B_CTREG_CHAN(x)    ((x) << 0)
  69#define ADQ12B_STINR            0x00
  70#define ADQ12B_STINR_OUT2       BIT(7)  /* timer 2 output state */
  71#define ADQ12B_STINR_OUTP       BIT(6)  /* pacer output state */
  72#define ADQ12B_STINR_EOC        BIT(5)  /* A/D end-of-conversion */
  73#define ADQ12B_STINR_IN_MASK    (0x1f << 0)
  74#define ADQ12B_OUTBR            0x04
  75#define ADQ12B_ADLOW            0x08
  76#define ADQ12B_ADHIG            0x09
  77#define ADQ12B_TIMER_BASE       0x0c
  78
  79/* available ranges through the PGA gains */
  80static const struct comedi_lrange range_adq12b_ai_bipolar = {
  81        4, {
  82                BIP_RANGE(5),
  83                BIP_RANGE(2),
  84                BIP_RANGE(1),
  85                BIP_RANGE(0.5)
  86        }
  87};
  88
  89static const struct comedi_lrange range_adq12b_ai_unipolar = {
  90        4, {
  91                UNI_RANGE(5),
  92                UNI_RANGE(2),
  93                UNI_RANGE(1),
  94                UNI_RANGE(0.5)
  95        }
  96};
  97
  98struct adq12b_private {
  99        unsigned int last_ctreg;
 100};
 101
 102static int adq12b_ai_eoc(struct comedi_device *dev,
 103                         struct comedi_subdevice *s,
 104                         struct comedi_insn *insn,
 105                         unsigned long context)
 106{
 107        unsigned char status;
 108
 109        status = inb(dev->iobase + ADQ12B_STINR);
 110        if (status & ADQ12B_STINR_EOC)
 111                return 0;
 112        return -EBUSY;
 113}
 114
 115static int adq12b_ai_insn_read(struct comedi_device *dev,
 116                               struct comedi_subdevice *s,
 117                               struct comedi_insn *insn,
 118                               unsigned int *data)
 119{
 120        struct adq12b_private *devpriv = dev->private;
 121        unsigned int chan = CR_CHAN(insn->chanspec);
 122        unsigned int range = CR_RANGE(insn->chanspec);
 123        unsigned int val;
 124        int ret;
 125        int i;
 126
 127        /* change channel and range only if it is different from the previous */
 128        val = ADQ12B_CTREG_RANGE(range) | ADQ12B_CTREG_CHAN(chan);
 129        if (val != devpriv->last_ctreg) {
 130                outb(val, dev->iobase + ADQ12B_CTREG);
 131                devpriv->last_ctreg = val;
 132                usleep_range(50, 100);  /* wait for the mux to settle */
 133        }
 134
 135        val = inb(dev->iobase + ADQ12B_ADLOW);  /* trigger A/D */
 136
 137        for (i = 0; i < insn->n; i++) {
 138                ret = comedi_timeout(dev, s, insn, adq12b_ai_eoc, 0);
 139                if (ret)
 140                        return ret;
 141
 142                val = inb(dev->iobase + ADQ12B_ADHIG) << 8;
 143                val |= inb(dev->iobase + ADQ12B_ADLOW); /* retriggers A/D */
 144
 145                data[i] = val;
 146        }
 147
 148        return insn->n;
 149}
 150
 151static int adq12b_di_insn_bits(struct comedi_device *dev,
 152                               struct comedi_subdevice *s,
 153                               struct comedi_insn *insn, unsigned int *data)
 154{
 155        /* only bits 0-4 have information about digital inputs */
 156        data[1] = (inb(dev->iobase + ADQ12B_STINR) & ADQ12B_STINR_IN_MASK);
 157
 158        return insn->n;
 159}
 160
 161static int adq12b_do_insn_bits(struct comedi_device *dev,
 162                               struct comedi_subdevice *s,
 163                               struct comedi_insn *insn,
 164                               unsigned int *data)
 165{
 166        unsigned int mask;
 167        unsigned int chan;
 168        unsigned int val;
 169
 170        mask = comedi_dio_update_state(s, data);
 171        if (mask) {
 172                for (chan = 0; chan < 8; chan++) {
 173                        if ((mask >> chan) & 0x01) {
 174                                val = (s->state >> chan) & 0x01;
 175                                outb((val << 3) | chan,
 176                                     dev->iobase + ADQ12B_OUTBR);
 177                        }
 178                }
 179        }
 180
 181        data[1] = s->state;
 182
 183        return insn->n;
 184}
 185
 186static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 187{
 188        struct adq12b_private *devpriv;
 189        struct comedi_subdevice *s;
 190        int ret;
 191
 192        ret = comedi_request_region(dev, it->options[0], 0x10);
 193        if (ret)
 194                return ret;
 195
 196        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 197        if (!devpriv)
 198                return -ENOMEM;
 199
 200        devpriv->last_ctreg = -1;       /* force ctreg update */
 201
 202        ret = comedi_alloc_subdevices(dev, 3);
 203        if (ret)
 204                return ret;
 205
 206        /* Analog Input subdevice */
 207        s = &dev->subdevices[0];
 208        s->type         = COMEDI_SUBD_AI;
 209        if (it->options[2]) {
 210                s->subdev_flags = SDF_READABLE | SDF_DIFF;
 211                s->n_chan       = 8;
 212        } else {
 213                s->subdev_flags = SDF_READABLE | SDF_GROUND;
 214                s->n_chan       = 16;
 215        }
 216        s->maxdata      = 0xfff;
 217        s->range_table  = it->options[1] ? &range_adq12b_ai_unipolar
 218                                         : &range_adq12b_ai_bipolar;
 219        s->insn_read    = adq12b_ai_insn_read;
 220
 221        /* Digital Input subdevice */
 222        s = &dev->subdevices[1];
 223        s->type         = COMEDI_SUBD_DI;
 224        s->subdev_flags = SDF_READABLE;
 225        s->n_chan       = 5;
 226        s->maxdata      = 1;
 227        s->range_table  = &range_digital;
 228        s->insn_bits    = adq12b_di_insn_bits;
 229
 230        /* Digital Output subdevice */
 231        s = &dev->subdevices[2];
 232        s->type         = COMEDI_SUBD_DO;
 233        s->subdev_flags = SDF_WRITABLE;
 234        s->n_chan       = 8;
 235        s->maxdata      = 1;
 236        s->range_table  = &range_digital;
 237        s->insn_bits    = adq12b_do_insn_bits;
 238
 239        return 0;
 240}
 241
 242static struct comedi_driver adq12b_driver = {
 243        .driver_name    = "adq12b",
 244        .module         = THIS_MODULE,
 245        .attach         = adq12b_attach,
 246        .detach         = comedi_legacy_detach,
 247};
 248module_comedi_driver(adq12b_driver);
 249
 250MODULE_AUTHOR("Comedi http://www.comedi.org");
 251MODULE_DESCRIPTION("Comedi low-level driver");
 252MODULE_LICENSE("GPL");
 253