linux/drivers/staging/comedi/drivers/pcl816.c
<<
>>
Prefs
   1/*
   2   comedi/drivers/pcl816.c
   3
   4   Author:  Juan Grigera <juan@grigera.com.ar>
   5            based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
   6
   7   hardware driver for Advantech cards:
   8    card:   PCL-816, PCL814B
   9    driver: pcl816
  10*/
  11/*
  12Driver: pcl816
  13Description: Advantech PCL-816 cards, PCL-814
  14Author: Juan Grigera <juan@grigera.com.ar>
  15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
  16Status: works
  17Updated: Tue,  2 Apr 2002 23:15:21 -0800
  18
  19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
  20Differences are at resolution (16 vs 12 bits).
  21
  22The driver support AI command mode, other subdevices not written.
  23
  24Analog output and digital input and output are not supported.
  25
  26Configuration Options:
  27  [0] - IO Base
  28  [1] - IRQ     (0=disable, 2, 3, 4, 5, 6, 7)
  29  [2] - DMA     (0=disable, 1, 3)
  30  [3] - 0, 10=10MHz clock for 8254
  31            1= 1MHz clock for 8254
  32
  33*/
  34
  35#include <linux/module.h>
  36#include "../comedidev.h"
  37
  38#include <linux/gfp.h>
  39#include <linux/delay.h>
  40#include <linux/io.h>
  41#include <linux/interrupt.h>
  42#include <asm/dma.h>
  43
  44#include "comedi_fc.h"
  45#include "8253.h"
  46
  47/*
  48 * Register I/O map
  49 */
  50#define PCL816_DO_DI_LSB_REG                    0x00
  51#define PCL816_DO_DI_MSB_REG                    0x01
  52#define PCL816_TIMER_BASE                       0x04
  53#define PCL816_AI_LSB_REG                       0x08
  54#define PCL816_AI_MSB_REG                       0x09
  55#define PCL816_RANGE_REG                        0x09
  56#define PCL816_CLRINT_REG                       0x0a
  57#define PCL816_MUX_REG                          0x0b
  58#define PCL816_MUX_SCAN(_first, _last)          (((_last) << 4) | (_first))
  59#define PCL816_CTRL_REG                         0x0c
  60#define PCL816_CTRL_DISABLE_TRIG                (0 << 0)
  61#define PCL816_CTRL_SOFT_TRIG                   (1 << 0)
  62#define PCL816_CTRL_PACER_TRIG                  (1 << 1)
  63#define PCL816_CTRL_EXT_TRIG                    (1 << 2)
  64#define PCL816_CTRL_POE                         (1 << 3)
  65#define PCL816_CTRL_DMAEN                       (1 << 4)
  66#define PCL816_CTRL_INTEN                       (1 << 5)
  67#define PCL816_CTRL_DMASRC_SLOT0                (0 << 6)
  68#define PCL816_CTRL_DMASRC_SLOT1                (1 << 6)
  69#define PCL816_CTRL_DMASRC_SLOT2                (2 << 6)
  70#define PCL816_STATUS_REG                       0x0d
  71#define PCL816_STATUS_NEXT_CHAN_MASK            (0xf << 0)
  72#define PCL816_STATUS_INTSRC_MASK               (3 << 4)
  73#define PCL816_STATUS_INTSRC_SLOT0              (0 << 4)
  74#define PCL816_STATUS_INTSRC_SLOT1              (1 << 4)
  75#define PCL816_STATUS_INTSRC_SLOT2              (2 << 4)
  76#define PCL816_STATUS_INTSRC_DMA                (3 << 4)
  77#define PCL816_STATUS_INTACT                    (1 << 6)
  78#define PCL816_STATUS_DRDY                      (1 << 7)
  79
  80#define MAGIC_DMA_WORD 0x5a5a
  81
  82static const struct comedi_lrange range_pcl816 = {
  83        8, {
  84                BIP_RANGE(10),
  85                BIP_RANGE(5),
  86                BIP_RANGE(2.5),
  87                BIP_RANGE(1.25),
  88                UNI_RANGE(10),
  89                UNI_RANGE(5),
  90                UNI_RANGE(2.5),
  91                UNI_RANGE(1.25)
  92        }
  93};
  94
  95struct pcl816_board {
  96        const char *name;
  97        int ai_maxdata;
  98        int ao_maxdata;
  99        int ai_chanlist;
 100};
 101
 102static const struct pcl816_board boardtypes[] = {
 103        {
 104                .name           = "pcl816",
 105                .ai_maxdata     = 0xffff,
 106                .ao_maxdata     = 0xffff,
 107                .ai_chanlist    = 1024,
 108        }, {
 109                .name           = "pcl814b",
 110                .ai_maxdata     = 0x3fff,
 111                .ao_maxdata     = 0x3fff,
 112                .ai_chanlist    = 1024,
 113        },
 114};
 115
 116struct pcl816_private {
 117        unsigned int dma;       /*  used DMA, 0=don't use DMA */
 118        unsigned int dmapages;
 119        unsigned int hwdmasize;
 120        unsigned long dmabuf[2];        /*  pointers to begin of DMA buffers */
 121        unsigned int hwdmaptr[2];       /*  hardware address of DMA buffers */
 122        int next_dma_buf;       /*  which DMA buffer will be used next round */
 123        long dma_runs_to_end;   /*  how many we must permorm DMA transfer to end of record */
 124        unsigned long last_dma_run;     /*  how many bytes we must transfer on last DMA page */
 125        int ai_act_scan;        /*  how many scans we finished */
 126        unsigned int ai_poll_ptr;       /*  how many sampes transfer poll */
 127        unsigned int divisor1;
 128        unsigned int divisor2;
 129        unsigned int ai_cmd_running:1;
 130        unsigned int ai_cmd_canceled:1;
 131};
 132
 133static int check_channel_list(struct comedi_device *dev,
 134                              struct comedi_subdevice *s,
 135                              unsigned int *chanlist, unsigned int chanlen);
 136
 137static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
 138{
 139        struct pcl816_private *devpriv = dev->private;
 140        unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
 141
 142        i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
 143        i8254_write(timer_base, 0, 0, 0x00ff);
 144        udelay(1);
 145
 146        i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
 147        i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
 148        udelay(1);
 149
 150        if (load_counters) {
 151                i8254_write(timer_base, 0, 2, devpriv->divisor2);
 152                i8254_write(timer_base, 0, 1, devpriv->divisor1);
 153        }
 154}
 155
 156static void pcl816_ai_setup_dma(struct comedi_device *dev,
 157                                struct comedi_subdevice *s)
 158{
 159        struct pcl816_private *devpriv = dev->private;
 160        struct comedi_cmd *cmd = &s->async->cmd;
 161        unsigned int dma_flags;
 162        unsigned int bytes;
 163
 164        bytes = devpriv->hwdmasize;
 165        if (cmd->stop_src == TRIG_COUNT) {
 166                /*  how many */
 167                bytes = s->async->cmd.chanlist_len *
 168                s->async->cmd.chanlist_len *
 169                sizeof(short);
 170
 171                /*  how many DMA pages we must fill */
 172                devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
 173
 174                /* on last dma transfer must be moved */
 175                devpriv->last_dma_run = bytes % devpriv->hwdmasize;
 176                devpriv->dma_runs_to_end--;
 177                if (devpriv->dma_runs_to_end >= 0)
 178                        bytes = devpriv->hwdmasize;
 179        } else
 180                devpriv->dma_runs_to_end = -1;
 181
 182        devpriv->next_dma_buf = 0;
 183        set_dma_mode(devpriv->dma, DMA_MODE_READ);
 184        dma_flags = claim_dma_lock();
 185        clear_dma_ff(devpriv->dma);
 186        set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
 187        set_dma_count(devpriv->dma, bytes);
 188        release_dma_lock(dma_flags);
 189        enable_dma(devpriv->dma);
 190}
 191
 192static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
 193                                     struct comedi_subdevice *s)
 194{
 195        struct pcl816_private *devpriv = dev->private;
 196        struct comedi_cmd *cmd = &s->async->cmd;
 197        unsigned long dma_flags;
 198
 199        disable_dma(devpriv->dma);
 200        if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
 201                /* switch dma bufs */
 202                devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
 203                set_dma_mode(devpriv->dma, DMA_MODE_READ);
 204                dma_flags = claim_dma_lock();
 205                set_dma_addr(devpriv->dma,
 206                             devpriv->hwdmaptr[devpriv->next_dma_buf]);
 207                if (devpriv->dma_runs_to_end)
 208                        set_dma_count(devpriv->dma, devpriv->hwdmasize);
 209                else
 210                        set_dma_count(devpriv->dma, devpriv->last_dma_run);
 211                release_dma_lock(dma_flags);
 212                enable_dma(devpriv->dma);
 213        }
 214
 215        devpriv->dma_runs_to_end--;
 216}
 217
 218static void pcl816_ai_set_chan_range(struct comedi_device *dev,
 219                                     unsigned int chan,
 220                                     unsigned int range)
 221{
 222        outb(chan, dev->iobase + PCL816_MUX_REG);
 223        outb(range, dev->iobase + PCL816_RANGE_REG);
 224}
 225
 226static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
 227                                    unsigned int first_chan,
 228                                    unsigned int last_chan)
 229{
 230        outb(PCL816_MUX_SCAN(first_chan, last_chan),
 231             dev->iobase + PCL816_MUX_REG);
 232}
 233
 234static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
 235                                     unsigned int *chanlist,
 236                                     unsigned int seglen)
 237{
 238        unsigned int first_chan = CR_CHAN(chanlist[0]);
 239        unsigned int last_chan;
 240        unsigned int range;
 241        unsigned int i;
 242
 243        /* store range list to card */
 244        for (i = 0; i < seglen; i++) {
 245                last_chan = CR_CHAN(chanlist[i]);
 246                range = CR_RANGE(chanlist[i]);
 247
 248                pcl816_ai_set_chan_range(dev, last_chan, range);
 249        }
 250
 251        udelay(1);
 252
 253        pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
 254}
 255
 256static void pcl816_ai_clear_eoc(struct comedi_device *dev)
 257{
 258        /* writing any value clears the interrupt request */
 259        outb(0, dev->iobase + PCL816_CLRINT_REG);
 260}
 261
 262static void pcl816_ai_soft_trig(struct comedi_device *dev)
 263{
 264        /* writing any value triggers a software conversion */
 265        outb(0, dev->iobase + PCL816_AI_LSB_REG);
 266}
 267
 268static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
 269                                         struct comedi_subdevice *s)
 270{
 271        unsigned int val;
 272
 273        val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
 274        val |= inb(dev->iobase + PCL816_AI_LSB_REG);
 275
 276        return val & s->maxdata;
 277}
 278
 279static int pcl816_ai_eoc(struct comedi_device *dev,
 280                         struct comedi_subdevice *s,
 281                         struct comedi_insn *insn,
 282                         unsigned long context)
 283{
 284        unsigned int status;
 285
 286        status = inb(dev->iobase + PCL816_STATUS_REG);
 287        if ((status & PCL816_STATUS_DRDY) == 0)
 288                return 0;
 289        return -EBUSY;
 290}
 291
 292static bool pcl816_ai_next_chan(struct comedi_device *dev,
 293                                struct comedi_subdevice *s)
 294{
 295        struct pcl816_private *devpriv = dev->private;
 296        struct comedi_cmd *cmd = &s->async->cmd;
 297
 298        s->async->events |= COMEDI_CB_BLOCK;
 299
 300        s->async->cur_chan++;
 301        if (s->async->cur_chan >= cmd->chanlist_len) {
 302                s->async->cur_chan = 0;
 303                devpriv->ai_act_scan++;
 304                s->async->events |= COMEDI_CB_EOS;
 305        }
 306
 307        if (cmd->stop_src == TRIG_COUNT &&
 308            devpriv->ai_act_scan >= cmd->stop_arg) {
 309                /* all data sampled */
 310                s->async->events |= COMEDI_CB_EOA;
 311                return false;
 312        }
 313
 314        return true;
 315}
 316
 317static void transfer_from_dma_buf(struct comedi_device *dev,
 318                                  struct comedi_subdevice *s,
 319                                  unsigned short *ptr,
 320                                  unsigned int bufptr, unsigned int len)
 321{
 322        int i;
 323
 324        for (i = 0; i < len; i++) {
 325                comedi_buf_put(s->async, ptr[bufptr++]);
 326
 327                if (!pcl816_ai_next_chan(dev, s))
 328                        return;
 329        }
 330}
 331
 332static irqreturn_t pcl816_interrupt(int irq, void *d)
 333{
 334        struct comedi_device *dev = d;
 335        struct comedi_subdevice *s = dev->read_subdev;
 336        struct pcl816_private *devpriv = dev->private;
 337        unsigned short *ptr;
 338        unsigned int bufptr;
 339        unsigned int len;
 340
 341        if (!dev->attached || !devpriv->ai_cmd_running) {
 342                pcl816_ai_clear_eoc(dev);
 343                return IRQ_HANDLED;
 344        }
 345
 346        if (devpriv->ai_cmd_canceled) {
 347                devpriv->ai_cmd_canceled = 0;
 348                pcl816_ai_clear_eoc(dev);
 349                return IRQ_HANDLED;
 350        }
 351
 352        ptr = (unsigned short *)devpriv->dmabuf[devpriv->next_dma_buf];
 353
 354        pcl816_ai_setup_next_dma(dev, s);
 355
 356        len = (devpriv->hwdmasize >> 1) - devpriv->ai_poll_ptr;
 357        bufptr = devpriv->ai_poll_ptr;
 358        devpriv->ai_poll_ptr = 0;
 359
 360        transfer_from_dma_buf(dev, s, ptr, bufptr, len);
 361
 362        pcl816_ai_clear_eoc(dev);
 363
 364        cfc_handle_events(dev, s);
 365        return IRQ_HANDLED;
 366}
 367
 368static int pcl816_ai_cmdtest(struct comedi_device *dev,
 369                             struct comedi_subdevice *s, struct comedi_cmd *cmd)
 370{
 371        struct pcl816_private *devpriv = dev->private;
 372        int err = 0;
 373        int tmp;
 374
 375        /* Step 1 : check if triggers are trivially valid */
 376
 377        err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
 378        err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
 379        err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
 380        err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 381        err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
 382
 383        if (err)
 384                return 1;
 385
 386        /* Step 2a : make sure trigger sources are unique */
 387
 388        err |= cfc_check_trigger_is_unique(cmd->convert_src);
 389        err |= cfc_check_trigger_is_unique(cmd->stop_src);
 390
 391        /* Step 2b : and mutually compatible */
 392
 393        if (err)
 394                return 2;
 395
 396
 397        /* Step 3: check if arguments are trivially valid */
 398
 399        err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
 400        err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 401
 402        if (cmd->convert_src == TRIG_TIMER)
 403                err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
 404        else    /* TRIG_EXT */
 405                err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
 406
 407        err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
 408
 409        if (cmd->stop_src == TRIG_COUNT)
 410                err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
 411        else    /* TRIG_NONE */
 412                err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
 413
 414        if (err)
 415                return 3;
 416
 417
 418        /* step 4: fix up any arguments */
 419        if (cmd->convert_src == TRIG_TIMER) {
 420                tmp = cmd->convert_arg;
 421                i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
 422                                          &devpriv->divisor1,
 423                                          &devpriv->divisor2,
 424                                          &cmd->convert_arg, cmd->flags);
 425                if (cmd->convert_arg < 10000)
 426                        cmd->convert_arg = 10000;
 427                if (tmp != cmd->convert_arg)
 428                        err++;
 429        }
 430
 431        if (err)
 432                return 4;
 433
 434
 435        /* step 5: complain about special chanlist considerations */
 436
 437        if (cmd->chanlist) {
 438                if (!check_channel_list(dev, s, cmd->chanlist,
 439                                        cmd->chanlist_len))
 440                        return 5;       /*  incorrect channels list */
 441        }
 442
 443        return 0;
 444}
 445
 446static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
 447{
 448        struct pcl816_private *devpriv = dev->private;
 449        struct comedi_cmd *cmd = &s->async->cmd;
 450        unsigned int ctrl;
 451        unsigned int seglen;
 452
 453        if (devpriv->ai_cmd_running)
 454                return -EBUSY;
 455
 456        pcl816_start_pacer(dev, false);
 457
 458        seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
 459        if (seglen < 1)
 460                return -EINVAL;
 461        pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
 462        udelay(1);
 463
 464        devpriv->ai_act_scan = 0;
 465        s->async->cur_chan = 0;
 466        devpriv->ai_cmd_running = 1;
 467        devpriv->ai_poll_ptr = 0;
 468        devpriv->ai_cmd_canceled = 0;
 469
 470        pcl816_ai_setup_dma(dev, s);
 471
 472        pcl816_start_pacer(dev, true);
 473
 474        ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
 475        if (cmd->convert_src == TRIG_TIMER)
 476                ctrl |= PCL816_CTRL_PACER_TRIG;
 477        else    /* TRIG_EXT */
 478                ctrl |= PCL816_CTRL_EXT_TRIG;
 479
 480        outb(ctrl, dev->iobase + PCL816_CTRL_REG);
 481        outb((devpriv->dma << 4) | dev->irq, dev->iobase + PCL816_STATUS_REG);
 482
 483        return 0;
 484}
 485
 486static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
 487{
 488        struct pcl816_private *devpriv = dev->private;
 489        unsigned long flags;
 490        unsigned int top1, top2, i;
 491
 492        spin_lock_irqsave(&dev->spinlock, flags);
 493
 494        for (i = 0; i < 20; i++) {
 495                top1 = get_dma_residue(devpriv->dma);   /*  where is now DMA */
 496                top2 = get_dma_residue(devpriv->dma);
 497                if (top1 == top2)
 498                        break;
 499        }
 500        if (top1 != top2) {
 501                spin_unlock_irqrestore(&dev->spinlock, flags);
 502                return 0;
 503        }
 504
 505        /*  where is now DMA in buffer */
 506        top1 = devpriv->hwdmasize - top1;
 507        top1 >>= 1;             /*  sample position */
 508        top2 = top1 - devpriv->ai_poll_ptr;
 509        if (top2 < 1) {         /*  no new samples */
 510                spin_unlock_irqrestore(&dev->spinlock, flags);
 511                return 0;
 512        }
 513
 514        transfer_from_dma_buf(dev, s,
 515                              (unsigned short *)devpriv->dmabuf[devpriv->
 516                                                                next_dma_buf],
 517                              devpriv->ai_poll_ptr, top2);
 518
 519        devpriv->ai_poll_ptr = top1;    /*  new buffer position */
 520        spin_unlock_irqrestore(&dev->spinlock, flags);
 521
 522        cfc_handle_events(dev, s);
 523
 524        return s->async->buf_write_count - s->async->buf_read_count;
 525}
 526
 527static int pcl816_ai_cancel(struct comedi_device *dev,
 528                            struct comedi_subdevice *s)
 529{
 530        struct pcl816_private *devpriv = dev->private;
 531
 532        if (!devpriv->ai_cmd_running)
 533                return 0;
 534
 535        outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
 536        pcl816_ai_clear_eoc(dev);
 537
 538        /* Stop pacer */
 539        i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
 540                        2, I8254_MODE0 | I8254_BINARY);
 541        i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
 542                        1, I8254_MODE0 | I8254_BINARY);
 543
 544        devpriv->ai_cmd_running = 0;
 545        devpriv->ai_cmd_canceled = 1;
 546
 547        return 0;
 548}
 549
 550static int
 551check_channel_list(struct comedi_device *dev,
 552                   struct comedi_subdevice *s, unsigned int *chanlist,
 553                   unsigned int chanlen)
 554{
 555        unsigned int chansegment[16];
 556        unsigned int i, nowmustbechan, seglen, segpos;
 557
 558        /*  correct channel and range number check itself comedi/range.c */
 559        if (chanlen < 1) {
 560                comedi_error(dev, "range/channel list is empty!");
 561                return 0;
 562        }
 563
 564        if (chanlen > 1) {
 565                /*  first channel is every time ok */
 566                chansegment[0] = chanlist[0];
 567                for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
 568                        /*  we detect loop, this must by finish */
 569                            if (chanlist[0] == chanlist[i])
 570                                break;
 571                        nowmustbechan =
 572                            (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
 573                        if (nowmustbechan != CR_CHAN(chanlist[i])) {
 574                                /*  channel list isn't continuous :-( */
 575                                dev_dbg(dev->class_dev,
 576                                        "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
 577                                        i, CR_CHAN(chanlist[i]), nowmustbechan,
 578                                        CR_CHAN(chanlist[0]));
 579                                return 0;
 580                        }
 581                        /*  well, this is next correct channel in list */
 582                        chansegment[i] = chanlist[i];
 583                }
 584
 585                /*  check whole chanlist */
 586                for (i = 0, segpos = 0; i < chanlen; i++) {
 587                            if (chanlist[i] != chansegment[i % seglen]) {
 588                                dev_dbg(dev->class_dev,
 589                                        "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
 590                                        i, CR_CHAN(chansegment[i]),
 591                                        CR_RANGE(chansegment[i]),
 592                                        CR_AREF(chansegment[i]),
 593                                        CR_CHAN(chanlist[i % seglen]),
 594                                        CR_RANGE(chanlist[i % seglen]),
 595                                        CR_AREF(chansegment[i % seglen]));
 596                                return 0;       /*  chan/gain list is strange */
 597                        }
 598                }
 599        } else {
 600                seglen = 1;
 601        }
 602
 603        return seglen;  /*  we can serve this with MUX logic */
 604}
 605
 606static int pcl816_ai_insn_read(struct comedi_device *dev,
 607                               struct comedi_subdevice *s,
 608                               struct comedi_insn *insn,
 609                               unsigned int *data)
 610{
 611        unsigned int chan = CR_CHAN(insn->chanspec);
 612        unsigned int range = CR_RANGE(insn->chanspec);
 613        int ret = 0;
 614        int i;
 615
 616        outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
 617
 618        pcl816_ai_set_chan_range(dev, chan, range);
 619        pcl816_ai_set_chan_scan(dev, chan, chan);
 620
 621        for (i = 0; i < insn->n; i++) {
 622                pcl816_ai_clear_eoc(dev);
 623                pcl816_ai_soft_trig(dev);
 624
 625                ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
 626                if (ret)
 627                        break;
 628
 629                data[i] = pcl816_ai_get_sample(dev, s);
 630        }
 631        outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
 632        pcl816_ai_clear_eoc(dev);
 633
 634        return ret ? ret : insn->n;
 635}
 636
 637static int pcl816_di_insn_bits(struct comedi_device *dev,
 638                               struct comedi_subdevice *s,
 639                               struct comedi_insn *insn,
 640                               unsigned int *data)
 641{
 642        data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
 643                  (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
 644
 645        return insn->n;
 646}
 647
 648static int pcl816_do_insn_bits(struct comedi_device *dev,
 649                               struct comedi_subdevice *s,
 650                               struct comedi_insn *insn,
 651                               unsigned int *data)
 652{
 653        if (comedi_dio_update_state(s, data)) {
 654                outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
 655                outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
 656        }
 657
 658        data[1] = s->state;
 659
 660        return insn->n;
 661}
 662
 663static void pcl816_reset(struct comedi_device *dev)
 664{
 665        unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
 666
 667        outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
 668        pcl816_ai_set_chan_range(dev, 0, 0);
 669        pcl816_ai_clear_eoc(dev);
 670
 671        /* Stop pacer */
 672        i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
 673        i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
 674        i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
 675
 676        /* set all digital outputs low */
 677        outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
 678        outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
 679}
 680
 681static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
 682{
 683        const struct pcl816_board *board = comedi_board(dev);
 684        struct pcl816_private *devpriv;
 685        struct comedi_subdevice *s;
 686        int ret;
 687        int i;
 688
 689        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 690        if (!devpriv)
 691                return -ENOMEM;
 692
 693        ret = comedi_request_region(dev, it->options[0], 0x10);
 694        if (ret)
 695                return ret;
 696
 697        /* we can use IRQ 2-7 for async command support */
 698        if (it->options[1] >= 2 && it->options[1] <= 7) {
 699                ret = request_irq(it->options[1], pcl816_interrupt, 0,
 700                                  dev->board_name, dev);
 701                if (ret == 0)
 702                        dev->irq = it->options[1];
 703        }
 704
 705        /* we need an IRQ to do DMA on channel 3 or 1 */
 706        if (dev->irq && (it->options[2] == 3 || it->options[2] == 1)) {
 707                ret = request_dma(it->options[2], dev->board_name);
 708                if (ret) {
 709                        dev_err(dev->class_dev,
 710                                "unable to request DMA channel %d\n",
 711                                it->options[2]);
 712                        return -EBUSY;
 713                }
 714                devpriv->dma = it->options[2];
 715
 716                devpriv->dmapages = 2;  /* we need 16KB */
 717                devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
 718
 719                for (i = 0; i < 2; i++) {
 720                        unsigned long dmabuf;
 721
 722                        dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
 723                        if (!dmabuf)
 724                                return -ENOMEM;
 725
 726                        devpriv->dmabuf[i] = dmabuf;
 727                        devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
 728                }
 729        }
 730
 731        ret = comedi_alloc_subdevices(dev, 4);
 732        if (ret)
 733                return ret;
 734
 735        s = &dev->subdevices[0];
 736        s->type         = COMEDI_SUBD_AI;
 737        s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
 738        s->n_chan       = 16;
 739        s->maxdata      = board->ai_maxdata;
 740        s->range_table  = &range_pcl816;
 741        s->insn_read    = pcl816_ai_insn_read;
 742        if (devpriv->dma) {
 743                dev->read_subdev = s;
 744                s->subdev_flags |= SDF_CMD_READ;
 745                s->len_chanlist = board->ai_chanlist;
 746                s->do_cmdtest   = pcl816_ai_cmdtest;
 747                s->do_cmd       = pcl816_ai_cmd;
 748                s->poll         = pcl816_ai_poll;
 749                s->cancel       = pcl816_ai_cancel;
 750        }
 751
 752        /* Analog OUtput subdevice */
 753        s = &dev->subdevices[2];
 754        s->type         = COMEDI_SUBD_UNUSED;
 755#if 0
 756        subdevs[1] = COMEDI_SUBD_AO;
 757        s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
 758        s->n_chan = 1;
 759        s->maxdata = board->ao_maxdata;
 760        s->range_table = &range_pcl816;
 761#endif
 762
 763        /* Digital Input subdevice */
 764        s = &dev->subdevices[2];
 765        s->type         = COMEDI_SUBD_DI;
 766        s->subdev_flags = SDF_READABLE;
 767        s->n_chan       = 16;
 768        s->maxdata      = 1;
 769        s->range_table  = &range_digital;
 770        s->insn_bits    = pcl816_di_insn_bits;
 771
 772        /* Digital Output subdevice */
 773        s = &dev->subdevices[3];
 774        s->type         = COMEDI_SUBD_DO;
 775        s->subdev_flags = SDF_WRITABLE;
 776        s->n_chan       = 16;
 777        s->maxdata      = 1;
 778        s->range_table  = &range_digital;
 779        s->insn_bits    = pcl816_do_insn_bits;
 780
 781        pcl816_reset(dev);
 782
 783        return 0;
 784}
 785
 786static void pcl816_detach(struct comedi_device *dev)
 787{
 788        struct pcl816_private *devpriv = dev->private;
 789
 790        if (dev->private) {
 791                pcl816_ai_cancel(dev, dev->read_subdev);
 792                pcl816_reset(dev);
 793                if (devpriv->dma)
 794                        free_dma(devpriv->dma);
 795                if (devpriv->dmabuf[0])
 796                        free_pages(devpriv->dmabuf[0], devpriv->dmapages);
 797                if (devpriv->dmabuf[1])
 798                        free_pages(devpriv->dmabuf[1], devpriv->dmapages);
 799        }
 800        comedi_legacy_detach(dev);
 801}
 802
 803static struct comedi_driver pcl816_driver = {
 804        .driver_name    = "pcl816",
 805        .module         = THIS_MODULE,
 806        .attach         = pcl816_attach,
 807        .detach         = pcl816_detach,
 808        .board_name     = &boardtypes[0].name,
 809        .num_names      = ARRAY_SIZE(boardtypes),
 810        .offset         = sizeof(struct pcl816_board),
 811};
 812module_comedi_driver(pcl816_driver);
 813
 814MODULE_AUTHOR("Comedi http://www.comedi.org");
 815MODULE_DESCRIPTION("Comedi low-level driver");
 816MODULE_LICENSE("GPL");
 817