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