linux/drivers/staging/comedi/drivers/dmm32at.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * dmm32at.c
   4 * Diamond Systems Diamond-MM-32-AT Comedi driver
   5 *
   6 * COMEDI - Linux Control and Measurement Device Interface
   7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   8 */
   9
  10/*
  11 * Driver: dmm32at
  12 * Description: Diamond Systems Diamond-MM-32-AT
  13 * Devices: [Diamond Systems] Diamond-MM-32-AT (dmm32at)
  14 * Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
  15 * Updated: Fri Jun  4 09:13:24 CDT 2004
  16 * Status: experimental
  17 *
  18 * Configuration Options:
  19 *      comedi_config /dev/comedi0 dmm32at baseaddr,irq
  20 *
  21 * This driver is for the Diamond Systems MM-32-AT board
  22 *      http://www.diamondsystems.com/products/diamondmm32at
  23 *
  24 * It is being used on several projects inside NASA, without
  25 * problems so far. For analog input commands, TRIG_EXT is not
  26 * yet supported.
  27 */
  28
  29#include <linux/module.h>
  30#include <linux/delay.h>
  31#include <linux/interrupt.h>
  32#include "../comedidev.h"
  33
  34#include "8255.h"
  35
  36/* Board register addresses */
  37#define DMM32AT_AI_START_CONV_REG       0x00
  38#define DMM32AT_AI_LSB_REG              0x00
  39#define DMM32AT_AUX_DOUT_REG            0x01
  40#define DMM32AT_AUX_DOUT2               BIT(2)  /* J3.42 - OUT2 (OUT2EN) */
  41#define DMM32AT_AUX_DOUT1               BIT(1)  /* J3.43 */
  42#define DMM32AT_AUX_DOUT0               BIT(0)  /* J3.44 - OUT0 (OUT0EN) */
  43#define DMM32AT_AI_MSB_REG              0x01
  44#define DMM32AT_AI_LO_CHAN_REG          0x02
  45#define DMM32AT_AI_HI_CHAN_REG          0x03
  46#define DMM32AT_AUX_DI_REG              0x04
  47#define DMM32AT_AUX_DI_DACBUSY          BIT(7)
  48#define DMM32AT_AUX_DI_CALBUSY          BIT(6)
  49#define DMM32AT_AUX_DI3                 BIT(3)  /* J3.45 - ADCLK (CLKSEL) */
  50#define DMM32AT_AUX_DI2                 BIT(2)  /* J3.46 - GATE12 (GT12EN) */
  51#define DMM32AT_AUX_DI1                 BIT(1)  /* J3.47 - GATE0 (GT0EN) */
  52#define DMM32AT_AUX_DI0                 BIT(0)  /* J3.48 - CLK0 (SRC0) */
  53#define DMM32AT_AO_LSB_REG              0x04
  54#define DMM32AT_AO_MSB_REG              0x05
  55#define DMM32AT_AO_MSB_DACH(x)          ((x) << 6)
  56#define DMM32AT_FIFO_DEPTH_REG          0x06
  57#define DMM32AT_FIFO_CTRL_REG           0x07
  58#define DMM32AT_FIFO_CTRL_FIFOEN        BIT(3)
  59#define DMM32AT_FIFO_CTRL_SCANEN        BIT(2)
  60#define DMM32AT_FIFO_CTRL_FIFORST       BIT(1)
  61#define DMM32AT_FIFO_STATUS_REG         0x07
  62#define DMM32AT_FIFO_STATUS_EF          BIT(7)
  63#define DMM32AT_FIFO_STATUS_HF          BIT(6)
  64#define DMM32AT_FIFO_STATUS_FF          BIT(5)
  65#define DMM32AT_FIFO_STATUS_OVF         BIT(4)
  66#define DMM32AT_FIFO_STATUS_FIFOEN      BIT(3)
  67#define DMM32AT_FIFO_STATUS_SCANEN      BIT(2)
  68#define DMM32AT_FIFO_STATUS_PAGE_MASK   (3 << 0)
  69#define DMM32AT_CTRL_REG                0x08
  70#define DMM32AT_CTRL_RESETA             BIT(5)
  71#define DMM32AT_CTRL_RESETD             BIT(4)
  72#define DMM32AT_CTRL_INTRST             BIT(3)
  73#define DMM32AT_CTRL_PAGE(x)            ((x) << 0)
  74#define DMM32AT_CTRL_PAGE_8254          DMM32AT_CTRL_PAGE(0)
  75#define DMM32AT_CTRL_PAGE_8255          DMM32AT_CTRL_PAGE(1)
  76#define DMM32AT_CTRL_PAGE_CALIB         DMM32AT_CTRL_PAGE(3)
  77#define DMM32AT_AI_STATUS_REG           0x08
  78#define DMM32AT_AI_STATUS_STS           BIT(7)
  79#define DMM32AT_AI_STATUS_SD1           BIT(6)
  80#define DMM32AT_AI_STATUS_SD0           BIT(5)
  81#define DMM32AT_AI_STATUS_ADCH_MASK     (0x1f << 0)
  82#define DMM32AT_INTCLK_REG              0x09
  83#define DMM32AT_INTCLK_ADINT            BIT(7)
  84#define DMM32AT_INTCLK_DINT             BIT(6)
  85#define DMM32AT_INTCLK_TINT             BIT(5)
  86#define DMM32AT_INTCLK_CLKEN            BIT(1)  /* 1=see below  0=software */
  87#define DMM32AT_INTCLK_CLKSEL           BIT(0)  /* 1=OUT2  0=EXTCLK */
  88#define DMM32AT_CTRDIO_CFG_REG          0x0a
  89#define DMM32AT_CTRDIO_CFG_FREQ12       BIT(7)  /* CLK12 1=100KHz 0=10MHz */
  90#define DMM32AT_CTRDIO_CFG_FREQ0        BIT(6)  /* CLK0  1=10KHz  0=10MHz */
  91#define DMM32AT_CTRDIO_CFG_OUT2EN       BIT(5)  /* J3.42 1=OUT2 is DOUT2 */
  92#define DMM32AT_CTRDIO_CFG_OUT0EN       BIT(4)  /* J3,44 1=OUT0 is DOUT0 */
  93#define DMM32AT_CTRDIO_CFG_GT0EN        BIT(2)  /* J3.47 1=DIN1 is GATE0 */
  94#define DMM32AT_CTRDIO_CFG_SRC0         BIT(1)  /* CLK0 is 0=FREQ0 1=J3.48 */
  95#define DMM32AT_CTRDIO_CFG_GT12EN       BIT(0)  /* J3.46 1=DIN2 is GATE12 */
  96#define DMM32AT_AI_CFG_REG              0x0b
  97#define DMM32AT_AI_CFG_SCINT(x)         ((x) << 4)
  98#define DMM32AT_AI_CFG_SCINT_20US       DMM32AT_AI_CFG_SCINT(0)
  99#define DMM32AT_AI_CFG_SCINT_15US       DMM32AT_AI_CFG_SCINT(1)
 100#define DMM32AT_AI_CFG_SCINT_10US       DMM32AT_AI_CFG_SCINT(2)
 101#define DMM32AT_AI_CFG_SCINT_5US        DMM32AT_AI_CFG_SCINT(3)
 102#define DMM32AT_AI_CFG_RANGE            BIT(3)  /* 0=5V  1=10V */
 103#define DMM32AT_AI_CFG_ADBU             BIT(2)  /* 0=bipolar  1=unipolar */
 104#define DMM32AT_AI_CFG_GAIN(x)          ((x) << 0)
 105#define DMM32AT_AI_READBACK_REG         0x0b
 106#define DMM32AT_AI_READBACK_WAIT        BIT(7)  /* DMM32AT_AI_STATUS_STS */
 107#define DMM32AT_AI_READBACK_RANGE       BIT(3)
 108#define DMM32AT_AI_READBACK_ADBU        BIT(2)
 109#define DMM32AT_AI_READBACK_GAIN_MASK   (3 << 0)
 110
 111#define DMM32AT_CLK1 0x0d
 112#define DMM32AT_CLK2 0x0e
 113#define DMM32AT_CLKCT 0x0f
 114
 115#define DMM32AT_8255_IOBASE             0x0c  /* Page 1 registers */
 116
 117/* Board register values. */
 118
 119/* DMM32AT_AI_CFG_REG 0x0b */
 120#define DMM32AT_RANGE_U10 0x0c
 121#define DMM32AT_RANGE_U5 0x0d
 122#define DMM32AT_RANGE_B10 0x08
 123#define DMM32AT_RANGE_B5 0x00
 124
 125/* DMM32AT_CLKCT 0x0f */
 126#define DMM32AT_CLKCT1 0x56     /* mode3 counter 1 - write low byte only */
 127#define DMM32AT_CLKCT2 0xb6     /*  mode3 counter 2 - write high and low byte */
 128
 129/* board AI ranges in comedi structure */
 130static const struct comedi_lrange dmm32at_airanges = {
 131        4, {
 132                UNI_RANGE(10),
 133                UNI_RANGE(5),
 134                BIP_RANGE(10),
 135                BIP_RANGE(5)
 136        }
 137};
 138
 139/* register values for above ranges */
 140static const unsigned char dmm32at_rangebits[] = {
 141        DMM32AT_RANGE_U10,
 142        DMM32AT_RANGE_U5,
 143        DMM32AT_RANGE_B10,
 144        DMM32AT_RANGE_B5,
 145};
 146
 147/* only one of these ranges is valid, as set by a jumper on the
 148 * board. The application should only use the range set by the jumper
 149 */
 150static const struct comedi_lrange dmm32at_aoranges = {
 151        4, {
 152                UNI_RANGE(10),
 153                UNI_RANGE(5),
 154                BIP_RANGE(10),
 155                BIP_RANGE(5)
 156        }
 157};
 158
 159static void dmm32at_ai_set_chanspec(struct comedi_device *dev,
 160                                    struct comedi_subdevice *s,
 161                                    unsigned int chanspec, int nchan)
 162{
 163        unsigned int chan = CR_CHAN(chanspec);
 164        unsigned int range = CR_RANGE(chanspec);
 165        unsigned int last_chan = (chan + nchan - 1) % s->n_chan;
 166
 167        outb(DMM32AT_FIFO_CTRL_FIFORST, dev->iobase + DMM32AT_FIFO_CTRL_REG);
 168
 169        if (nchan > 1)
 170                outb(DMM32AT_FIFO_CTRL_SCANEN,
 171                     dev->iobase + DMM32AT_FIFO_CTRL_REG);
 172
 173        outb(chan, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
 174        outb(last_chan, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
 175        outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AI_CFG_REG);
 176}
 177
 178static unsigned int dmm32at_ai_get_sample(struct comedi_device *dev,
 179                                          struct comedi_subdevice *s)
 180{
 181        unsigned int val;
 182
 183        val = inb(dev->iobase + DMM32AT_AI_LSB_REG);
 184        val |= (inb(dev->iobase + DMM32AT_AI_MSB_REG) << 8);
 185
 186        /* munge two's complement value to offset binary */
 187        return comedi_offset_munge(s, val);
 188}
 189
 190static int dmm32at_ai_status(struct comedi_device *dev,
 191                             struct comedi_subdevice *s,
 192                             struct comedi_insn *insn,
 193                             unsigned long context)
 194{
 195        unsigned char status;
 196
 197        status = inb(dev->iobase + context);
 198        if ((status & DMM32AT_AI_STATUS_STS) == 0)
 199                return 0;
 200        return -EBUSY;
 201}
 202
 203static int dmm32at_ai_insn_read(struct comedi_device *dev,
 204                                struct comedi_subdevice *s,
 205                                struct comedi_insn *insn,
 206                                unsigned int *data)
 207{
 208        int ret;
 209        int i;
 210
 211        dmm32at_ai_set_chanspec(dev, s, insn->chanspec, 1);
 212
 213        /* wait for circuit to settle */
 214        ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
 215                             DMM32AT_AI_READBACK_REG);
 216        if (ret)
 217                return ret;
 218
 219        for (i = 0; i < insn->n; i++) {
 220                outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
 221
 222                ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
 223                                     DMM32AT_AI_STATUS_REG);
 224                if (ret)
 225                        return ret;
 226
 227                data[i] = dmm32at_ai_get_sample(dev, s);
 228        }
 229
 230        return insn->n;
 231}
 232
 233static int dmm32at_ai_check_chanlist(struct comedi_device *dev,
 234                                     struct comedi_subdevice *s,
 235                                     struct comedi_cmd *cmd)
 236{
 237        unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
 238        unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
 239        int i;
 240
 241        for (i = 1; i < cmd->chanlist_len; i++) {
 242                unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 243                unsigned int range = CR_RANGE(cmd->chanlist[i]);
 244
 245                if (chan != (chan0 + i) % s->n_chan) {
 246                        dev_dbg(dev->class_dev,
 247                                "entries in chanlist must be consecutive channels, counting upwards\n");
 248                        return -EINVAL;
 249                }
 250                if (range != range0) {
 251                        dev_dbg(dev->class_dev,
 252                                "entries in chanlist must all have the same gain\n");
 253                        return -EINVAL;
 254                }
 255        }
 256
 257        return 0;
 258}
 259
 260static int dmm32at_ai_cmdtest(struct comedi_device *dev,
 261                              struct comedi_subdevice *s,
 262                              struct comedi_cmd *cmd)
 263{
 264        int err = 0;
 265        unsigned int arg;
 266
 267        /* Step 1 : check if triggers are trivially valid */
 268
 269        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 270        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
 271        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 272        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 273        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 274
 275        if (err)
 276                return 1;
 277
 278        /* Step 2a : make sure trigger sources are unique */
 279
 280        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 281
 282        /* Step 2b : and mutually compatible */
 283
 284        if (err)
 285                return 2;
 286
 287        /* Step 3: check if arguments are trivially valid */
 288
 289        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 290
 291        err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 1000000);
 292        err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
 293
 294        if (cmd->convert_arg >= 17500)
 295                cmd->convert_arg = 20000;
 296        else if (cmd->convert_arg >= 12500)
 297                cmd->convert_arg = 15000;
 298        else if (cmd->convert_arg >= 7500)
 299                cmd->convert_arg = 10000;
 300        else
 301                cmd->convert_arg = 5000;
 302
 303        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 304                                           cmd->chanlist_len);
 305
 306        if (cmd->stop_src == TRIG_COUNT)
 307                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 308        else /* TRIG_NONE */
 309                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 310
 311        if (err)
 312                return 3;
 313
 314        /* Step 4: fix up any arguments */
 315
 316        arg = cmd->convert_arg * cmd->scan_end_arg;
 317        err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
 318
 319        if (err)
 320                return 4;
 321
 322        /* Step 5: check channel list if it exists */
 323        if (cmd->chanlist && cmd->chanlist_len > 0)
 324                err |= dmm32at_ai_check_chanlist(dev, s, cmd);
 325
 326        if (err)
 327                return 5;
 328
 329        return 0;
 330}
 331
 332static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
 333{
 334        unsigned char lo1, lo2, hi2;
 335        unsigned short both2;
 336
 337        /* based on 10mhz clock */
 338        lo1 = 200;
 339        both2 = nansec / 20000;
 340        hi2 = (both2 & 0xff00) >> 8;
 341        lo2 = both2 & 0x00ff;
 342
 343        /* set counter clocks to 10MHz, disable all aux dio */
 344        outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
 345
 346        /* get access to the clock regs */
 347        outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
 348
 349        /* write the counter 1 control word and low byte to counter */
 350        outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
 351        outb(lo1, dev->iobase + DMM32AT_CLK1);
 352
 353        /* write the counter 2 control word and low byte then to counter */
 354        outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
 355        outb(lo2, dev->iobase + DMM32AT_CLK2);
 356        outb(hi2, dev->iobase + DMM32AT_CLK2);
 357
 358        /* enable the ai conversion interrupt and the clock to start scans */
 359        outb(DMM32AT_INTCLK_ADINT |
 360             DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
 361             dev->iobase + DMM32AT_INTCLK_REG);
 362}
 363
 364static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 365{
 366        struct comedi_cmd *cmd = &s->async->cmd;
 367        int ret;
 368
 369        dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
 370
 371        /* reset the interrupt just in case */
 372        outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
 373
 374        /*
 375         * wait for circuit to settle
 376         * we don't have the 'insn' here but it's not needed
 377         */
 378        ret = comedi_timeout(dev, s, NULL, dmm32at_ai_status,
 379                             DMM32AT_AI_READBACK_REG);
 380        if (ret)
 381                return ret;
 382
 383        if (cmd->stop_src == TRIG_NONE || cmd->stop_arg > 1) {
 384                /* start the clock and enable the interrupts */
 385                dmm32at_setaitimer(dev, cmd->scan_begin_arg);
 386        } else {
 387                /* start the interrupts and initiate a single scan */
 388                outb(DMM32AT_INTCLK_ADINT, dev->iobase + DMM32AT_INTCLK_REG);
 389                outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
 390        }
 391
 392        return 0;
 393}
 394
 395static int dmm32at_ai_cancel(struct comedi_device *dev,
 396                             struct comedi_subdevice *s)
 397{
 398        /* disable further interrupts and clocks */
 399        outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
 400        return 0;
 401}
 402
 403static irqreturn_t dmm32at_isr(int irq, void *d)
 404{
 405        struct comedi_device *dev = d;
 406        unsigned char intstat;
 407        unsigned int val;
 408        int i;
 409
 410        if (!dev->attached) {
 411                dev_err(dev->class_dev, "spurious interrupt\n");
 412                return IRQ_HANDLED;
 413        }
 414
 415        intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
 416
 417        if (intstat & DMM32AT_INTCLK_ADINT) {
 418                struct comedi_subdevice *s = dev->read_subdev;
 419                struct comedi_cmd *cmd = &s->async->cmd;
 420
 421                for (i = 0; i < cmd->chanlist_len; i++) {
 422                        val = dmm32at_ai_get_sample(dev, s);
 423                        comedi_buf_write_samples(s, &val, 1);
 424                }
 425
 426                if (cmd->stop_src == TRIG_COUNT &&
 427                    s->async->scans_done >= cmd->stop_arg)
 428                        s->async->events |= COMEDI_CB_EOA;
 429
 430                comedi_handle_events(dev, s);
 431        }
 432
 433        /* reset the interrupt */
 434        outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
 435        return IRQ_HANDLED;
 436}
 437
 438static int dmm32at_ao_eoc(struct comedi_device *dev,
 439                          struct comedi_subdevice *s,
 440                          struct comedi_insn *insn,
 441                          unsigned long context)
 442{
 443        unsigned char status;
 444
 445        status = inb(dev->iobase + DMM32AT_AUX_DI_REG);
 446        if ((status & DMM32AT_AUX_DI_DACBUSY) == 0)
 447                return 0;
 448        return -EBUSY;
 449}
 450
 451static int dmm32at_ao_insn_write(struct comedi_device *dev,
 452                                 struct comedi_subdevice *s,
 453                                 struct comedi_insn *insn,
 454                                 unsigned int *data)
 455{
 456        unsigned int chan = CR_CHAN(insn->chanspec);
 457        int i;
 458
 459        for (i = 0; i < insn->n; i++) {
 460                unsigned int val = data[i];
 461                int ret;
 462
 463                /* write LSB then MSB + chan to load DAC */
 464                outb(val & 0xff, dev->iobase + DMM32AT_AO_LSB_REG);
 465                outb((val >> 8) | DMM32AT_AO_MSB_DACH(chan),
 466                     dev->iobase + DMM32AT_AO_MSB_REG);
 467
 468                /* wait for circuit to settle */
 469                ret = comedi_timeout(dev, s, insn, dmm32at_ao_eoc, 0);
 470                if (ret)
 471                        return ret;
 472
 473                /* dummy read to update DAC */
 474                inb(dev->iobase + DMM32AT_AO_MSB_REG);
 475
 476                s->readback[chan] = val;
 477        }
 478
 479        return insn->n;
 480}
 481
 482static int dmm32at_8255_io(struct comedi_device *dev,
 483                           int dir, int port, int data, unsigned long regbase)
 484{
 485        /* get access to the DIO regs */
 486        outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
 487
 488        if (dir) {
 489                outb(data, dev->iobase + regbase + port);
 490                return 0;
 491        }
 492        return inb(dev->iobase + regbase + port);
 493}
 494
 495/* Make sure the board is there and put it to a known state */
 496static int dmm32at_reset(struct comedi_device *dev)
 497{
 498        unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
 499
 500        /* reset the board */
 501        outb(DMM32AT_CTRL_RESETA, dev->iobase + DMM32AT_CTRL_REG);
 502
 503        /* allow a millisecond to reset */
 504        usleep_range(1000, 3000);
 505
 506        /* zero scan and fifo control */
 507        outb(0x0, dev->iobase + DMM32AT_FIFO_CTRL_REG);
 508
 509        /* zero interrupt and clock control */
 510        outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
 511
 512        /* write a test channel range, the high 3 bits should drop */
 513        outb(0x80, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
 514        outb(0xff, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
 515
 516        /* set the range at 10v unipolar */
 517        outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AI_CFG_REG);
 518
 519        /* should take 10 us to settle, here's a hundred */
 520        usleep_range(100, 200);
 521
 522        /* read back the values */
 523        ailo = inb(dev->iobase + DMM32AT_AI_LO_CHAN_REG);
 524        aihi = inb(dev->iobase + DMM32AT_AI_HI_CHAN_REG);
 525        fifostat = inb(dev->iobase + DMM32AT_FIFO_STATUS_REG);
 526        aistat = inb(dev->iobase + DMM32AT_AI_STATUS_REG);
 527        intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
 528        airback = inb(dev->iobase + DMM32AT_AI_READBACK_REG);
 529
 530        /*
 531         * NOTE: The (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0)
 532         * test makes this driver only work if the board is configured
 533         * with all A/D channels set for single-ended operation.
 534         */
 535        if (ailo != 0x00 || aihi != 0x1f ||
 536            fifostat != DMM32AT_FIFO_STATUS_EF ||
 537            aistat != (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0) ||
 538            intstat != 0x00 || airback != 0x0c)
 539                return -EIO;
 540
 541        return 0;
 542}
 543
 544static int dmm32at_attach(struct comedi_device *dev,
 545                          struct comedi_devconfig *it)
 546{
 547        struct comedi_subdevice *s;
 548        int ret;
 549
 550        ret = comedi_request_region(dev, it->options[0], 0x10);
 551        if (ret)
 552                return ret;
 553
 554        ret = dmm32at_reset(dev);
 555        if (ret) {
 556                dev_err(dev->class_dev, "board detection failed\n");
 557                return ret;
 558        }
 559
 560        if (it->options[1]) {
 561                ret = request_irq(it->options[1], dmm32at_isr, 0,
 562                                  dev->board_name, dev);
 563                if (ret == 0)
 564                        dev->irq = it->options[1];
 565        }
 566
 567        ret = comedi_alloc_subdevices(dev, 3);
 568        if (ret)
 569                return ret;
 570
 571        /* Analog Input subdevice */
 572        s = &dev->subdevices[0];
 573        s->type         = COMEDI_SUBD_AI;
 574        s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
 575        s->n_chan       = 32;
 576        s->maxdata      = 0xffff;
 577        s->range_table  = &dmm32at_airanges;
 578        s->insn_read    = dmm32at_ai_insn_read;
 579        if (dev->irq) {
 580                dev->read_subdev = s;
 581                s->subdev_flags |= SDF_CMD_READ;
 582                s->len_chanlist = s->n_chan;
 583                s->do_cmd       = dmm32at_ai_cmd;
 584                s->do_cmdtest   = dmm32at_ai_cmdtest;
 585                s->cancel       = dmm32at_ai_cancel;
 586        }
 587
 588        /* Analog Output subdevice */
 589        s = &dev->subdevices[1];
 590        s->type         = COMEDI_SUBD_AO;
 591        s->subdev_flags = SDF_WRITABLE;
 592        s->n_chan       = 4;
 593        s->maxdata      = 0x0fff;
 594        s->range_table  = &dmm32at_aoranges;
 595        s->insn_write   = dmm32at_ao_insn_write;
 596
 597        ret = comedi_alloc_subdev_readback(s);
 598        if (ret)
 599                return ret;
 600
 601        /* Digital I/O subdevice */
 602        s = &dev->subdevices[2];
 603        return subdev_8255_init(dev, s, dmm32at_8255_io, DMM32AT_8255_IOBASE);
 604}
 605
 606static struct comedi_driver dmm32at_driver = {
 607        .driver_name    = "dmm32at",
 608        .module         = THIS_MODULE,
 609        .attach         = dmm32at_attach,
 610        .detach         = comedi_legacy_detach,
 611};
 612module_comedi_driver(dmm32at_driver);
 613
 614MODULE_AUTHOR("Comedi http://www.comedi.org");
 615MODULE_DESCRIPTION("Comedi: Diamond Systems Diamond-MM-32-AT");
 616MODULE_LICENSE("GPL");
 617