linux/drivers/staging/comedi/drivers/pcl818.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * comedi/drivers/pcl818.c
   4 *
   5 * Driver: pcl818
   6 * Description: Advantech PCL-818 cards, PCL-718
   7 * Author: Michal Dobes <dobes@tesnet.cz>
   8 * Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
   9 *   PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
  10 *   PCL-718 (pcl718)
  11 * Status: works
  12 *
  13 * All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
  14 * Differences are only at maximal sample speed, range list and FIFO
  15 * support.
  16 * The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
  17 * only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
  18 * PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
  19 * but this code is untested.
  20 * A word or two about DMA. Driver support DMA operations at two ways:
  21 * 1) DMA uses two buffers and after one is filled then is generated
  22 *    INT and DMA restart with second buffer. With this mode I'm unable run
  23 *    more that 80Ksamples/secs without data dropouts on K6/233.
  24 * 2) DMA uses one buffer and run in autoinit mode and the data are
  25 *    from DMA buffer moved on the fly with 2kHz interrupts from RTC.
  26 *    This mode is used if the interrupt 8 is available for allocation.
  27 *    If not, then first DMA mode is used. With this I can run at
  28 *    full speed one card (100ksamples/secs) or two cards with
  29 *    60ksamples/secs each (more is problem on account of ISA limitations).
  30 *    To use this mode you must have compiled  kernel with disabled
  31 *    "Enhanced Real Time Clock Support".
  32 *    Maybe you can have problems if you use xntpd or similar.
  33 *    If you've data dropouts with DMA mode 2 then:
  34 *     a) disable IDE DMA
  35 *     b) switch text mode console to fb.
  36 *
  37 *  Options for PCL-818L:
  38 *  [0] - IO Base
  39 *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
  40 *  [2] - DMA        (0=disable, 1, 3)
  41 *  [3] - 0, 10=10MHz clock for 8254
  42 *            1= 1MHz clock for 8254
  43 *  [4] - 0,  5=A/D input  -5V.. +5V
  44 *        1, 10=A/D input -10V..+10V
  45 *  [5] - 0,  5=D/A output 0-5V  (internal reference -5V)
  46 *        1, 10=D/A output 0-10V (internal reference -10V)
  47 *        2    =D/A output unknown (external reference)
  48 *
  49 *  Options for PCL-818, PCL-818H:
  50 *  [0] - IO Base
  51 *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
  52 *  [2] - DMA        (0=disable, 1, 3)
  53 *  [3] - 0, 10=10MHz clock for 8254
  54 *            1= 1MHz clock for 8254
  55 *  [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
  56 *        1, 10=D/A output 0-10V (internal reference -10V)
  57 *        2    =D/A output unknown (external reference)
  58 *
  59 *  Options for PCL-818HD, PCL-818HG:
  60 *  [0] - IO Base
  61 *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
  62 *  [2] - DMA/FIFO  (-1=use FIFO, 0=disable both FIFO and DMA,
  63 *                    1=use DMA ch 1, 3=use DMA ch 3)
  64 *  [3] - 0, 10=10MHz clock for 8254
  65 *            1= 1MHz clock for 8254
  66 *  [4] - 0,  5=D/A output 0-5V  (internal reference -5V)
  67 *        1, 10=D/A output 0-10V (internal reference -10V)
  68 *        2    =D/A output unknown (external reference)
  69 *
  70 *  Options for PCL-718:
  71 *  [0] - IO Base
  72 *  [1] - IRQ        (0=disable, 2, 3, 4, 5, 6, 7)
  73 *  [2] - DMA        (0=disable, 1, 3)
  74 *  [3] - 0, 10=10MHz clock for 8254
  75 *            1= 1MHz clock for 8254
  76 *  [4] -     0=A/D Range is +/-10V
  77 *            1=             +/-5V
  78 *            2=             +/-2.5V
  79 *            3=             +/-1V
  80 *            4=             +/-0.5V
  81 *            5=             user defined bipolar
  82 *            6=             0-10V
  83 *            7=             0-5V
  84 *            8=             0-2V
  85 *            9=             0-1V
  86 *           10=             user defined unipolar
  87 *  [5] - 0,  5=D/A outputs 0-5V  (internal reference -5V)
  88 *        1, 10=D/A outputs 0-10V (internal reference -10V)
  89 *            2=D/A outputs unknown (external reference)
  90 *  [6] - 0, 60=max  60kHz A/D sampling
  91 *        1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
  92 *
  93 */
  94
  95#include <linux/module.h>
  96#include <linux/gfp.h>
  97#include <linux/delay.h>
  98#include <linux/io.h>
  99#include <linux/interrupt.h>
 100
 101#include "../comedidev.h"
 102
 103#include "comedi_isadma.h"
 104#include "comedi_8254.h"
 105
 106/*
 107 * Register I/O map
 108 */
 109#define PCL818_AI_LSB_REG                       0x00
 110#define PCL818_AI_MSB_REG                       0x01
 111#define PCL818_RANGE_REG                        0x01
 112#define PCL818_MUX_REG                          0x02
 113#define PCL818_MUX_SCAN(_first, _last)          (((_last) << 4) | (_first))
 114#define PCL818_DO_DI_LSB_REG                    0x03
 115#define PCL818_AO_LSB_REG(x)                    (0x04 + ((x) * 2))
 116#define PCL818_AO_MSB_REG(x)                    (0x05 + ((x) * 2))
 117#define PCL818_STATUS_REG                       0x08
 118#define PCL818_STATUS_NEXT_CHAN_MASK            (0xf << 0)
 119#define PCL818_STATUS_INT                       BIT(4)
 120#define PCL818_STATUS_MUX                       BIT(5)
 121#define PCL818_STATUS_UNI                       BIT(6)
 122#define PCL818_STATUS_EOC                       BIT(7)
 123#define PCL818_CTRL_REG                         0x09
 124#define PCL818_CTRL_TRIG(x)                     (((x) & 0x3) << 0)
 125#define PCL818_CTRL_DISABLE_TRIG                PCL818_CTRL_TRIG(0)
 126#define PCL818_CTRL_SOFT_TRIG                   PCL818_CTRL_TRIG(1)
 127#define PCL818_CTRL_EXT_TRIG                    PCL818_CTRL_TRIG(2)
 128#define PCL818_CTRL_PACER_TRIG                  PCL818_CTRL_TRIG(3)
 129#define PCL818_CTRL_DMAE                        BIT(2)
 130#define PCL818_CTRL_IRQ(x)                      ((x) << 4)
 131#define PCL818_CTRL_INTE                        BIT(7)
 132#define PCL818_CNTENABLE_REG                    0x0a
 133#define PCL818_CNTENABLE_PACER_TRIG0            BIT(0)
 134#define PCL818_CNTENABLE_CNT0_INT_CLK           BIT(1)  /* 0=ext clk */
 135#define PCL818_DO_DI_MSB_REG                    0x0b
 136#define PCL818_TIMER_BASE                       0x0c
 137
 138/* W: fifo enable/disable */
 139#define PCL818_FI_ENABLE 6
 140/* W: fifo interrupt clear */
 141#define PCL818_FI_INTCLR 20
 142/* W: fifo interrupt clear */
 143#define PCL818_FI_FLUSH 25
 144/* R: fifo status */
 145#define PCL818_FI_STATUS 25
 146/* R: one record from FIFO */
 147#define PCL818_FI_DATALO 23
 148#define PCL818_FI_DATAHI 24
 149
 150#define MAGIC_DMA_WORD 0x5a5a
 151
 152static const struct comedi_lrange range_pcl818h_ai = {
 153        9, {
 154                BIP_RANGE(5),
 155                BIP_RANGE(2.5),
 156                BIP_RANGE(1.25),
 157                BIP_RANGE(0.625),
 158                UNI_RANGE(10),
 159                UNI_RANGE(5),
 160                UNI_RANGE(2.5),
 161                UNI_RANGE(1.25),
 162                BIP_RANGE(10)
 163        }
 164};
 165
 166static const struct comedi_lrange range_pcl818hg_ai = {
 167        10, {
 168                BIP_RANGE(5),
 169                BIP_RANGE(0.5),
 170                BIP_RANGE(0.05),
 171                BIP_RANGE(0.005),
 172                UNI_RANGE(10),
 173                UNI_RANGE(1),
 174                UNI_RANGE(0.1),
 175                UNI_RANGE(0.01),
 176                BIP_RANGE(10),
 177                BIP_RANGE(1),
 178                BIP_RANGE(0.1),
 179                BIP_RANGE(0.01)
 180        }
 181};
 182
 183static const struct comedi_lrange range_pcl818l_l_ai = {
 184        4, {
 185                BIP_RANGE(5),
 186                BIP_RANGE(2.5),
 187                BIP_RANGE(1.25),
 188                BIP_RANGE(0.625)
 189        }
 190};
 191
 192static const struct comedi_lrange range_pcl818l_h_ai = {
 193        4, {
 194                BIP_RANGE(10),
 195                BIP_RANGE(5),
 196                BIP_RANGE(2.5),
 197                BIP_RANGE(1.25)
 198        }
 199};
 200
 201static const struct comedi_lrange range718_bipolar1 = {
 202        1, {
 203                BIP_RANGE(1)
 204        }
 205};
 206
 207static const struct comedi_lrange range718_bipolar0_5 = {
 208        1, {
 209                BIP_RANGE(0.5)
 210        }
 211};
 212
 213static const struct comedi_lrange range718_unipolar2 = {
 214        1, {
 215                UNI_RANGE(2)
 216        }
 217};
 218
 219static const struct comedi_lrange range718_unipolar1 = {
 220        1, {
 221                BIP_RANGE(1)
 222        }
 223};
 224
 225struct pcl818_board {
 226        const char *name;
 227        unsigned int ns_min;
 228        int n_aochan;
 229        const struct comedi_lrange *ai_range_type;
 230        unsigned int has_dma:1;
 231        unsigned int has_fifo:1;
 232        unsigned int is_818:1;
 233};
 234
 235static const struct pcl818_board boardtypes[] = {
 236        {
 237                .name           = "pcl818l",
 238                .ns_min         = 25000,
 239                .n_aochan       = 1,
 240                .ai_range_type  = &range_pcl818l_l_ai,
 241                .has_dma        = 1,
 242                .is_818         = 1,
 243        }, {
 244                .name           = "pcl818h",
 245                .ns_min         = 10000,
 246                .n_aochan       = 1,
 247                .ai_range_type  = &range_pcl818h_ai,
 248                .has_dma        = 1,
 249                .is_818         = 1,
 250        }, {
 251                .name           = "pcl818hd",
 252                .ns_min         = 10000,
 253                .n_aochan       = 1,
 254                .ai_range_type  = &range_pcl818h_ai,
 255                .has_dma        = 1,
 256                .has_fifo       = 1,
 257                .is_818         = 1,
 258        }, {
 259                .name           = "pcl818hg",
 260                .ns_min         = 10000,
 261                .n_aochan       = 1,
 262                .ai_range_type  = &range_pcl818hg_ai,
 263                .has_dma        = 1,
 264                .has_fifo       = 1,
 265                .is_818         = 1,
 266        }, {
 267                .name           = "pcl818",
 268                .ns_min         = 10000,
 269                .n_aochan       = 2,
 270                .ai_range_type  = &range_pcl818h_ai,
 271                .has_dma        = 1,
 272                .is_818         = 1,
 273        }, {
 274                .name           = "pcl718",
 275                .ns_min         = 16000,
 276                .n_aochan       = 2,
 277                .ai_range_type  = &range_unipolar5,
 278                .has_dma        = 1,
 279        }, {
 280                .name           = "pcm3718",
 281                .ns_min         = 10000,
 282                .ai_range_type  = &range_pcl818h_ai,
 283                .has_dma        = 1,
 284                .is_818         = 1,
 285        },
 286};
 287
 288struct pcl818_private {
 289        struct comedi_isadma *dma;
 290        /*  manimal allowed delay between samples (in us) for actual card */
 291        unsigned int ns_min;
 292        /*  MUX setting for actual AI operations */
 293        unsigned int act_chanlist[16];
 294        unsigned int act_chanlist_len;  /*  how long is actual MUX list */
 295        unsigned int act_chanlist_pos;  /*  actual position in MUX list */
 296        unsigned int usefifo:1;
 297        unsigned int ai_cmd_running:1;
 298        unsigned int ai_cmd_canceled:1;
 299};
 300
 301static void pcl818_ai_setup_dma(struct comedi_device *dev,
 302                                struct comedi_subdevice *s,
 303                                unsigned int unread_samples)
 304{
 305        struct pcl818_private *devpriv = dev->private;
 306        struct comedi_isadma *dma = devpriv->dma;
 307        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 308        unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
 309        unsigned int nsamples;
 310
 311        comedi_isadma_disable(dma->chan);
 312
 313        /*
 314         * Determine dma size based on the buffer maxsize plus the number of
 315         * unread samples and the number of samples remaining in the command.
 316         */
 317        nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
 318        if (nsamples > unread_samples) {
 319                nsamples -= unread_samples;
 320                desc->size = comedi_samples_to_bytes(s, nsamples);
 321                comedi_isadma_program(desc);
 322        }
 323}
 324
 325static void pcl818_ai_set_chan_range(struct comedi_device *dev,
 326                                     unsigned int chan,
 327                                     unsigned int range)
 328{
 329        outb(chan, dev->iobase + PCL818_MUX_REG);
 330        outb(range, dev->iobase + PCL818_RANGE_REG);
 331}
 332
 333static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
 334                                    unsigned int first_chan,
 335                                    unsigned int last_chan)
 336{
 337        outb(PCL818_MUX_SCAN(first_chan, last_chan),
 338             dev->iobase + PCL818_MUX_REG);
 339}
 340
 341static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
 342                                     unsigned int *chanlist,
 343                                     unsigned int seglen)
 344{
 345        struct pcl818_private *devpriv = dev->private;
 346        unsigned int first_chan = CR_CHAN(chanlist[0]);
 347        unsigned int last_chan;
 348        unsigned int range;
 349        int i;
 350
 351        devpriv->act_chanlist_len = seglen;
 352        devpriv->act_chanlist_pos = 0;
 353
 354        /* store range list to card */
 355        for (i = 0; i < seglen; i++) {
 356                last_chan = CR_CHAN(chanlist[i]);
 357                range = CR_RANGE(chanlist[i]);
 358
 359                devpriv->act_chanlist[i] = last_chan;
 360
 361                pcl818_ai_set_chan_range(dev, last_chan, range);
 362        }
 363
 364        udelay(1);
 365
 366        pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
 367}
 368
 369static void pcl818_ai_clear_eoc(struct comedi_device *dev)
 370{
 371        /* writing any value clears the interrupt request */
 372        outb(0, dev->iobase + PCL818_STATUS_REG);
 373}
 374
 375static void pcl818_ai_soft_trig(struct comedi_device *dev)
 376{
 377        /* writing any value triggers a software conversion */
 378        outb(0, dev->iobase + PCL818_AI_LSB_REG);
 379}
 380
 381static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
 382                                              struct comedi_subdevice *s,
 383                                              unsigned int *chan)
 384{
 385        unsigned int val;
 386
 387        val = inb(dev->iobase + PCL818_FI_DATALO);
 388        val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
 389
 390        if (chan)
 391                *chan = val & 0xf;
 392
 393        return (val >> 4) & s->maxdata;
 394}
 395
 396static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
 397                                         struct comedi_subdevice *s,
 398                                         unsigned int *chan)
 399{
 400        unsigned int val;
 401
 402        val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
 403        val |= inb(dev->iobase + PCL818_AI_LSB_REG);
 404
 405        if (chan)
 406                *chan = val & 0xf;
 407
 408        return (val >> 4) & s->maxdata;
 409}
 410
 411static int pcl818_ai_eoc(struct comedi_device *dev,
 412                         struct comedi_subdevice *s,
 413                         struct comedi_insn *insn,
 414                         unsigned long context)
 415{
 416        unsigned int status;
 417
 418        status = inb(dev->iobase + PCL818_STATUS_REG);
 419        if (status & PCL818_STATUS_INT)
 420                return 0;
 421        return -EBUSY;
 422}
 423
 424static bool pcl818_ai_write_sample(struct comedi_device *dev,
 425                                   struct comedi_subdevice *s,
 426                                   unsigned int chan, unsigned int val)
 427{
 428        struct pcl818_private *devpriv = dev->private;
 429        struct comedi_cmd *cmd = &s->async->cmd;
 430        unsigned int expected_chan;
 431
 432        expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
 433        if (chan != expected_chan) {
 434                dev_dbg(dev->class_dev,
 435                        "A/D mode1/3 %s - channel dropout %d!=%d !\n",
 436                        (devpriv->dma) ? "DMA" :
 437                        (devpriv->usefifo) ? "FIFO" : "IRQ",
 438                        chan, expected_chan);
 439                s->async->events |= COMEDI_CB_ERROR;
 440                return false;
 441        }
 442
 443        comedi_buf_write_samples(s, &val, 1);
 444
 445        devpriv->act_chanlist_pos++;
 446        if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
 447                devpriv->act_chanlist_pos = 0;
 448
 449        if (cmd->stop_src == TRIG_COUNT &&
 450            s->async->scans_done >= cmd->stop_arg) {
 451                s->async->events |= COMEDI_CB_EOA;
 452                return false;
 453        }
 454
 455        return true;
 456}
 457
 458static void pcl818_handle_eoc(struct comedi_device *dev,
 459                              struct comedi_subdevice *s)
 460{
 461        unsigned int chan;
 462        unsigned int val;
 463
 464        if (pcl818_ai_eoc(dev, s, NULL, 0)) {
 465                dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
 466                s->async->events |= COMEDI_CB_ERROR;
 467                return;
 468        }
 469
 470        val = pcl818_ai_get_sample(dev, s, &chan);
 471        pcl818_ai_write_sample(dev, s, chan, val);
 472}
 473
 474static void pcl818_handle_dma(struct comedi_device *dev,
 475                              struct comedi_subdevice *s)
 476{
 477        struct pcl818_private *devpriv = dev->private;
 478        struct comedi_isadma *dma = devpriv->dma;
 479        struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
 480        unsigned short *ptr = desc->virt_addr;
 481        unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
 482        unsigned int chan;
 483        unsigned int val;
 484        int i;
 485
 486        /* restart dma with the next buffer */
 487        dma->cur_dma = 1 - dma->cur_dma;
 488        pcl818_ai_setup_dma(dev, s, nsamples);
 489
 490        for (i = 0; i < nsamples; i++) {
 491                val = ptr[i];
 492                chan = val & 0xf;
 493                val = (val >> 4) & s->maxdata;
 494                if (!pcl818_ai_write_sample(dev, s, chan, val))
 495                        break;
 496        }
 497}
 498
 499static void pcl818_handle_fifo(struct comedi_device *dev,
 500                               struct comedi_subdevice *s)
 501{
 502        unsigned int status;
 503        unsigned int chan;
 504        unsigned int val;
 505        int i, len;
 506
 507        status = inb(dev->iobase + PCL818_FI_STATUS);
 508
 509        if (status & 4) {
 510                dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
 511                s->async->events |= COMEDI_CB_ERROR;
 512                return;
 513        }
 514
 515        if (status & 1) {
 516                dev_err(dev->class_dev,
 517                        "A/D mode1/3 FIFO interrupt without data!\n");
 518                s->async->events |= COMEDI_CB_ERROR;
 519                return;
 520        }
 521
 522        if (status & 2)
 523                len = 512;
 524        else
 525                len = 0;
 526
 527        for (i = 0; i < len; i++) {
 528                val = pcl818_ai_get_fifo_sample(dev, s, &chan);
 529                if (!pcl818_ai_write_sample(dev, s, chan, val))
 530                        break;
 531        }
 532}
 533
 534static irqreturn_t pcl818_interrupt(int irq, void *d)
 535{
 536        struct comedi_device *dev = d;
 537        struct pcl818_private *devpriv = dev->private;
 538        struct comedi_subdevice *s = dev->read_subdev;
 539        struct comedi_cmd *cmd = &s->async->cmd;
 540
 541        if (!dev->attached || !devpriv->ai_cmd_running) {
 542                pcl818_ai_clear_eoc(dev);
 543                return IRQ_HANDLED;
 544        }
 545
 546        if (devpriv->ai_cmd_canceled) {
 547                /*
 548                 * The cleanup from ai_cancel() has been delayed
 549                 * until now because the card doesn't seem to like
 550                 * being reprogrammed while a DMA transfer is in
 551                 * progress.
 552                 */
 553                s->async->scans_done = cmd->stop_arg;
 554                s->cancel(dev, s);
 555                return IRQ_HANDLED;
 556        }
 557
 558        if (devpriv->dma)
 559                pcl818_handle_dma(dev, s);
 560        else if (devpriv->usefifo)
 561                pcl818_handle_fifo(dev, s);
 562        else
 563                pcl818_handle_eoc(dev, s);
 564
 565        pcl818_ai_clear_eoc(dev);
 566
 567        comedi_handle_events(dev, s);
 568        return IRQ_HANDLED;
 569}
 570
 571static int check_channel_list(struct comedi_device *dev,
 572                              struct comedi_subdevice *s,
 573                              unsigned int *chanlist, unsigned int n_chan)
 574{
 575        unsigned int chansegment[16];
 576        unsigned int i, nowmustbechan, seglen;
 577
 578        /* correct channel and range number check itself comedi/range.c */
 579        if (n_chan < 1) {
 580                dev_err(dev->class_dev, "range/channel list is empty!\n");
 581                return 0;
 582        }
 583
 584        if (n_chan > 1) {
 585                /*  first channel is every time ok */
 586                chansegment[0] = chanlist[0];
 587                /*  build part of chanlist */
 588                for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
 589                        /* we detect loop, this must by finish */
 590
 591                        if (chanlist[0] == chanlist[i])
 592                                break;
 593                        nowmustbechan =
 594                            (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
 595                        if (nowmustbechan != CR_CHAN(chanlist[i])) {
 596                                /*  channel list isn't continuous :-( */
 597                                dev_dbg(dev->class_dev,
 598                                        "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
 599                                        i, CR_CHAN(chanlist[i]), nowmustbechan,
 600                                        CR_CHAN(chanlist[0]));
 601                                return 0;
 602                        }
 603                        /*  well, this is next correct channel in list */
 604                        chansegment[i] = chanlist[i];
 605                }
 606
 607                /*  check whole chanlist */
 608                for (i = 0; i < n_chan; i++) {
 609                        if (chanlist[i] != chansegment[i % seglen]) {
 610                                dev_dbg(dev->class_dev,
 611                                        "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
 612                                        i, CR_CHAN(chansegment[i]),
 613                                        CR_RANGE(chansegment[i]),
 614                                        CR_AREF(chansegment[i]),
 615                                        CR_CHAN(chanlist[i % seglen]),
 616                                        CR_RANGE(chanlist[i % seglen]),
 617                                        CR_AREF(chansegment[i % seglen]));
 618                                return 0;       /*  chan/gain list is strange */
 619                        }
 620                }
 621        } else {
 622                seglen = 1;
 623        }
 624        return seglen;
 625}
 626
 627static int check_single_ended(unsigned int port)
 628{
 629        if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
 630                return 1;
 631        return 0;
 632}
 633
 634static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
 635                      struct comedi_cmd *cmd)
 636{
 637        const struct pcl818_board *board = dev->board_ptr;
 638        int err = 0;
 639
 640        /* Step 1 : check if triggers are trivially valid */
 641
 642        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 643        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 644        err |= comedi_check_trigger_src(&cmd->convert_src,
 645                                        TRIG_TIMER | TRIG_EXT);
 646        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 647        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 648
 649        if (err)
 650                return 1;
 651
 652        /* Step 2a : make sure trigger sources are unique */
 653
 654        err |= comedi_check_trigger_is_unique(cmd->convert_src);
 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->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_is(&cmd->scan_end_arg,
 675                                           cmd->chanlist_len);
 676
 677        if (cmd->stop_src == TRIG_COUNT)
 678                err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
 679        else    /* TRIG_NONE */
 680                err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 681
 682        if (err)
 683                return 3;
 684
 685        /* step 4: fix up any arguments */
 686
 687        if (cmd->convert_src == TRIG_TIMER) {
 688                unsigned int arg = cmd->convert_arg;
 689
 690                comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
 691                err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
 692        }
 693
 694        if (err)
 695                return 4;
 696
 697        /* step 5: complain about special chanlist considerations */
 698
 699        if (cmd->chanlist) {
 700                if (!check_channel_list(dev, s, cmd->chanlist,
 701                                        cmd->chanlist_len))
 702                        return 5;       /*  incorrect channels list */
 703        }
 704
 705        return 0;
 706}
 707
 708static int pcl818_ai_cmd(struct comedi_device *dev,
 709                         struct comedi_subdevice *s)
 710{
 711        struct pcl818_private *devpriv = dev->private;
 712        struct comedi_isadma *dma = devpriv->dma;
 713        struct comedi_cmd *cmd = &s->async->cmd;
 714        unsigned int ctrl = 0;
 715        unsigned int seglen;
 716
 717        if (devpriv->ai_cmd_running)
 718                return -EBUSY;
 719
 720        seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
 721        if (seglen < 1)
 722                return -EINVAL;
 723        pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
 724
 725        devpriv->ai_cmd_running = 1;
 726        devpriv->ai_cmd_canceled = 0;
 727        devpriv->act_chanlist_pos = 0;
 728
 729        if (cmd->convert_src == TRIG_TIMER)
 730                ctrl |= PCL818_CTRL_PACER_TRIG;
 731        else
 732                ctrl |= PCL818_CTRL_EXT_TRIG;
 733
 734        outb(0, dev->iobase + PCL818_CNTENABLE_REG);
 735
 736        if (dma) {
 737                /* setup and enable dma for the first buffer */
 738                dma->cur_dma = 0;
 739                pcl818_ai_setup_dma(dev, s, 0);
 740
 741                ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
 742                        PCL818_CTRL_DMAE;
 743        } else if (devpriv->usefifo) {
 744                /* enable FIFO */
 745                outb(1, dev->iobase + PCL818_FI_ENABLE);
 746        } else {
 747                ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
 748        }
 749        outb(ctrl, dev->iobase + PCL818_CTRL_REG);
 750
 751        if (cmd->convert_src == TRIG_TIMER) {
 752                comedi_8254_update_divisors(dev->pacer);
 753                comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
 754        }
 755
 756        return 0;
 757}
 758
 759static int pcl818_ai_cancel(struct comedi_device *dev,
 760                            struct comedi_subdevice *s)
 761{
 762        struct pcl818_private *devpriv = dev->private;
 763        struct comedi_isadma *dma = devpriv->dma;
 764        struct comedi_cmd *cmd = &s->async->cmd;
 765
 766        if (!devpriv->ai_cmd_running)
 767                return 0;
 768
 769        if (dma) {
 770                if (cmd->stop_src == TRIG_NONE ||
 771                    (cmd->stop_src == TRIG_COUNT &&
 772                     s->async->scans_done < cmd->stop_arg)) {
 773                        if (!devpriv->ai_cmd_canceled) {
 774                                /*
 775                                 * Wait for running dma transfer to end,
 776                                 * do cleanup in interrupt.
 777                                 */
 778                                devpriv->ai_cmd_canceled = 1;
 779                                return 0;
 780                        }
 781                }
 782                comedi_isadma_disable(dma->chan);
 783        }
 784
 785        outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
 786        comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
 787        pcl818_ai_clear_eoc(dev);
 788
 789        if (devpriv->usefifo) { /*  FIFO shutdown */
 790                outb(0, dev->iobase + PCL818_FI_INTCLR);
 791                outb(0, dev->iobase + PCL818_FI_FLUSH);
 792                outb(0, dev->iobase + PCL818_FI_ENABLE);
 793        }
 794        devpriv->ai_cmd_running = 0;
 795        devpriv->ai_cmd_canceled = 0;
 796
 797        return 0;
 798}
 799
 800static int pcl818_ai_insn_read(struct comedi_device *dev,
 801                               struct comedi_subdevice *s,
 802                               struct comedi_insn *insn,
 803                               unsigned int *data)
 804{
 805        unsigned int chan = CR_CHAN(insn->chanspec);
 806        unsigned int range = CR_RANGE(insn->chanspec);
 807        int ret = 0;
 808        int i;
 809
 810        outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
 811
 812        pcl818_ai_set_chan_range(dev, chan, range);
 813        pcl818_ai_set_chan_scan(dev, chan, chan);
 814
 815        for (i = 0; i < insn->n; i++) {
 816                pcl818_ai_clear_eoc(dev);
 817                pcl818_ai_soft_trig(dev);
 818
 819                ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
 820                if (ret)
 821                        break;
 822
 823                data[i] = pcl818_ai_get_sample(dev, s, NULL);
 824        }
 825        pcl818_ai_clear_eoc(dev);
 826
 827        return ret ? ret : insn->n;
 828}
 829
 830static int pcl818_ao_insn_write(struct comedi_device *dev,
 831                                struct comedi_subdevice *s,
 832                                struct comedi_insn *insn,
 833                                unsigned int *data)
 834{
 835        unsigned int chan = CR_CHAN(insn->chanspec);
 836        unsigned int val = s->readback[chan];
 837        int i;
 838
 839        for (i = 0; i < insn->n; i++) {
 840                val = data[i];
 841                outb((val & 0x000f) << 4,
 842                     dev->iobase + PCL818_AO_LSB_REG(chan));
 843                outb((val & 0x0ff0) >> 4,
 844                     dev->iobase + PCL818_AO_MSB_REG(chan));
 845        }
 846        s->readback[chan] = val;
 847
 848        return insn->n;
 849}
 850
 851static int pcl818_di_insn_bits(struct comedi_device *dev,
 852                               struct comedi_subdevice *s,
 853                               struct comedi_insn *insn,
 854                               unsigned int *data)
 855{
 856        data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
 857                  (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
 858
 859        return insn->n;
 860}
 861
 862static int pcl818_do_insn_bits(struct comedi_device *dev,
 863                               struct comedi_subdevice *s,
 864                               struct comedi_insn *insn,
 865                               unsigned int *data)
 866{
 867        if (comedi_dio_update_state(s, data)) {
 868                outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
 869                outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
 870        }
 871
 872        data[1] = s->state;
 873
 874        return insn->n;
 875}
 876
 877static void pcl818_reset(struct comedi_device *dev)
 878{
 879        const struct pcl818_board *board = dev->board_ptr;
 880        unsigned int chan;
 881
 882        /* flush and disable the FIFO */
 883        if (board->has_fifo) {
 884                outb(0, dev->iobase + PCL818_FI_INTCLR);
 885                outb(0, dev->iobase + PCL818_FI_FLUSH);
 886                outb(0, dev->iobase + PCL818_FI_ENABLE);
 887        }
 888
 889        /* disable analog input trigger */
 890        outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
 891        pcl818_ai_clear_eoc(dev);
 892
 893        pcl818_ai_set_chan_range(dev, 0, 0);
 894
 895        /* stop pacer */
 896        outb(0, dev->iobase + PCL818_CNTENABLE_REG);
 897
 898        /* set analog output channels to 0V */
 899        for (chan = 0; chan < board->n_aochan; chan++) {
 900                outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
 901                outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
 902        }
 903
 904        /* set all digital outputs low */
 905        outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
 906        outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
 907}
 908
 909static void pcl818_set_ai_range_table(struct comedi_device *dev,
 910                                      struct comedi_subdevice *s,
 911                                      struct comedi_devconfig *it)
 912{
 913        const struct pcl818_board *board = dev->board_ptr;
 914
 915        /* default to the range table from the boardinfo */
 916        s->range_table = board->ai_range_type;
 917
 918        /* now check the user config option based on the boardtype */
 919        if (board->is_818) {
 920                if (it->options[4] == 1 || it->options[4] == 10) {
 921                        /* secondary range list jumper selectable */
 922                        s->range_table = &range_pcl818l_h_ai;
 923                }
 924        } else {
 925                switch (it->options[4]) {
 926                case 0:
 927                        s->range_table = &range_bipolar10;
 928                        break;
 929                case 1:
 930                        s->range_table = &range_bipolar5;
 931                        break;
 932                case 2:
 933                        s->range_table = &range_bipolar2_5;
 934                        break;
 935                case 3:
 936                        s->range_table = &range718_bipolar1;
 937                        break;
 938                case 4:
 939                        s->range_table = &range718_bipolar0_5;
 940                        break;
 941                case 6:
 942                        s->range_table = &range_unipolar10;
 943                        break;
 944                case 7:
 945                        s->range_table = &range_unipolar5;
 946                        break;
 947                case 8:
 948                        s->range_table = &range718_unipolar2;
 949                        break;
 950                case 9:
 951                        s->range_table = &range718_unipolar1;
 952                        break;
 953                default:
 954                        s->range_table = &range_unknown;
 955                        break;
 956                }
 957        }
 958}
 959
 960static void pcl818_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
 961{
 962        struct pcl818_private *devpriv = dev->private;
 963
 964        /* only DMA channels 3 and 1 are valid */
 965        if (!(dma_chan == 3 || dma_chan == 1))
 966                return;
 967
 968        /* DMA uses two 16K buffers */
 969        devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
 970                                           PAGE_SIZE * 4, COMEDI_ISADMA_READ);
 971}
 972
 973static void pcl818_free_dma(struct comedi_device *dev)
 974{
 975        struct pcl818_private *devpriv = dev->private;
 976
 977        if (devpriv)
 978                comedi_isadma_free(devpriv->dma);
 979}
 980
 981static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 982{
 983        const struct pcl818_board *board = dev->board_ptr;
 984        struct pcl818_private *devpriv;
 985        struct comedi_subdevice *s;
 986        unsigned int osc_base;
 987        int ret;
 988
 989        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 990        if (!devpriv)
 991                return -ENOMEM;
 992
 993        ret = comedi_request_region(dev, it->options[0],
 994                                    board->has_fifo ? 0x20 : 0x10);
 995        if (ret)
 996                return ret;
 997
 998        /* we can use IRQ 2-7 for async command support */
 999        if (it->options[1] >= 2 && it->options[1] <= 7) {
1000                ret = request_irq(it->options[1], pcl818_interrupt, 0,
1001                                  dev->board_name, dev);
1002                if (ret == 0)
1003                        dev->irq = it->options[1];
1004        }
1005
1006        /* should we use the FIFO? */
1007        if (dev->irq && board->has_fifo && it->options[2] == -1)
1008                devpriv->usefifo = 1;
1009
1010        /* we need an IRQ to do DMA on channel 3 or 1 */
1011        if (dev->irq && board->has_dma)
1012                pcl818_alloc_dma(dev, it->options[2]);
1013
1014        /* use 1MHz or 10MHz oscilator */
1015        if ((it->options[3] == 0) || (it->options[3] == 10))
1016                osc_base = I8254_OSC_BASE_10MHZ;
1017        else
1018                osc_base = I8254_OSC_BASE_1MHZ;
1019
1020        dev->pacer = comedi_8254_init(dev->iobase + PCL818_TIMER_BASE,
1021                                      osc_base, I8254_IO8, 0);
1022        if (!dev->pacer)
1023                return -ENOMEM;
1024
1025        /* max sampling speed */
1026        devpriv->ns_min = board->ns_min;
1027        if (!board->is_818) {
1028                /* extended PCL718 to 100kHz DAC */
1029                if ((it->options[6] == 1) || (it->options[6] == 100))
1030                        devpriv->ns_min = 10000;
1031        }
1032
1033        ret = comedi_alloc_subdevices(dev, 4);
1034        if (ret)
1035                return ret;
1036
1037        s = &dev->subdevices[0];
1038        s->type         = COMEDI_SUBD_AI;
1039        s->subdev_flags = SDF_READABLE;
1040        if (check_single_ended(dev->iobase)) {
1041                s->n_chan       = 16;
1042                s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1043        } else {
1044                s->n_chan       = 8;
1045                s->subdev_flags |= SDF_DIFF;
1046        }
1047        s->maxdata      = 0x0fff;
1048
1049        pcl818_set_ai_range_table(dev, s, it);
1050
1051        s->insn_read    = pcl818_ai_insn_read;
1052        if (dev->irq) {
1053                dev->read_subdev = s;
1054                s->subdev_flags |= SDF_CMD_READ;
1055                s->len_chanlist = s->n_chan;
1056                s->do_cmdtest   = ai_cmdtest;
1057                s->do_cmd       = pcl818_ai_cmd;
1058                s->cancel       = pcl818_ai_cancel;
1059        }
1060
1061        /* Analog Output subdevice */
1062        s = &dev->subdevices[1];
1063        if (board->n_aochan) {
1064                s->type         = COMEDI_SUBD_AO;
1065                s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1066                s->n_chan       = board->n_aochan;
1067                s->maxdata      = 0x0fff;
1068                s->range_table  = &range_unipolar5;
1069                if (board->is_818) {
1070                        if ((it->options[4] == 1) || (it->options[4] == 10))
1071                                s->range_table = &range_unipolar10;
1072                        if (it->options[4] == 2)
1073                                s->range_table = &range_unknown;
1074                } else {
1075                        if ((it->options[5] == 1) || (it->options[5] == 10))
1076                                s->range_table = &range_unipolar10;
1077                        if (it->options[5] == 2)
1078                                s->range_table = &range_unknown;
1079                }
1080                s->insn_write   = pcl818_ao_insn_write;
1081
1082                ret = comedi_alloc_subdev_readback(s);
1083                if (ret)
1084                        return ret;
1085        } else {
1086                s->type         = COMEDI_SUBD_UNUSED;
1087        }
1088
1089        /* Digital Input subdevice */
1090        s = &dev->subdevices[2];
1091        s->type         = COMEDI_SUBD_DI;
1092        s->subdev_flags = SDF_READABLE;
1093        s->n_chan       = 16;
1094        s->maxdata      = 1;
1095        s->range_table  = &range_digital;
1096        s->insn_bits    = pcl818_di_insn_bits;
1097
1098        /* Digital Output subdevice */
1099        s = &dev->subdevices[3];
1100        s->type         = COMEDI_SUBD_DO;
1101        s->subdev_flags = SDF_WRITABLE;
1102        s->n_chan       = 16;
1103        s->maxdata      = 1;
1104        s->range_table  = &range_digital;
1105        s->insn_bits    = pcl818_do_insn_bits;
1106
1107        pcl818_reset(dev);
1108
1109        return 0;
1110}
1111
1112static void pcl818_detach(struct comedi_device *dev)
1113{
1114        struct pcl818_private *devpriv = dev->private;
1115
1116        if (devpriv) {
1117                pcl818_ai_cancel(dev, dev->read_subdev);
1118                pcl818_reset(dev);
1119        }
1120        pcl818_free_dma(dev);
1121        comedi_legacy_detach(dev);
1122}
1123
1124static struct comedi_driver pcl818_driver = {
1125        .driver_name    = "pcl818",
1126        .module         = THIS_MODULE,
1127        .attach         = pcl818_attach,
1128        .detach         = pcl818_detach,
1129        .board_name     = &boardtypes[0].name,
1130        .num_names      = ARRAY_SIZE(boardtypes),
1131        .offset         = sizeof(struct pcl818_board),
1132};
1133module_comedi_driver(pcl818_driver);
1134
1135MODULE_AUTHOR("Comedi http://www.comedi.org");
1136MODULE_DESCRIPTION("Comedi low-level driver");
1137MODULE_LICENSE("GPL");
1138