linux/drivers/staging/comedi/drivers/dt2814.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/dt2814.c
   3 * Hardware driver for Data Translation DT2814
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
   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/*
  19 * Driver: dt2814
  20 * Description: Data Translation DT2814
  21 * Author: ds
  22 * Status: complete
  23 * Devices: [Data Translation] DT2814 (dt2814)
  24 *
  25 * Configuration options:
  26 * [0] - I/O port base address
  27 * [1] - IRQ
  28 *
  29 * This card has 16 analog inputs multiplexed onto a 12 bit ADC.  There
  30 * is a minimally useful onboard clock.  The base frequency for the
  31 * clock is selected by jumpers, and the clock divider can be selected
  32 * via programmed I/O.  Unfortunately, the clock divider can only be
  33 * a power of 10, from 1 to 10^7, of which only 3 or 4 are useful.  In
  34 * addition, the clock does not seem to be very accurate.
  35 */
  36
  37#include <linux/module.h>
  38#include <linux/interrupt.h>
  39#include "../comedidev.h"
  40
  41#include <linux/delay.h>
  42
  43#define DT2814_CSR 0
  44#define DT2814_DATA 1
  45
  46/*
  47 * flags
  48 */
  49
  50#define DT2814_FINISH 0x80
  51#define DT2814_ERR 0x40
  52#define DT2814_BUSY 0x20
  53#define DT2814_ENB 0x10
  54#define DT2814_CHANMASK 0x0f
  55
  56struct dt2814_private {
  57        int ntrig;
  58        int curadchan;
  59};
  60
  61#define DT2814_TIMEOUT 10
  62#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
  63
  64static int dt2814_ai_eoc(struct comedi_device *dev,
  65                         struct comedi_subdevice *s,
  66                         struct comedi_insn *insn,
  67                         unsigned long context)
  68{
  69        unsigned int status;
  70
  71        status = inb(dev->iobase + DT2814_CSR);
  72        if (status & DT2814_FINISH)
  73                return 0;
  74        return -EBUSY;
  75}
  76
  77static int dt2814_ai_insn_read(struct comedi_device *dev,
  78                               struct comedi_subdevice *s,
  79                               struct comedi_insn *insn, unsigned int *data)
  80{
  81        int n, hi, lo;
  82        int chan;
  83        int ret;
  84
  85        for (n = 0; n < insn->n; n++) {
  86                chan = CR_CHAN(insn->chanspec);
  87
  88                outb(chan, dev->iobase + DT2814_CSR);
  89
  90                ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0);
  91                if (ret)
  92                        return ret;
  93
  94                hi = inb(dev->iobase + DT2814_DATA);
  95                lo = inb(dev->iobase + DT2814_DATA);
  96
  97                data[n] = (hi << 4) | (lo >> 4);
  98        }
  99
 100        return n;
 101}
 102
 103static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
 104{
 105        int i;
 106        unsigned int f;
 107
 108        /* XXX ignores flags */
 109
 110        f = 10000;              /* ns */
 111        for (i = 0; i < 8; i++) {
 112                if ((2 * (*ns)) < (f * 11))
 113                        break;
 114                f *= 10;
 115        }
 116
 117        *ns = f;
 118
 119        return i;
 120}
 121
 122static int dt2814_ai_cmdtest(struct comedi_device *dev,
 123                             struct comedi_subdevice *s, struct comedi_cmd *cmd)
 124{
 125        int err = 0;
 126        unsigned int arg;
 127
 128        /* Step 1 : check if triggers are trivially valid */
 129
 130        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 131        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 132        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
 133        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 134        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 135
 136        if (err)
 137                return 1;
 138
 139        /* Step 2a : make sure trigger sources are unique */
 140
 141        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 142
 143        /* Step 2b : and mutually compatible */
 144
 145        if (err)
 146                return 2;
 147
 148        /* Step 3: check if arguments are trivially valid */
 149
 150        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 151
 152        err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
 153        err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
 154                                            DT2814_MAX_SPEED);
 155
 156        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 157                                           cmd->chanlist_len);
 158
 159        if (cmd->stop_src == TRIG_COUNT)
 160                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2);
 161        else    /* TRIG_NONE */
 162                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 163
 164        if (err)
 165                return 3;
 166
 167        /* step 4: fix up any arguments */
 168
 169        arg = cmd->scan_begin_arg;
 170        dt2814_ns_to_timer(&arg, cmd->flags);
 171        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
 172
 173        if (err)
 174                return 4;
 175
 176        return 0;
 177}
 178
 179static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 180{
 181        struct dt2814_private *devpriv = dev->private;
 182        struct comedi_cmd *cmd = &s->async->cmd;
 183        int chan;
 184        int trigvar;
 185
 186        trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
 187
 188        chan = CR_CHAN(cmd->chanlist[0]);
 189
 190        devpriv->ntrig = cmd->stop_arg;
 191        outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
 192
 193        return 0;
 194}
 195
 196static irqreturn_t dt2814_interrupt(int irq, void *d)
 197{
 198        int lo, hi;
 199        struct comedi_device *dev = d;
 200        struct dt2814_private *devpriv = dev->private;
 201        struct comedi_subdevice *s = dev->read_subdev;
 202        int data;
 203
 204        if (!dev->attached) {
 205                dev_err(dev->class_dev, "spurious interrupt\n");
 206                return IRQ_HANDLED;
 207        }
 208
 209        hi = inb(dev->iobase + DT2814_DATA);
 210        lo = inb(dev->iobase + DT2814_DATA);
 211
 212        data = (hi << 4) | (lo >> 4);
 213
 214        if (!(--devpriv->ntrig)) {
 215                int i;
 216
 217                outb(0, dev->iobase + DT2814_CSR);
 218                /*
 219                 * note: turning off timed mode triggers another
 220                 * sample.
 221                 */
 222
 223                for (i = 0; i < DT2814_TIMEOUT; i++) {
 224                        if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
 225                                break;
 226                }
 227                inb(dev->iobase + DT2814_DATA);
 228                inb(dev->iobase + DT2814_DATA);
 229
 230                s->async->events |= COMEDI_CB_EOA;
 231        }
 232        comedi_handle_events(dev, s);
 233        return IRQ_HANDLED;
 234}
 235
 236static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 237{
 238        struct dt2814_private *devpriv;
 239        struct comedi_subdevice *s;
 240        int ret;
 241        int i;
 242
 243        ret = comedi_request_region(dev, it->options[0], 0x2);
 244        if (ret)
 245                return ret;
 246
 247        outb(0, dev->iobase + DT2814_CSR);
 248        usleep_range(100, 200);
 249        if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
 250                dev_err(dev->class_dev, "reset error (fatal)\n");
 251                return -EIO;
 252        }
 253        i = inb(dev->iobase + DT2814_DATA);
 254        i = inb(dev->iobase + DT2814_DATA);
 255
 256        if (it->options[1]) {
 257                ret = request_irq(it->options[1], dt2814_interrupt, 0,
 258                                  dev->board_name, dev);
 259                if (ret == 0)
 260                        dev->irq = it->options[1];
 261        }
 262
 263        ret = comedi_alloc_subdevices(dev, 1);
 264        if (ret)
 265                return ret;
 266
 267        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 268        if (!devpriv)
 269                return -ENOMEM;
 270
 271        s = &dev->subdevices[0];
 272        s->type = COMEDI_SUBD_AI;
 273        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 274        s->n_chan = 16;         /* XXX */
 275        s->insn_read = dt2814_ai_insn_read;
 276        s->maxdata = 0xfff;
 277        s->range_table = &range_unknown;        /* XXX */
 278        if (dev->irq) {
 279                dev->read_subdev = s;
 280                s->subdev_flags |= SDF_CMD_READ;
 281                s->len_chanlist = 1;
 282                s->do_cmd = dt2814_ai_cmd;
 283                s->do_cmdtest = dt2814_ai_cmdtest;
 284        }
 285
 286        return 0;
 287}
 288
 289static struct comedi_driver dt2814_driver = {
 290        .driver_name    = "dt2814",
 291        .module         = THIS_MODULE,
 292        .attach         = dt2814_attach,
 293        .detach         = comedi_legacy_detach,
 294};
 295module_comedi_driver(dt2814_driver);
 296
 297MODULE_AUTHOR("Comedi http://www.comedi.org");
 298MODULE_DESCRIPTION("Comedi low-level driver");
 299MODULE_LICENSE("GPL");
 300