root/drivers/staging/comedi/drivers/pcl818.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcl818_ai_setup_dma
  2. pcl818_ai_set_chan_range
  3. pcl818_ai_set_chan_scan
  4. pcl818_ai_setup_chanlist
  5. pcl818_ai_clear_eoc
  6. pcl818_ai_soft_trig
  7. pcl818_ai_get_fifo_sample
  8. pcl818_ai_get_sample
  9. pcl818_ai_eoc
  10. pcl818_ai_write_sample
  11. pcl818_handle_eoc
  12. pcl818_handle_dma
  13. pcl818_handle_fifo
  14. pcl818_interrupt
  15. check_channel_list
  16. check_single_ended
  17. ai_cmdtest
  18. pcl818_ai_cmd
  19. pcl818_ai_cancel
  20. pcl818_ai_insn_read
  21. pcl818_ao_insn_write
  22. pcl818_di_insn_bits
  23. pcl818_do_insn_bits
  24. pcl818_reset
  25. pcl818_set_ai_range_table
  26. pcl818_alloc_dma
  27. pcl818_free_dma
  28. pcl818_attach
  29. pcl818_detach

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

/* [<][>][^][v][top][bottom][index][help] */