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