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/*
  32 * MULTIQ-3 port offsets
  33 */
  34#define MULTIQ3_DIGIN_PORT 0
  35#define MULTIQ3_DIGOUT_PORT 0
  36#define MULTIQ3_DAC_DATA 2
  37#define MULTIQ3_AD_DATA 4
  38#define MULTIQ3_AD_CS 4
  39#define MULTIQ3_STATUS 6
  40#define MULTIQ3_CONTROL 6
  41#define MULTIQ3_CLK_DATA 8
  42#define MULTIQ3_ENC_DATA 12
  43#define MULTIQ3_ENC_CONTROL 14
  44
  45/*
  46 * flags for CONTROL register
  47 */
  48#define MULTIQ3_AD_MUX_EN      0x0040
  49#define MULTIQ3_AD_AUTOZ       0x0080
  50#define MULTIQ3_AD_AUTOCAL     0x0100
  51#define MULTIQ3_AD_SH          0x0200
  52#define MULTIQ3_AD_CLOCK_4M    0x0400
  53#define MULTIQ3_DA_LOAD                0x1800
  54
  55#define MULTIQ3_CONTROL_MUST    0x0600
  56
  57/*
  58 * flags for STATUS register
  59 */
  60#define MULTIQ3_STATUS_EOC      0x008
  61#define MULTIQ3_STATUS_EOC_I    0x010
  62
  63/*
  64 * flags for encoder control
  65 */
  66#define MULTIQ3_CLOCK_DATA      0x00
  67#define MULTIQ3_CLOCK_SETUP     0x18
  68#define MULTIQ3_INPUT_SETUP     0x41
  69#define MULTIQ3_QUAD_X4         0x38
  70#define MULTIQ3_BP_RESET        0x01
  71#define MULTIQ3_CNTR_RESET      0x02
  72#define MULTIQ3_TRSFRPR_CTR     0x08
  73#define MULTIQ3_TRSFRCNTR_OL    0x10
  74#define MULTIQ3_EFLAG_RESET     0x06
  75
  76#define MULTIQ3_TIMEOUT 30
  77
  78static int multiq3_ai_status(struct comedi_device *dev,
  79                             struct comedi_subdevice *s,
  80                             struct comedi_insn *insn,
  81                             unsigned long context)
  82{
  83        unsigned int status;
  84
  85        status = inw(dev->iobase + MULTIQ3_STATUS);
  86        if (status & context)
  87                return 0;
  88        return -EBUSY;
  89}
  90
  91static int multiq3_ai_insn_read(struct comedi_device *dev,
  92                                struct comedi_subdevice *s,
  93                                struct comedi_insn *insn, unsigned int *data)
  94{
  95        int n;
  96        int chan;
  97        unsigned int hi, lo;
  98        int ret;
  99
 100        chan = CR_CHAN(insn->chanspec);
 101        outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
 102             dev->iobase + MULTIQ3_CONTROL);
 103
 104        ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 105                             MULTIQ3_STATUS_EOC);
 106        if (ret)
 107                return ret;
 108
 109        for (n = 0; n < insn->n; n++) {
 110                outw(0, dev->iobase + MULTIQ3_AD_CS);
 111
 112                ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
 113                                     MULTIQ3_STATUS_EOC_I);
 114                if (ret)
 115                        return ret;
 116
 117                hi = inb(dev->iobase + MULTIQ3_AD_CS);
 118                lo = inb(dev->iobase + MULTIQ3_AD_CS);
 119                data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
 120        }
 121
 122        return n;
 123}
 124
 125static int multiq3_ao_insn_write(struct comedi_device *dev,
 126                                 struct comedi_subdevice *s,
 127                                 struct comedi_insn *insn,
 128                                 unsigned int *data)
 129{
 130        unsigned int chan = CR_CHAN(insn->chanspec);
 131        unsigned int val = s->readback[chan];
 132        int i;
 133
 134        for (i = 0; i < insn->n; i++) {
 135                val = data[i];
 136                outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
 137                     dev->iobase + MULTIQ3_CONTROL);
 138                outw(val, dev->iobase + MULTIQ3_DAC_DATA);
 139                outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
 140        }
 141        s->readback[chan] = val;
 142
 143        return insn->n;
 144}
 145
 146static int multiq3_di_insn_bits(struct comedi_device *dev,
 147                                struct comedi_subdevice *s,
 148                                struct comedi_insn *insn, unsigned int *data)
 149{
 150        data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
 151
 152        return insn->n;
 153}
 154
 155static int multiq3_do_insn_bits(struct comedi_device *dev,
 156                                struct comedi_subdevice *s,
 157                                struct comedi_insn *insn,
 158                                unsigned int *data)
 159{
 160        if (comedi_dio_update_state(s, data))
 161                outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
 162
 163        data[1] = s->state;
 164
 165        return insn->n;
 166}
 167
 168static int multiq3_encoder_insn_read(struct comedi_device *dev,
 169                                     struct comedi_subdevice *s,
 170                                     struct comedi_insn *insn,
 171                                     unsigned int *data)
 172{
 173        int chan = CR_CHAN(insn->chanspec);
 174        int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
 175        int value;
 176        int n;
 177
 178        for (n = 0; n < insn->n; n++) {
 179                outw(control, dev->iobase + MULTIQ3_CONTROL);
 180                outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 181                outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
 182                value = inb(dev->iobase + MULTIQ3_ENC_DATA);
 183                value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
 184                value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
 185                data[n] = (value + 0x800000) & 0xffffff;
 186        }
 187
 188        return n;
 189}
 190
 191static void encoder_reset(struct comedi_device *dev)
 192{
 193        struct comedi_subdevice *s = &dev->subdevices[4];
 194        int chan;
 195
 196        for (chan = 0; chan < s->n_chan; chan++) {
 197                int control =
 198                    MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
 199                outw(control, dev->iobase + MULTIQ3_CONTROL);
 200                outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 201                outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 202                outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA);
 203                outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
 204                outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
 205                outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CONTROL);
 206                outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
 207        }
 208}
 209
 210static int multiq3_attach(struct comedi_device *dev,
 211                          struct comedi_devconfig *it)
 212{
 213        struct comedi_subdevice *s;
 214        int ret;
 215
 216        ret = comedi_request_region(dev, it->options[0], 0x10);
 217        if (ret)
 218                return ret;
 219
 220        ret = comedi_alloc_subdevices(dev, 5);
 221        if (ret)
 222                return ret;
 223
 224        s = &dev->subdevices[0];
 225        /* ai subdevice */
 226        s->type = COMEDI_SUBD_AI;
 227        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 228        s->n_chan = 8;
 229        s->insn_read = multiq3_ai_insn_read;
 230        s->maxdata = 0x1fff;
 231        s->range_table = &range_bipolar5;
 232
 233        s = &dev->subdevices[1];
 234        /* ao subdevice */
 235        s->type = COMEDI_SUBD_AO;
 236        s->subdev_flags = SDF_WRITABLE;
 237        s->n_chan = 8;
 238        s->maxdata = 0xfff;
 239        s->range_table = &range_bipolar5;
 240        s->insn_write = multiq3_ao_insn_write;
 241
 242        ret = comedi_alloc_subdev_readback(s);
 243        if (ret)
 244                return ret;
 245
 246        s = &dev->subdevices[2];
 247        /* di subdevice */
 248        s->type = COMEDI_SUBD_DI;
 249        s->subdev_flags = SDF_READABLE;
 250        s->n_chan = 16;
 251        s->insn_bits = multiq3_di_insn_bits;
 252        s->maxdata = 1;
 253        s->range_table = &range_digital;
 254
 255        s = &dev->subdevices[3];
 256        /* do subdevice */
 257        s->type = COMEDI_SUBD_DO;
 258        s->subdev_flags = SDF_WRITABLE;
 259        s->n_chan = 16;
 260        s->insn_bits = multiq3_do_insn_bits;
 261        s->maxdata = 1;
 262        s->range_table = &range_digital;
 263        s->state = 0;
 264
 265        s = &dev->subdevices[4];
 266        /* encoder (counter) subdevice */
 267        s->type = COMEDI_SUBD_COUNTER;
 268        s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
 269        s->n_chan = it->options[2] * 2;
 270        s->insn_read = multiq3_encoder_insn_read;
 271        s->maxdata = 0xffffff;
 272        s->range_table = &range_unknown;
 273
 274        encoder_reset(dev);
 275
 276        return 0;
 277}
 278
 279static struct comedi_driver multiq3_driver = {
 280        .driver_name    = "multiq3",
 281        .module         = THIS_MODULE,
 282        .attach         = multiq3_attach,
 283        .detach         = comedi_legacy_detach,
 284};
 285module_comedi_driver(multiq3_driver);
 286
 287MODULE_AUTHOR("Comedi http://www.comedi.org");
 288MODULE_DESCRIPTION("Comedi low-level driver");
 289MODULE_LICENSE("GPL");
 290