linux/drivers/staging/comedi/drivers/ni_atmio16d.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Comedi driver for National Instruments AT-MIO16D board
   4 * Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
   5 */
   6
   7/*
   8 * Driver: ni_atmio16d
   9 * Description: National Instruments AT-MIO-16D
  10 * Author: Chris R. Baugher <baugher@enteract.com>
  11 * Status: unknown
  12 * Devices: [National Instruments] AT-MIO-16 (atmio16), AT-MIO-16D (atmio16d)
  13 *
  14 * Configuration options:
  15 *   [0] - I/O port
  16 *   [1] - MIO irq (0 == no irq; or 3,4,5,6,7,9,10,11,12,14,15)
  17 *   [2] - DIO irq (0 == no irq; or 3,4,5,6,7,9)
  18 *   [3] - DMA1 channel (0 == no DMA; or 5,6,7)
  19 *   [4] - DMA2 channel (0 == no DMA; or 5,6,7)
  20 *   [5] - a/d mux (0=differential; 1=single)
  21 *   [6] - a/d range (0=bipolar10; 1=bipolar5; 2=unipolar10)
  22 *   [7] - dac0 range (0=bipolar; 1=unipolar)
  23 *   [8] - dac0 reference (0=internal; 1=external)
  24 *   [9] - dac0 coding (0=2's comp; 1=straight binary)
  25 *   [10] - dac1 range (same as dac0 options)
  26 *   [11] - dac1 reference (same as dac0 options)
  27 *   [12] - dac1 coding (same as dac0 options)
  28 */
  29
  30/*
  31 * I must give credit here to Michal Dobes <dobes@tesnet.cz> who
  32 * wrote the driver for Advantec's pcl812 boards. I used the interrupt
  33 * handling code from his driver as an example for this one.
  34 *
  35 * Chris Baugher
  36 * 5/1/2000
  37 *
  38 */
  39
  40#include <linux/module.h>
  41#include <linux/interrupt.h>
  42#include "../comedidev.h"
  43
  44#include "8255.h"
  45
  46/* Configuration and Status Registers */
  47#define COM_REG_1       0x00    /* wo 16 */
  48#define STAT_REG        0x00    /* ro 16 */
  49#define COM_REG_2       0x02    /* wo 16 */
  50/* Event Strobe Registers */
  51#define START_CONVERT_REG       0x08    /* wo 16 */
  52#define START_DAQ_REG           0x0A    /* wo 16 */
  53#define AD_CLEAR_REG            0x0C    /* wo 16 */
  54#define EXT_STROBE_REG          0x0E    /* wo 16 */
  55/* Analog Output Registers */
  56#define DAC0_REG                0x10    /* wo 16 */
  57#define DAC1_REG                0x12    /* wo 16 */
  58#define INT2CLR_REG             0x14    /* wo 16 */
  59/* Analog Input Registers */
  60#define MUX_CNTR_REG            0x04    /* wo 16 */
  61#define MUX_GAIN_REG            0x06    /* wo 16 */
  62#define AD_FIFO_REG             0x16    /* ro 16 */
  63#define DMA_TC_INT_CLR_REG      0x16    /* wo 16 */
  64/* AM9513A Counter/Timer Registers */
  65#define AM9513A_DATA_REG        0x18    /* rw 16 */
  66#define AM9513A_COM_REG         0x1A    /* wo 16 */
  67#define AM9513A_STAT_REG        0x1A    /* ro 16 */
  68/* MIO-16 Digital I/O Registers */
  69#define MIO_16_DIG_IN_REG       0x1C    /* ro 16 */
  70#define MIO_16_DIG_OUT_REG      0x1C    /* wo 16 */
  71/* RTSI Switch Registers */
  72#define RTSI_SW_SHIFT_REG       0x1E    /* wo 8 */
  73#define RTSI_SW_STROBE_REG      0x1F    /* wo 8 */
  74/* DIO-24 Registers */
  75#define DIO_24_PORTA_REG        0x00    /* rw 8 */
  76#define DIO_24_PORTB_REG        0x01    /* rw 8 */
  77#define DIO_24_PORTC_REG        0x02    /* rw 8 */
  78#define DIO_24_CNFG_REG         0x03    /* wo 8 */
  79
  80/* Command Register bits */
  81#define COMREG1_2SCADC          0x0001
  82#define COMREG1_1632CNT         0x0002
  83#define COMREG1_SCANEN          0x0008
  84#define COMREG1_DAQEN           0x0010
  85#define COMREG1_DMAEN           0x0020
  86#define COMREG1_CONVINTEN       0x0080
  87#define COMREG2_SCN2            0x0010
  88#define COMREG2_INTEN           0x0080
  89#define COMREG2_DOUTEN0         0x0100
  90#define COMREG2_DOUTEN1         0x0200
  91/* Status Register bits */
  92#define STAT_AD_OVERRUN         0x0100
  93#define STAT_AD_OVERFLOW        0x0200
  94#define STAT_AD_DAQPROG         0x0800
  95#define STAT_AD_CONVAVAIL       0x2000
  96#define STAT_AD_DAQSTOPINT      0x4000
  97/* AM9513A Counter/Timer defines */
  98#define CLOCK_1_MHZ             0x8B25
  99#define CLOCK_100_KHZ   0x8C25
 100#define CLOCK_10_KHZ    0x8D25
 101#define CLOCK_1_KHZ             0x8E25
 102#define CLOCK_100_HZ    0x8F25
 103
 104struct atmio16_board_t {
 105        const char *name;
 106        int has_8255;
 107};
 108
 109/* range structs */
 110static const struct comedi_lrange range_atmio16d_ai_10_bipolar = {
 111        4, {
 112                BIP_RANGE(10),
 113                BIP_RANGE(1),
 114                BIP_RANGE(0.1),
 115                BIP_RANGE(0.02)
 116        }
 117};
 118
 119static const struct comedi_lrange range_atmio16d_ai_5_bipolar = {
 120        4, {
 121                BIP_RANGE(5),
 122                BIP_RANGE(0.5),
 123                BIP_RANGE(0.05),
 124                BIP_RANGE(0.01)
 125        }
 126};
 127
 128static const struct comedi_lrange range_atmio16d_ai_unipolar = {
 129        4, {
 130                UNI_RANGE(10),
 131                UNI_RANGE(1),
 132                UNI_RANGE(0.1),
 133                UNI_RANGE(0.02)
 134        }
 135};
 136
 137/* private data struct */
 138struct atmio16d_private {
 139        enum { adc_diff, adc_singleended } adc_mux;
 140        enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
 141        enum { adc_2comp, adc_straight } adc_coding;
 142        enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
 143        enum { dac_internal, dac_external } dac0_reference, dac1_reference;
 144        enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
 145        const struct comedi_lrange *ao_range_type_list[2];
 146        unsigned int com_reg_1_state; /* current state of command register 1 */
 147        unsigned int com_reg_2_state; /* current state of command register 2 */
 148};
 149
 150static void reset_counters(struct comedi_device *dev)
 151{
 152        /* Counter 2 */
 153        outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
 154        outw(0xFF02, dev->iobase + AM9513A_COM_REG);
 155        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 156        outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
 157        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 158        outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 159        outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 160        /* Counter 3 */
 161        outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
 162        outw(0xFF03, dev->iobase + AM9513A_COM_REG);
 163        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 164        outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
 165        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 166        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 167        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 168        /* Counter 4 */
 169        outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
 170        outw(0xFF04, dev->iobase + AM9513A_COM_REG);
 171        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 172        outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
 173        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 174        outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 175        outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 176        /* Counter 5 */
 177        outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
 178        outw(0xFF05, dev->iobase + AM9513A_COM_REG);
 179        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 180        outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
 181        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 182        outw(0xFF50, dev->iobase + AM9513A_COM_REG);
 183        outw(0xFF50, dev->iobase + AM9513A_COM_REG);
 184
 185        outw(0, dev->iobase + AD_CLEAR_REG);
 186}
 187
 188static void reset_atmio16d(struct comedi_device *dev)
 189{
 190        struct atmio16d_private *devpriv = dev->private;
 191        int i;
 192
 193        /* now we need to initialize the board */
 194        outw(0, dev->iobase + COM_REG_1);
 195        outw(0, dev->iobase + COM_REG_2);
 196        outw(0, dev->iobase + MUX_GAIN_REG);
 197        /* init AM9513A timer */
 198        outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
 199        outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
 200        outw(0xFF17, dev->iobase + AM9513A_COM_REG);
 201        outw(0xF000, dev->iobase + AM9513A_DATA_REG);
 202        for (i = 1; i <= 5; ++i) {
 203                outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
 204                outw(0x0004, dev->iobase + AM9513A_DATA_REG);
 205                outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
 206                outw(0x3, dev->iobase + AM9513A_DATA_REG);
 207        }
 208        outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
 209        /* timer init done */
 210        outw(0, dev->iobase + AD_CLEAR_REG);
 211        outw(0, dev->iobase + INT2CLR_REG);
 212        /* select straight binary mode for Analog Input */
 213        devpriv->com_reg_1_state |= 1;
 214        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 215        devpriv->adc_coding = adc_straight;
 216        /* zero the analog outputs */
 217        outw(2048, dev->iobase + DAC0_REG);
 218        outw(2048, dev->iobase + DAC1_REG);
 219}
 220
 221static irqreturn_t atmio16d_interrupt(int irq, void *d)
 222{
 223        struct comedi_device *dev = d;
 224        struct comedi_subdevice *s = dev->read_subdev;
 225        unsigned short val;
 226
 227        val = inw(dev->iobase + AD_FIFO_REG);
 228        comedi_buf_write_samples(s, &val, 1);
 229        comedi_handle_events(dev, s);
 230
 231        return IRQ_HANDLED;
 232}
 233
 234static int atmio16d_ai_cmdtest(struct comedi_device *dev,
 235                               struct comedi_subdevice *s,
 236                               struct comedi_cmd *cmd)
 237{
 238        int err = 0;
 239
 240        /* Step 1 : check if triggers are trivially valid */
 241
 242        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 243        err |= comedi_check_trigger_src(&cmd->scan_begin_src,
 244                                        TRIG_FOLLOW | TRIG_TIMER);
 245        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 246        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 247        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 248
 249        if (err)
 250                return 1;
 251
 252        /* Step 2a : make sure trigger sources are unique */
 253
 254        err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
 255        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 256
 257        /* Step 2b : and mutually compatible */
 258
 259        if (err)
 260                return 2;
 261
 262        /* Step 3: check if arguments are trivially valid */
 263
 264        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 265
 266        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 267                /* internal trigger */
 268                err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 269        } else {
 270#if 0
 271                /* external trigger */
 272                /* should be level/edge, hi/lo specification here */
 273                err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 274#endif
 275        }
 276
 277        err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
 278#if 0
 279        err |= comedi_check_trigger_arg_max(&cmd->convert_arg, SLOWEST_TIMER);
 280#endif
 281
 282        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 283                                           cmd->chanlist_len);
 284
 285        if (cmd->stop_src == TRIG_COUNT)
 286                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 287        else    /* TRIG_NONE */
 288                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 289
 290        if (err)
 291                return 3;
 292
 293        return 0;
 294}
 295
 296static int atmio16d_ai_cmd(struct comedi_device *dev,
 297                           struct comedi_subdevice *s)
 298{
 299        struct atmio16d_private *devpriv = dev->private;
 300        struct comedi_cmd *cmd = &s->async->cmd;
 301        unsigned int timer, base_clock;
 302        unsigned int sample_count, tmp, chan, gain;
 303        int i;
 304
 305        /*
 306         * This is slowly becoming a working command interface.
 307         * It is still uber-experimental
 308         */
 309
 310        reset_counters(dev);
 311
 312        /* check if scanning multiple channels */
 313        if (cmd->chanlist_len < 2) {
 314                devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
 315                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 316        } else {
 317                devpriv->com_reg_1_state |= COMREG1_SCANEN;
 318                devpriv->com_reg_2_state |= COMREG2_SCN2;
 319                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 320                outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 321        }
 322
 323        /* Setup the Mux-Gain Counter */
 324        for (i = 0; i < cmd->chanlist_len; ++i) {
 325                chan = CR_CHAN(cmd->chanlist[i]);
 326                gain = CR_RANGE(cmd->chanlist[i]);
 327                outw(i, dev->iobase + MUX_CNTR_REG);
 328                tmp = chan | (gain << 6);
 329                if (i == cmd->scan_end_arg - 1)
 330                        tmp |= 0x0010;  /* set LASTONE bit */
 331                outw(tmp, dev->iobase + MUX_GAIN_REG);
 332        }
 333
 334        /*
 335         * Now program the sample interval timer.
 336         * Figure out which clock to use then get an appropriate timer value.
 337         */
 338        if (cmd->convert_arg < 65536000) {
 339                base_clock = CLOCK_1_MHZ;
 340                timer = cmd->convert_arg / 1000;
 341        } else if (cmd->convert_arg < 655360000) {
 342                base_clock = CLOCK_100_KHZ;
 343                timer = cmd->convert_arg / 10000;
 344        } else /* cmd->convert_arg < 6553600000 */ {
 345                base_clock = CLOCK_10_KHZ;
 346                timer = cmd->convert_arg / 100000;
 347        }
 348        outw(0xFF03, dev->iobase + AM9513A_COM_REG);
 349        outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 350        outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
 351        outw(0x2, dev->iobase + AM9513A_DATA_REG);
 352        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 353        outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
 354        outw(timer, dev->iobase + AM9513A_DATA_REG);
 355        outw(0xFF24, dev->iobase + AM9513A_COM_REG);
 356
 357        /* Now figure out how many samples to get */
 358        /* and program the sample counter */
 359        sample_count = cmd->stop_arg * cmd->scan_end_arg;
 360        outw(0xFF04, dev->iobase + AM9513A_COM_REG);
 361        outw(0x1025, dev->iobase + AM9513A_DATA_REG);
 362        outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
 363        if (sample_count < 65536) {
 364                /* use only Counter 4 */
 365                outw(sample_count, dev->iobase + AM9513A_DATA_REG);
 366                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 367                outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
 368                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 369                devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
 370                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 371        } else {
 372                /* Counter 4 and 5 are needed */
 373
 374                tmp = sample_count & 0xFFFF;
 375                if (tmp)
 376                        outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
 377                else
 378                        outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
 379
 380                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 381                outw(0, dev->iobase + AM9513A_DATA_REG);
 382                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 383                outw(0xFF05, dev->iobase + AM9513A_COM_REG);
 384                outw(0x25, dev->iobase + AM9513A_DATA_REG);
 385                outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
 386                tmp = sample_count & 0xFFFF;
 387                if ((tmp == 0) || (tmp == 1)) {
 388                        outw((sample_count >> 16) & 0xFFFF,
 389                             dev->iobase + AM9513A_DATA_REG);
 390                } else {
 391                        outw(((sample_count >> 16) & 0xFFFF) + 1,
 392                             dev->iobase + AM9513A_DATA_REG);
 393                }
 394                outw(0xFF70, dev->iobase + AM9513A_COM_REG);
 395                devpriv->com_reg_1_state |= COMREG1_1632CNT;
 396                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 397        }
 398
 399        /*
 400         * Program the scan interval timer ONLY IF SCANNING IS ENABLED.
 401         * Figure out which clock to use then get an appropriate timer value.
 402         */
 403        if (cmd->chanlist_len > 1) {
 404                if (cmd->scan_begin_arg < 65536000) {
 405                        base_clock = CLOCK_1_MHZ;
 406                        timer = cmd->scan_begin_arg / 1000;
 407                } else if (cmd->scan_begin_arg < 655360000) {
 408                        base_clock = CLOCK_100_KHZ;
 409                        timer = cmd->scan_begin_arg / 10000;
 410                } else /* cmd->scan_begin_arg < 6553600000 */ {
 411                        base_clock = CLOCK_10_KHZ;
 412                        timer = cmd->scan_begin_arg / 100000;
 413                }
 414                outw(0xFF02, dev->iobase + AM9513A_COM_REG);
 415                outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 416                outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
 417                outw(0x2, dev->iobase + AM9513A_DATA_REG);
 418                outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 419                outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
 420                outw(timer, dev->iobase + AM9513A_DATA_REG);
 421                outw(0xFF22, dev->iobase + AM9513A_COM_REG);
 422        }
 423
 424        /* Clear the A/D FIFO and reset the MUX counter */
 425        outw(0, dev->iobase + AD_CLEAR_REG);
 426        outw(0, dev->iobase + MUX_CNTR_REG);
 427        outw(0, dev->iobase + INT2CLR_REG);
 428        /* enable this acquisition operation */
 429        devpriv->com_reg_1_state |= COMREG1_DAQEN;
 430        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 431        /* enable interrupts for conversion completion */
 432        devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
 433        devpriv->com_reg_2_state |= COMREG2_INTEN;
 434        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 435        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 436        /* apply a trigger. this starts the counters! */
 437        outw(0, dev->iobase + START_DAQ_REG);
 438
 439        return 0;
 440}
 441
 442/* This will cancel a running acquisition operation */
 443static int atmio16d_ai_cancel(struct comedi_device *dev,
 444                              struct comedi_subdevice *s)
 445{
 446        reset_atmio16d(dev);
 447
 448        return 0;
 449}
 450
 451static int atmio16d_ai_eoc(struct comedi_device *dev,
 452                           struct comedi_subdevice *s,
 453                           struct comedi_insn *insn,
 454                           unsigned long context)
 455{
 456        unsigned int status;
 457
 458        status = inw(dev->iobase + STAT_REG);
 459        if (status & STAT_AD_CONVAVAIL)
 460                return 0;
 461        if (status & STAT_AD_OVERFLOW) {
 462                outw(0, dev->iobase + AD_CLEAR_REG);
 463                return -EOVERFLOW;
 464        }
 465        return -EBUSY;
 466}
 467
 468static int atmio16d_ai_insn_read(struct comedi_device *dev,
 469                                 struct comedi_subdevice *s,
 470                                 struct comedi_insn *insn, unsigned int *data)
 471{
 472        struct atmio16d_private *devpriv = dev->private;
 473        int i;
 474        int chan;
 475        int gain;
 476        int ret;
 477
 478        chan = CR_CHAN(insn->chanspec);
 479        gain = CR_RANGE(insn->chanspec);
 480
 481        /* reset the Analog input circuitry */
 482        /* outw( 0, dev->iobase+AD_CLEAR_REG ); */
 483        /* reset the Analog Input MUX Counter to 0 */
 484        /* outw( 0, dev->iobase+MUX_CNTR_REG ); */
 485
 486        /* set the Input MUX gain */
 487        outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
 488
 489        for (i = 0; i < insn->n; i++) {
 490                /* start the conversion */
 491                outw(0, dev->iobase + START_CONVERT_REG);
 492
 493                /* wait for it to finish */
 494                ret = comedi_timeout(dev, s, insn, atmio16d_ai_eoc, 0);
 495                if (ret)
 496                        return ret;
 497
 498                /* read the data now */
 499                data[i] = inw(dev->iobase + AD_FIFO_REG);
 500                /* change to two's complement if need be */
 501                if (devpriv->adc_coding == adc_2comp)
 502                        data[i] ^= 0x800;
 503        }
 504
 505        return i;
 506}
 507
 508static int atmio16d_ao_insn_write(struct comedi_device *dev,
 509                                  struct comedi_subdevice *s,
 510                                  struct comedi_insn *insn,
 511                                  unsigned int *data)
 512{
 513        struct atmio16d_private *devpriv = dev->private;
 514        unsigned int chan = CR_CHAN(insn->chanspec);
 515        unsigned int reg = (chan) ? DAC1_REG : DAC0_REG;
 516        bool munge = false;
 517        int i;
 518
 519        if (chan == 0 && devpriv->dac0_coding == dac_2comp)
 520                munge = true;
 521        if (chan == 1 && devpriv->dac1_coding == dac_2comp)
 522                munge = true;
 523
 524        for (i = 0; i < insn->n; i++) {
 525                unsigned int val = data[i];
 526
 527                s->readback[chan] = val;
 528
 529                if (munge)
 530                        val ^= 0x800;
 531
 532                outw(val, dev->iobase + reg);
 533        }
 534
 535        return insn->n;
 536}
 537
 538static int atmio16d_dio_insn_bits(struct comedi_device *dev,
 539                                  struct comedi_subdevice *s,
 540                                  struct comedi_insn *insn,
 541                                  unsigned int *data)
 542{
 543        if (comedi_dio_update_state(s, data))
 544                outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
 545
 546        data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
 547
 548        return insn->n;
 549}
 550
 551static int atmio16d_dio_insn_config(struct comedi_device *dev,
 552                                    struct comedi_subdevice *s,
 553                                    struct comedi_insn *insn,
 554                                    unsigned int *data)
 555{
 556        struct atmio16d_private *devpriv = dev->private;
 557        unsigned int chan = CR_CHAN(insn->chanspec);
 558        unsigned int mask;
 559        int ret;
 560
 561        if (chan < 4)
 562                mask = 0x0f;
 563        else
 564                mask = 0xf0;
 565
 566        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 567        if (ret)
 568                return ret;
 569
 570        devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
 571        if (s->io_bits & 0x0f)
 572                devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
 573        if (s->io_bits & 0xf0)
 574                devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
 575        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 576
 577        return insn->n;
 578}
 579
 580static int atmio16d_attach(struct comedi_device *dev,
 581                           struct comedi_devconfig *it)
 582{
 583        const struct atmio16_board_t *board = dev->board_ptr;
 584        struct atmio16d_private *devpriv;
 585        struct comedi_subdevice *s;
 586        int ret;
 587
 588        ret = comedi_request_region(dev, it->options[0], 0x20);
 589        if (ret)
 590                return ret;
 591
 592        ret = comedi_alloc_subdevices(dev, 4);
 593        if (ret)
 594                return ret;
 595
 596        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 597        if (!devpriv)
 598                return -ENOMEM;
 599
 600        /* reset the atmio16d hardware */
 601        reset_atmio16d(dev);
 602
 603        if (it->options[1]) {
 604                ret = request_irq(it->options[1], atmio16d_interrupt, 0,
 605                                  dev->board_name, dev);
 606                if (ret == 0)
 607                        dev->irq = it->options[1];
 608        }
 609
 610        /* set device options */
 611        devpriv->adc_mux = it->options[5];
 612        devpriv->adc_range = it->options[6];
 613
 614        devpriv->dac0_range = it->options[7];
 615        devpriv->dac0_reference = it->options[8];
 616        devpriv->dac0_coding = it->options[9];
 617        devpriv->dac1_range = it->options[10];
 618        devpriv->dac1_reference = it->options[11];
 619        devpriv->dac1_coding = it->options[12];
 620
 621        /* setup sub-devices */
 622        s = &dev->subdevices[0];
 623        /* ai subdevice */
 624        s->type = COMEDI_SUBD_AI;
 625        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 626        s->n_chan = (devpriv->adc_mux ? 16 : 8);
 627        s->insn_read = atmio16d_ai_insn_read;
 628        s->maxdata = 0xfff;     /* 4095 decimal */
 629        switch (devpriv->adc_range) {
 630        case adc_bipolar10:
 631                s->range_table = &range_atmio16d_ai_10_bipolar;
 632                break;
 633        case adc_bipolar5:
 634                s->range_table = &range_atmio16d_ai_5_bipolar;
 635                break;
 636        case adc_unipolar10:
 637                s->range_table = &range_atmio16d_ai_unipolar;
 638                break;
 639        }
 640        if (dev->irq) {
 641                dev->read_subdev = s;
 642                s->subdev_flags |= SDF_CMD_READ;
 643                s->len_chanlist = 16;
 644                s->do_cmdtest = atmio16d_ai_cmdtest;
 645                s->do_cmd = atmio16d_ai_cmd;
 646                s->cancel = atmio16d_ai_cancel;
 647        }
 648
 649        /* ao subdevice */
 650        s = &dev->subdevices[1];
 651        s->type = COMEDI_SUBD_AO;
 652        s->subdev_flags = SDF_WRITABLE;
 653        s->n_chan = 2;
 654        s->maxdata = 0xfff;     /* 4095 decimal */
 655        s->range_table_list = devpriv->ao_range_type_list;
 656        switch (devpriv->dac0_range) {
 657        case dac_bipolar:
 658                devpriv->ao_range_type_list[0] = &range_bipolar10;
 659                break;
 660        case dac_unipolar:
 661                devpriv->ao_range_type_list[0] = &range_unipolar10;
 662                break;
 663        }
 664        switch (devpriv->dac1_range) {
 665        case dac_bipolar:
 666                devpriv->ao_range_type_list[1] = &range_bipolar10;
 667                break;
 668        case dac_unipolar:
 669                devpriv->ao_range_type_list[1] = &range_unipolar10;
 670                break;
 671        }
 672        s->insn_write = atmio16d_ao_insn_write;
 673
 674        ret = comedi_alloc_subdev_readback(s);
 675        if (ret)
 676                return ret;
 677
 678        /* Digital I/O */
 679        s = &dev->subdevices[2];
 680        s->type = COMEDI_SUBD_DIO;
 681        s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 682        s->n_chan = 8;
 683        s->insn_bits = atmio16d_dio_insn_bits;
 684        s->insn_config = atmio16d_dio_insn_config;
 685        s->maxdata = 1;
 686        s->range_table = &range_digital;
 687
 688        /* 8255 subdevice */
 689        s = &dev->subdevices[3];
 690        if (board->has_8255) {
 691                ret = subdev_8255_init(dev, s, NULL, 0x00);
 692                if (ret)
 693                        return ret;
 694        } else {
 695                s->type = COMEDI_SUBD_UNUSED;
 696        }
 697
 698/* don't yet know how to deal with counter/timers */
 699#if 0
 700        s = &dev->subdevices[4];
 701        /* do */
 702        s->type = COMEDI_SUBD_TIMER;
 703        s->n_chan = 0;
 704        s->maxdata = 0
 705#endif
 706
 707        return 0;
 708}
 709
 710static void atmio16d_detach(struct comedi_device *dev)
 711{
 712        reset_atmio16d(dev);
 713        comedi_legacy_detach(dev);
 714}
 715
 716static const struct atmio16_board_t atmio16_boards[] = {
 717        {
 718                .name           = "atmio16",
 719                .has_8255       = 0,
 720        }, {
 721                .name           = "atmio16d",
 722                .has_8255       = 1,
 723        },
 724};
 725
 726static struct comedi_driver atmio16d_driver = {
 727        .driver_name    = "atmio16",
 728        .module         = THIS_MODULE,
 729        .attach         = atmio16d_attach,
 730        .detach         = atmio16d_detach,
 731        .board_name     = &atmio16_boards[0].name,
 732        .num_names      = ARRAY_SIZE(atmio16_boards),
 733        .offset         = sizeof(struct atmio16_board_t),
 734};
 735module_comedi_driver(atmio16d_driver);
 736
 737MODULE_AUTHOR("Comedi http://www.comedi.org");
 738MODULE_DESCRIPTION("Comedi low-level driver");
 739MODULE_LICENSE("GPL");
 740