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