linux/drivers/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        }
 270
 271        err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
 272        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 273                                           cmd->chanlist_len);
 274
 275        if (cmd->stop_src == TRIG_COUNT)
 276                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 277        else    /* TRIG_NONE */
 278                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 279
 280        if (err)
 281                return 3;
 282
 283        return 0;
 284}
 285
 286static int atmio16d_ai_cmd(struct comedi_device *dev,
 287                           struct comedi_subdevice *s)
 288{
 289        struct atmio16d_private *devpriv = dev->private;
 290        struct comedi_cmd *cmd = &s->async->cmd;
 291        unsigned int timer, base_clock;
 292        unsigned int sample_count, tmp, chan, gain;
 293        int i;
 294
 295        /*
 296         * This is slowly becoming a working command interface.
 297         * It is still uber-experimental
 298         */
 299
 300        reset_counters(dev);
 301
 302        /* check if scanning multiple channels */
 303        if (cmd->chanlist_len < 2) {
 304                devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
 305                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 306        } else {
 307                devpriv->com_reg_1_state |= COMREG1_SCANEN;
 308                devpriv->com_reg_2_state |= COMREG2_SCN2;
 309                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 310                outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 311        }
 312
 313        /* Setup the Mux-Gain Counter */
 314        for (i = 0; i < cmd->chanlist_len; ++i) {
 315                chan = CR_CHAN(cmd->chanlist[i]);
 316                gain = CR_RANGE(cmd->chanlist[i]);
 317                outw(i, dev->iobase + MUX_CNTR_REG);
 318                tmp = chan | (gain << 6);
 319                if (i == cmd->scan_end_arg - 1)
 320                        tmp |= 0x0010;  /* set LASTONE bit */
 321                outw(tmp, dev->iobase + MUX_GAIN_REG);
 322        }
 323
 324        /*
 325         * Now program the sample interval timer.
 326         * Figure out which clock to use then get an appropriate timer value.
 327         */
 328        if (cmd->convert_arg < 65536000) {
 329                base_clock = CLOCK_1_MHZ;
 330                timer = cmd->convert_arg / 1000;
 331        } else if (cmd->convert_arg < 655360000) {
 332                base_clock = CLOCK_100_KHZ;
 333                timer = cmd->convert_arg / 10000;
 334        } else /* cmd->convert_arg < 6553600000 */ {
 335                base_clock = CLOCK_10_KHZ;
 336                timer = cmd->convert_arg / 100000;
 337        }
 338        outw(0xFF03, dev->iobase + AM9513A_COM_REG);
 339        outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 340        outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
 341        outw(0x2, dev->iobase + AM9513A_DATA_REG);
 342        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 343        outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
 344        outw(timer, dev->iobase + AM9513A_DATA_REG);
 345        outw(0xFF24, dev->iobase + AM9513A_COM_REG);
 346
 347        /* Now figure out how many samples to get */
 348        /* and program the sample counter */
 349        sample_count = cmd->stop_arg * cmd->scan_end_arg;
 350        outw(0xFF04, dev->iobase + AM9513A_COM_REG);
 351        outw(0x1025, dev->iobase + AM9513A_DATA_REG);
 352        outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
 353        if (sample_count < 65536) {
 354                /* use only Counter 4 */
 355                outw(sample_count, dev->iobase + AM9513A_DATA_REG);
 356                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 357                outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
 358                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 359                devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
 360                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 361        } else {
 362                /* Counter 4 and 5 are needed */
 363
 364                tmp = sample_count & 0xFFFF;
 365                if (tmp)
 366                        outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
 367                else
 368                        outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
 369
 370                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 371                outw(0, dev->iobase + AM9513A_DATA_REG);
 372                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 373                outw(0xFF05, dev->iobase + AM9513A_COM_REG);
 374                outw(0x25, dev->iobase + AM9513A_DATA_REG);
 375                outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
 376                tmp = sample_count & 0xFFFF;
 377                if ((tmp == 0) || (tmp == 1)) {
 378                        outw((sample_count >> 16) & 0xFFFF,
 379                             dev->iobase + AM9513A_DATA_REG);
 380                } else {
 381                        outw(((sample_count >> 16) & 0xFFFF) + 1,
 382                             dev->iobase + AM9513A_DATA_REG);
 383                }
 384                outw(0xFF70, dev->iobase + AM9513A_COM_REG);
 385                devpriv->com_reg_1_state |= COMREG1_1632CNT;
 386                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 387        }
 388
 389        /*
 390         * Program the scan interval timer ONLY IF SCANNING IS ENABLED.
 391         * Figure out which clock to use then get an appropriate timer value.
 392         */
 393        if (cmd->chanlist_len > 1) {
 394                if (cmd->scan_begin_arg < 65536000) {
 395                        base_clock = CLOCK_1_MHZ;
 396                        timer = cmd->scan_begin_arg / 1000;
 397                } else if (cmd->scan_begin_arg < 655360000) {
 398                        base_clock = CLOCK_100_KHZ;
 399                        timer = cmd->scan_begin_arg / 10000;
 400                } else /* cmd->scan_begin_arg < 6553600000 */ {
 401                        base_clock = CLOCK_10_KHZ;
 402                        timer = cmd->scan_begin_arg / 100000;
 403                }
 404                outw(0xFF02, dev->iobase + AM9513A_COM_REG);
 405                outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 406                outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
 407                outw(0x2, dev->iobase + AM9513A_DATA_REG);
 408                outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 409                outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
 410                outw(timer, dev->iobase + AM9513A_DATA_REG);
 411                outw(0xFF22, dev->iobase + AM9513A_COM_REG);
 412        }
 413
 414        /* Clear the A/D FIFO and reset the MUX counter */
 415        outw(0, dev->iobase + AD_CLEAR_REG);
 416        outw(0, dev->iobase + MUX_CNTR_REG);
 417        outw(0, dev->iobase + INT2CLR_REG);
 418        /* enable this acquisition operation */
 419        devpriv->com_reg_1_state |= COMREG1_DAQEN;
 420        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 421        /* enable interrupts for conversion completion */
 422        devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
 423        devpriv->com_reg_2_state |= COMREG2_INTEN;
 424        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 425        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 426        /* apply a trigger. this starts the counters! */
 427        outw(0, dev->iobase + START_DAQ_REG);
 428
 429        return 0;
 430}
 431
 432/* This will cancel a running acquisition operation */
 433static int atmio16d_ai_cancel(struct comedi_device *dev,
 434                              struct comedi_subdevice *s)
 435{
 436        reset_atmio16d(dev);
 437
 438        return 0;
 439}
 440
 441static int atmio16d_ai_eoc(struct comedi_device *dev,
 442                           struct comedi_subdevice *s,
 443                           struct comedi_insn *insn,
 444                           unsigned long context)
 445{
 446        unsigned int status;
 447
 448        status = inw(dev->iobase + STAT_REG);
 449        if (status & STAT_AD_CONVAVAIL)
 450                return 0;
 451        if (status & STAT_AD_OVERFLOW) {
 452                outw(0, dev->iobase + AD_CLEAR_REG);
 453                return -EOVERFLOW;
 454        }
 455        return -EBUSY;
 456}
 457
 458static int atmio16d_ai_insn_read(struct comedi_device *dev,
 459                                 struct comedi_subdevice *s,
 460                                 struct comedi_insn *insn, unsigned int *data)
 461{
 462        struct atmio16d_private *devpriv = dev->private;
 463        int i;
 464        int chan;
 465        int gain;
 466        int ret;
 467
 468        chan = CR_CHAN(insn->chanspec);
 469        gain = CR_RANGE(insn->chanspec);
 470
 471        /* reset the Analog input circuitry */
 472        /* outw( 0, dev->iobase+AD_CLEAR_REG ); */
 473        /* reset the Analog Input MUX Counter to 0 */
 474        /* outw( 0, dev->iobase+MUX_CNTR_REG ); */
 475
 476        /* set the Input MUX gain */
 477        outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
 478
 479        for (i = 0; i < insn->n; i++) {
 480                /* start the conversion */
 481                outw(0, dev->iobase + START_CONVERT_REG);
 482
 483                /* wait for it to finish */
 484                ret = comedi_timeout(dev, s, insn, atmio16d_ai_eoc, 0);
 485                if (ret)
 486                        return ret;
 487
 488                /* read the data now */
 489                data[i] = inw(dev->iobase + AD_FIFO_REG);
 490                /* change to two's complement if need be */
 491                if (devpriv->adc_coding == adc_2comp)
 492                        data[i] ^= 0x800;
 493        }
 494
 495        return i;
 496}
 497
 498static int atmio16d_ao_insn_write(struct comedi_device *dev,
 499                                  struct comedi_subdevice *s,
 500                                  struct comedi_insn *insn,
 501                                  unsigned int *data)
 502{
 503        struct atmio16d_private *devpriv = dev->private;
 504        unsigned int chan = CR_CHAN(insn->chanspec);
 505        unsigned int reg = (chan) ? DAC1_REG : DAC0_REG;
 506        bool munge = false;
 507        int i;
 508
 509        if (chan == 0 && devpriv->dac0_coding == dac_2comp)
 510                munge = true;
 511        if (chan == 1 && devpriv->dac1_coding == dac_2comp)
 512                munge = true;
 513
 514        for (i = 0; i < insn->n; i++) {
 515                unsigned int val = data[i];
 516
 517                s->readback[chan] = val;
 518
 519                if (munge)
 520                        val ^= 0x800;
 521
 522                outw(val, dev->iobase + reg);
 523        }
 524
 525        return insn->n;
 526}
 527
 528static int atmio16d_dio_insn_bits(struct comedi_device *dev,
 529                                  struct comedi_subdevice *s,
 530                                  struct comedi_insn *insn,
 531                                  unsigned int *data)
 532{
 533        if (comedi_dio_update_state(s, data))
 534                outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
 535
 536        data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
 537
 538        return insn->n;
 539}
 540
 541static int atmio16d_dio_insn_config(struct comedi_device *dev,
 542                                    struct comedi_subdevice *s,
 543                                    struct comedi_insn *insn,
 544                                    unsigned int *data)
 545{
 546        struct atmio16d_private *devpriv = dev->private;
 547        unsigned int chan = CR_CHAN(insn->chanspec);
 548        unsigned int mask;
 549        int ret;
 550
 551        if (chan < 4)
 552                mask = 0x0f;
 553        else
 554                mask = 0xf0;
 555
 556        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 557        if (ret)
 558                return ret;
 559
 560        devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
 561        if (s->io_bits & 0x0f)
 562                devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
 563        if (s->io_bits & 0xf0)
 564                devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
 565        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 566
 567        return insn->n;
 568}
 569
 570static int atmio16d_attach(struct comedi_device *dev,
 571                           struct comedi_devconfig *it)
 572{
 573        const struct atmio16_board_t *board = dev->board_ptr;
 574        struct atmio16d_private *devpriv;
 575        struct comedi_subdevice *s;
 576        int ret;
 577
 578        ret = comedi_request_region(dev, it->options[0], 0x20);
 579        if (ret)
 580                return ret;
 581
 582        ret = comedi_alloc_subdevices(dev, 4);
 583        if (ret)
 584                return ret;
 585
 586        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 587        if (!devpriv)
 588                return -ENOMEM;
 589
 590        /* reset the atmio16d hardware */
 591        reset_atmio16d(dev);
 592
 593        if (it->options[1]) {
 594                ret = request_irq(it->options[1], atmio16d_interrupt, 0,
 595                                  dev->board_name, dev);
 596                if (ret == 0)
 597                        dev->irq = it->options[1];
 598        }
 599
 600        /* set device options */
 601        devpriv->adc_mux = it->options[5];
 602        devpriv->adc_range = it->options[6];
 603
 604        devpriv->dac0_range = it->options[7];
 605        devpriv->dac0_reference = it->options[8];
 606        devpriv->dac0_coding = it->options[9];
 607        devpriv->dac1_range = it->options[10];
 608        devpriv->dac1_reference = it->options[11];
 609        devpriv->dac1_coding = it->options[12];
 610
 611        /* setup sub-devices */
 612        s = &dev->subdevices[0];
 613        /* ai subdevice */
 614        s->type = COMEDI_SUBD_AI;
 615        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 616        s->n_chan = (devpriv->adc_mux ? 16 : 8);
 617        s->insn_read = atmio16d_ai_insn_read;
 618        s->maxdata = 0xfff;     /* 4095 decimal */
 619        switch (devpriv->adc_range) {
 620        case adc_bipolar10:
 621                s->range_table = &range_atmio16d_ai_10_bipolar;
 622                break;
 623        case adc_bipolar5:
 624                s->range_table = &range_atmio16d_ai_5_bipolar;
 625                break;
 626        case adc_unipolar10:
 627                s->range_table = &range_atmio16d_ai_unipolar;
 628                break;
 629        }
 630        if (dev->irq) {
 631                dev->read_subdev = s;
 632                s->subdev_flags |= SDF_CMD_READ;
 633                s->len_chanlist = 16;
 634                s->do_cmdtest = atmio16d_ai_cmdtest;
 635                s->do_cmd = atmio16d_ai_cmd;
 636                s->cancel = atmio16d_ai_cancel;
 637        }
 638
 639        /* ao subdevice */
 640        s = &dev->subdevices[1];
 641        s->type = COMEDI_SUBD_AO;
 642        s->subdev_flags = SDF_WRITABLE;
 643        s->n_chan = 2;
 644        s->maxdata = 0xfff;     /* 4095 decimal */
 645        s->range_table_list = devpriv->ao_range_type_list;
 646        switch (devpriv->dac0_range) {
 647        case dac_bipolar:
 648                devpriv->ao_range_type_list[0] = &range_bipolar10;
 649                break;
 650        case dac_unipolar:
 651                devpriv->ao_range_type_list[0] = &range_unipolar10;
 652                break;
 653        }
 654        switch (devpriv->dac1_range) {
 655        case dac_bipolar:
 656                devpriv->ao_range_type_list[1] = &range_bipolar10;
 657                break;
 658        case dac_unipolar:
 659                devpriv->ao_range_type_list[1] = &range_unipolar10;
 660                break;
 661        }
 662        s->insn_write = atmio16d_ao_insn_write;
 663
 664        ret = comedi_alloc_subdev_readback(s);
 665        if (ret)
 666                return ret;
 667
 668        /* Digital I/O */
 669        s = &dev->subdevices[2];
 670        s->type = COMEDI_SUBD_DIO;
 671        s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 672        s->n_chan = 8;
 673        s->insn_bits = atmio16d_dio_insn_bits;
 674        s->insn_config = atmio16d_dio_insn_config;
 675        s->maxdata = 1;
 676        s->range_table = &range_digital;
 677
 678        /* 8255 subdevice */
 679        s = &dev->subdevices[3];
 680        if (board->has_8255) {
 681                ret = subdev_8255_init(dev, s, NULL, 0x00);
 682                if (ret)
 683                        return ret;
 684        } else {
 685                s->type = COMEDI_SUBD_UNUSED;
 686        }
 687
 688/* don't yet know how to deal with counter/timers */
 689#if 0
 690        s = &dev->subdevices[4];
 691        /* do */
 692        s->type = COMEDI_SUBD_TIMER;
 693        s->n_chan = 0;
 694        s->maxdata = 0
 695#endif
 696
 697        return 0;
 698}
 699
 700static void atmio16d_detach(struct comedi_device *dev)
 701{
 702        reset_atmio16d(dev);
 703        comedi_legacy_detach(dev);
 704}
 705
 706static const struct atmio16_board_t atmio16_boards[] = {
 707        {
 708                .name           = "atmio16",
 709                .has_8255       = 0,
 710        }, {
 711                .name           = "atmio16d",
 712                .has_8255       = 1,
 713        },
 714};
 715
 716static struct comedi_driver atmio16d_driver = {
 717        .driver_name    = "atmio16",
 718        .module         = THIS_MODULE,
 719        .attach         = atmio16d_attach,
 720        .detach         = atmio16d_detach,
 721        .board_name     = &atmio16_boards[0].name,
 722        .num_names      = ARRAY_SIZE(atmio16_boards),
 723        .offset         = sizeof(struct atmio16_board_t),
 724};
 725module_comedi_driver(atmio16d_driver);
 726
 727MODULE_AUTHOR("Comedi https://www.comedi.org");
 728MODULE_DESCRIPTION("Comedi low-level driver");
 729MODULE_LICENSE("GPL");
 730