linux/drivers/staging/comedi/drivers/multiq3.c
<<
>>
Prefs
   1/*
   2   comedi/drivers/multiq3.c
   3   Hardware driver for Quanser Consulting MultiQ-3 board
   4
   5   COMEDI - Linux Control and Measurement Device Interface
   6   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
   7
   8   This program is free software; you can redistribute it and/or modify
   9   it under the terms of the GNU General Public License as published by
  10   the Free Software Foundation; either version 2 of the License, or
  11   (at your option) any later version.
  12
  13   This program is distributed in the hope that it will be useful,
  14   but WITHOUT ANY WARRANTY; without even the implied warranty of
  15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16   GNU General Public License for more details.
  17 */
  18/*
  19Driver: multiq3
  20Description: Quanser Consulting MultiQ-3
  21Author: Anders Blomdell <anders.blomdell@control.lth.se>
  22Status: works
  23Devices: [Quanser Consulting] MultiQ-3 (multiq3)
  24
  25*/
  26
  27#include <linux/module.h>
  28#include <linux/interrupt.h>
  29#include "../comedidev.h"
  30
  31#define MULTIQ3_SIZE 16
  32
  33/*
  34 * MULTIQ-3 port offsets
  35 */
  36#define MULTIQ3_DIGIN_PORT 0
  37#define MULTIQ3_DIGOUT_PORT 0
  38#define MULTIQ3_DAC_DATA 2
  39#define MULTIQ3_AD_DATA 4
  40#define MULTIQ3_AD_CS 4
  41#define MULTIQ3_STATUS 6
  42#define MULTIQ3_CONTROL 6
  43#define MULTIQ3_CLK_DATA 8
  44#define MULTIQ3_ENC_DATA 12
  45#define MULTIQ3_ENC_CONTROL 14
  46
  47/*
  48 * flags for CONTROL register
  49 */
  50#define MULTIQ3_AD_MUX_EN      0x0040
  51#define MULTIQ3_AD_AUTOZ       0x0080
  52#define MULTIQ3_AD_AUTOCAL     0x0100
  53#define MULTIQ3_AD_SH          0x0200
  54#define MULTIQ3_AD_CLOCK_4M    0x0400
  55#define MULTIQ3_DA_LOAD                0x1800
  56
  57#define MULTIQ3_CONTROL_MUST    0x0600
  58
  59/*
  60 * flags for STATUS register
  61 */
  62#define MULTIQ3_STATUS_EOC      0x008
  63#define MULTIQ3_STATUS_EOC_I    0x010
  64
  65/*
  66 * flags for encoder control
  67 */
  68#define MULTIQ3_CLOCK_DATA      0x00
  69#define MULTIQ3_CLOCK_SETUP     0x18
  70#define MULTIQ3_INPUT_SETUP     0x41
  71#define MULTIQ3_QUAD_X4         0x38
  72#define MULTIQ3_BP_RESET        0x01
  73#define MULTIQ3_CNTR_RESET      0x02
  74#define MULTIQ3_TRSFRPR_CTR     0x08
  75#define MULTIQ3_TRSFRCNTR_OL    0x10
  76#define MULTIQ3_EFLAG_RESET     0x06
  77
  78#define MULTIQ3_TIMEOUT 30
  79
  80struct multiq3_private {
  81        unsigned int ao_readback[2];
  82};
  83
  84static int multiq3_ai_status(struct comedi_device *dev,
  85                             struct comedi_subdevice *s,
  86                             struct comedi_insn *insn,
  87                             unsigned long context)
  88{
  89        unsigned int status;
  90
  91        status = inw(dev->iobase + MULTIQ3_STATUS);
  92        if (status & context)
  93                return 0;
  94        return -EBUSY;
  95}
  96
  97static int multiq3_ai_insn_read(struct comedi_device *dev,
  98                                struct comedi_subdevice *s,
  99                                struct comedi_insn *insn, unsigned int *data)
 100{
 101        int n;
 102        int chan;
 103        unsigned int hi, lo;
 104        int ret;
 105
 106        chan = CR_CHAN(insn->chanspec);
 107        outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
 108             dev->iobase + MULTIQ3_CONTROL);
 109
 110        ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 111                             MULTIQ3_STATUS_EOC);
 112        if (ret)
 113                return ret;
 114
 115        for (n = 0; n < insn->n; n++) {
 116                outw(0, dev->iobase + MULTIQ3_AD_CS);
 117
 118                ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 119                                     MULTIQ3_STATUS_EOC_I);
 120                if (ret)
 121                        return ret;
 122
 123                hi = inb(dev->iobase + MULTIQ3_AD_CS);
 124                lo = inb(dev->iobase + MULTIQ3_AD_CS);
 125                data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
 126        }
 127
 128        return n;
 129}
 130
 131static int multiq3_ao_insn_read(struct comedi_device *dev,
 132                                struct comedi_subdevice *s,
 133                                struct comedi_insn *insn, unsigned int *data)
 134{
 135        struct multiq3_private *devpriv = dev->private;
 136        int i;
 137        int chan = CR_CHAN(insn->chanspec);
 138
 139        for (i = 0; i < insn->n; i++)
 140                data[i] = devpriv->ao_readback[chan];
 141
 142        return i;
 143}
 144
 145static int multiq3_ao_insn_write(struct comedi_device *dev,
 146                                 struct comedi_subdevice *s,
 147                                 struct comedi_insn *insn, unsigned int *data)
 148{
 149        struct multiq3_private *devpriv = dev->private;
 150        int i;
 151        int chan = CR_CHAN(insn->chanspec);
 152
 153        for (i = 0; i < insn->n; i++) {
 154                outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
 155                     dev->iobase + MULTIQ3_CONTROL);
 156                outw(data[i], dev->iobase + MULTIQ3_DAC_DATA);
 157                outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
 158
 159                devpriv->ao_readback[chan] = data[i];
 160        }
 161
 162        return i;
 163}
 164
 165static int multiq3_di_insn_bits(struct comedi_device *dev,
 166                                struct comedi_subdevice *s,
 167                                struct comedi_insn *insn, unsigned int *data)
 168{
 169        data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
 170
 171        return insn->n;
 172}
 173
 174static int multiq3_do_insn_bits(struct comedi_device *dev,
 175                                struct comedi_subdevice *s,
 176                                struct comedi_insn *insn,
 177                                unsigned int *data)
 178{
 179        if (comedi_dio_update_state(s, data))
 180                outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
 181
 182        data[1] = s->state;
 183
 184        return insn->n;
 185}
 186
 187static int multiq3_encoder_insn_read(struct comedi_device *dev,
 188                                     struct comedi_subdevice *s,
 189                                     struct comedi_insn *insn,
 190                                     unsigned int *data)
 191{
 192        int n;
 193        int chan = CR_CHAN(insn->chanspec);
 194        int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
 195
 196        for (n = 0; n < insn->n; n++) {
 197                int value;
 198                outw(control, dev->iobase + MULTIQ3_CONTROL);
 199                outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 200                outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
 201                value = inb(dev->iobase + MULTIQ3_ENC_DATA);
 202                value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
 203                value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
 204                data[n] = (value + 0x800000) & 0xffffff;
 205        }
 206
 207        return n;
 208}
 209
 210static void encoder_reset(struct comedi_device *dev)
 211{
 212        struct comedi_subdevice *s = &dev->subdevices[4];
 213        int chan;
 214
 215        for (chan = 0; chan < s->n_chan; chan++) {
 216                int control =
 217                    MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
 218                outw(control, dev->iobase + MULTIQ3_CONTROL);
 219                outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 220                outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 221                outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA);
 222                outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
 223                outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
 224                outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CONTROL);
 225                outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 226        }
 227}
 228
 229static int multiq3_attach(struct comedi_device *dev,
 230                          struct comedi_devconfig *it)
 231{
 232        struct multiq3_private *devpriv;
 233        struct comedi_subdevice *s;
 234        int ret;
 235
 236        ret = comedi_request_region(dev, it->options[0], MULTIQ3_SIZE);
 237        if (ret)
 238                return ret;
 239
 240        ret = comedi_alloc_subdevices(dev, 5);
 241        if (ret)
 242                return ret;
 243
 244        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 245        if (!devpriv)
 246                return -ENOMEM;
 247
 248        s = &dev->subdevices[0];
 249        /* ai subdevice */
 250        s->type = COMEDI_SUBD_AI;
 251        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 252        s->n_chan = 8;
 253        s->insn_read = multiq3_ai_insn_read;
 254        s->maxdata = 0x1fff;
 255        s->range_table = &range_bipolar5;
 256
 257        s = &dev->subdevices[1];
 258        /* ao subdevice */
 259        s->type = COMEDI_SUBD_AO;
 260        s->subdev_flags = SDF_WRITABLE;
 261        s->n_chan = 8;
 262        s->insn_read = multiq3_ao_insn_read;
 263        s->insn_write = multiq3_ao_insn_write;
 264        s->maxdata = 0xfff;
 265        s->range_table = &range_bipolar5;
 266
 267        s = &dev->subdevices[2];
 268        /* di subdevice */
 269        s->type = COMEDI_SUBD_DI;
 270        s->subdev_flags = SDF_READABLE;
 271        s->n_chan = 16;
 272        s->insn_bits = multiq3_di_insn_bits;
 273        s->maxdata = 1;
 274        s->range_table = &range_digital;
 275
 276        s = &dev->subdevices[3];
 277        /* do subdevice */
 278        s->type = COMEDI_SUBD_DO;
 279        s->subdev_flags = SDF_WRITABLE;
 280        s->n_chan = 16;
 281        s->insn_bits = multiq3_do_insn_bits;
 282        s->maxdata = 1;
 283        s->range_table = &range_digital;
 284        s->state = 0;
 285
 286        s = &dev->subdevices[4];
 287        /* encoder (counter) subdevice */
 288        s->type = COMEDI_SUBD_COUNTER;
 289        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 290        s->n_chan = it->options[2] * 2;
 291        s->insn_read = multiq3_encoder_insn_read;
 292        s->maxdata = 0xffffff;
 293        s->range_table = &range_unknown;
 294
 295        encoder_reset(dev);
 296
 297        return 0;
 298}
 299
 300static struct comedi_driver multiq3_driver = {
 301        .driver_name    = "multiq3",
 302        .module         = THIS_MODULE,
 303        .attach         = multiq3_attach,
 304        .detach         = comedi_legacy_detach,
 305};
 306module_comedi_driver(multiq3_driver);
 307
 308MODULE_AUTHOR("Comedi http://www.comedi.org");
 309MODULE_DESCRIPTION("Comedi low-level driver");
 310MODULE_LICENSE("GPL");
 311