root/drivers/staging/comedi/drivers/addi_apci_3xxx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. apci3xxx_irq_handler
  2. apci3xxx_ai_started
  3. apci3xxx_ai_setup
  4. apci3xxx_ai_eoc
  5. apci3xxx_ai_insn_read
  6. apci3xxx_ai_ns_to_timer
  7. apci3xxx_ai_cmdtest
  8. apci3xxx_ai_cmd
  9. apci3xxx_ai_cancel
  10. apci3xxx_ao_eoc
  11. apci3xxx_ao_insn_write
  12. apci3xxx_di_insn_bits
  13. apci3xxx_do_insn_bits
  14. apci3xxx_dio_insn_config
  15. apci3xxx_dio_insn_bits
  16. apci3xxx_reset
  17. apci3xxx_auto_attach
  18. apci3xxx_detach
  19. apci3xxx_pci_probe

   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 
  25 static 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 
  38 static const struct comedi_lrange apci3xxx_ao_range = {
  39         2, {
  40                 BIP_RANGE(10),
  41                 UNI_RANGE(10)
  42         }
  43 };
  44 
  45 enum 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 
  73 struct 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 
  86 static 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 
 343 struct apci3xxx_private {
 344         unsigned int ai_timer;
 345         unsigned char ai_time_base;
 346 };
 347 
 348 static 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 
 372 static 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 
 380 static 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 
 419 static 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 
 432 static 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 
 460 static 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 
 510 static 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 
 567 static 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 
 590 static int apci3xxx_ai_cancel(struct comedi_device *dev,
 591                               struct comedi_subdevice *s)
 592 {
 593         return 0;
 594 }
 595 
 596 static 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 
 609 static 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 
 639 static 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 
 649 static 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 
 664 static 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 
 697 static 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 
 725 static 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 
 753 static 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 
 901 static void apci3xxx_detach(struct comedi_device *dev)
 902 {
 903         if (dev->iobase)
 904                 apci3xxx_reset(dev);
 905         comedi_pci_detach(dev);
 906 }
 907 
 908 static 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 
 915 static 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 
 921 static 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 };
 949 MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
 950 
 951 static 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 };
 957 module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
 958 
 959 MODULE_AUTHOR("Comedi http://www.comedi.org");
 960 MODULE_DESCRIPTION("Comedi low-level driver");
 961 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */