root/drivers/staging/comedi/drivers/dt2814.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dt2814_ai_eoc
  2. dt2814_ai_insn_read
  3. dt2814_ns_to_timer
  4. dt2814_ai_cmdtest
  5. dt2814_ai_cmd
  6. dt2814_interrupt
  7. dt2814_attach

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

/* [<][>][^][v][top][bottom][index][help] */