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