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