linux/drivers/staging/comedi/drivers/rti800.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/rti800.c
   3 * Hardware driver for Analog Devices RTI-800/815 board
   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/*
  20 * Driver: rti800
  21 * Description: Analog Devices RTI-800/815
  22 * Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
  23 * Author: David A. Schleef <ds@schleef.org>
  24 * Status: unknown
  25 * Updated: Fri, 05 Sep 2008 14:50:44 +0100
  26 *
  27 * Configuration options:
  28 *   [0] - I/O port base address
  29 *   [1] - IRQ (not supported / unused)
  30 *   [2] - A/D mux/reference (number of channels)
  31 *         0 = differential
  32 *         1 = pseudodifferential (common)
  33 *         2 = single-ended
  34 *   [3] - A/D range
  35 *         0 = [-10,10]
  36 *         1 = [-5,5]
  37 *         2 = [0,10]
  38 *   [4] - A/D encoding
  39 *         0 = two's complement
  40 *         1 = straight binary
  41 *   [5] - DAC 0 range
  42 *         0 = [-10,10]
  43 *         1 = [0,10]
  44 *   [6] - DAC 0 encoding
  45 *         0 = two's complement
  46 *         1 = straight binary
  47 *   [7] - DAC 1 range (same as DAC 0)
  48 *   [8] - DAC 1 encoding (same as DAC 0)
  49 */
  50
  51#include <linux/module.h>
  52#include <linux/delay.h>
  53#include <linux/interrupt.h>
  54#include "../comedidev.h"
  55
  56/*
  57 * Register map
  58 */
  59#define RTI800_CSR              0x00
  60#define RTI800_CSR_BUSY         BIT(7)
  61#define RTI800_CSR_DONE         BIT(6)
  62#define RTI800_CSR_OVERRUN      BIT(5)
  63#define RTI800_CSR_TCR          BIT(4)
  64#define RTI800_CSR_DMA_ENAB     BIT(3)
  65#define RTI800_CSR_INTR_TC      BIT(2)
  66#define RTI800_CSR_INTR_EC      BIT(1)
  67#define RTI800_CSR_INTR_OVRN    BIT(0)
  68#define RTI800_MUXGAIN          0x01
  69#define RTI800_CONVERT          0x02
  70#define RTI800_ADCLO            0x03
  71#define RTI800_ADCHI            0x04
  72#define RTI800_DAC0LO           0x05
  73#define RTI800_DAC0HI           0x06
  74#define RTI800_DAC1LO           0x07
  75#define RTI800_DAC1HI           0x08
  76#define RTI800_CLRFLAGS         0x09
  77#define RTI800_DI               0x0a
  78#define RTI800_DO               0x0b
  79#define RTI800_9513A_DATA       0x0c
  80#define RTI800_9513A_CNTRL      0x0d
  81#define RTI800_9513A_STATUS     0x0d
  82
  83static const struct comedi_lrange range_rti800_ai_10_bipolar = {
  84        4, {
  85                BIP_RANGE(10),
  86                BIP_RANGE(1),
  87                BIP_RANGE(0.1),
  88                BIP_RANGE(0.02)
  89        }
  90};
  91
  92static const struct comedi_lrange range_rti800_ai_5_bipolar = {
  93        4, {
  94                BIP_RANGE(5),
  95                BIP_RANGE(0.5),
  96                BIP_RANGE(0.05),
  97                BIP_RANGE(0.01)
  98        }
  99};
 100
 101static const struct comedi_lrange range_rti800_ai_unipolar = {
 102        4, {
 103                UNI_RANGE(10),
 104                UNI_RANGE(1),
 105                UNI_RANGE(0.1),
 106                UNI_RANGE(0.02)
 107        }
 108};
 109
 110static const struct comedi_lrange *const rti800_ai_ranges[] = {
 111        &range_rti800_ai_10_bipolar,
 112        &range_rti800_ai_5_bipolar,
 113        &range_rti800_ai_unipolar,
 114};
 115
 116static const struct comedi_lrange *const rti800_ao_ranges[] = {
 117        &range_bipolar10,
 118        &range_unipolar10,
 119};
 120
 121struct rti800_board {
 122        const char *name;
 123        int has_ao;
 124};
 125
 126static const struct rti800_board rti800_boardtypes[] = {
 127        {
 128                .name           = "rti800",
 129        }, {
 130                .name           = "rti815",
 131                .has_ao         = 1,
 132        },
 133};
 134
 135struct rti800_private {
 136        bool adc_2comp;
 137        bool dac_2comp[2];
 138        const struct comedi_lrange *ao_range_type_list[2];
 139        unsigned char muxgain_bits;
 140};
 141
 142static int rti800_ai_eoc(struct comedi_device *dev,
 143                         struct comedi_subdevice *s,
 144                         struct comedi_insn *insn,
 145                         unsigned long context)
 146{
 147        unsigned char status;
 148
 149        status = inb(dev->iobase + RTI800_CSR);
 150        if (status & RTI800_CSR_OVERRUN) {
 151                outb(0, dev->iobase + RTI800_CLRFLAGS);
 152                return -EOVERFLOW;
 153        }
 154        if (status & RTI800_CSR_DONE)
 155                return 0;
 156        return -EBUSY;
 157}
 158
 159static int rti800_ai_insn_read(struct comedi_device *dev,
 160                               struct comedi_subdevice *s,
 161                               struct comedi_insn *insn,
 162                               unsigned int *data)
 163{
 164        struct rti800_private *devpriv = dev->private;
 165        unsigned int chan = CR_CHAN(insn->chanspec);
 166        unsigned int gain = CR_RANGE(insn->chanspec);
 167        unsigned char muxgain_bits;
 168        int ret;
 169        int i;
 170
 171        inb(dev->iobase + RTI800_ADCHI);
 172        outb(0, dev->iobase + RTI800_CLRFLAGS);
 173
 174        muxgain_bits = chan | (gain << 5);
 175        if (muxgain_bits != devpriv->muxgain_bits) {
 176                devpriv->muxgain_bits = muxgain_bits;
 177                outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
 178                /*
 179                 * Without a delay here, the RTI_CSR_OVERRUN bit
 180                 * gets set, and you will have an error.
 181                 */
 182                if (insn->n > 0) {
 183                        int delay = (gain == 0) ? 10 :
 184                                    (gain == 1) ? 20 :
 185                                    (gain == 2) ? 40 : 80;
 186
 187                        udelay(delay);
 188                }
 189        }
 190
 191        for (i = 0; i < insn->n; i++) {
 192                unsigned int val;
 193
 194                outb(0, dev->iobase + RTI800_CONVERT);
 195
 196                ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0);
 197                if (ret)
 198                        return ret;
 199
 200                val = inb(dev->iobase + RTI800_ADCLO);
 201                val |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8;
 202
 203                if (devpriv->adc_2comp)
 204                        val = comedi_offset_munge(s, val);
 205
 206                data[i] = val;
 207        }
 208
 209        return insn->n;
 210}
 211
 212static int rti800_ao_insn_write(struct comedi_device *dev,
 213                                struct comedi_subdevice *s,
 214                                struct comedi_insn *insn,
 215                                unsigned int *data)
 216{
 217        struct rti800_private *devpriv = dev->private;
 218        unsigned int chan = CR_CHAN(insn->chanspec);
 219        int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO;
 220        int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI;
 221        int i;
 222
 223        for (i = 0; i < insn->n; i++) {
 224                unsigned int val = data[i];
 225
 226                s->readback[chan] = val;
 227
 228                if (devpriv->dac_2comp[chan])
 229                        val = comedi_offset_munge(s, val);
 230
 231                outb(val & 0xff, dev->iobase + reg_lo);
 232                outb((val >> 8) & 0xff, dev->iobase + reg_hi);
 233        }
 234
 235        return insn->n;
 236}
 237
 238static int rti800_di_insn_bits(struct comedi_device *dev,
 239                               struct comedi_subdevice *s,
 240                               struct comedi_insn *insn,
 241                               unsigned int *data)
 242{
 243        data[1] = inb(dev->iobase + RTI800_DI);
 244        return insn->n;
 245}
 246
 247static int rti800_do_insn_bits(struct comedi_device *dev,
 248                               struct comedi_subdevice *s,
 249                               struct comedi_insn *insn,
 250                               unsigned int *data)
 251{
 252        if (comedi_dio_update_state(s, data)) {
 253                /* Outputs are inverted... */
 254                outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
 255        }
 256
 257        data[1] = s->state;
 258
 259        return insn->n;
 260}
 261
 262static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 263{
 264        const struct rti800_board *board = dev->board_ptr;
 265        struct rti800_private *devpriv;
 266        struct comedi_subdevice *s;
 267        int ret;
 268
 269        ret = comedi_request_region(dev, it->options[0], 0x10);
 270        if (ret)
 271                return ret;
 272
 273        outb(0, dev->iobase + RTI800_CSR);
 274        inb(dev->iobase + RTI800_ADCHI);
 275        outb(0, dev->iobase + RTI800_CLRFLAGS);
 276
 277        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 278        if (!devpriv)
 279                return -ENOMEM;
 280
 281        devpriv->adc_2comp = (it->options[4] == 0);
 282        devpriv->dac_2comp[0] = (it->options[6] == 0);
 283        devpriv->dac_2comp[1] = (it->options[8] == 0);
 284        /* invalid, forces the MUXGAIN register to be set when first used */
 285        devpriv->muxgain_bits = 0xff;
 286
 287        ret = comedi_alloc_subdevices(dev, 4);
 288        if (ret)
 289                return ret;
 290
 291        s = &dev->subdevices[0];
 292        /* ai subdevice */
 293        s->type         = COMEDI_SUBD_AI;
 294        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 295        s->n_chan       = (it->options[2] ? 16 : 8);
 296        s->insn_read    = rti800_ai_insn_read;
 297        s->maxdata      = 0x0fff;
 298        s->range_table  = (it->options[3] < ARRAY_SIZE(rti800_ai_ranges))
 299                                ? rti800_ai_ranges[it->options[3]]
 300                                : &range_unknown;
 301
 302        s = &dev->subdevices[1];
 303        if (board->has_ao) {
 304                /* ao subdevice (only on rti815) */
 305                s->type         = COMEDI_SUBD_AO;
 306                s->subdev_flags = SDF_WRITABLE;
 307                s->n_chan       = 2;
 308                s->maxdata      = 0x0fff;
 309                s->range_table_list = devpriv->ao_range_type_list;
 310                devpriv->ao_range_type_list[0] =
 311                        (it->options[5] < ARRAY_SIZE(rti800_ao_ranges))
 312                                ? rti800_ao_ranges[it->options[5]]
 313                                : &range_unknown;
 314                devpriv->ao_range_type_list[1] =
 315                        (it->options[7] < ARRAY_SIZE(rti800_ao_ranges))
 316                                ? rti800_ao_ranges[it->options[7]]
 317                                : &range_unknown;
 318                s->insn_write   = rti800_ao_insn_write;
 319
 320                ret = comedi_alloc_subdev_readback(s);
 321                if (ret)
 322                        return ret;
 323        } else {
 324                s->type         = COMEDI_SUBD_UNUSED;
 325        }
 326
 327        s = &dev->subdevices[2];
 328        /* di */
 329        s->type         = COMEDI_SUBD_DI;
 330        s->subdev_flags = SDF_READABLE;
 331        s->n_chan       = 8;
 332        s->insn_bits    = rti800_di_insn_bits;
 333        s->maxdata      = 1;
 334        s->range_table  = &range_digital;
 335
 336        s = &dev->subdevices[3];
 337        /* do */
 338        s->type         = COMEDI_SUBD_DO;
 339        s->subdev_flags = SDF_WRITABLE;
 340        s->n_chan       = 8;
 341        s->insn_bits    = rti800_do_insn_bits;
 342        s->maxdata      = 1;
 343        s->range_table  = &range_digital;
 344
 345        /*
 346         * There is also an Am9513 timer on these boards. This subdevice
 347         * is not currently supported.
 348         */
 349
 350        return 0;
 351}
 352
 353static struct comedi_driver rti800_driver = {
 354        .driver_name    = "rti800",
 355        .module         = THIS_MODULE,
 356        .attach         = rti800_attach,
 357        .detach         = comedi_legacy_detach,
 358        .num_names      = ARRAY_SIZE(rti800_boardtypes),
 359        .board_name     = &rti800_boardtypes[0].name,
 360        .offset         = sizeof(struct rti800_board),
 361};
 362module_comedi_driver(rti800_driver);
 363
 364MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board");
 365MODULE_AUTHOR("Comedi http://www.comedi.org");
 366MODULE_LICENSE("GPL");
 367