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