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    You should have received a copy of the GNU General Public License
  19    along with this program; if not, write to the Free Software
  20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21
  22*/
  23/*
  24Driver: dt2814
  25Description: Data Translation DT2814
  26Author: ds
  27Status: complete
  28Devices: [Data Translation] DT2814 (dt2814)
  29
  30Configuration options:
  31  [0] - I/O port base address
  32  [1] - IRQ
  33
  34This card has 16 analog inputs multiplexed onto a 12 bit ADC.  There
  35is a minimally useful onboard clock.  The base frequency for the
  36clock is selected by jumpers, and the clock divider can be selected
  37via programmed I/O.  Unfortunately, the clock divider can only be
  38a power of 10, from 1 to 10^7, of which only 3 or 4 are useful.  In
  39addition, the clock does not seem to be very accurate.
  40*/
  41
  42#include <linux/interrupt.h>
  43#include "../comedidev.h"
  44
  45#include <linux/ioport.h>
  46#include <linux/delay.h>
  47
  48#define DT2814_SIZE 2
  49
  50#define DT2814_CSR 0
  51#define DT2814_DATA 1
  52
  53/*
  54 * flags
  55 */
  56
  57#define DT2814_FINISH 0x80
  58#define DT2814_ERR 0x40
  59#define DT2814_BUSY 0x20
  60#define DT2814_ENB 0x10
  61#define DT2814_CHANMASK 0x0f
  62
  63static int dt2814_attach(struct comedi_device *dev,
  64                         struct comedi_devconfig *it);
  65static int dt2814_detach(struct comedi_device *dev);
  66static struct comedi_driver driver_dt2814 = {
  67        .driver_name = "dt2814",
  68        .module = THIS_MODULE,
  69        .attach = dt2814_attach,
  70        .detach = dt2814_detach,
  71};
  72
  73static int __init driver_dt2814_init_module(void)
  74{
  75        return comedi_driver_register(&driver_dt2814);
  76}
  77
  78static void __exit driver_dt2814_cleanup_module(void)
  79{
  80        comedi_driver_unregister(&driver_dt2814);
  81}
  82
  83module_init(driver_dt2814_init_module);
  84module_exit(driver_dt2814_cleanup_module);
  85
  86static irqreturn_t dt2814_interrupt(int irq, void *dev);
  87
  88struct dt2814_private {
  89
  90        int ntrig;
  91        int curadchan;
  92};
  93
  94#define devpriv ((struct dt2814_private *)dev->private)
  95
  96#define DT2814_TIMEOUT 10
  97#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
  98
  99static int dt2814_ai_insn_read(struct comedi_device *dev,
 100                               struct comedi_subdevice *s,
 101                               struct comedi_insn *insn, unsigned int *data)
 102{
 103        int n, i, hi, lo;
 104        int chan;
 105        int status = 0;
 106
 107        for (n = 0; n < insn->n; n++) {
 108                chan = CR_CHAN(insn->chanspec);
 109
 110                outb(chan, dev->iobase + DT2814_CSR);
 111                for (i = 0; i < DT2814_TIMEOUT; i++) {
 112                        status = inb(dev->iobase + DT2814_CSR);
 113                        printk(KERN_INFO "dt2814: status: %02x\n", status);
 114                        udelay(10);
 115                        if (status & DT2814_FINISH)
 116                                break;
 117                }
 118                if (i >= DT2814_TIMEOUT) {
 119                        printk(KERN_INFO "dt2814: status: %02x\n", status);
 120                        return -ETIMEDOUT;
 121                }
 122
 123                hi = inb(dev->iobase + DT2814_DATA);
 124                lo = inb(dev->iobase + DT2814_DATA);
 125
 126                data[n] = (hi << 4) | (lo >> 4);
 127        }
 128
 129        return n;
 130}
 131
 132static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
 133{
 134        int i;
 135        unsigned int f;
 136
 137        /* XXX ignores flags */
 138
 139        f = 10000;              /* ns */
 140        for (i = 0; i < 8; i++) {
 141                if ((2 * (*ns)) < (f * 11))
 142                        break;
 143                f *= 10;
 144        }
 145
 146        *ns = f;
 147
 148        return i;
 149}
 150
 151static int dt2814_ai_cmdtest(struct comedi_device *dev,
 152                             struct comedi_subdevice *s, struct comedi_cmd *cmd)
 153{
 154        int err = 0;
 155        int tmp;
 156
 157        /* step 1: make sure trigger sources are trivially valid */
 158
 159        tmp = cmd->start_src;
 160        cmd->start_src &= TRIG_NOW;
 161        if (!cmd->start_src || tmp != cmd->start_src)
 162                err++;
 163
 164        tmp = cmd->scan_begin_src;
 165        cmd->scan_begin_src &= TRIG_TIMER;
 166        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 167                err++;
 168
 169        tmp = cmd->convert_src;
 170        cmd->convert_src &= TRIG_NOW;
 171        if (!cmd->convert_src || tmp != cmd->convert_src)
 172                err++;
 173
 174        tmp = cmd->scan_end_src;
 175        cmd->scan_end_src &= TRIG_COUNT;
 176        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 177                err++;
 178
 179        tmp = cmd->stop_src;
 180        cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
 181        if (!cmd->stop_src || tmp != cmd->stop_src)
 182                err++;
 183
 184        if (err)
 185                return 1;
 186
 187        /* step 2: make sure trigger sources are
 188         * unique and mutually compatible */
 189
 190        /* note that mutual compatibility is not an issue here */
 191        if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
 192                err++;
 193
 194        if (err)
 195                return 2;
 196
 197        /* step 3: make sure arguments are trivially compatible */
 198
 199        if (cmd->start_arg != 0) {
 200                cmd->start_arg = 0;
 201                err++;
 202        }
 203        if (cmd->scan_begin_arg > 1000000000) {
 204                cmd->scan_begin_arg = 1000000000;
 205                err++;
 206        }
 207        if (cmd->scan_begin_arg < DT2814_MAX_SPEED) {
 208                cmd->scan_begin_arg = DT2814_MAX_SPEED;
 209                err++;
 210        }
 211        if (cmd->scan_end_arg != cmd->chanlist_len) {
 212                cmd->scan_end_arg = cmd->chanlist_len;
 213                err++;
 214        }
 215        if (cmd->stop_src == TRIG_COUNT) {
 216                if (cmd->stop_arg < 2) {
 217                        cmd->stop_arg = 2;
 218                        err++;
 219                }
 220        } else {
 221                /* TRIG_NONE */
 222                if (cmd->stop_arg != 0) {
 223                        cmd->stop_arg = 0;
 224                        err++;
 225                }
 226        }
 227
 228        if (err)
 229                return 3;
 230
 231        /* step 4: fix up any arguments */
 232
 233        tmp = cmd->scan_begin_arg;
 234        dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
 235        if (tmp != cmd->scan_begin_arg)
 236                err++;
 237
 238        if (err)
 239                return 4;
 240
 241        return 0;
 242}
 243
 244static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 245{
 246        struct comedi_cmd *cmd = &s->async->cmd;
 247        int chan;
 248        int trigvar;
 249
 250        trigvar =
 251            dt2814_ns_to_timer(&cmd->scan_begin_arg,
 252                               cmd->flags & TRIG_ROUND_MASK);
 253
 254        chan = CR_CHAN(cmd->chanlist[0]);
 255
 256        devpriv->ntrig = cmd->stop_arg;
 257        outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
 258
 259        return 0;
 260
 261}
 262
 263static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 264{
 265        int i, irq;
 266        int ret;
 267        struct comedi_subdevice *s;
 268        unsigned long iobase;
 269
 270        iobase = it->options[0];
 271        printk(KERN_INFO "comedi%d: dt2814: 0x%04lx ", dev->minor, iobase);
 272        if (!request_region(iobase, DT2814_SIZE, "dt2814")) {
 273                printk(KERN_ERR "I/O port conflict\n");
 274                return -EIO;
 275        }
 276        dev->iobase = iobase;
 277        dev->board_name = "dt2814";
 278
 279        outb(0, dev->iobase + DT2814_CSR);
 280        udelay(100);
 281        if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
 282                printk(KERN_ERR "reset error (fatal)\n");
 283                return -EIO;
 284        }
 285        i = inb(dev->iobase + DT2814_DATA);
 286        i = inb(dev->iobase + DT2814_DATA);
 287
 288        irq = it->options[1];
 289#if 0
 290        if (irq < 0) {
 291                save_flags(flags);
 292                sti();
 293                irqs = probe_irq_on();
 294
 295                outb(0, dev->iobase + DT2814_CSR);
 296
 297                udelay(100);
 298
 299                irq = probe_irq_off(irqs);
 300                restore_flags(flags);
 301                if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR)
 302                        printk(KERN_DEBUG "error probing irq (bad)\n");
 303
 304
 305                i = inb(dev->iobase + DT2814_DATA);
 306                i = inb(dev->iobase + DT2814_DATA);
 307        }
 308#endif
 309        dev->irq = 0;
 310        if (irq > 0) {
 311                if (request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) {
 312                        printk(KERN_WARNING "(irq %d unavailable)\n", irq);
 313                } else {
 314                        printk(KERN_INFO "( irq = %d )\n", irq);
 315                        dev->irq = irq;
 316                }
 317        } else if (irq == 0) {
 318                printk(KERN_WARNING "(no irq)\n");
 319        } else {
 320#if 0
 321                printk(KERN_DEBUG "(probe returned multiple irqs--bad)\n");
 322#else
 323                printk(KERN_WARNING "(irq probe not implemented)\n");
 324#endif
 325        }
 326
 327        ret = alloc_subdevices(dev, 1);
 328        if (ret < 0)
 329                return ret;
 330
 331        ret = alloc_private(dev, sizeof(struct dt2814_private));
 332        if (ret < 0)
 333                return ret;
 334
 335        s = dev->subdevices + 0;
 336        dev->read_subdev = s;
 337        s->type = COMEDI_SUBD_AI;
 338        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
 339        s->n_chan = 16;         /* XXX */
 340        s->len_chanlist = 1;
 341        s->insn_read = dt2814_ai_insn_read;
 342        s->do_cmd = dt2814_ai_cmd;
 343        s->do_cmdtest = dt2814_ai_cmdtest;
 344        s->maxdata = 0xfff;
 345        s->range_table = &range_unknown;        /* XXX */
 346
 347        return 0;
 348}
 349
 350static int dt2814_detach(struct comedi_device *dev)
 351{
 352        printk(KERN_INFO "comedi%d: dt2814: remove\n", dev->minor);
 353
 354        if (dev->irq)
 355                free_irq(dev->irq, dev);
 356
 357        if (dev->iobase)
 358                release_region(dev->iobase, DT2814_SIZE);
 359
 360        return 0;
 361}
 362
 363static irqreturn_t dt2814_interrupt(int irq, void *d)
 364{
 365        int lo, hi;
 366        struct comedi_device *dev = d;
 367        struct comedi_subdevice *s;
 368        int data;
 369
 370        if (!dev->attached) {
 371                comedi_error(dev, "spurious interrupt");
 372                return IRQ_HANDLED;
 373        }
 374
 375        s = dev->subdevices + 0;
 376
 377        hi = inb(dev->iobase + DT2814_DATA);
 378        lo = inb(dev->iobase + DT2814_DATA);
 379
 380        data = (hi << 4) | (lo >> 4);
 381
 382        if (!(--devpriv->ntrig)) {
 383                int i;
 384
 385                outb(0, dev->iobase + DT2814_CSR);
 386                /* note: turning off timed mode triggers another
 387                   sample. */
 388
 389                for (i = 0; i < DT2814_TIMEOUT; i++) {
 390                        if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
 391                                break;
 392                }
 393                inb(dev->iobase + DT2814_DATA);
 394                inb(dev->iobase + DT2814_DATA);
 395
 396                s->async->events |= COMEDI_CB_EOA;
 397        }
 398        comedi_event(dev, s);
 399        return IRQ_HANDLED;
 400}
 401
 402MODULE_AUTHOR("Comedi http://www.comedi.org");
 403MODULE_DESCRIPTION("Comedi low-level driver");
 404MODULE_LICENSE("GPL");
 405