linux/drivers/comedi/drivers/das800.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * comedi/drivers/das800.c
   4 * Driver for Keitley das800 series boards and compatibles
   5 * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
   6 *
   7 * COMEDI - Linux Control and Measurement Device Interface
   8 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
   9 */
  10/*
  11 * Driver: das800
  12 * Description: Keithley Metrabyte DAS800 (& compatibles)
  13 * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
  14 * Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
  15 * DAS-802 (das-802),
  16 * [Measurement Computing] CIO-DAS800 (cio-das800),
  17 * CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
  18 * CIO-DAS802/16 (cio-das802/16)
  19 * Status: works, cio-das802/16 untested - email me if you have tested it
  20 *
  21 * Configuration options:
  22 * [0] - I/O port base address
  23 * [1] - IRQ (optional, required for timed or externally triggered conversions)
  24 *
  25 * Notes:
  26 *      IRQ can be omitted, although the cmd interface will not work without it.
  27 *
  28 *      All entries in the channel/gain list must use the same gain and be
  29 *      consecutive channels counting upwards in channel number (these are
  30 *      hardware limitations.)
  31 *
  32 *      I've never tested the gain setting stuff since I only have a
  33 *      DAS-800 board with fixed gain.
  34 *
  35 *      The cio-das802/16 does not have a fifo-empty status bit!  Therefore
  36 *      only fifo-half-full transfers are possible with this card.
  37 *
  38 * cmd triggers supported:
  39 *      start_src:      TRIG_NOW | TRIG_EXT
  40 *      scan_begin_src: TRIG_FOLLOW
  41 *      scan_end_src:   TRIG_COUNT
  42 *      convert_src:    TRIG_TIMER | TRIG_EXT
  43 *      stop_src:       TRIG_NONE | TRIG_COUNT
  44 */
  45
  46#include <linux/module.h>
  47#include <linux/interrupt.h>
  48#include <linux/delay.h>
  49
  50#include "../comedidev.h"
  51
  52#include "comedi_8254.h"
  53
  54#define N_CHAN_AI             8 /*  number of analog input channels */
  55
  56/* Registers for the das800 */
  57
  58#define DAS800_LSB            0
  59#define   FIFO_EMPTY            0x1
  60#define   FIFO_OVF              0x2
  61#define DAS800_MSB            1
  62#define DAS800_CONTROL1       2
  63#define   CONTROL1_INTE         0x8
  64#define DAS800_CONV_CONTROL   2
  65#define   ITE                   0x1
  66#define   CASC                  0x2
  67#define   DTEN                  0x4
  68#define   IEOC                  0x8
  69#define   EACS                  0x10
  70#define   CONV_HCEN             0x80
  71#define DAS800_SCAN_LIMITS    2
  72#define DAS800_STATUS         2
  73#define   IRQ                   0x8
  74#define   BUSY                  0x80
  75#define DAS800_GAIN           3
  76#define   CIO_FFOV              0x8   /* cio-das802/16 fifo overflow */
  77#define   CIO_ENHF              0x90  /* cio-das802/16 fifo half full int ena */
  78#define   CONTROL1              0x80
  79#define   CONV_CONTROL          0xa0
  80#define   SCAN_LIMITS           0xc0
  81#define   ID                    0xe0
  82#define DAS800_8254           4
  83#define DAS800_STATUS2        7
  84#define   STATUS2_HCEN          0x80
  85#define   STATUS2_INTE          0X20
  86#define DAS800_ID             7
  87
  88#define DAS802_16_HALF_FIFO_SZ  128
  89
  90struct das800_board {
  91        const char *name;
  92        int ai_speed;
  93        const struct comedi_lrange *ai_range;
  94        int resolution;
  95};
  96
  97static const struct comedi_lrange range_das801_ai = {
  98        9, {
  99                BIP_RANGE(5),
 100                BIP_RANGE(10),
 101                UNI_RANGE(10),
 102                BIP_RANGE(0.5),
 103                UNI_RANGE(1),
 104                BIP_RANGE(0.05),
 105                UNI_RANGE(0.1),
 106                BIP_RANGE(0.01),
 107                UNI_RANGE(0.02)
 108        }
 109};
 110
 111static const struct comedi_lrange range_cio_das801_ai = {
 112        9, {
 113                BIP_RANGE(5),
 114                BIP_RANGE(10),
 115                UNI_RANGE(10),
 116                BIP_RANGE(0.5),
 117                UNI_RANGE(1),
 118                BIP_RANGE(0.05),
 119                UNI_RANGE(0.1),
 120                BIP_RANGE(0.005),
 121                UNI_RANGE(0.01)
 122        }
 123};
 124
 125static const struct comedi_lrange range_das802_ai = {
 126        9, {
 127                BIP_RANGE(5),
 128                BIP_RANGE(10),
 129                UNI_RANGE(10),
 130                BIP_RANGE(2.5),
 131                UNI_RANGE(5),
 132                BIP_RANGE(1.25),
 133                UNI_RANGE(2.5),
 134                BIP_RANGE(0.625),
 135                UNI_RANGE(1.25)
 136        }
 137};
 138
 139static const struct comedi_lrange range_das80216_ai = {
 140        8, {
 141                BIP_RANGE(10),
 142                UNI_RANGE(10),
 143                BIP_RANGE(5),
 144                UNI_RANGE(5),
 145                BIP_RANGE(2.5),
 146                UNI_RANGE(2.5),
 147                BIP_RANGE(1.25),
 148                UNI_RANGE(1.25)
 149        }
 150};
 151
 152enum das800_boardinfo {
 153        BOARD_DAS800,
 154        BOARD_CIODAS800,
 155        BOARD_DAS801,
 156        BOARD_CIODAS801,
 157        BOARD_DAS802,
 158        BOARD_CIODAS802,
 159        BOARD_CIODAS80216,
 160};
 161
 162static const struct das800_board das800_boards[] = {
 163        [BOARD_DAS800] = {
 164                .name           = "das-800",
 165                .ai_speed       = 25000,
 166                .ai_range       = &range_bipolar5,
 167                .resolution     = 12,
 168        },
 169        [BOARD_CIODAS800] = {
 170                .name           = "cio-das800",
 171                .ai_speed       = 20000,
 172                .ai_range       = &range_bipolar5,
 173                .resolution     = 12,
 174        },
 175        [BOARD_DAS801] = {
 176                .name           = "das-801",
 177                .ai_speed       = 25000,
 178                .ai_range       = &range_das801_ai,
 179                .resolution     = 12,
 180        },
 181        [BOARD_CIODAS801] = {
 182                .name           = "cio-das801",
 183                .ai_speed       = 20000,
 184                .ai_range       = &range_cio_das801_ai,
 185                .resolution     = 12,
 186        },
 187        [BOARD_DAS802] = {
 188                .name           = "das-802",
 189                .ai_speed       = 25000,
 190                .ai_range       = &range_das802_ai,
 191                .resolution     = 12,
 192        },
 193        [BOARD_CIODAS802] = {
 194                .name           = "cio-das802",
 195                .ai_speed       = 20000,
 196                .ai_range       = &range_das802_ai,
 197                .resolution     = 12,
 198        },
 199        [BOARD_CIODAS80216] = {
 200                .name           = "cio-das802/16",
 201                .ai_speed       = 10000,
 202                .ai_range       = &range_das80216_ai,
 203                .resolution     = 16,
 204        },
 205};
 206
 207struct das800_private {
 208        unsigned int do_bits;   /* digital output bits */
 209};
 210
 211static void das800_ind_write(struct comedi_device *dev,
 212                             unsigned int val, unsigned int reg)
 213{
 214        /*
 215         * Select dev->iobase + 2 to be desired register
 216         * then write to that register.
 217         */
 218        outb(reg, dev->iobase + DAS800_GAIN);
 219        outb(val, dev->iobase + 2);
 220}
 221
 222static unsigned int das800_ind_read(struct comedi_device *dev, unsigned int reg)
 223{
 224        /*
 225         * Select dev->iobase + 7 to be desired register
 226         * then read from that register.
 227         */
 228        outb(reg, dev->iobase + DAS800_GAIN);
 229        return inb(dev->iobase + 7);
 230}
 231
 232static void das800_enable(struct comedi_device *dev)
 233{
 234        const struct das800_board *board = dev->board_ptr;
 235        struct das800_private *devpriv = dev->private;
 236        unsigned long irq_flags;
 237
 238        spin_lock_irqsave(&dev->spinlock, irq_flags);
 239        /*  enable fifo-half full interrupts for cio-das802/16 */
 240        if (board->resolution == 16)
 241                outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
 242        /* enable hardware triggering */
 243        das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
 244        /* enable card's interrupt */
 245        das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
 246        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 247}
 248
 249static void das800_disable(struct comedi_device *dev)
 250{
 251        unsigned long irq_flags;
 252
 253        spin_lock_irqsave(&dev->spinlock, irq_flags);
 254        /* disable hardware triggering of conversions */
 255        das800_ind_write(dev, 0x0, CONV_CONTROL);
 256        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 257}
 258
 259static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
 260{
 261        das800_disable(dev);
 262        return 0;
 263}
 264
 265static int das800_ai_check_chanlist(struct comedi_device *dev,
 266                                    struct comedi_subdevice *s,
 267                                    struct comedi_cmd *cmd)
 268{
 269        unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
 270        unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
 271        int i;
 272
 273        for (i = 1; i < cmd->chanlist_len; i++) {
 274                unsigned int chan = CR_CHAN(cmd->chanlist[i]);
 275                unsigned int range = CR_RANGE(cmd->chanlist[i]);
 276
 277                if (chan != (chan0 + i) % s->n_chan) {
 278                        dev_dbg(dev->class_dev,
 279                                "chanlist must be consecutive, counting upwards\n");
 280                        return -EINVAL;
 281                }
 282
 283                if (range != range0) {
 284                        dev_dbg(dev->class_dev,
 285                                "chanlist must all have the same gain\n");
 286                        return -EINVAL;
 287                }
 288        }
 289
 290        return 0;
 291}
 292
 293static int das800_ai_do_cmdtest(struct comedi_device *dev,
 294                                struct comedi_subdevice *s,
 295                                struct comedi_cmd *cmd)
 296{
 297        const struct das800_board *board = dev->board_ptr;
 298        int err = 0;
 299
 300        /* Step 1 : check if triggers are trivially valid */
 301
 302        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
 303        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 304        err |= comedi_check_trigger_src(&cmd->convert_src,
 305                                        TRIG_TIMER | TRIG_EXT);
 306        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 307        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 308
 309        if (err)
 310                return 1;
 311
 312        /* Step 2a : make sure trigger sources are unique */
 313
 314        err |= comedi_check_trigger_is_unique(cmd->start_src);
 315        err |= comedi_check_trigger_is_unique(cmd->convert_src);
 316        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 317
 318        /* Step 2b : and mutually compatible */
 319
 320        if (err)
 321                return 2;
 322
 323        /* Step 3: check if arguments are trivially valid */
 324
 325        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 326
 327        if (cmd->convert_src == TRIG_TIMER) {
 328                err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
 329                                                    board->ai_speed);
 330        }
 331
 332        err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
 333        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 334                                           cmd->chanlist_len);
 335
 336        if (cmd->stop_src == TRIG_COUNT)
 337                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 338        else    /* TRIG_NONE */
 339                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 340
 341        if (err)
 342                return 3;
 343
 344        /* step 4: fix up any arguments */
 345
 346        if (cmd->convert_src == TRIG_TIMER) {
 347                unsigned int arg = cmd->convert_arg;
 348
 349                comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
 350                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 351        }
 352
 353        if (err)
 354                return 4;
 355
 356        /* Step 5: check channel list if it exists */
 357        if (cmd->chanlist && cmd->chanlist_len > 0)
 358                err |= das800_ai_check_chanlist(dev, s, cmd);
 359
 360        if (err)
 361                return 5;
 362
 363        return 0;
 364}
 365
 366static int das800_ai_do_cmd(struct comedi_device *dev,
 367                            struct comedi_subdevice *s)
 368{
 369        const struct das800_board *board = dev->board_ptr;
 370        struct comedi_async *async = s->async;
 371        struct comedi_cmd *cmd = &async->cmd;
 372        unsigned int gain = CR_RANGE(cmd->chanlist[0]);
 373        unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
 374        unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
 375        unsigned int scan_chans = (end_chan << 3) | start_chan;
 376        int conv_bits;
 377        unsigned long irq_flags;
 378
 379        das800_disable(dev);
 380
 381        spin_lock_irqsave(&dev->spinlock, irq_flags);
 382        /* set scan limits */
 383        das800_ind_write(dev, scan_chans, SCAN_LIMITS);
 384        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 385
 386        /* set gain */
 387        if (board->resolution == 12 && gain > 0)
 388                gain += 0x7;
 389        gain &= 0xf;
 390        outb(gain, dev->iobase + DAS800_GAIN);
 391
 392        /* enable auto channel scan, send interrupts on end of conversion
 393         * and set clock source to internal or external
 394         */
 395        conv_bits = 0;
 396        conv_bits |= EACS | IEOC;
 397        if (cmd->start_src == TRIG_EXT)
 398                conv_bits |= DTEN;
 399        if (cmd->convert_src == TRIG_TIMER) {
 400                conv_bits |= CASC | ITE;
 401                comedi_8254_update_divisors(dev->pacer);
 402                comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
 403        }
 404
 405        spin_lock_irqsave(&dev->spinlock, irq_flags);
 406        das800_ind_write(dev, conv_bits, CONV_CONTROL);
 407        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 408
 409        das800_enable(dev);
 410        return 0;
 411}
 412
 413static unsigned int das800_ai_get_sample(struct comedi_device *dev)
 414{
 415        unsigned int lsb = inb(dev->iobase + DAS800_LSB);
 416        unsigned int msb = inb(dev->iobase + DAS800_MSB);
 417
 418        return (msb << 8) | lsb;
 419}
 420
 421static irqreturn_t das800_interrupt(int irq, void *d)
 422{
 423        struct comedi_device *dev = d;
 424        struct das800_private *devpriv = dev->private;
 425        struct comedi_subdevice *s = dev->read_subdev;
 426        struct comedi_async *async;
 427        struct comedi_cmd *cmd;
 428        unsigned long irq_flags;
 429        unsigned int status;
 430        unsigned short val;
 431        bool fifo_empty;
 432        bool fifo_overflow;
 433        int i;
 434
 435        status = inb(dev->iobase + DAS800_STATUS);
 436        if (!(status & IRQ))
 437                return IRQ_NONE;
 438        if (!dev->attached)
 439                return IRQ_HANDLED;
 440
 441        async = s->async;
 442        cmd = &async->cmd;
 443
 444        spin_lock_irqsave(&dev->spinlock, irq_flags);
 445        status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
 446        /*
 447         * Don't release spinlock yet since we want to make sure
 448         * no one else disables hardware conversions.
 449         */
 450
 451        /* if hardware conversions are not enabled, then quit */
 452        if (status == 0) {
 453                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 454                return IRQ_HANDLED;
 455        }
 456
 457        for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
 458                val = das800_ai_get_sample(dev);
 459                if (s->maxdata == 0x0fff) {
 460                        fifo_empty = !!(val & FIFO_EMPTY);
 461                        fifo_overflow = !!(val & FIFO_OVF);
 462                } else {
 463                        /* cio-das802/16 has no fifo empty status bit */
 464                        fifo_empty = false;
 465                        fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
 466                                                CIO_FFOV);
 467                }
 468                if (fifo_empty || fifo_overflow)
 469                        break;
 470
 471                if (s->maxdata == 0x0fff)
 472                        val >>= 4;      /* 12-bit sample */
 473
 474                val &= s->maxdata;
 475                comedi_buf_write_samples(s, &val, 1);
 476
 477                if (cmd->stop_src == TRIG_COUNT &&
 478                    async->scans_done >= cmd->stop_arg) {
 479                        async->events |= COMEDI_CB_EOA;
 480                        break;
 481                }
 482        }
 483
 484        if (fifo_overflow) {
 485                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 486                async->events |= COMEDI_CB_ERROR;
 487                comedi_handle_events(dev, s);
 488                return IRQ_HANDLED;
 489        }
 490
 491        if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
 492                /*
 493                 * Re-enable card's interrupt.
 494                 * We already have spinlock, so indirect addressing is safe
 495                 */
 496                das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
 497                                 CONTROL1);
 498                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 499        } else {
 500                /* otherwise, stop taking data */
 501                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 502                das800_disable(dev);
 503        }
 504        comedi_handle_events(dev, s);
 505        return IRQ_HANDLED;
 506}
 507
 508static int das800_ai_eoc(struct comedi_device *dev,
 509                         struct comedi_subdevice *s,
 510                         struct comedi_insn *insn,
 511                         unsigned long context)
 512{
 513        unsigned int status;
 514
 515        status = inb(dev->iobase + DAS800_STATUS);
 516        if ((status & BUSY) == 0)
 517                return 0;
 518        return -EBUSY;
 519}
 520
 521static int das800_ai_insn_read(struct comedi_device *dev,
 522                               struct comedi_subdevice *s,
 523                               struct comedi_insn *insn,
 524                               unsigned int *data)
 525{
 526        struct das800_private *devpriv = dev->private;
 527        unsigned int chan = CR_CHAN(insn->chanspec);
 528        unsigned int range = CR_RANGE(insn->chanspec);
 529        unsigned long irq_flags;
 530        unsigned int val;
 531        int ret;
 532        int i;
 533
 534        das800_disable(dev);
 535
 536        /* set multiplexer */
 537        spin_lock_irqsave(&dev->spinlock, irq_flags);
 538        das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
 539        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 540
 541        /* set gain / range */
 542        if (s->maxdata == 0x0fff && range)
 543                range += 0x7;
 544        range &= 0xf;
 545        outb(range, dev->iobase + DAS800_GAIN);
 546
 547        udelay(5);
 548
 549        for (i = 0; i < insn->n; i++) {
 550                /* trigger conversion */
 551                outb_p(0, dev->iobase + DAS800_MSB);
 552
 553                ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
 554                if (ret)
 555                        return ret;
 556
 557                val = das800_ai_get_sample(dev);
 558                if (s->maxdata == 0x0fff)
 559                        val >>= 4;      /* 12-bit sample */
 560                data[i] = val & s->maxdata;
 561        }
 562
 563        return insn->n;
 564}
 565
 566static int das800_di_insn_bits(struct comedi_device *dev,
 567                               struct comedi_subdevice *s,
 568                               struct comedi_insn *insn,
 569                               unsigned int *data)
 570{
 571        data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
 572
 573        return insn->n;
 574}
 575
 576static int das800_do_insn_bits(struct comedi_device *dev,
 577                               struct comedi_subdevice *s,
 578                               struct comedi_insn *insn,
 579                               unsigned int *data)
 580{
 581        struct das800_private *devpriv = dev->private;
 582        unsigned long irq_flags;
 583
 584        if (comedi_dio_update_state(s, data)) {
 585                devpriv->do_bits = s->state << 4;
 586
 587                spin_lock_irqsave(&dev->spinlock, irq_flags);
 588                das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
 589                                 CONTROL1);
 590                spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 591        }
 592
 593        data[1] = s->state;
 594
 595        return insn->n;
 596}
 597
 598static const struct das800_board *das800_probe(struct comedi_device *dev)
 599{
 600        const struct das800_board *board = dev->board_ptr;
 601        int index = board ? board - das800_boards : -EINVAL;
 602        int id_bits;
 603        unsigned long irq_flags;
 604
 605        /*
 606         * The dev->board_ptr will be set by comedi_device_attach() if the
 607         * board name provided by the user matches a board->name in this
 608         * driver. If so, this function sanity checks the id_bits to verify
 609         * that the board is correct.
 610         *
 611         * If the dev->board_ptr is not set, the user is trying to attach
 612         * an unspecified board to this driver. In this case the id_bits
 613         * are used to 'probe' for the correct dev->board_ptr.
 614         */
 615        spin_lock_irqsave(&dev->spinlock, irq_flags);
 616        id_bits = das800_ind_read(dev, ID) & 0x3;
 617        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 618
 619        switch (id_bits) {
 620        case 0x0:
 621                if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
 622                        return board;
 623                index = BOARD_DAS800;
 624                break;
 625        case 0x2:
 626                if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
 627                        return board;
 628                index = BOARD_DAS801;
 629                break;
 630        case 0x3:
 631                if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
 632                    index == BOARD_CIODAS80216)
 633                        return board;
 634                index = BOARD_DAS802;
 635                break;
 636        default:
 637                dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
 638                        id_bits);
 639                return NULL;
 640        }
 641        dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
 642                das800_boards[index].name);
 643
 644        return &das800_boards[index];
 645}
 646
 647static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 648{
 649        const struct das800_board *board;
 650        struct das800_private *devpriv;
 651        struct comedi_subdevice *s;
 652        unsigned int irq = it->options[1];
 653        unsigned long irq_flags;
 654        int ret;
 655
 656        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 657        if (!devpriv)
 658                return -ENOMEM;
 659
 660        ret = comedi_request_region(dev, it->options[0], 0x8);
 661        if (ret)
 662                return ret;
 663
 664        board = das800_probe(dev);
 665        if (!board)
 666                return -ENODEV;
 667        dev->board_ptr = board;
 668        dev->board_name = board->name;
 669
 670        if (irq > 1 && irq <= 7) {
 671                ret = request_irq(irq, das800_interrupt, 0, "das800",
 672                                  dev);
 673                if (ret == 0)
 674                        dev->irq = irq;
 675        }
 676
 677        dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
 678                                      I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
 679        if (!dev->pacer)
 680                return -ENOMEM;
 681
 682        ret = comedi_alloc_subdevices(dev, 3);
 683        if (ret)
 684                return ret;
 685
 686        /* Analog Input subdevice */
 687        s = &dev->subdevices[0];
 688        dev->read_subdev = s;
 689        s->type         = COMEDI_SUBD_AI;
 690        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 691        s->n_chan       = 8;
 692        s->maxdata      = (1 << board->resolution) - 1;
 693        s->range_table  = board->ai_range;
 694        s->insn_read    = das800_ai_insn_read;
 695        if (dev->irq) {
 696                s->subdev_flags |= SDF_CMD_READ;
 697                s->len_chanlist = 8;
 698                s->do_cmdtest   = das800_ai_do_cmdtest;
 699                s->do_cmd       = das800_ai_do_cmd;
 700                s->cancel       = das800_cancel;
 701        }
 702
 703        /* Digital Input subdevice */
 704        s = &dev->subdevices[1];
 705        s->type         = COMEDI_SUBD_DI;
 706        s->subdev_flags = SDF_READABLE;
 707        s->n_chan       = 3;
 708        s->maxdata      = 1;
 709        s->range_table  = &range_digital;
 710        s->insn_bits    = das800_di_insn_bits;
 711
 712        /* Digital Output subdevice */
 713        s = &dev->subdevices[2];
 714        s->type         = COMEDI_SUBD_DO;
 715        s->subdev_flags = SDF_WRITABLE;
 716        s->n_chan       = 4;
 717        s->maxdata      = 1;
 718        s->range_table  = &range_digital;
 719        s->insn_bits    = das800_do_insn_bits;
 720
 721        das800_disable(dev);
 722
 723        /* initialize digital out channels */
 724        spin_lock_irqsave(&dev->spinlock, irq_flags);
 725        das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
 726        spin_unlock_irqrestore(&dev->spinlock, irq_flags);
 727
 728        return 0;
 729};
 730
 731static struct comedi_driver driver_das800 = {
 732        .driver_name    = "das800",
 733        .module         = THIS_MODULE,
 734        .attach         = das800_attach,
 735        .detach         = comedi_legacy_detach,
 736        .num_names      = ARRAY_SIZE(das800_boards),
 737        .board_name     = &das800_boards[0].name,
 738        .offset         = sizeof(struct das800_board),
 739};
 740module_comedi_driver(driver_das800);
 741
 742MODULE_AUTHOR("Comedi https://www.comedi.org");
 743MODULE_DESCRIPTION("Comedi low-level driver");
 744MODULE_LICENSE("GPL");
 745