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