linux/drivers/staging/comedi/drivers/pcl812.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/pcl812.c
   3 *
   4 * Author: Michal Dobes <dobes@tesnet.cz>
   5 *
   6 * hardware driver for Advantech cards
   7 *  card:   PCL-812, PCL-812PG, PCL-813, PCL-813B
   8 *  driver: pcl812,  pcl812pg,  pcl813,  pcl813b
   9 * and for ADlink cards
  10 *  card:   ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216
  11 *  driver: acl8112dg,  acl8112hg,  acl8112pg,  acl8113,  acl8216
  12 * and for ICP DAS cards
  13 *  card:   ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL,
  14 *  driver: iso813,  a821pgh,  a-821pgl, a-821pglnda,  a822pgh,  a822pgl,
  15 *  card:   A-823PGH, A-823PGL, A-826PG
  16 * driver:  a823pgh,  a823pgl,  a826pg
  17 */
  18
  19/*
  20 * Driver: pcl812
  21 * Description: Advantech PCL-812/PG, PCL-813/B,
  22 *           ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216,
  23 *           ICP DAS A-821PGH/PGL/PGL-NDA, A-822PGH/PGL, A-823PGH/PGL, A-826PG,
  24 *           ICP DAS ISO-813
  25 * Author: Michal Dobes <dobes@tesnet.cz>
  26 * Devices: [Advantech] PCL-812 (pcl812), PCL-812PG (pcl812pg),
  27 *      PCL-813 (pcl813), PCL-813B (pcl813b), [ADLink] ACL-8112DG (acl8112dg),
  28 *      ACL-8112HG (acl8112hg), ACL-8113 (acl-8113), ACL-8216 (acl8216),
  29 *      [ICP] ISO-813 (iso813), A-821PGH (a821pgh), A-821PGL (a821pgl),
  30 *      A-821PGL-NDA (a821pclnda), A-822PGH (a822pgh), A-822PGL (a822pgl),
  31 *      A-823PGH (a823pgh), A-823PGL (a823pgl), A-826PG (a826pg)
  32 * Updated: Mon, 06 Aug 2007 12:03:15 +0100
  33 * Status: works (I hope. My board fire up under my hands
  34 *             and I cann't test all features.)
  35 *
  36 * This driver supports insn and cmd interfaces. Some boards support only insn
  37 * because their hardware don't allow more (PCL-813/B, ACL-8113, ISO-813).
  38 * Data transfer over DMA is supported only when you measure only one
  39 * channel, this is too hardware limitation of these boards.
  40 *
  41 * Options for PCL-812:
  42 *   [0] - IO Base
  43 *   [1] - IRQ  (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
  44 *   [2] - DMA  (0=disable, 1, 3)
  45 *   [3] - 0=trigger source is internal 8253 with 2MHz clock
  46 *         1=trigger source is external
  47 *   [4] - 0=A/D input range is +/-10V
  48 *         1=A/D input range is +/-5V
  49 *         2=A/D input range is +/-2.5V
  50 *         3=A/D input range is +/-1.25V
  51 *         4=A/D input range is +/-0.625V
  52 *         5=A/D input range is +/-0.3125V
  53 *   [5] - 0=D/A outputs 0-5V  (internal reference -5V)
  54 *         1=D/A outputs 0-10V (internal reference -10V)
  55 *         2=D/A outputs unknown (external reference)
  56 *
  57 * Options for PCL-812PG, ACL-8112PG:
  58 *   [0] - IO Base
  59 *   [1] - IRQ  (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
  60 *   [2] - DMA  (0=disable, 1, 3)
  61 *   [3] - 0=trigger source is internal 8253 with 2MHz clock
  62 *         1=trigger source is external
  63 *   [4] - 0=A/D have max +/-5V input
  64 *         1=A/D have max +/-10V input
  65 *   [5] - 0=D/A outputs 0-5V  (internal reference -5V)
  66 *         1=D/A outputs 0-10V (internal reference -10V)
  67 *         2=D/A outputs unknown (external reference)
  68 *
  69 * Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG:
  70 *   [0] - IO Base
  71 *   [1] - IRQ  (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
  72 *   [2] - DMA  (0=disable, 1, 3)
  73 *   [3] - 0=trigger source is internal 8253 with 2MHz clock
  74 *         1=trigger source is external
  75 *   [4] - 0=A/D channels are S.E.
  76 *         1=A/D channels are DIFF
  77 *   [5] - 0=D/A outputs 0-5V  (internal reference -5V)
  78 *         1=D/A outputs 0-10V (internal reference -10V)
  79 *         2=D/A outputs unknown (external reference)
  80 *
  81 * Options for A-821PGL/PGH:
  82 *   [0] - IO Base
  83 *   [1] - IRQ  (0=disable, 2, 3, 4, 5, 6, 7)
  84 *   [2] - 0=A/D channels are S.E.
  85 *         1=A/D channels are DIFF
  86 *   [3] - 0=D/A output 0-5V  (internal reference -5V)
  87 *         1=D/A output 0-10V (internal reference -10V)
  88 *
  89 * Options for A-821PGL-NDA:
  90 *   [0] - IO Base
  91 *   [1] - IRQ  (0=disable, 2, 3, 4, 5, 6, 7)
  92 *   [2] - 0=A/D channels are S.E.
  93 *         1=A/D channels are DIFF
  94 *
  95 * Options for PCL-813:
  96 *   [0] - IO Base
  97 *
  98 * Options for PCL-813B:
  99 *   [0] - IO Base
 100 *   [1] - 0= bipolar inputs
 101 *         1= unipolar inputs
 102 *
 103 * Options for ACL-8113, ISO-813:
 104 *   [0] - IO Base
 105 *   [1] - 0= 10V bipolar inputs
 106 *         1= 10V unipolar inputs
 107 *         2= 20V bipolar inputs
 108 *         3= 20V unipolar inputs
 109 */
 110
 111#include <linux/module.h>
 112#include <linux/interrupt.h>
 113#include <linux/gfp.h>
 114#include <linux/delay.h>
 115#include <linux/io.h>
 116
 117#include "../comedidev.h"
 118
 119#include "comedi_isadma.h"
 120#include "comedi_8254.h"
 121
 122/* hardware types of the cards */
 123#define boardPCL812PG         0 /* and ACL-8112PG */
 124#define boardPCL813B          1
 125#define boardPCL812           2
 126#define boardPCL813           3
 127#define boardISO813           5
 128#define boardACL8113          6
 129#define boardACL8112          7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */
 130#define boardACL8216          8 /* and ICP DAS A-826PG */
 131#define boardA821             9 /* PGH, PGL, PGL/NDA versions */
 132
 133/*
 134 * Register I/O map
 135 */
 136#define PCL812_TIMER_BASE                       0x00
 137#define PCL812_AI_LSB_REG                       0x04
 138#define PCL812_AI_MSB_REG                       0x05
 139#define PCL812_AI_MSB_DRDY                      (1 << 4)
 140#define PCL812_AO_LSB_REG(x)                    (0x04 + ((x) * 2))
 141#define PCL812_AO_MSB_REG(x)                    (0x05 + ((x) * 2))
 142#define PCL812_DI_LSB_REG                       0x06
 143#define PCL812_DI_MSB_REG                       0x07
 144#define PCL812_STATUS_REG                       0x08
 145#define PCL812_STATUS_DRDY                      (1 << 5)
 146#define PCL812_RANGE_REG                        0x09
 147#define PCL812_MUX_REG                          0x0a
 148#define PCL812_MUX_CHAN(x)                      ((x) << 0)
 149#define PCL812_MUX_CS0                          (1 << 4)
 150#define PCL812_MUX_CS1                          (1 << 5)
 151#define PCL812_CTRL_REG                         0x0b
 152#define PCL812_CTRL_DISABLE_TRIG                (0 << 0)
 153#define PCL812_CTRL_SOFT_TRIG                   (1 << 0)
 154#define PCL812_CTRL_PACER_DMA_TRIG              (2 << 0)
 155#define PCL812_CTRL_PACER_EOC_TRIG              (6 << 0)
 156#define PCL812_SOFTTRIG_REG                     0x0c
 157#define PCL812_DO_LSB_REG                       0x0d
 158#define PCL812_DO_MSB_REG                       0x0e
 159
 160#define MAX_CHANLIST_LEN    256 /* length of scan list */
 161
 162static const struct comedi_lrange range_pcl812pg_ai = {
 163        5, {
 164                BIP_RANGE(5),
 165                BIP_RANGE(2.5),
 166                BIP_RANGE(1.25),
 167                BIP_RANGE(0.625),
 168                BIP_RANGE(0.3125)
 169        }
 170};
 171
 172static const struct comedi_lrange range_pcl812pg2_ai = {
 173        5, {
 174                BIP_RANGE(10),
 175                BIP_RANGE(5),
 176                BIP_RANGE(2.5),
 177                BIP_RANGE(1.25),
 178                BIP_RANGE(0.625)
 179        }
 180};
 181
 182static const struct comedi_lrange range812_bipolar1_25 = {
 183        1, {
 184                BIP_RANGE(1.25)
 185        }
 186};
 187
 188static const struct comedi_lrange range812_bipolar0_625 = {
 189        1, {
 190                BIP_RANGE(0.625)
 191        }
 192};
 193
 194static const struct comedi_lrange range812_bipolar0_3125 = {
 195        1, {
 196                BIP_RANGE(0.3125)
 197        }
 198};
 199
 200static const struct comedi_lrange range_pcl813b_ai = {
 201        4, {
 202                BIP_RANGE(5),
 203                BIP_RANGE(2.5),
 204                BIP_RANGE(1.25),
 205                BIP_RANGE(0.625)
 206        }
 207};
 208
 209static const struct comedi_lrange range_pcl813b2_ai = {
 210        4, {
 211                UNI_RANGE(10),
 212                UNI_RANGE(5),
 213                UNI_RANGE(2.5),
 214                UNI_RANGE(1.25)
 215        }
 216};
 217
 218static const struct comedi_lrange range_iso813_1_ai = {
 219        5, {
 220                BIP_RANGE(5),
 221                BIP_RANGE(2.5),
 222                BIP_RANGE(1.25),
 223                BIP_RANGE(0.625),
 224                BIP_RANGE(0.3125)
 225        }
 226};
 227
 228static const struct comedi_lrange range_iso813_1_2_ai = {
 229        5, {
 230                UNI_RANGE(10),
 231                UNI_RANGE(5),
 232                UNI_RANGE(2.5),
 233                UNI_RANGE(1.25),
 234                UNI_RANGE(0.625)
 235        }
 236};
 237
 238static const struct comedi_lrange range_iso813_2_ai = {
 239        4, {
 240                BIP_RANGE(5),
 241                BIP_RANGE(2.5),
 242                BIP_RANGE(1.25),
 243                BIP_RANGE(0.625)
 244        }
 245};
 246
 247static const struct comedi_lrange range_iso813_2_2_ai = {
 248        4, {
 249                UNI_RANGE(10),
 250                UNI_RANGE(5),
 251                UNI_RANGE(2.5),
 252                UNI_RANGE(1.25)
 253        }
 254};
 255
 256static const struct comedi_lrange range_acl8113_1_ai = {
 257        4, {
 258                BIP_RANGE(5),
 259                BIP_RANGE(2.5),
 260                BIP_RANGE(1.25),
 261                BIP_RANGE(0.625)
 262        }
 263};
 264
 265static const struct comedi_lrange range_acl8113_1_2_ai = {
 266        4, {
 267                UNI_RANGE(10),
 268                UNI_RANGE(5),
 269                UNI_RANGE(2.5),
 270                UNI_RANGE(1.25)
 271        }
 272};
 273
 274static const struct comedi_lrange range_acl8113_2_ai = {
 275        3, {
 276                BIP_RANGE(5),
 277                BIP_RANGE(2.5),
 278                BIP_RANGE(1.25)
 279        }
 280};
 281
 282static const struct comedi_lrange range_acl8113_2_2_ai = {
 283        3, {
 284                UNI_RANGE(10),
 285                UNI_RANGE(5),
 286                UNI_RANGE(2.5)
 287        }
 288};
 289
 290static const struct comedi_lrange range_acl8112dg_ai = {
 291        9, {
 292                BIP_RANGE(5),
 293                BIP_RANGE(2.5),
 294                BIP_RANGE(1.25),
 295                BIP_RANGE(0.625),
 296                UNI_RANGE(10),
 297                UNI_RANGE(5),
 298                UNI_RANGE(2.5),
 299                UNI_RANGE(1.25),
 300                BIP_RANGE(10)
 301        }
 302};
 303
 304static const struct comedi_lrange range_acl8112hg_ai = {
 305        12, {
 306                BIP_RANGE(5),
 307                BIP_RANGE(0.5),
 308                BIP_RANGE(0.05),
 309                BIP_RANGE(0.005),
 310                UNI_RANGE(10),
 311                UNI_RANGE(1),
 312                UNI_RANGE(0.1),
 313                UNI_RANGE(0.01),
 314                BIP_RANGE(10),
 315                BIP_RANGE(1),
 316                BIP_RANGE(0.1),
 317                BIP_RANGE(0.01)
 318        }
 319};
 320
 321static const struct comedi_lrange range_a821pgh_ai = {
 322        4, {
 323                BIP_RANGE(5),
 324                BIP_RANGE(0.5),
 325                BIP_RANGE(0.05),
 326                BIP_RANGE(0.005)
 327        }
 328};
 329
 330struct pcl812_board {
 331        const char *name;
 332        int board_type;
 333        int n_aichan;
 334        int n_aochan;
 335        unsigned int ai_ns_min;
 336        const struct comedi_lrange *rangelist_ai;
 337        unsigned int IRQbits;
 338        unsigned int has_dma:1;
 339        unsigned int has_16bit_ai:1;
 340        unsigned int has_mpc508_mux:1;
 341        unsigned int has_dio:1;
 342};
 343
 344static const struct pcl812_board boardtypes[] = {
 345        {
 346                .name           = "pcl812",
 347                .board_type     = boardPCL812,
 348                .n_aichan       = 16,
 349                .n_aochan       = 2,
 350                .ai_ns_min      = 33000,
 351                .rangelist_ai   = &range_bipolar10,
 352                .IRQbits        = 0xdcfc,
 353                .has_dma        = 1,
 354                .has_dio        = 1,
 355        }, {
 356                .name           = "pcl812pg",
 357                .board_type     = boardPCL812PG,
 358                .n_aichan       = 16,
 359                .n_aochan       = 2,
 360                .ai_ns_min      = 33000,
 361                .rangelist_ai   = &range_pcl812pg_ai,
 362                .IRQbits        = 0xdcfc,
 363                .has_dma        = 1,
 364                .has_dio        = 1,
 365        }, {
 366                .name           = "acl8112pg",
 367                .board_type     = boardPCL812PG,
 368                .n_aichan       = 16,
 369                .n_aochan       = 2,
 370                .ai_ns_min      = 10000,
 371                .rangelist_ai   = &range_pcl812pg_ai,
 372                .IRQbits        = 0xdcfc,
 373                .has_dma        = 1,
 374                .has_dio        = 1,
 375        }, {
 376                .name           = "acl8112dg",
 377                .board_type     = boardACL8112,
 378                .n_aichan       = 16,   /* 8 differential */
 379                .n_aochan       = 2,
 380                .ai_ns_min      = 10000,
 381                .rangelist_ai   = &range_acl8112dg_ai,
 382                .IRQbits        = 0xdcfc,
 383                .has_dma        = 1,
 384                .has_mpc508_mux = 1,
 385                .has_dio        = 1,
 386        }, {
 387                .name           = "acl8112hg",
 388                .board_type     = boardACL8112,
 389                .n_aichan       = 16,   /* 8 differential */
 390                .n_aochan       = 2,
 391                .ai_ns_min      = 10000,
 392                .rangelist_ai   = &range_acl8112hg_ai,
 393                .IRQbits        = 0xdcfc,
 394                .has_dma        = 1,
 395                .has_mpc508_mux = 1,
 396                .has_dio        = 1,
 397        }, {
 398                .name           = "a821pgl",
 399                .board_type     = boardA821,
 400                .n_aichan       = 16,   /* 8 differential */
 401                .n_aochan       = 1,
 402                .ai_ns_min      = 10000,
 403                .rangelist_ai   = &range_pcl813b_ai,
 404                .IRQbits        = 0x000c,
 405                .has_dio        = 1,
 406        }, {
 407                .name           = "a821pglnda",
 408                .board_type     = boardA821,
 409                .n_aichan       = 16,   /* 8 differential */
 410                .ai_ns_min      = 10000,
 411                .rangelist_ai   = &range_pcl813b_ai,
 412                .IRQbits        = 0x000c,
 413        }, {
 414                .name           = "a821pgh",
 415                .board_type     = boardA821,
 416                .n_aichan       = 16,   /* 8 differential */
 417                .n_aochan       = 1,
 418                .ai_ns_min      = 10000,
 419                .rangelist_ai   = &range_a821pgh_ai,
 420                .IRQbits        = 0x000c,
 421                .has_dio        = 1,
 422        }, {
 423                .name           = "a822pgl",
 424                .board_type     = boardACL8112,
 425                .n_aichan       = 16,   /* 8 differential */
 426                .n_aochan       = 2,
 427                .ai_ns_min      = 10000,
 428                .rangelist_ai   = &range_acl8112dg_ai,
 429                .IRQbits        = 0xdcfc,
 430                .has_dma        = 1,
 431                .has_dio        = 1,
 432        }, {
 433                .name           = "a822pgh",
 434                .board_type     = boardACL8112,
 435                .n_aichan       = 16,   /* 8 differential */
 436                .n_aochan       = 2,
 437                .ai_ns_min      = 10000,
 438                .rangelist_ai   = &range_acl8112hg_ai,
 439                .IRQbits        = 0xdcfc,
 440                .has_dma        = 1,
 441                .has_dio        = 1,
 442        }, {
 443                .name           = "a823pgl",
 444                .board_type     = boardACL8112,
 445                .n_aichan       = 16,   /* 8 differential */
 446                .n_aochan       = 2,
 447                .ai_ns_min      = 8000,
 448                .rangelist_ai   = &range_acl8112dg_ai,
 449                .IRQbits        = 0xdcfc,
 450                .has_dma        = 1,
 451                .has_dio        = 1,
 452        }, {
 453                .name           = "a823pgh",
 454                .board_type     = boardACL8112,
 455                .n_aichan       = 16,   /* 8 differential */
 456                .n_aochan       = 2,
 457                .ai_ns_min      = 8000,
 458                .rangelist_ai   = &range_acl8112hg_ai,
 459                .IRQbits        = 0xdcfc,
 460                .has_dma        = 1,
 461                .has_dio        = 1,
 462        }, {
 463                .name           = "pcl813",
 464                .board_type     = boardPCL813,
 465                .n_aichan       = 32,
 466                .rangelist_ai   = &range_pcl813b_ai,
 467        }, {
 468                .name           = "pcl813b",
 469                .board_type     = boardPCL813B,
 470                .n_aichan       = 32,
 471                .rangelist_ai   = &range_pcl813b_ai,
 472        }, {
 473                .name           = "acl8113",
 474                .board_type     = boardACL8113,
 475                .n_aichan       = 32,
 476                .rangelist_ai   = &range_acl8113_1_ai,
 477        }, {
 478                .name           = "iso813",
 479                .board_type     = boardISO813,
 480                .n_aichan       = 32,
 481                .rangelist_ai   = &range_iso813_1_ai,
 482        }, {
 483                .name           = "acl8216",
 484                .board_type     = boardACL8216,
 485                .n_aichan       = 16,   /* 8 differential */
 486                .n_aochan       = 2,
 487                .ai_ns_min      = 10000,
 488                .rangelist_ai   = &range_pcl813b2_ai,
 489                .IRQbits        = 0xdcfc,
 490                .has_dma        = 1,
 491                .has_16bit_ai   = 1,
 492                .has_mpc508_mux = 1,
 493                .has_dio        = 1,
 494        }, {
 495                .name           = "a826pg",
 496                .board_type     = boardACL8216,
 497                .n_aichan       = 16,   /* 8 differential */
 498                .n_aochan       = 2,
 499                .ai_ns_min      = 10000,
 500                .rangelist_ai   = &range_pcl813b2_ai,
 501                .IRQbits        = 0xdcfc,
 502                .has_dma        = 1,
 503                .has_16bit_ai   = 1,
 504                .has_dio        = 1,
 505        },
 506};
 507
 508struct pcl812_private {
 509        struct comedi_isadma *dma;
 510        unsigned char range_correction; /*  =1 we must add 1 to range number */
 511        unsigned int last_ai_chanspec;
 512        unsigned char mode_reg_int;     /*  there is stored INT number for some card */
 513        unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
 514        unsigned int max_812_ai_mode0_rangewait;        /*  setling time for gain */
 515        unsigned int use_diff:1;
 516        unsigned int use_mpc508:1;
 517        unsigned int use_ext_trg:1;
 518        unsigned int ai_dma:1;
 519        unsigned int ai_eos:1;
 520};
 521
 522static void pcl812_ai_setup_dma(struct comedi_device *dev,
 523                                struct comedi_subdevice *s,
 524                                unsigned int unread_samples)
 525{
 526        struct pcl812_private *devpriv = dev->private;
 527        struct comedi_isadma *dma = devpriv->dma;
 528        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 529        unsigned int bytes;
 530        unsigned int max_samples;
 531        unsigned int nsamples;
 532
 533        comedi_isadma_disable(dma->chan);
 534
 535        /* if using EOS, adapt DMA buffer to one scan */
 536        bytes = devpriv->ai_eos ? comedi_bytes_per_scan(s) : desc->maxsize;
 537        max_samples = comedi_bytes_to_samples(s, bytes);
 538
 539        /*
 540         * Determine dma size based on the buffer size plus the number of
 541         * unread samples and the number of samples remaining in the command.
 542         */
 543        nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
 544        if (nsamples > unread_samples) {
 545                nsamples -= unread_samples;
 546                desc->size = comedi_samples_to_bytes(s, nsamples);
 547                comedi_isadma_program(desc);
 548        }
 549}
 550
 551static void pcl812_ai_set_chan_range(struct comedi_device *dev,
 552                                     unsigned int chanspec, char wait)
 553{
 554        struct pcl812_private *devpriv = dev->private;
 555        unsigned int chan = CR_CHAN(chanspec);
 556        unsigned int range = CR_RANGE(chanspec);
 557        unsigned int mux = 0;
 558
 559        if (chanspec == devpriv->last_ai_chanspec)
 560                return;
 561
 562        devpriv->last_ai_chanspec = chanspec;
 563
 564        if (devpriv->use_mpc508) {
 565                if (devpriv->use_diff) {
 566                        mux |= PCL812_MUX_CS0 | PCL812_MUX_CS1;
 567                } else {
 568                        if (chan < 8)
 569                                mux |= PCL812_MUX_CS0;
 570                        else
 571                                mux |= PCL812_MUX_CS1;
 572                }
 573        }
 574
 575        outb(mux | PCL812_MUX_CHAN(chan), dev->iobase + PCL812_MUX_REG);
 576        outb(range + devpriv->range_correction, dev->iobase + PCL812_RANGE_REG);
 577
 578        if (wait)
 579                /*
 580                 * XXX this depends on selected range and can be very long for
 581                 * some high gain ranges!
 582                 */
 583                udelay(devpriv->max_812_ai_mode0_rangewait);
 584}
 585
 586static void pcl812_ai_clear_eoc(struct comedi_device *dev)
 587{
 588        /* writing any value clears the interrupt request */
 589        outb(0, dev->iobase + PCL812_STATUS_REG);
 590}
 591
 592static void pcl812_ai_soft_trig(struct comedi_device *dev)
 593{
 594        /* writing any value triggers a software conversion */
 595        outb(255, dev->iobase + PCL812_SOFTTRIG_REG);
 596}
 597
 598static unsigned int pcl812_ai_get_sample(struct comedi_device *dev,
 599                                         struct comedi_subdevice *s)
 600{
 601        unsigned int val;
 602
 603        val = inb(dev->iobase + PCL812_AI_MSB_REG) << 8;
 604        val |= inb(dev->iobase + PCL812_AI_LSB_REG);
 605
 606        return val & s->maxdata;
 607}
 608
 609static int pcl812_ai_eoc(struct comedi_device *dev,
 610                         struct comedi_subdevice *s,
 611                         struct comedi_insn *insn,
 612                         unsigned long context)
 613{
 614        unsigned int status;
 615
 616        if (s->maxdata > 0x0fff) {
 617                status = inb(dev->iobase + PCL812_STATUS_REG);
 618                if ((status & PCL812_STATUS_DRDY) == 0)
 619                        return 0;
 620        } else {
 621                status = inb(dev->iobase + PCL812_AI_MSB_REG);
 622                if ((status & PCL812_AI_MSB_DRDY) == 0)
 623                        return 0;
 624        }
 625        return -EBUSY;
 626}
 627
 628static int pcl812_ai_cmdtest(struct comedi_device *dev,
 629                             struct comedi_subdevice *s, struct comedi_cmd *cmd)
 630{
 631        const struct pcl812_board *board = dev->board_ptr;
 632        struct pcl812_private *devpriv = dev->private;
 633        int err = 0;
 634        unsigned int flags;
 635
 636        /* Step 1 : check if triggers are trivially valid */
 637
 638        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 639        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 640
 641        if (devpriv->use_ext_trg)
 642                flags = TRIG_EXT;
 643        else
 644                flags = TRIG_TIMER;
 645        err |= comedi_check_trigger_src(&cmd->convert_src, flags);
 646
 647        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 648        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 649
 650        if (err)
 651                return 1;
 652
 653        /* Step 2a : make sure trigger sources are unique */
 654
 655        err |= comedi_check_trigger_is_unique(cmd->stop_src);
 656
 657        /* Step 2b : and mutually compatible */
 658
 659        if (err)
 660                return 2;
 661
 662        /* Step 3: check if arguments are trivially valid */
 663
 664        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 665        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 666
 667        if (cmd->convert_src == TRIG_TIMER) {
 668                err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
 669                                                    board->ai_ns_min);
 670        } else {        /* TRIG_EXT */
 671                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 672        }
 673
 674        err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
 675        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 676                                           cmd->chanlist_len);
 677
 678        if (cmd->stop_src == TRIG_COUNT)
 679                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 680        else    /* TRIG_NONE */
 681                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 682
 683        if (err)
 684                return 3;
 685
 686        /* step 4: fix up any arguments */
 687
 688        if (cmd->convert_src == TRIG_TIMER) {
 689                unsigned int arg = cmd->convert_arg;
 690
 691                comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
 692                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 693        }
 694
 695        if (err)
 696                return 4;
 697
 698        return 0;
 699}
 700
 701static int pcl812_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 702{
 703        struct pcl812_private *devpriv = dev->private;
 704        struct comedi_isadma *dma = devpriv->dma;
 705        struct comedi_cmd *cmd = &s->async->cmd;
 706        unsigned int ctrl = 0;
 707        unsigned int i;
 708
 709        pcl812_ai_set_chan_range(dev, cmd->chanlist[0], 1);
 710
 711        if (dma) {      /*  check if we can use DMA transfer */
 712                devpriv->ai_dma = 1;
 713                for (i = 1; i < cmd->chanlist_len; i++)
 714                        if (cmd->chanlist[0] != cmd->chanlist[i]) {
 715                                /*  we cann't use DMA :-( */
 716                                devpriv->ai_dma = 0;
 717                                break;
 718                        }
 719        } else {
 720                devpriv->ai_dma = 0;
 721        }
 722
 723        devpriv->ai_poll_ptr = 0;
 724
 725        /*  don't we want wake up every scan? */
 726        if (cmd->flags & CMDF_WAKE_EOS) {
 727                devpriv->ai_eos = 1;
 728
 729                /*  DMA is useless for this situation */
 730                if (cmd->chanlist_len == 1)
 731                        devpriv->ai_dma = 0;
 732        }
 733
 734        if (devpriv->ai_dma) {
 735                /* setup and enable dma for the first buffer */
 736                dma->cur_dma = 0;
 737                pcl812_ai_setup_dma(dev, s, 0);
 738        }
 739
 740        switch (cmd->convert_src) {
 741        case TRIG_TIMER:
 742                comedi_8254_update_divisors(dev->pacer);
 743                comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
 744                break;
 745        }
 746
 747        if (devpriv->ai_dma)
 748                ctrl |= PCL812_CTRL_PACER_DMA_TRIG;
 749        else
 750                ctrl |= PCL812_CTRL_PACER_EOC_TRIG;
 751        outb(devpriv->mode_reg_int | ctrl, dev->iobase + PCL812_CTRL_REG);
 752
 753        return 0;
 754}
 755
 756static bool pcl812_ai_next_chan(struct comedi_device *dev,
 757                                struct comedi_subdevice *s)
 758{
 759        struct comedi_cmd *cmd = &s->async->cmd;
 760
 761        if (cmd->stop_src == TRIG_COUNT &&
 762            s->async->scans_done >= cmd->stop_arg) {
 763                s->async->events |= COMEDI_CB_EOA;
 764                return false;
 765        }
 766
 767        return true;
 768}
 769
 770static void pcl812_handle_eoc(struct comedi_device *dev,
 771                              struct comedi_subdevice *s)
 772{
 773        struct comedi_cmd *cmd = &s->async->cmd;
 774        unsigned int chan = s->async->cur_chan;
 775        unsigned int next_chan;
 776        unsigned short val;
 777
 778        if (pcl812_ai_eoc(dev, s, NULL, 0)) {
 779                dev_dbg(dev->class_dev, "A/D cmd IRQ without DRDY!\n");
 780                s->async->events |= COMEDI_CB_ERROR;
 781                return;
 782        }
 783
 784        val = pcl812_ai_get_sample(dev, s);
 785        comedi_buf_write_samples(s, &val, 1);
 786
 787        /* Set up next channel. Added by abbotti 2010-01-20, but untested. */
 788        next_chan = s->async->cur_chan;
 789        if (cmd->chanlist[chan] != cmd->chanlist[next_chan])
 790                pcl812_ai_set_chan_range(dev, cmd->chanlist[next_chan], 0);
 791
 792        pcl812_ai_next_chan(dev, s);
 793}
 794
 795static void transfer_from_dma_buf(struct comedi_device *dev,
 796                                  struct comedi_subdevice *s,
 797                                  unsigned short *ptr,
 798                                  unsigned int bufptr, unsigned int len)
 799{
 800        unsigned int i;
 801        unsigned short val;
 802
 803        for (i = len; i; i--) {
 804                val = ptr[bufptr++];
 805                comedi_buf_write_samples(s, &val, 1);
 806
 807                if (!pcl812_ai_next_chan(dev, s))
 808                        break;
 809        }
 810}
 811
 812static void pcl812_handle_dma(struct comedi_device *dev,
 813                              struct comedi_subdevice *s)
 814{
 815        struct pcl812_private *devpriv = dev->private;
 816        struct comedi_isadma *dma = devpriv->dma;
 817        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 818        unsigned int nsamples;
 819        int bufptr;
 820
 821        nsamples = comedi_bytes_to_samples(s, desc->size) -
 822                   devpriv->ai_poll_ptr;
 823        bufptr = devpriv->ai_poll_ptr;
 824        devpriv->ai_poll_ptr = 0;
 825
 826        /* restart dma with the next buffer */
 827        dma->cur_dma = 1 - dma->cur_dma;
 828        pcl812_ai_setup_dma(dev, s, nsamples);
 829
 830        transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
 831}
 832
 833static irqreturn_t pcl812_interrupt(int irq, void *d)
 834{
 835        struct comedi_device *dev = d;
 836        struct comedi_subdevice *s = dev->read_subdev;
 837        struct pcl812_private *devpriv = dev->private;
 838
 839        if (!dev->attached) {
 840                pcl812_ai_clear_eoc(dev);
 841                return IRQ_HANDLED;
 842        }
 843
 844        if (devpriv->ai_dma)
 845                pcl812_handle_dma(dev, s);
 846        else
 847                pcl812_handle_eoc(dev, s);
 848
 849        pcl812_ai_clear_eoc(dev);
 850
 851        comedi_handle_events(dev, s);
 852        return IRQ_HANDLED;
 853}
 854
 855static int pcl812_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
 856{
 857        struct pcl812_private *devpriv = dev->private;
 858        struct comedi_isadma *dma = devpriv->dma;
 859        struct comedi_isadma_desc *desc;
 860        unsigned long flags;
 861        unsigned int poll;
 862        int ret;
 863
 864        /* poll is valid only for DMA transfer */
 865        if (!devpriv->ai_dma)
 866                return 0;
 867
 868        spin_lock_irqsave(&dev->spinlock, flags);
 869
 870        poll = comedi_isadma_poll(dma);
 871        poll = comedi_bytes_to_samples(s, poll);
 872        if (poll > devpriv->ai_poll_ptr) {
 873                desc = &dma->desc[dma->cur_dma];
 874                transfer_from_dma_buf(dev, s, desc->virt_addr,
 875                                      devpriv->ai_poll_ptr,
 876                                      poll - devpriv->ai_poll_ptr);
 877                /* new buffer position */
 878                devpriv->ai_poll_ptr = poll;
 879
 880                ret = comedi_buf_n_bytes_ready(s);
 881        } else {
 882                /* no new samples */
 883                ret = 0;
 884        }
 885
 886        spin_unlock_irqrestore(&dev->spinlock, flags);
 887
 888        return ret;
 889}
 890
 891static int pcl812_ai_cancel(struct comedi_device *dev,
 892                            struct comedi_subdevice *s)
 893{
 894        struct pcl812_private *devpriv = dev->private;
 895
 896        if (devpriv->ai_dma)
 897                comedi_isadma_disable(devpriv->dma->chan);
 898
 899        outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
 900             dev->iobase + PCL812_CTRL_REG);
 901        comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
 902        pcl812_ai_clear_eoc(dev);
 903        return 0;
 904}
 905
 906static int pcl812_ai_insn_read(struct comedi_device *dev,
 907                               struct comedi_subdevice *s,
 908                               struct comedi_insn *insn,
 909                               unsigned int *data)
 910{
 911        struct pcl812_private *devpriv = dev->private;
 912        int ret = 0;
 913        int i;
 914
 915        outb(devpriv->mode_reg_int | PCL812_CTRL_SOFT_TRIG,
 916             dev->iobase + PCL812_CTRL_REG);
 917
 918        pcl812_ai_set_chan_range(dev, insn->chanspec, 1);
 919
 920        for (i = 0; i < insn->n; i++) {
 921                pcl812_ai_clear_eoc(dev);
 922                pcl812_ai_soft_trig(dev);
 923
 924                ret = comedi_timeout(dev, s, insn, pcl812_ai_eoc, 0);
 925                if (ret)
 926                        break;
 927
 928                data[i] = pcl812_ai_get_sample(dev, s);
 929        }
 930        outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
 931             dev->iobase + PCL812_CTRL_REG);
 932        pcl812_ai_clear_eoc(dev);
 933
 934        return ret ? ret : insn->n;
 935}
 936
 937static int pcl812_ao_insn_write(struct comedi_device *dev,
 938                                struct comedi_subdevice *s,
 939                                struct comedi_insn *insn,
 940                                unsigned int *data)
 941{
 942        unsigned int chan = CR_CHAN(insn->chanspec);
 943        unsigned int val = s->readback[chan];
 944        int i;
 945
 946        for (i = 0; i < insn->n; i++) {
 947                val = data[i];
 948                outb(val & 0xff, dev->iobase + PCL812_AO_LSB_REG(chan));
 949                outb((val >> 8) & 0x0f, dev->iobase + PCL812_AO_MSB_REG(chan));
 950        }
 951        s->readback[chan] = val;
 952
 953        return insn->n;
 954}
 955
 956static int pcl812_di_insn_bits(struct comedi_device *dev,
 957                               struct comedi_subdevice *s,
 958                               struct comedi_insn *insn,
 959                               unsigned int *data)
 960{
 961        data[1] = inb(dev->iobase + PCL812_DI_LSB_REG) |
 962                  (inb(dev->iobase + PCL812_DI_MSB_REG) << 8);
 963
 964        return insn->n;
 965}
 966
 967static int pcl812_do_insn_bits(struct comedi_device *dev,
 968                               struct comedi_subdevice *s,
 969                               struct comedi_insn *insn,
 970                               unsigned int *data)
 971{
 972        if (comedi_dio_update_state(s, data)) {
 973                outb(s->state & 0xff, dev->iobase + PCL812_DO_LSB_REG);
 974                outb((s->state >> 8), dev->iobase + PCL812_DO_MSB_REG);
 975        }
 976
 977        data[1] = s->state;
 978
 979        return insn->n;
 980}
 981
 982static void pcl812_reset(struct comedi_device *dev)
 983{
 984        const struct pcl812_board *board = dev->board_ptr;
 985        struct pcl812_private *devpriv = dev->private;
 986        unsigned int chan;
 987
 988        /* disable analog input trigger */
 989        outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
 990             dev->iobase + PCL812_CTRL_REG);
 991        pcl812_ai_clear_eoc(dev);
 992
 993        /*
 994         * Invalidate last_ai_chanspec then set analog input to
 995         * known channel/range.
 996         */
 997        devpriv->last_ai_chanspec = CR_PACK(16, 0, 0);
 998        pcl812_ai_set_chan_range(dev, CR_PACK(0, 0, 0), 0);
 999
1000        /* set analog output channels to 0V */
1001        for (chan = 0; chan < board->n_aochan; chan++) {
1002                outb(0, dev->iobase + PCL812_AO_LSB_REG(chan));
1003                outb(0, dev->iobase + PCL812_AO_MSB_REG(chan));
1004        }
1005
1006        /* set all digital outputs low */
1007        if (board->has_dio) {
1008                outb(0, dev->iobase + PCL812_DO_MSB_REG);
1009                outb(0, dev->iobase + PCL812_DO_LSB_REG);
1010        }
1011}
1012
1013static void pcl812_set_ai_range_table(struct comedi_device *dev,
1014                                      struct comedi_subdevice *s,
1015                                      struct comedi_devconfig *it)
1016{
1017        const struct pcl812_board *board = dev->board_ptr;
1018        struct pcl812_private *devpriv = dev->private;
1019
1020        /* default to the range table from the boardinfo */
1021        s->range_table = board->rangelist_ai;
1022
1023        /* now check the user config option based on the boardtype */
1024        switch (board->board_type) {
1025        case boardPCL812PG:
1026                if (it->options[4] == 1)
1027                        s->range_table = &range_pcl812pg2_ai;
1028                break;
1029        case boardPCL812:
1030                switch (it->options[4]) {
1031                case 0:
1032                        s->range_table = &range_bipolar10;
1033                        break;
1034                case 1:
1035                        s->range_table = &range_bipolar5;
1036                        break;
1037                case 2:
1038                        s->range_table = &range_bipolar2_5;
1039                        break;
1040                case 3:
1041                        s->range_table = &range812_bipolar1_25;
1042                        break;
1043                case 4:
1044                        s->range_table = &range812_bipolar0_625;
1045                        break;
1046                case 5:
1047                        s->range_table = &range812_bipolar0_3125;
1048                        break;
1049                default:
1050                        s->range_table = &range_bipolar10;
1051                        break;
1052                }
1053                break;
1054        case boardPCL813B:
1055                if (it->options[1] == 1)
1056                        s->range_table = &range_pcl813b2_ai;
1057                break;
1058        case boardISO813:
1059                switch (it->options[1]) {
1060                case 0:
1061                        s->range_table = &range_iso813_1_ai;
1062                        break;
1063                case 1:
1064                        s->range_table = &range_iso813_1_2_ai;
1065                        break;
1066                case 2:
1067                        s->range_table = &range_iso813_2_ai;
1068                        devpriv->range_correction = 1;
1069                        break;
1070                case 3:
1071                        s->range_table = &range_iso813_2_2_ai;
1072                        devpriv->range_correction = 1;
1073                        break;
1074                default:
1075                        s->range_table = &range_iso813_1_ai;
1076                        break;
1077                }
1078                break;
1079        case boardACL8113:
1080                switch (it->options[1]) {
1081                case 0:
1082                        s->range_table = &range_acl8113_1_ai;
1083                        break;
1084                case 1:
1085                        s->range_table = &range_acl8113_1_2_ai;
1086                        break;
1087                case 2:
1088                        s->range_table = &range_acl8113_2_ai;
1089                        devpriv->range_correction = 1;
1090                        break;
1091                case 3:
1092                        s->range_table = &range_acl8113_2_2_ai;
1093                        devpriv->range_correction = 1;
1094                        break;
1095                default:
1096                        s->range_table = &range_acl8113_1_ai;
1097                        break;
1098                }
1099                break;
1100        }
1101}
1102
1103static void pcl812_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
1104{
1105        struct pcl812_private *devpriv = dev->private;
1106
1107        /* only DMA channels 3 and 1 are valid */
1108        if (!(dma_chan == 3 || dma_chan == 1))
1109                return;
1110
1111        /* DMA uses two 8K buffers */
1112        devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
1113                                           PAGE_SIZE * 2, COMEDI_ISADMA_READ);
1114}
1115
1116static void pcl812_free_dma(struct comedi_device *dev)
1117{
1118        struct pcl812_private *devpriv = dev->private;
1119
1120        if (devpriv)
1121                comedi_isadma_free(devpriv->dma);
1122}
1123
1124static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1125{
1126        const struct pcl812_board *board = dev->board_ptr;
1127        struct pcl812_private *devpriv;
1128        struct comedi_subdevice *s;
1129        int n_subdevices;
1130        int subdev;
1131        int ret;
1132
1133        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1134        if (!devpriv)
1135                return -ENOMEM;
1136
1137        ret = comedi_request_region(dev, it->options[0], 0x10);
1138        if (ret)
1139                return ret;
1140
1141        if (board->IRQbits) {
1142                dev->pacer = comedi_8254_init(dev->iobase + PCL812_TIMER_BASE,
1143                                              I8254_OSC_BASE_2MHZ,
1144                                              I8254_IO8, 0);
1145                if (!dev->pacer)
1146                        return -ENOMEM;
1147
1148                if ((1 << it->options[1]) & board->IRQbits) {
1149                        ret = request_irq(it->options[1], pcl812_interrupt, 0,
1150                                          dev->board_name, dev);
1151                        if (ret == 0)
1152                                dev->irq = it->options[1];
1153                }
1154        }
1155
1156        /* we need an IRQ to do DMA on channel 3 or 1 */
1157        if (dev->irq && board->has_dma)
1158                 pcl812_alloc_dma(dev, it->options[2]);
1159
1160        /* differential analog inputs? */
1161        switch (board->board_type) {
1162        case boardA821:
1163                if (it->options[2] == 1)
1164                        devpriv->use_diff = 1;
1165                break;
1166        case boardACL8112:
1167        case boardACL8216:
1168                if (it->options[4] == 1)
1169                        devpriv->use_diff = 1;
1170                break;
1171        }
1172
1173        n_subdevices = 1;               /* all boardtypes have analog inputs */
1174        if (board->n_aochan > 0)
1175                n_subdevices++;
1176        if (board->has_dio)
1177                n_subdevices += 2;
1178
1179        ret = comedi_alloc_subdevices(dev, n_subdevices);
1180        if (ret)
1181                return ret;
1182
1183        subdev = 0;
1184
1185        /* Analog Input subdevice */
1186        s = &dev->subdevices[subdev];
1187        s->type         = COMEDI_SUBD_AI;
1188        s->subdev_flags = SDF_READABLE;
1189        if (devpriv->use_diff) {
1190                s->subdev_flags |= SDF_DIFF;
1191                s->n_chan       = board->n_aichan / 2;
1192        } else {
1193                s->subdev_flags |= SDF_GROUND;
1194                s->n_chan       = board->n_aichan;
1195        }
1196        s->maxdata      = board->has_16bit_ai ? 0xffff : 0x0fff;
1197
1198        pcl812_set_ai_range_table(dev, s, it);
1199
1200        s->insn_read    = pcl812_ai_insn_read;
1201
1202        if (dev->irq) {
1203                dev->read_subdev = s;
1204                s->subdev_flags |= SDF_CMD_READ;
1205                s->len_chanlist = MAX_CHANLIST_LEN;
1206                s->do_cmdtest   = pcl812_ai_cmdtest;
1207                s->do_cmd       = pcl812_ai_cmd;
1208                s->poll         = pcl812_ai_poll;
1209                s->cancel       = pcl812_ai_cancel;
1210        }
1211
1212        devpriv->use_mpc508 = board->has_mpc508_mux;
1213
1214        subdev++;
1215
1216        /* analog output */
1217        if (board->n_aochan > 0) {
1218                s = &dev->subdevices[subdev];
1219                s->type         = COMEDI_SUBD_AO;
1220                s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1221                s->n_chan       = board->n_aochan;
1222                s->maxdata      = 0xfff;
1223                s->range_table  = &range_unipolar5;
1224                switch (board->board_type) {
1225                case boardA821:
1226                        if (it->options[3] == 1)
1227                                s->range_table = &range_unipolar10;
1228                        break;
1229                case boardPCL812:
1230                case boardACL8112:
1231                case boardPCL812PG:
1232                case boardACL8216:
1233                        if (it->options[5] == 1)
1234                                s->range_table = &range_unipolar10;
1235                        if (it->options[5] == 2)
1236                                s->range_table = &range_unknown;
1237                        break;
1238                }
1239                s->insn_write   = pcl812_ao_insn_write;
1240
1241                ret = comedi_alloc_subdev_readback(s);
1242                if (ret)
1243                        return ret;
1244
1245                subdev++;
1246        }
1247
1248        if (board->has_dio) {
1249                /* Digital Input subdevice */
1250                s = &dev->subdevices[subdev];
1251                s->type         = COMEDI_SUBD_DI;
1252                s->subdev_flags = SDF_READABLE;
1253                s->n_chan       = 16;
1254                s->maxdata      = 1;
1255                s->range_table  = &range_digital;
1256                s->insn_bits    = pcl812_di_insn_bits;
1257                subdev++;
1258
1259                /* Digital Output subdevice */
1260                s = &dev->subdevices[subdev];
1261                s->type         = COMEDI_SUBD_DO;
1262                s->subdev_flags = SDF_WRITABLE;
1263                s->n_chan       = 16;
1264                s->maxdata      = 1;
1265                s->range_table  = &range_digital;
1266                s->insn_bits    = pcl812_do_insn_bits;
1267                subdev++;
1268        }
1269
1270        switch (board->board_type) {
1271        case boardACL8216:
1272        case boardPCL812PG:
1273        case boardPCL812:
1274        case boardACL8112:
1275                devpriv->max_812_ai_mode0_rangewait = 1;
1276                if (it->options[3] > 0)
1277                                                /*  we use external trigger */
1278                        devpriv->use_ext_trg = 1;
1279                break;
1280        case boardA821:
1281                devpriv->max_812_ai_mode0_rangewait = 1;
1282                devpriv->mode_reg_int = (dev->irq << 4) & 0xf0;
1283                break;
1284        case boardPCL813B:
1285        case boardPCL813:
1286        case boardISO813:
1287        case boardACL8113:
1288                /* maybe there must by greatest timeout */
1289                devpriv->max_812_ai_mode0_rangewait = 5;
1290                break;
1291        }
1292
1293        pcl812_reset(dev);
1294
1295        return 0;
1296}
1297
1298static void pcl812_detach(struct comedi_device *dev)
1299{
1300        pcl812_free_dma(dev);
1301        comedi_legacy_detach(dev);
1302}
1303
1304static struct comedi_driver pcl812_driver = {
1305        .driver_name    = "pcl812",
1306        .module         = THIS_MODULE,
1307        .attach         = pcl812_attach,
1308        .detach         = pcl812_detach,
1309        .board_name     = &boardtypes[0].name,
1310        .num_names      = ARRAY_SIZE(boardtypes),
1311        .offset         = sizeof(struct pcl812_board),
1312};
1313module_comedi_driver(pcl812_driver);
1314
1315MODULE_AUTHOR("Comedi http://www.comedi.org");
1316MODULE_DESCRIPTION("Comedi low-level driver");
1317MODULE_LICENSE("GPL");
1318