linux/drivers/staging/comedi/drivers/addi_apci_3xxx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * addi_apci_3xxx.c
   4 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
   5 * Project manager: S. Weber
   6 *
   7 *      ADDI-DATA GmbH
   8 *      Dieselstrasse 3
   9 *      D-77833 Ottersweier
  10 *      Tel: +19(0)7223/9493-0
  11 *      Fax: +49(0)7223/9493-92
  12 *      http://www.addi-data.com
  13 *      info@addi-data.com
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/interrupt.h>
  18
  19#include "../comedi_pci.h"
  20
  21#define CONV_UNIT_NS            BIT(0)
  22#define CONV_UNIT_US            BIT(1)
  23#define CONV_UNIT_MS            BIT(2)
  24
  25static const struct comedi_lrange apci3xxx_ai_range = {
  26        8, {
  27                BIP_RANGE(10),
  28                BIP_RANGE(5),
  29                BIP_RANGE(2),
  30                BIP_RANGE(1),
  31                UNI_RANGE(10),
  32                UNI_RANGE(5),
  33                UNI_RANGE(2),
  34                UNI_RANGE(1)
  35        }
  36};
  37
  38static const struct comedi_lrange apci3xxx_ao_range = {
  39        2, {
  40                BIP_RANGE(10),
  41                UNI_RANGE(10)
  42        }
  43};
  44
  45enum apci3xxx_boardid {
  46        BOARD_APCI3000_16,
  47        BOARD_APCI3000_8,
  48        BOARD_APCI3000_4,
  49        BOARD_APCI3006_16,
  50        BOARD_APCI3006_8,
  51        BOARD_APCI3006_4,
  52        BOARD_APCI3010_16,
  53        BOARD_APCI3010_8,
  54        BOARD_APCI3010_4,
  55        BOARD_APCI3016_16,
  56        BOARD_APCI3016_8,
  57        BOARD_APCI3016_4,
  58        BOARD_APCI3100_16_4,
  59        BOARD_APCI3100_8_4,
  60        BOARD_APCI3106_16_4,
  61        BOARD_APCI3106_8_4,
  62        BOARD_APCI3110_16_4,
  63        BOARD_APCI3110_8_4,
  64        BOARD_APCI3116_16_4,
  65        BOARD_APCI3116_8_4,
  66        BOARD_APCI3003,
  67        BOARD_APCI3002_16,
  68        BOARD_APCI3002_8,
  69        BOARD_APCI3002_4,
  70        BOARD_APCI3500,
  71};
  72
  73struct apci3xxx_boardinfo {
  74        const char *name;
  75        int ai_subdev_flags;
  76        int ai_n_chan;
  77        unsigned int ai_maxdata;
  78        unsigned char ai_conv_units;
  79        unsigned int ai_min_acq_ns;
  80        unsigned int has_ao:1;
  81        unsigned int has_dig_in:1;
  82        unsigned int has_dig_out:1;
  83        unsigned int has_ttl_io:1;
  84};
  85
  86static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
  87        [BOARD_APCI3000_16] = {
  88                .name                   = "apci3000-16",
  89                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  90                .ai_n_chan              = 16,
  91                .ai_maxdata             = 0x0fff,
  92                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
  93                .ai_min_acq_ns          = 10000,
  94                .has_ttl_io             = 1,
  95        },
  96        [BOARD_APCI3000_8] = {
  97                .name                   = "apci3000-8",
  98                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
  99                .ai_n_chan              = 8,
 100                .ai_maxdata             = 0x0fff,
 101                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 102                .ai_min_acq_ns          = 10000,
 103                .has_ttl_io             = 1,
 104        },
 105        [BOARD_APCI3000_4] = {
 106                .name                   = "apci3000-4",
 107                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 108                .ai_n_chan              = 4,
 109                .ai_maxdata             = 0x0fff,
 110                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 111                .ai_min_acq_ns          = 10000,
 112                .has_ttl_io             = 1,
 113        },
 114        [BOARD_APCI3006_16] = {
 115                .name                   = "apci3006-16",
 116                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 117                .ai_n_chan              = 16,
 118                .ai_maxdata             = 0xffff,
 119                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 120                .ai_min_acq_ns          = 10000,
 121                .has_ttl_io             = 1,
 122        },
 123        [BOARD_APCI3006_8] = {
 124                .name                   = "apci3006-8",
 125                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 126                .ai_n_chan              = 8,
 127                .ai_maxdata             = 0xffff,
 128                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 129                .ai_min_acq_ns          = 10000,
 130                .has_ttl_io             = 1,
 131        },
 132        [BOARD_APCI3006_4] = {
 133                .name                   = "apci3006-4",
 134                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 135                .ai_n_chan              = 4,
 136                .ai_maxdata             = 0xffff,
 137                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 138                .ai_min_acq_ns          = 10000,
 139                .has_ttl_io             = 1,
 140        },
 141        [BOARD_APCI3010_16] = {
 142                .name                   = "apci3010-16",
 143                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 144                .ai_n_chan              = 16,
 145                .ai_maxdata             = 0x0fff,
 146                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 147                .ai_min_acq_ns          = 5000,
 148                .has_dig_in             = 1,
 149                .has_dig_out            = 1,
 150                .has_ttl_io             = 1,
 151        },
 152        [BOARD_APCI3010_8] = {
 153                .name                   = "apci3010-8",
 154                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 155                .ai_n_chan              = 8,
 156                .ai_maxdata             = 0x0fff,
 157                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 158                .ai_min_acq_ns          = 5000,
 159                .has_dig_in             = 1,
 160                .has_dig_out            = 1,
 161                .has_ttl_io             = 1,
 162        },
 163        [BOARD_APCI3010_4] = {
 164                .name                   = "apci3010-4",
 165                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 166                .ai_n_chan              = 4,
 167                .ai_maxdata             = 0x0fff,
 168                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 169                .ai_min_acq_ns          = 5000,
 170                .has_dig_in             = 1,
 171                .has_dig_out            = 1,
 172                .has_ttl_io             = 1,
 173        },
 174        [BOARD_APCI3016_16] = {
 175                .name                   = "apci3016-16",
 176                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 177                .ai_n_chan              = 16,
 178                .ai_maxdata             = 0xffff,
 179                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 180                .ai_min_acq_ns          = 5000,
 181                .has_dig_in             = 1,
 182                .has_dig_out            = 1,
 183                .has_ttl_io             = 1,
 184        },
 185        [BOARD_APCI3016_8] = {
 186                .name                   = "apci3016-8",
 187                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 188                .ai_n_chan              = 8,
 189                .ai_maxdata             = 0xffff,
 190                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 191                .ai_min_acq_ns          = 5000,
 192                .has_dig_in             = 1,
 193                .has_dig_out            = 1,
 194                .has_ttl_io             = 1,
 195        },
 196        [BOARD_APCI3016_4] = {
 197                .name                   = "apci3016-4",
 198                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 199                .ai_n_chan              = 4,
 200                .ai_maxdata             = 0xffff,
 201                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 202                .ai_min_acq_ns          = 5000,
 203                .has_dig_in             = 1,
 204                .has_dig_out            = 1,
 205                .has_ttl_io             = 1,
 206        },
 207        [BOARD_APCI3100_16_4] = {
 208                .name                   = "apci3100-16-4",
 209                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 210                .ai_n_chan              = 16,
 211                .ai_maxdata             = 0x0fff,
 212                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 213                .ai_min_acq_ns          = 10000,
 214                .has_ao                 = 1,
 215                .has_ttl_io             = 1,
 216        },
 217        [BOARD_APCI3100_8_4] = {
 218                .name                   = "apci3100-8-4",
 219                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 220                .ai_n_chan              = 8,
 221                .ai_maxdata             = 0x0fff,
 222                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 223                .ai_min_acq_ns          = 10000,
 224                .has_ao                 = 1,
 225                .has_ttl_io             = 1,
 226        },
 227        [BOARD_APCI3106_16_4] = {
 228                .name                   = "apci3106-16-4",
 229                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 230                .ai_n_chan              = 16,
 231                .ai_maxdata             = 0xffff,
 232                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 233                .ai_min_acq_ns          = 10000,
 234                .has_ao                 = 1,
 235                .has_ttl_io             = 1,
 236        },
 237        [BOARD_APCI3106_8_4] = {
 238                .name                   = "apci3106-8-4",
 239                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 240                .ai_n_chan              = 8,
 241                .ai_maxdata             = 0xffff,
 242                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 243                .ai_min_acq_ns          = 10000,
 244                .has_ao                 = 1,
 245                .has_ttl_io             = 1,
 246        },
 247        [BOARD_APCI3110_16_4] = {
 248                .name                   = "apci3110-16-4",
 249                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 250                .ai_n_chan              = 16,
 251                .ai_maxdata             = 0x0fff,
 252                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 253                .ai_min_acq_ns          = 5000,
 254                .has_ao                 = 1,
 255                .has_dig_in             = 1,
 256                .has_dig_out            = 1,
 257                .has_ttl_io             = 1,
 258        },
 259        [BOARD_APCI3110_8_4] = {
 260                .name                   = "apci3110-8-4",
 261                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 262                .ai_n_chan              = 8,
 263                .ai_maxdata             = 0x0fff,
 264                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 265                .ai_min_acq_ns          = 5000,
 266                .has_ao                 = 1,
 267                .has_dig_in             = 1,
 268                .has_dig_out            = 1,
 269                .has_ttl_io             = 1,
 270        },
 271        [BOARD_APCI3116_16_4] = {
 272                .name                   = "apci3116-16-4",
 273                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 274                .ai_n_chan              = 16,
 275                .ai_maxdata             = 0xffff,
 276                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 277                .ai_min_acq_ns          = 5000,
 278                .has_ao                 = 1,
 279                .has_dig_in             = 1,
 280                .has_dig_out            = 1,
 281                .has_ttl_io             = 1,
 282        },
 283        [BOARD_APCI3116_8_4] = {
 284                .name                   = "apci3116-8-4",
 285                .ai_subdev_flags        = SDF_COMMON | SDF_GROUND | SDF_DIFF,
 286                .ai_n_chan              = 8,
 287                .ai_maxdata             = 0xffff,
 288                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 289                .ai_min_acq_ns          = 5000,
 290                .has_ao                 = 1,
 291                .has_dig_in             = 1,
 292                .has_dig_out            = 1,
 293                .has_ttl_io             = 1,
 294        },
 295        [BOARD_APCI3003] = {
 296                .name                   = "apci3003",
 297                .ai_subdev_flags        = SDF_DIFF,
 298                .ai_n_chan              = 4,
 299                .ai_maxdata             = 0xffff,
 300                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US |
 301                                          CONV_UNIT_NS,
 302                .ai_min_acq_ns          = 2500,
 303                .has_dig_in             = 1,
 304                .has_dig_out            = 1,
 305        },
 306        [BOARD_APCI3002_16] = {
 307                .name                   = "apci3002-16",
 308                .ai_subdev_flags        = SDF_DIFF,
 309                .ai_n_chan              = 16,
 310                .ai_maxdata             = 0xffff,
 311                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 312                .ai_min_acq_ns          = 5000,
 313                .has_dig_in             = 1,
 314                .has_dig_out            = 1,
 315        },
 316        [BOARD_APCI3002_8] = {
 317                .name                   = "apci3002-8",
 318                .ai_subdev_flags        = SDF_DIFF,
 319                .ai_n_chan              = 8,
 320                .ai_maxdata             = 0xffff,
 321                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 322                .ai_min_acq_ns          = 5000,
 323                .has_dig_in             = 1,
 324                .has_dig_out            = 1,
 325        },
 326        [BOARD_APCI3002_4] = {
 327                .name                   = "apci3002-4",
 328                .ai_subdev_flags        = SDF_DIFF,
 329                .ai_n_chan              = 4,
 330                .ai_maxdata             = 0xffff,
 331                .ai_conv_units          = CONV_UNIT_MS | CONV_UNIT_US,
 332                .ai_min_acq_ns          = 5000,
 333                .has_dig_in             = 1,
 334                .has_dig_out            = 1,
 335        },
 336        [BOARD_APCI3500] = {
 337                .name                   = "apci3500",
 338                .has_ao                 = 1,
 339                .has_ttl_io             = 1,
 340        },
 341};
 342
 343struct apci3xxx_private {
 344        unsigned int ai_timer;
 345        unsigned char ai_time_base;
 346};
 347
 348static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
 349{
 350        struct comedi_device *dev = d;
 351        struct comedi_subdevice *s = dev->read_subdev;
 352        unsigned int status;
 353        unsigned int val;
 354
 355        /* Test if interrupt occur */
 356        status = readl(dev->mmio + 16);
 357        if ((status & 0x2) == 0x2) {
 358                /* Reset the interrupt */
 359                writel(status, dev->mmio + 16);
 360
 361                val = readl(dev->mmio + 28);
 362                comedi_buf_write_samples(s, &val, 1);
 363
 364                s->async->events |= COMEDI_CB_EOA;
 365                comedi_handle_events(dev, s);
 366
 367                return IRQ_HANDLED;
 368        }
 369        return IRQ_NONE;
 370}
 371
 372static int apci3xxx_ai_started(struct comedi_device *dev)
 373{
 374        if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
 375                return 1;
 376
 377        return 0;
 378}
 379
 380static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
 381{
 382        unsigned int chan = CR_CHAN(chanspec);
 383        unsigned int range = CR_RANGE(chanspec);
 384        unsigned int aref = CR_AREF(chanspec);
 385        unsigned int delay_mode;
 386        unsigned int val;
 387
 388        if (apci3xxx_ai_started(dev))
 389                return -EBUSY;
 390
 391        /* Clear the FIFO */
 392        writel(0x10000, dev->mmio + 12);
 393
 394        /* Get and save the delay mode */
 395        delay_mode = readl(dev->mmio + 4);
 396        delay_mode &= 0xfffffef0;
 397
 398        /* Channel configuration selection */
 399        writel(delay_mode, dev->mmio + 4);
 400
 401        /* Make the configuration */
 402        val = (range & 3) | ((range >> 2) << 6) |
 403              ((aref == AREF_DIFF) << 7);
 404        writel(val, dev->mmio + 0);
 405
 406        /* Channel selection */
 407        writel(delay_mode | 0x100, dev->mmio + 4);
 408        writel(chan, dev->mmio + 0);
 409
 410        /* Restore delay mode */
 411        writel(delay_mode, dev->mmio + 4);
 412
 413        /* Set the number of sequence to 1 */
 414        writel(1, dev->mmio + 48);
 415
 416        return 0;
 417}
 418
 419static int apci3xxx_ai_eoc(struct comedi_device *dev,
 420                           struct comedi_subdevice *s,
 421                           struct comedi_insn *insn,
 422                           unsigned long context)
 423{
 424        unsigned int status;
 425
 426        status = readl(dev->mmio + 20);
 427        if (status & 0x1)
 428                return 0;
 429        return -EBUSY;
 430}
 431
 432static int apci3xxx_ai_insn_read(struct comedi_device *dev,
 433                                 struct comedi_subdevice *s,
 434                                 struct comedi_insn *insn,
 435                                 unsigned int *data)
 436{
 437        int ret;
 438        int i;
 439
 440        ret = apci3xxx_ai_setup(dev, insn->chanspec);
 441        if (ret)
 442                return ret;
 443
 444        for (i = 0; i < insn->n; i++) {
 445                /* Start the conversion */
 446                writel(0x80000, dev->mmio + 8);
 447
 448                /* Wait the EOS */
 449                ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
 450                if (ret)
 451                        return ret;
 452
 453                /* Read the analog value */
 454                data[i] = readl(dev->mmio + 28);
 455        }
 456
 457        return insn->n;
 458}
 459
 460static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
 461                                   unsigned int *ns, unsigned int flags)
 462{
 463        const struct apci3xxx_boardinfo *board = dev->board_ptr;
 464        struct apci3xxx_private *devpriv = dev->private;
 465        unsigned int base;
 466        unsigned int timer;
 467        int time_base;
 468
 469        /* time_base: 0 = ns, 1 = us, 2 = ms */
 470        for (time_base = 0; time_base < 3; time_base++) {
 471                /* skip unsupported time bases */
 472                if (!(board->ai_conv_units & (1 << time_base)))
 473                        continue;
 474
 475                switch (time_base) {
 476                case 0:
 477                        base = 1;
 478                        break;
 479                case 1:
 480                        base = 1000;
 481                        break;
 482                case 2:
 483                        base = 1000000;
 484                        break;
 485                }
 486
 487                switch (flags & CMDF_ROUND_MASK) {
 488                case CMDF_ROUND_NEAREST:
 489                default:
 490                        timer = DIV_ROUND_CLOSEST(*ns, base);
 491                        break;
 492                case CMDF_ROUND_DOWN:
 493                        timer = *ns / base;
 494                        break;
 495                case CMDF_ROUND_UP:
 496                        timer = DIV_ROUND_UP(*ns, base);
 497                        break;
 498                }
 499
 500                if (timer < 0x10000) {
 501                        devpriv->ai_time_base = time_base;
 502                        devpriv->ai_timer = timer;
 503                        *ns = timer * time_base;
 504                        return 0;
 505                }
 506        }
 507        return -EINVAL;
 508}
 509
 510static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
 511                               struct comedi_subdevice *s,
 512                               struct comedi_cmd *cmd)
 513{
 514        const struct apci3xxx_boardinfo *board = dev->board_ptr;
 515        int err = 0;
 516        unsigned int arg;
 517
 518        /* Step 1 : check if triggers are trivially valid */
 519
 520        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 521        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 522        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
 523        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 524        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 525
 526        if (err)
 527                return 1;
 528
 529        /* Step 2a : make sure trigger sources are unique */
 530
 531        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 532
 533        /* Step 2b : and mutually compatible */
 534
 535        if (err)
 536                return 2;
 537
 538        /* Step 3: check if arguments are trivially valid */
 539
 540        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 541        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 542        err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
 543                                            board->ai_min_acq_ns);
 544        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 545                                           cmd->chanlist_len);
 546
 547        if (cmd->stop_src == TRIG_COUNT)
 548                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 549        else    /* TRIG_NONE */
 550                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 551
 552        if (err)
 553                return 3;
 554
 555        /* step 4: fix up any arguments */
 556
 557        arg = cmd->convert_arg;
 558        err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
 559        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 560
 561        if (err)
 562                return 4;
 563
 564        return 0;
 565}
 566
 567static int apci3xxx_ai_cmd(struct comedi_device *dev,
 568                           struct comedi_subdevice *s)
 569{
 570        struct apci3xxx_private *devpriv = dev->private;
 571        struct comedi_cmd *cmd = &s->async->cmd;
 572        int ret;
 573
 574        ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
 575        if (ret)
 576                return ret;
 577
 578        /* Set the convert timing unit */
 579        writel(devpriv->ai_time_base, dev->mmio + 36);
 580
 581        /* Set the convert timing */
 582        writel(devpriv->ai_timer, dev->mmio + 32);
 583
 584        /* Start the conversion */
 585        writel(0x180000, dev->mmio + 8);
 586
 587        return 0;
 588}
 589
 590static int apci3xxx_ai_cancel(struct comedi_device *dev,
 591                              struct comedi_subdevice *s)
 592{
 593        return 0;
 594}
 595
 596static int apci3xxx_ao_eoc(struct comedi_device *dev,
 597                           struct comedi_subdevice *s,
 598                           struct comedi_insn *insn,
 599                           unsigned long context)
 600{
 601        unsigned int status;
 602
 603        status = readl(dev->mmio + 96);
 604        if (status & 0x100)
 605                return 0;
 606        return -EBUSY;
 607}
 608
 609static int apci3xxx_ao_insn_write(struct comedi_device *dev,
 610                                  struct comedi_subdevice *s,
 611                                  struct comedi_insn *insn,
 612                                  unsigned int *data)
 613{
 614        unsigned int chan = CR_CHAN(insn->chanspec);
 615        unsigned int range = CR_RANGE(insn->chanspec);
 616        int ret;
 617        int i;
 618
 619        for (i = 0; i < insn->n; i++) {
 620                unsigned int val = data[i];
 621
 622                /* Set the range selection */
 623                writel(range, dev->mmio + 96);
 624
 625                /* Write the analog value to the selected channel */
 626                writel((val << 8) | chan, dev->mmio + 100);
 627
 628                /* Wait the end of transfer */
 629                ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
 630                if (ret)
 631                        return ret;
 632
 633                s->readback[chan] = val;
 634        }
 635
 636        return insn->n;
 637}
 638
 639static int apci3xxx_di_insn_bits(struct comedi_device *dev,
 640                                 struct comedi_subdevice *s,
 641                                 struct comedi_insn *insn,
 642                                 unsigned int *data)
 643{
 644        data[1] = inl(dev->iobase + 32) & 0xf;
 645
 646        return insn->n;
 647}
 648
 649static int apci3xxx_do_insn_bits(struct comedi_device *dev,
 650                                 struct comedi_subdevice *s,
 651                                 struct comedi_insn *insn,
 652                                 unsigned int *data)
 653{
 654        s->state = inl(dev->iobase + 48) & 0xf;
 655
 656        if (comedi_dio_update_state(s, data))
 657                outl(s->state, dev->iobase + 48);
 658
 659        data[1] = s->state;
 660
 661        return insn->n;
 662}
 663
 664static int apci3xxx_dio_insn_config(struct comedi_device *dev,
 665                                    struct comedi_subdevice *s,
 666                                    struct comedi_insn *insn,
 667                                    unsigned int *data)
 668{
 669        unsigned int chan = CR_CHAN(insn->chanspec);
 670        unsigned int mask = 0;
 671        int ret;
 672
 673        /*
 674         * Port 0 (channels 0-7) are always inputs
 675         * Port 1 (channels 8-15) are always outputs
 676         * Port 2 (channels 16-23) are programmable i/o
 677         */
 678        if (data[0] != INSN_CONFIG_DIO_QUERY) {
 679                /* ignore all other instructions for ports 0 and 1 */
 680                if (chan < 16)
 681                        return -EINVAL;
 682
 683                /* changing any channel in port 2 changes the entire port */
 684                mask = 0xff0000;
 685        }
 686
 687        ret = comedi_dio_insn_config(dev, s, insn, data, mask);
 688        if (ret)
 689                return ret;
 690
 691        /* update port 2 configuration */
 692        outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
 693
 694        return insn->n;
 695}
 696
 697static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
 698                                  struct comedi_subdevice *s,
 699                                  struct comedi_insn *insn,
 700                                  unsigned int *data)
 701{
 702        unsigned int mask;
 703        unsigned int val;
 704
 705        mask = comedi_dio_update_state(s, data);
 706        if (mask) {
 707                if (mask & 0xff)
 708                        outl(s->state & 0xff, dev->iobase + 80);
 709                if (mask & 0xff0000)
 710                        outl((s->state >> 16) & 0xff, dev->iobase + 112);
 711        }
 712
 713        val = inl(dev->iobase + 80);
 714        val |= (inl(dev->iobase + 64) << 8);
 715        if (s->io_bits & 0xff0000)
 716                val |= (inl(dev->iobase + 112) << 16);
 717        else
 718                val |= (inl(dev->iobase + 96) << 16);
 719
 720        data[1] = val;
 721
 722        return insn->n;
 723}
 724
 725static int apci3xxx_reset(struct comedi_device *dev)
 726{
 727        unsigned int val;
 728        int i;
 729
 730        /* Disable the interrupt */
 731        disable_irq(dev->irq);
 732
 733        /* Clear the start command */
 734        writel(0, dev->mmio + 8);
 735
 736        /* Reset the interrupt flags */
 737        val = readl(dev->mmio + 16);
 738        writel(val, dev->mmio + 16);
 739
 740        /* clear the EOS */
 741        readl(dev->mmio + 20);
 742
 743        /* Clear the FIFO */
 744        for (i = 0; i < 16; i++)
 745                val = readl(dev->mmio + 28);
 746
 747        /* Enable the interrupt */
 748        enable_irq(dev->irq);
 749
 750        return 0;
 751}
 752
 753static int apci3xxx_auto_attach(struct comedi_device *dev,
 754                                unsigned long context)
 755{
 756        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 757        const struct apci3xxx_boardinfo *board = NULL;
 758        struct apci3xxx_private *devpriv;
 759        struct comedi_subdevice *s;
 760        int n_subdevices;
 761        int subdev;
 762        int ret;
 763
 764        if (context < ARRAY_SIZE(apci3xxx_boardtypes))
 765                board = &apci3xxx_boardtypes[context];
 766        if (!board)
 767                return -ENODEV;
 768        dev->board_ptr = board;
 769        dev->board_name = board->name;
 770
 771        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 772        if (!devpriv)
 773                return -ENOMEM;
 774
 775        ret = comedi_pci_enable(dev);
 776        if (ret)
 777                return ret;
 778
 779        dev->iobase = pci_resource_start(pcidev, 2);
 780        dev->mmio = pci_ioremap_bar(pcidev, 3);
 781        if (!dev->mmio)
 782                return -ENOMEM;
 783
 784        if (pcidev->irq > 0) {
 785                ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
 786                                  IRQF_SHARED, dev->board_name, dev);
 787                if (ret == 0)
 788                        dev->irq = pcidev->irq;
 789        }
 790
 791        n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
 792                       board->has_dig_in + board->has_dig_out +
 793                       board->has_ttl_io;
 794        ret = comedi_alloc_subdevices(dev, n_subdevices);
 795        if (ret)
 796                return ret;
 797
 798        subdev = 0;
 799
 800        /* Analog Input subdevice */
 801        if (board->ai_n_chan) {
 802                s = &dev->subdevices[subdev];
 803                s->type         = COMEDI_SUBD_AI;
 804                s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
 805                s->n_chan       = board->ai_n_chan;
 806                s->maxdata      = board->ai_maxdata;
 807                s->range_table  = &apci3xxx_ai_range;
 808                s->insn_read    = apci3xxx_ai_insn_read;
 809                if (dev->irq) {
 810                        /*
 811                         * FIXME: The hardware supports multiple scan modes
 812                         * but the original addi-data driver only supported
 813                         * reading a single channel with interrupts. Need a
 814                         * proper datasheet to fix this.
 815                         *
 816                         * The following scan modes are supported by the
 817                         * hardware:
 818                         *   1) Single software scan
 819                         *   2) Single hardware triggered scan
 820                         *   3) Continuous software scan
 821                         *   4) Continuous software scan with timer delay
 822                         *   5) Continuous hardware triggered scan
 823                         *   6) Continuous hardware triggered scan with timer
 824                         *      delay
 825                         *
 826                         * For now, limit the chanlist to a single channel.
 827                         */
 828                        dev->read_subdev = s;
 829                        s->subdev_flags |= SDF_CMD_READ;
 830                        s->len_chanlist = 1;
 831                        s->do_cmdtest   = apci3xxx_ai_cmdtest;
 832                        s->do_cmd       = apci3xxx_ai_cmd;
 833                        s->cancel       = apci3xxx_ai_cancel;
 834                }
 835
 836                subdev++;
 837        }
 838
 839        /* Analog Output subdevice */
 840        if (board->has_ao) {
 841                s = &dev->subdevices[subdev];
 842                s->type         = COMEDI_SUBD_AO;
 843                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
 844                s->n_chan       = 4;
 845                s->maxdata      = 0x0fff;
 846                s->range_table  = &apci3xxx_ao_range;
 847                s->insn_write   = apci3xxx_ao_insn_write;
 848
 849                ret = comedi_alloc_subdev_readback(s);
 850                if (ret)
 851                        return ret;
 852
 853                subdev++;
 854        }
 855
 856        /* Digital Input subdevice */
 857        if (board->has_dig_in) {
 858                s = &dev->subdevices[subdev];
 859                s->type         = COMEDI_SUBD_DI;
 860                s->subdev_flags = SDF_READABLE;
 861                s->n_chan       = 4;
 862                s->maxdata      = 1;
 863                s->range_table  = &range_digital;
 864                s->insn_bits    = apci3xxx_di_insn_bits;
 865
 866                subdev++;
 867        }
 868
 869        /* Digital Output subdevice */
 870        if (board->has_dig_out) {
 871                s = &dev->subdevices[subdev];
 872                s->type         = COMEDI_SUBD_DO;
 873                s->subdev_flags = SDF_WRITABLE;
 874                s->n_chan       = 4;
 875                s->maxdata      = 1;
 876                s->range_table  = &range_digital;
 877                s->insn_bits    = apci3xxx_do_insn_bits;
 878
 879                subdev++;
 880        }
 881
 882        /* TTL Digital I/O subdevice */
 883        if (board->has_ttl_io) {
 884                s = &dev->subdevices[subdev];
 885                s->type         = COMEDI_SUBD_DIO;
 886                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 887                s->n_chan       = 24;
 888                s->maxdata      = 1;
 889                s->io_bits      = 0xff; /* channels 0-7 are always outputs */
 890                s->range_table  = &range_digital;
 891                s->insn_config  = apci3xxx_dio_insn_config;
 892                s->insn_bits    = apci3xxx_dio_insn_bits;
 893
 894                subdev++;
 895        }
 896
 897        apci3xxx_reset(dev);
 898        return 0;
 899}
 900
 901static void apci3xxx_detach(struct comedi_device *dev)
 902{
 903        if (dev->iobase)
 904                apci3xxx_reset(dev);
 905        comedi_pci_detach(dev);
 906}
 907
 908static struct comedi_driver apci3xxx_driver = {
 909        .driver_name    = "addi_apci_3xxx",
 910        .module         = THIS_MODULE,
 911        .auto_attach    = apci3xxx_auto_attach,
 912        .detach         = apci3xxx_detach,
 913};
 914
 915static int apci3xxx_pci_probe(struct pci_dev *dev,
 916                              const struct pci_device_id *id)
 917{
 918        return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
 919}
 920
 921static const struct pci_device_id apci3xxx_pci_table[] = {
 922        { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
 923        { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
 924        { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
 925        { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
 926        { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
 927        { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
 928        { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
 929        { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
 930        { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
 931        { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
 932        { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
 933        { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
 934        { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
 935        { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
 936        { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
 937        { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
 938        { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
 939        { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
 940        { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
 941        { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
 942        { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
 943        { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
 944        { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
 945        { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
 946        { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
 947        { 0 }
 948};
 949MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
 950
 951static struct pci_driver apci3xxx_pci_driver = {
 952        .name           = "addi_apci_3xxx",
 953        .id_table       = apci3xxx_pci_table,
 954        .probe          = apci3xxx_pci_probe,
 955        .remove         = comedi_pci_auto_unconfig,
 956};
 957module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
 958
 959MODULE_AUTHOR("Comedi http://www.comedi.org");
 960MODULE_DESCRIPTION("Comedi low-level driver");
 961MODULE_LICENSE("GPL");
 962