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
 154static int __init driver_atmio16d_init_module(void)
 155{
 156        return comedi_driver_register(&driver_atmio16d);
 157}
 158
 159static void __exit driver_atmio16d_cleanup_module(void)
 160{
 161        comedi_driver_unregister(&driver_atmio16d);
 162}
 163
 164module_init(driver_atmio16d_init_module);
 165module_exit(driver_atmio16d_cleanup_module);
 166
 167/* range structs */
 168static const struct comedi_lrange range_atmio16d_ai_10_bipolar = { 4, {
 169                                                                       BIP_RANGE
 170                                                                       (10),
 171                                                                       BIP_RANGE
 172                                                                       (1),
 173                                                                       BIP_RANGE
 174                                                                       (0.1),
 175                                                                       BIP_RANGE
 176                                                                       (0.02)
 177                                                                       }
 178};
 179
 180static const struct comedi_lrange range_atmio16d_ai_5_bipolar = { 4, {
 181                                                                      BIP_RANGE
 182                                                                      (5),
 183                                                                      BIP_RANGE
 184                                                                      (0.5),
 185                                                                      BIP_RANGE
 186                                                                      (0.05),
 187                                                                      BIP_RANGE
 188                                                                      (0.01)
 189                                                                      }
 190};
 191
 192static const struct comedi_lrange range_atmio16d_ai_unipolar = { 4, {
 193                                                                     UNI_RANGE
 194                                                                     (10),
 195                                                                     UNI_RANGE
 196                                                                     (1),
 197                                                                     UNI_RANGE
 198                                                                     (0.1),
 199                                                                     UNI_RANGE
 200                                                                     (0.02)
 201                                                                     }
 202};
 203
 204/* private data struct */
 205struct atmio16d_private {
 206        enum { adc_diff, adc_singleended } adc_mux;
 207        enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
 208        enum { adc_2comp, adc_straight } adc_coding;
 209        enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
 210        enum { dac_internal, dac_external } dac0_reference, dac1_reference;
 211        enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
 212        const struct comedi_lrange *ao_range_type_list[2];
 213        unsigned int ao_readback[2];
 214        unsigned int com_reg_1_state; /* current state of command register 1 */
 215        unsigned int com_reg_2_state; /* current state of command register 2 */
 216};
 217
 218static void reset_counters(struct comedi_device *dev)
 219{
 220        /* Counter 2 */
 221        outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
 222        outw(0xFF02, dev->iobase + AM9513A_COM_REG);
 223        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 224        outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
 225        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 226        outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 227        outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 228        /* Counter 3 */
 229        outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
 230        outw(0xFF03, dev->iobase + AM9513A_COM_REG);
 231        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 232        outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
 233        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 234        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 235        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 236        /* Counter 4 */
 237        outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
 238        outw(0xFF04, dev->iobase + AM9513A_COM_REG);
 239        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 240        outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
 241        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 242        outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 243        outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 244        /* Counter 5 */
 245        outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
 246        outw(0xFF05, dev->iobase + AM9513A_COM_REG);
 247        outw(0x4, dev->iobase + AM9513A_DATA_REG);
 248        outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
 249        outw(0x3, dev->iobase + AM9513A_DATA_REG);
 250        outw(0xFF50, dev->iobase + AM9513A_COM_REG);
 251        outw(0xFF50, dev->iobase + AM9513A_COM_REG);
 252
 253        outw(0, dev->iobase + AD_CLEAR_REG);
 254}
 255
 256static void reset_atmio16d(struct comedi_device *dev)
 257{
 258        int i;
 259
 260        /* now we need to initialize the board */
 261        outw(0, dev->iobase + COM_REG_1);
 262        outw(0, dev->iobase + COM_REG_2);
 263        outw(0, dev->iobase + MUX_GAIN_REG);
 264        /* init AM9513A timer */
 265        outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
 266        outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
 267        outw(0xFF17, dev->iobase + AM9513A_COM_REG);
 268        outw(0xF000, dev->iobase + AM9513A_DATA_REG);
 269        for (i = 1; i <= 5; ++i) {
 270                outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
 271                outw(0x0004, dev->iobase + AM9513A_DATA_REG);
 272                outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
 273                outw(0x3, dev->iobase + AM9513A_DATA_REG);
 274        }
 275        outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
 276        /* timer init done */
 277        outw(0, dev->iobase + AD_CLEAR_REG);
 278        outw(0, dev->iobase + INT2CLR_REG);
 279        /* select straight binary mode for Analog Input */
 280        devpriv->com_reg_1_state |= 1;
 281        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 282        devpriv->adc_coding = adc_straight;
 283        /* zero the analog outputs */
 284        outw(2048, dev->iobase + DAC0_REG);
 285        outw(2048, dev->iobase + DAC1_REG);
 286}
 287
 288static irqreturn_t atmio16d_interrupt(int irq, void *d)
 289{
 290        struct comedi_device *dev = d;
 291        struct comedi_subdevice *s = dev->subdevices + 0;
 292
 293#ifdef DEBUG1
 294        printk(KERN_DEBUG "atmio16d_interrupt!\n");
 295#endif
 296
 297        comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));
 298
 299        comedi_event(dev, s);
 300        return IRQ_HANDLED;
 301}
 302
 303static int atmio16d_ai_cmdtest(struct comedi_device *dev,
 304                               struct comedi_subdevice *s,
 305                               struct comedi_cmd *cmd)
 306{
 307        int err = 0, tmp;
 308#ifdef DEBUG1
 309        printk(KERN_DEBUG "atmio16d_ai_cmdtest\n");
 310#endif
 311        /* make sure triggers are valid */
 312        tmp = cmd->start_src;
 313        cmd->start_src &= TRIG_NOW;
 314        if (!cmd->start_src || tmp != cmd->start_src)
 315                err++;
 316
 317        tmp = cmd->scan_begin_src;
 318        cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER;
 319        if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
 320                err++;
 321
 322        tmp = cmd->convert_src;
 323        cmd->convert_src &= TRIG_TIMER;
 324        if (!cmd->convert_src || tmp != cmd->convert_src)
 325                err++;
 326
 327        tmp = cmd->scan_end_src;
 328        cmd->scan_end_src &= TRIG_COUNT;
 329        if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
 330                err++;
 331
 332        tmp = cmd->stop_src;
 333        cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
 334        if (!cmd->stop_src || tmp != cmd->stop_src)
 335                err++;
 336
 337        if (err)
 338                return 1;
 339
 340        /* step 2: make sure trigger sources are unique & mutually compatible */
 341        /* note that mutual compatibility is not an issue here */
 342        if (cmd->scan_begin_src != TRIG_FOLLOW &&
 343            cmd->scan_begin_src != TRIG_EXT &&
 344            cmd->scan_begin_src != TRIG_TIMER)
 345                err++;
 346        if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
 347                err++;
 348
 349        if (err)
 350                return 2;
 351
 352        /* step 3: make sure arguments are trivially compatible */
 353
 354        if (cmd->start_arg != 0) {
 355                cmd->start_arg = 0;
 356                err++;
 357        }
 358        if (cmd->scan_begin_src == TRIG_FOLLOW) {
 359                /* internal trigger */
 360                if (cmd->scan_begin_arg != 0) {
 361                        cmd->scan_begin_arg = 0;
 362                        err++;
 363                }
 364        } else {
 365#if 0
 366                /* external trigger */
 367                /* should be level/edge, hi/lo specification here */
 368                if (cmd->scan_begin_arg != 0) {
 369                        cmd->scan_begin_arg = 0;
 370                        err++;
 371                }
 372#endif
 373        }
 374
 375        if (cmd->convert_arg < 10000) {
 376                cmd->convert_arg = 10000;
 377                err++;
 378        }
 379#if 0
 380        if (cmd->convert_arg > SLOWEST_TIMER) {
 381                cmd->convert_arg = SLOWEST_TIMER;
 382                err++;
 383        }
 384#endif
 385        if (cmd->scan_end_arg != cmd->chanlist_len) {
 386                cmd->scan_end_arg = cmd->chanlist_len;
 387                err++;
 388        }
 389        if (cmd->stop_src == TRIG_COUNT) {
 390                /* any count is allowed */
 391        } else {
 392                /* TRIG_NONE */
 393                if (cmd->stop_arg != 0) {
 394                        cmd->stop_arg = 0;
 395                        err++;
 396                }
 397        }
 398
 399        if (err)
 400                return 3;
 401
 402        return 0;
 403}
 404
 405static int atmio16d_ai_cmd(struct comedi_device *dev,
 406                           struct comedi_subdevice *s)
 407{
 408        struct comedi_cmd *cmd = &s->async->cmd;
 409        unsigned int timer, base_clock;
 410        unsigned int sample_count, tmp, chan, gain;
 411        int i;
 412#ifdef DEBUG1
 413        printk(KERN_DEBUG "atmio16d_ai_cmd\n");
 414#endif
 415        /* This is slowly becoming a working command interface. *
 416         * It is still uber-experimental */
 417
 418        reset_counters(dev);
 419        s->async->cur_chan = 0;
 420
 421        /* check if scanning multiple channels */
 422        if (cmd->chanlist_len < 2) {
 423                devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
 424                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 425        } else {
 426                devpriv->com_reg_1_state |= COMREG1_SCANEN;
 427                devpriv->com_reg_2_state |= COMREG2_SCN2;
 428                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 429                outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 430        }
 431
 432        /* Setup the Mux-Gain Counter */
 433        for (i = 0; i < cmd->chanlist_len; ++i) {
 434                chan = CR_CHAN(cmd->chanlist[i]);
 435                gain = CR_RANGE(cmd->chanlist[i]);
 436                outw(i, dev->iobase + MUX_CNTR_REG);
 437                tmp = chan | (gain << 6);
 438                if (i == cmd->scan_end_arg - 1)
 439                        tmp |= 0x0010;  /* set LASTONE bit */
 440                outw(tmp, dev->iobase + MUX_GAIN_REG);
 441        }
 442
 443        /* Now program the sample interval timer */
 444        /* Figure out which clock to use then get an
 445         * appropriate timer value */
 446        if (cmd->convert_arg < 65536000) {
 447                base_clock = CLOCK_1_MHZ;
 448                timer = cmd->convert_arg / 1000;
 449        } else if (cmd->convert_arg < 655360000) {
 450                base_clock = CLOCK_100_KHZ;
 451                timer = cmd->convert_arg / 10000;
 452        } else if (cmd->convert_arg <= 0xffffffff /* 6553600000 */) {
 453                base_clock = CLOCK_10_KHZ;
 454                timer = cmd->convert_arg / 100000;
 455        } else if (cmd->convert_arg <= 0xffffffff /* 65536000000 */) {
 456                base_clock = CLOCK_1_KHZ;
 457                timer = cmd->convert_arg / 1000000;
 458        }
 459        outw(0xFF03, dev->iobase + AM9513A_COM_REG);
 460        outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 461        outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
 462        outw(0x2, dev->iobase + AM9513A_DATA_REG);
 463        outw(0xFF44, dev->iobase + AM9513A_COM_REG);
 464        outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
 465        outw(timer, dev->iobase + AM9513A_DATA_REG);
 466        outw(0xFF24, dev->iobase + AM9513A_COM_REG);
 467
 468        /* Now figure out how many samples to get */
 469        /* and program the sample counter */
 470        sample_count = cmd->stop_arg * cmd->scan_end_arg;
 471        outw(0xFF04, dev->iobase + AM9513A_COM_REG);
 472        outw(0x1025, dev->iobase + AM9513A_DATA_REG);
 473        outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
 474        if (sample_count < 65536) {
 475                /* use only Counter 4 */
 476                outw(sample_count, dev->iobase + AM9513A_DATA_REG);
 477                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 478                outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
 479                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 480                devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
 481                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 482        } else {
 483                /* Counter 4 and 5 are needed */
 484
 485                tmp = sample_count & 0xFFFF;
 486                if (tmp)
 487                        outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
 488                else
 489                        outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
 490
 491                outw(0xFF48, dev->iobase + AM9513A_COM_REG);
 492                outw(0, dev->iobase + AM9513A_DATA_REG);
 493                outw(0xFF28, dev->iobase + AM9513A_COM_REG);
 494                outw(0xFF05, dev->iobase + AM9513A_COM_REG);
 495                outw(0x25, dev->iobase + AM9513A_DATA_REG);
 496                outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
 497                tmp = sample_count & 0xFFFF;
 498                if ((tmp == 0) || (tmp == 1)) {
 499                        outw((sample_count >> 16) & 0xFFFF,
 500                             dev->iobase + AM9513A_DATA_REG);
 501                } else {
 502                        outw(((sample_count >> 16) & 0xFFFF) + 1,
 503                             dev->iobase + AM9513A_DATA_REG);
 504                }
 505                outw(0xFF70, dev->iobase + AM9513A_COM_REG);
 506                devpriv->com_reg_1_state |= COMREG1_1632CNT;
 507                outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 508        }
 509
 510        /* Program the scan interval timer ONLY IF SCANNING IS ENABLED */
 511        /* Figure out which clock to use then get an
 512         * appropriate timer value */
 513        if (cmd->chanlist_len > 1) {
 514                if (cmd->scan_begin_arg < 65536000) {
 515                        base_clock = CLOCK_1_MHZ;
 516                        timer = cmd->scan_begin_arg / 1000;
 517                } else if (cmd->scan_begin_arg < 655360000) {
 518                        base_clock = CLOCK_100_KHZ;
 519                        timer = cmd->scan_begin_arg / 10000;
 520                } else if (cmd->scan_begin_arg < 0xffffffff /* 6553600000 */) {
 521                        base_clock = CLOCK_10_KHZ;
 522                        timer = cmd->scan_begin_arg / 100000;
 523                } else if (cmd->scan_begin_arg < 0xffffffff /* 65536000000 */) {
 524                        base_clock = CLOCK_1_KHZ;
 525                        timer = cmd->scan_begin_arg / 1000000;
 526                }
 527                outw(0xFF02, dev->iobase + AM9513A_COM_REG);
 528                outw(base_clock, dev->iobase + AM9513A_DATA_REG);
 529                outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
 530                outw(0x2, dev->iobase + AM9513A_DATA_REG);
 531                outw(0xFF42, dev->iobase + AM9513A_COM_REG);
 532                outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
 533                outw(timer, dev->iobase + AM9513A_DATA_REG);
 534                outw(0xFF22, dev->iobase + AM9513A_COM_REG);
 535        }
 536
 537        /* Clear the A/D FIFO and reset the MUX counter */
 538        outw(0, dev->iobase + AD_CLEAR_REG);
 539        outw(0, dev->iobase + MUX_CNTR_REG);
 540        outw(0, dev->iobase + INT2CLR_REG);
 541        /* enable this acquisition operation */
 542        devpriv->com_reg_1_state |= COMREG1_DAQEN;
 543        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 544        /* enable interrupts for conversion completion */
 545        devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
 546        devpriv->com_reg_2_state |= COMREG2_INTEN;
 547        outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
 548        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 549        /* apply a trigger. this starts the counters! */
 550        outw(0, dev->iobase + START_DAQ_REG);
 551
 552        return 0;
 553}
 554
 555/* This will cancel a running acquisition operation */
 556static int atmio16d_ai_cancel(struct comedi_device *dev,
 557                              struct comedi_subdevice *s)
 558{
 559        reset_atmio16d(dev);
 560
 561        return 0;
 562}
 563
 564/* Mode 0 is used to get a single conversion on demand */
 565static int atmio16d_ai_insn_read(struct comedi_device *dev,
 566                                 struct comedi_subdevice *s,
 567                                 struct comedi_insn *insn, unsigned int *data)
 568{
 569        int i, t;
 570        int chan;
 571        int gain;
 572        int status;
 573
 574#ifdef DEBUG1
 575        printk(KERN_DEBUG "atmio16d_ai_insn_read\n");
 576#endif
 577        chan = CR_CHAN(insn->chanspec);
 578        gain = CR_RANGE(insn->chanspec);
 579
 580        /* reset the Analog input circuitry */
 581        /* outw( 0, dev->iobase+AD_CLEAR_REG ); */
 582        /* reset the Analog Input MUX Counter to 0 */
 583        /* outw( 0, dev->iobase+MUX_CNTR_REG ); */
 584
 585        /* set the Input MUX gain */
 586        outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
 587
 588        for (i = 0; i < insn->n; i++) {
 589                /* start the conversion */
 590                outw(0, dev->iobase + START_CONVERT_REG);
 591                /* wait for it to finish */
 592                for (t = 0; t < ATMIO16D_TIMEOUT; t++) {
 593                        /* check conversion status */
 594                        status = inw(dev->iobase + STAT_REG);
 595#ifdef DEBUG1
 596                        printk(KERN_DEBUG "status=%x\n", status);
 597#endif
 598                        if (status & STAT_AD_CONVAVAIL) {
 599                                /* read the data now */
 600                                data[i] = inw(dev->iobase + AD_FIFO_REG);
 601                                /* change to two's complement if need be */
 602                                if (devpriv->adc_coding == adc_2comp)
 603                                        data[i] ^= 0x800;
 604                                break;
 605                        }
 606                        if (status & STAT_AD_OVERFLOW) {
 607                                printk(KERN_INFO "atmio16d: a/d FIFO overflow\n");
 608                                outw(0, dev->iobase + AD_CLEAR_REG);
 609
 610                                return -ETIME;
 611                        }
 612                }
 613                /* end waiting, now check if it timed out */
 614                if (t == ATMIO16D_TIMEOUT) {
 615                        printk(KERN_INFO "atmio16d: timeout\n");
 616
 617                        return -ETIME;
 618                }
 619        }
 620
 621        return i;
 622}
 623
 624static int atmio16d_ao_insn_read(struct comedi_device *dev,
 625                                 struct comedi_subdevice *s,
 626                                 struct comedi_insn *insn, unsigned int *data)
 627{
 628        int i;
 629#ifdef DEBUG1
 630        printk(KERN_DEBUG "atmio16d_ao_insn_read\n");
 631#endif
 632
 633        for (i = 0; i < insn->n; i++)
 634                data[i] = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
 635        return i;
 636}
 637
 638static int atmio16d_ao_insn_write(struct comedi_device *dev,
 639                                  struct comedi_subdevice *s,
 640                                  struct comedi_insn *insn, unsigned int *data)
 641{
 642        int i;
 643        int chan;
 644        int d;
 645#ifdef DEBUG1
 646        printk(KERN_DEBUG "atmio16d_ao_insn_write\n");
 647#endif
 648
 649        chan = CR_CHAN(insn->chanspec);
 650
 651        for (i = 0; i < insn->n; i++) {
 652                d = data[i];
 653                switch (chan) {
 654                case 0:
 655                        if (devpriv->dac0_coding == dac_2comp)
 656                                d ^= 0x800;
 657                        outw(d, dev->iobase + DAC0_REG);
 658                        break;
 659                case 1:
 660                        if (devpriv->dac1_coding == dac_2comp)
 661                                d ^= 0x800;
 662                        outw(d, dev->iobase + DAC1_REG);
 663                        break;
 664                default:
 665                        return -EINVAL;
 666                }
 667                devpriv->ao_readback[chan] = data[i];
 668        }
 669        return i;
 670}
 671
 672static int atmio16d_dio_insn_bits(struct comedi_device *dev,
 673                                  struct comedi_subdevice *s,
 674                                  struct comedi_insn *insn, unsigned int *data)
 675{
 676        if (insn->n != 2)
 677                return -EINVAL;
 678
 679        if (data[0]) {
 680                s->state &= ~data[0];
 681                s->state |= (data[0] | data[1]);
 682                outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
 683        }
 684        data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
 685
 686        return 2;
 687}
 688
 689static int atmio16d_dio_insn_config(struct comedi_device *dev,
 690                                    struct comedi_subdevice *s,
 691                                    struct comedi_insn *insn,
 692                                    unsigned int *data)
 693{
 694        int i;
 695        int mask;
 696
 697        for (i = 0; i < insn->n; i++) {
 698                mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
 699                s->io_bits &= ~mask;
 700                if (data[i])
 701                        s->io_bits |= mask;
 702        }
 703        devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
 704        if (s->io_bits & 0x0f)
 705                devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
 706        if (s->io_bits & 0xf0)
 707                devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
 708        outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
 709
 710        return i;
 711}
 712
 713/*
 714   options[0] - I/O port
 715   options[1] - MIO irq
 716                0 == no irq
 717                N == irq N {3,4,5,6,7,9,10,11,12,14,15}
 718   options[2] - DIO irq
 719                0 == no irq
 720                N == irq N {3,4,5,6,7,9}
 721   options[3] - DMA1 channel
 722                0 == no DMA
 723                N == DMA N {5,6,7}
 724   options[4] - DMA2 channel
 725                0 == no DMA
 726                N == DMA N {5,6,7}
 727
 728   options[5] - a/d mux
 729        0=differential, 1=single
 730   options[6] - a/d range
 731        0=bipolar10, 1=bipolar5, 2=unipolar10
 732
 733   options[7] - dac0 range
 734        0=bipolar, 1=unipolar
 735   options[8] - dac0 reference
 736        0=internal, 1=external
 737   options[9] - dac0 coding
 738        0=2's comp, 1=straight binary
 739
 740   options[10] - dac1 range
 741   options[11] - dac1 reference
 742   options[12] - dac1 coding
 743 */
 744
 745static int atmio16d_attach(struct comedi_device *dev,
 746                           struct comedi_devconfig *it)
 747{
 748        unsigned int irq;
 749        unsigned long iobase;
 750        int ret;
 751
 752        struct comedi_subdevice *s;
 753
 754        /* make sure the address range is free and allocate it */
 755        iobase = it->options[0];
 756        printk(KERN_INFO "comedi%d: atmio16d: 0x%04lx ", dev->minor, iobase);
 757        if (!request_region(iobase, ATMIO16D_SIZE, "ni_atmio16d")) {
 758                printk("I/O port conflict\n");
 759                return -EIO;
 760        }
 761        dev->iobase = iobase;
 762
 763        /* board name */
 764        dev->board_name = boardtype->name;
 765
 766        ret = alloc_subdevices(dev, 4);
 767        if (ret < 0)
 768                return ret;
 769
 770        ret = alloc_private(dev, sizeof(struct atmio16d_private));
 771        if (ret < 0)
 772                return ret;
 773
 774        /* reset the atmio16d hardware */
 775        reset_atmio16d(dev);
 776
 777        /* check if our interrupt is available and get it */
 778        irq = it->options[1];
 779        if (irq) {
 780
 781                ret = request_irq(irq, atmio16d_interrupt, 0, "atmio16d", dev);
 782                if (ret < 0) {
 783                        printk(KERN_INFO "failed to allocate irq %u\n", irq);
 784                        return ret;
 785                }
 786                dev->irq = irq;
 787                printk(KERN_INFO "( irq = %u )\n", irq);
 788        } else {
 789                printk(KERN_INFO "( no irq )");
 790        }
 791
 792        /* set device options */
 793        devpriv->adc_mux = it->options[5];
 794        devpriv->adc_range = it->options[6];
 795
 796        devpriv->dac0_range = it->options[7];
 797        devpriv->dac0_reference = it->options[8];
 798        devpriv->dac0_coding = it->options[9];
 799        devpriv->dac1_range = it->options[10];
 800        devpriv->dac1_reference = it->options[11];
 801        devpriv->dac1_coding = it->options[12];
 802
 803        /* setup sub-devices */
 804        s = dev->subdevices + 0;
 805        dev->read_subdev = s;
 806        /* ai subdevice */
 807        s->type = COMEDI_SUBD_AI;
 808        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
 809        s->n_chan = (devpriv->adc_mux ? 16 : 8);
 810        s->len_chanlist = 16;
 811        s->insn_read = atmio16d_ai_insn_read;
 812        s->do_cmdtest = atmio16d_ai_cmdtest;
 813        s->do_cmd = atmio16d_ai_cmd;
 814        s->cancel = atmio16d_ai_cancel;
 815        s->maxdata = 0xfff;     /* 4095 decimal */
 816        switch (devpriv->adc_range) {
 817        case adc_bipolar10:
 818                s->range_table = &range_atmio16d_ai_10_bipolar;
 819                break;
 820        case adc_bipolar5:
 821                s->range_table = &range_atmio16d_ai_5_bipolar;
 822                break;
 823        case adc_unipolar10:
 824                s->range_table = &range_atmio16d_ai_unipolar;
 825                break;
 826        }
 827
 828        /* ao subdevice */
 829        s++;
 830        s->type = COMEDI_SUBD_AO;
 831        s->subdev_flags = SDF_WRITABLE;
 832        s->n_chan = 2;
 833        s->insn_read = atmio16d_ao_insn_read;
 834        s->insn_write = atmio16d_ao_insn_write;
 835        s->maxdata = 0xfff;     /* 4095 decimal */
 836        s->range_table_list = devpriv->ao_range_type_list;
 837        switch (devpriv->dac0_range) {
 838        case dac_bipolar:
 839                devpriv->ao_range_type_list[0] = &range_bipolar10;
 840                break;
 841        case dac_unipolar:
 842                devpriv->ao_range_type_list[0] = &range_unipolar10;
 843                break;
 844        }
 845        switch (devpriv->dac1_range) {
 846        case dac_bipolar:
 847                devpriv->ao_range_type_list[1] = &range_bipolar10;
 848                break;
 849        case dac_unipolar:
 850                devpriv->ao_range_type_list[1] = &range_unipolar10;
 851                break;
 852        }
 853
 854        /* Digital I/O */
 855        s++;
 856        s->type = COMEDI_SUBD_DIO;
 857        s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 858        s->n_chan = 8;
 859        s->insn_bits = atmio16d_dio_insn_bits;
 860        s->insn_config = atmio16d_dio_insn_config;
 861        s->maxdata = 1;
 862        s->range_table = &range_digital;
 863
 864        /* 8255 subdevice */
 865        s++;
 866        if (boardtype->has_8255)
 867                subdev_8255_init(dev, s, NULL, dev->iobase);
 868        else
 869                s->type = COMEDI_SUBD_UNUSED;
 870
 871/* don't yet know how to deal with counter/timers */
 872#if 0
 873        s++;
 874        /* do */
 875        s->type = COMEDI_SUBD_TIMER;
 876        s->n_chan = 0;
 877        s->maxdata = 0
 878#endif
 879            printk("\n");
 880
 881        return 0;
 882}
 883
 884static int atmio16d_detach(struct comedi_device *dev)
 885{
 886        printk(KERN_INFO "comedi%d: atmio16d: remove\n", dev->minor);
 887
 888        if (dev->subdevices && boardtype->has_8255)
 889                subdev_8255_cleanup(dev, dev->subdevices + 3);
 890
 891        if (dev->irq)
 892                free_irq(dev->irq, dev);
 893
 894        reset_atmio16d(dev);
 895
 896        if (dev->iobase)
 897                release_region(dev->iobase, ATMIO16D_SIZE);
 898
 899        return 0;
 900}
 901
 902MODULE_AUTHOR("Comedi http://www.comedi.org");
 903MODULE_DESCRIPTION("Comedi low-level driver");
 904MODULE_LICENSE("GPL");
 905