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