pcl816.c revision fe0ff175329a83de8f9aac7c0b759cde15cb2247
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 "../comedidev.h" 36 37#include <linux/ioport.h> 38#include <linux/mc146818rtc.h> 39#include <linux/delay.h> 40#include <asm/dma.h> 41 42#include "8253.h" 43 44#define DEBUG(x) x 45 46// boards constants 47// IO space len 48#define PCLx1x_RANGE 16 49 50//#define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) 51 52// INTEL 8254 counters 53#define PCL816_CTR0 4 54#define PCL816_CTR1 5 55#define PCL816_CTR2 6 56// R: counter read-back register W: counter control 57#define PCL816_CTRCTL 7 58 59// R: A/D high byte W: A/D range control 60#define PCL816_RANGE 9 61// W: clear INT request 62#define PCL816_CLRINT 10 63// R: next mux scan channel W: mux scan channel & range control pointer 64#define PCL816_MUX 11 65// R/W: operation control register 66#define PCL816_CONTROL 12 67 68// R: return status byte W: set DMA/IRQ 69#define PCL816_STATUS 13 70#define PCL816_STATUS_DRDY_MASK 0x80 71 72// R: low byte of A/D W: soft A/D trigger 73#define PCL816_AD_LO 8 74// R: high byte of A/D W: A/D range control 75#define PCL816_AD_HI 9 76 77// type of interrupt handler 78#define INT_TYPE_AI1_INT 1 79#define INT_TYPE_AI1_DMA 2 80#define INT_TYPE_AI3_INT 4 81#define INT_TYPE_AI3_DMA 5 82#ifdef unused 83#define INT_TYPE_AI1_DMA_RTC 9 84#define INT_TYPE_AI3_DMA_RTC 10 85 86// RTC stuff... 87#define RTC_IRQ 8 88#define RTC_IO_EXTENT 0x10 89#endif 90 91#define MAGIC_DMA_WORD 0x5a5a 92 93static const struct comedi_lrange range_pcl816 = { 8, { 94 BIP_RANGE(10), 95 BIP_RANGE(5), 96 BIP_RANGE(2.5), 97 BIP_RANGE(1.25), 98 UNI_RANGE(10), 99 UNI_RANGE(5), 100 UNI_RANGE(2.5), 101 UNI_RANGE(1.25), 102 } 103}; 104typedef struct { 105 const char *name; // board name 106 int n_ranges; // len of range list 107 int n_aichan; // num of A/D chans in diferencial mode 108 unsigned int ai_ns_min; // minimal alllowed delay between samples (in ns) 109 int n_aochan; // num of D/A chans 110 int n_dichan; // num of DI chans 111 int n_dochan; // num of DO chans 112 const struct comedi_lrange *ai_range_type; // default A/D rangelist 113 const struct comedi_lrange *ao_range_type; // dafault D/A rangelist 114 unsigned int io_range; // len of IO space 115 unsigned int IRQbits; // allowed interrupts 116 unsigned int DMAbits; // allowed DMA chans 117 int ai_maxdata; // maxdata for A/D 118 int ao_maxdata; // maxdata for D/A 119 int ai_chanlist; // allowed len of channel list A/D 120 int ao_chanlist; // allowed len of channel list D/A 121 int i8254_osc_base; // 1/frequency of on board oscilator in ns 122} boardtype; 123 124static const boardtype boardtypes[] = { 125 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816, 126 &range_pcl816, PCLx1x_RANGE, 127 0x00fc, // IRQ mask 128 0x0a, // DMA mask 129 0xffff, // 16-bit card 130 0xffff, // D/A maxdata 131 1024, 132 1, // ao chan list 133 100}, 134 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816, 135 &range_pcl816, PCLx1x_RANGE, 136 0x00fc, 137 0x0a, 138 0x3fff, /* 14 bit card */ 139 0x3fff, 140 1024, 141 1, 142 100}, 143}; 144 145#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype)) 146#define devpriv ((struct pcl816_private *)dev->private) 147#define this_board ((const boardtype *)dev->board_ptr) 148 149static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it); 150static int pcl816_detach(struct comedi_device * dev); 151 152#ifdef unused 153static int RTC_lock = 0; /* RTC lock */ 154static int RTC_timer_lock = 0; /* RTC int lock */ 155#endif 156 157static struct comedi_driver driver_pcl816 = { 158 driver_name:"pcl816", 159 module:THIS_MODULE, 160 attach:pcl816_attach, 161 detach:pcl816_detach, 162 board_name:&boardtypes[0].name, 163 num_names:n_boardtypes, 164 offset:sizeof(boardtype), 165}; 166 167COMEDI_INITCLEANUP(driver_pcl816); 168 169struct pcl816_private { 170 171 unsigned int dma; // used DMA, 0=don't use DMA 172 int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc 173#ifdef unused 174 unsigned long rtc_iobase; // RTC port region 175 unsigned int rtc_iosize; 176 unsigned int rtc_irq; 177#endif 178 unsigned long dmabuf[2]; // pointers to begin of DMA buffers 179 unsigned int dmapages[2]; // len of DMA buffers in PAGE_SIZEs 180 unsigned int hwdmaptr[2]; // hardware address of DMA buffers 181 unsigned int hwdmasize[2]; // len of DMA buffers in Bytes 182 unsigned int dmasamplsize; // size in samples hwdmasize[0]/2 183 unsigned int last_top_dma; // DMA pointer in last RTC int 184 int next_dma_buf; // which DMA buffer will be used next round 185 long dma_runs_to_end; // how many we must permorm DMA transfer to end of record 186 unsigned long last_dma_run; // how many bytes we must transfer on last DMA page 187 188 unsigned int ai_scans; // len of scanlist 189 unsigned char ai_neverending; // if=1, then we do neverending record (you must use cancel()) 190 int irq_free; // 1=have allocated IRQ 191 int irq_blocked; // 1=IRQ now uses any subdev 192#ifdef unused 193 int rtc_irq_blocked; // 1=we now do AI with DMA&RTC 194#endif 195 int irq_was_now_closed; // when IRQ finish, there's stored int816_mode for last interrupt 196 int int816_mode; // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma 197 struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish 198 int ai_act_scan; // how many scans we finished 199 unsigned int ai_act_chanlist[16]; // MUX setting for actual AI operations 200 unsigned int ai_act_chanlist_len; // how long is actual MUX list 201 unsigned int ai_act_chanlist_pos; // actual position in MUX list 202 unsigned int ai_poll_ptr; // how many sampes transfer poll 203 struct comedi_subdevice *sub_ai; // ptr to AI subdevice 204#ifdef unused 205 struct timer_list rtc_irq_timer; // timer for RTC sanity check 206 unsigned long rtc_freq; // RTC int freq 207#endif 208}; 209 210 211/* 212============================================================================== 213*/ 214static int check_and_setup_channel_list(struct comedi_device * dev, 215 struct comedi_subdevice * s, unsigned int *chanlist, int chanlen); 216static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s); 217static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, 218 unsigned int divisor2); 219#ifdef unused 220static int set_rtc_irq_bit(unsigned char bit); 221#endif 222 223static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, 224 struct comedi_cmd * cmd); 225static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s); 226 227/* 228============================================================================== 229 ANALOG INPUT MODE0, 816 cards, slow version 230*/ 231static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 232 struct comedi_insn * insn, unsigned int * data) 233{ 234 int n; 235 int timeout; 236 237 DPRINTK("mode 0 analog input\n"); 238 // software trigger, DMA and INT off 239 outb(0, dev->iobase + PCL816_CONTROL); 240 // clear INT (conversion end) flag 241 outb(0, dev->iobase + PCL816_CLRINT); 242 243 // Set the input channel 244 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX); 245 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */ 246 247 for (n = 0; n < insn->n; n++) { 248 249 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */ 250 251 timeout = 100; 252 while (timeout--) { 253 if (!(inb(dev->iobase + PCL816_STATUS) & 254 PCL816_STATUS_DRDY_MASK)) { 255 // return read value 256 data[n] = 257 ((inb(dev->iobase + 258 PCL816_AD_HI) << 8) | 259 (inb(dev->iobase + PCL816_AD_LO))); 260 261 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */ 262 break; 263 } 264 comedi_udelay(1); 265 } 266 // Return timeout error 267 if (!timeout) { 268 comedi_error(dev, "A/D insn timeout\n"); 269 data[0] = 0; 270 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */ 271 return -EIO; 272 } 273 274 } 275 return n; 276} 277 278/* 279============================================================================== 280 analog input interrupt mode 1 & 3, 818 cards 281 one sample per interrupt version 282*/ 283static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d) 284{ 285 struct comedi_device *dev = d; 286 struct comedi_subdevice *s = dev->subdevices + 0; 287 int low, hi; 288 int timeout = 50; /* wait max 50us */ 289 290 while (timeout--) { 291 if (!(inb(dev->iobase + PCL816_STATUS) & 292 PCL816_STATUS_DRDY_MASK)) 293 break; 294 comedi_udelay(1); 295 } 296 if (!timeout) { // timeout, bail error 297 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 298 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!"); 299 pcl816_ai_cancel(dev, s); 300 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; 301 comedi_event(dev, s); 302 return IRQ_HANDLED; 303 304 } 305 306 // get the sample 307 low = inb(dev->iobase + PCL816_AD_LO); 308 hi = inb(dev->iobase + PCL816_AD_HI); 309 310 comedi_buf_put(s->async, (hi << 8) | low); 311 312 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 313 314 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len) 315 devpriv->ai_act_chanlist_pos = 0; 316 317 if (s->async->cur_chan == 0) { 318 devpriv->ai_act_scan++; 319 } 320 321 if (!devpriv->ai_neverending) 322 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */ 323 /* all data sampled */ 324 pcl816_ai_cancel(dev, s); 325 s->async->events |= COMEDI_CB_EOA; 326 } 327 comedi_event(dev, s); 328 return IRQ_HANDLED; 329} 330 331/* 332============================================================================== 333 analog input dma mode 1 & 3, 816 cards 334*/ 335static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s, 336 short * ptr, unsigned int bufptr, unsigned int len) 337{ 338 int i; 339 340 s->async->events = 0; 341 342 for (i = 0; i < len; i++) { 343 344 comedi_buf_put(s->async, ptr[bufptr++]); 345 346 if (++devpriv->ai_act_chanlist_pos >= 347 devpriv->ai_act_chanlist_len) { 348 devpriv->ai_act_chanlist_pos = 0; 349 devpriv->ai_act_scan++; 350 } 351 352 if (!devpriv->ai_neverending) 353 if (devpriv->ai_act_scan >= devpriv->ai_scans) { // all data sampled 354 pcl816_ai_cancel(dev, s); 355 s->async->events |= COMEDI_CB_EOA; 356 s->async->events |= COMEDI_CB_BLOCK; 357 break; 358 } 359 } 360 361 comedi_event(dev, s); 362} 363 364static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d) 365{ 366 struct comedi_device *dev = d; 367 struct comedi_subdevice *s = dev->subdevices + 0; 368 int len, bufptr, this_dma_buf; 369 unsigned long dma_flags; 370 short *ptr; 371 372 disable_dma(devpriv->dma); 373 this_dma_buf = devpriv->next_dma_buf; 374 375 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { // switch dma bufs 376 377 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf; 378 set_dma_mode(devpriv->dma, DMA_MODE_READ); 379 dma_flags = claim_dma_lock(); 380// clear_dma_ff (devpriv->dma); 381 set_dma_addr(devpriv->dma, 382 devpriv->hwdmaptr[devpriv->next_dma_buf]); 383 if (devpriv->dma_runs_to_end) { 384 set_dma_count(devpriv->dma, 385 devpriv->hwdmasize[devpriv->next_dma_buf]); 386 } else { 387 set_dma_count(devpriv->dma, devpriv->last_dma_run); 388 } 389 release_dma_lock(dma_flags); 390 enable_dma(devpriv->dma); 391 } 392 393 devpriv->dma_runs_to_end--; 394 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 395 396 ptr = (short *) devpriv->dmabuf[this_dma_buf]; 397 398 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr; 399 bufptr = devpriv->ai_poll_ptr; 400 devpriv->ai_poll_ptr = 0; 401 402 transfer_from_dma_buf(dev, s, ptr, bufptr, len); 403 return IRQ_HANDLED; 404} 405 406/* 407============================================================================== 408 INT procedure 409*/ 410static irqreturn_t interrupt_pcl816(int irq, void *d PT_REGS_ARG) 411{ 412 struct comedi_device *dev = d; 413 DPRINTK("<I>"); 414 415 if (!dev->attached) { 416 comedi_error(dev, "premature interrupt"); 417 return IRQ_HANDLED; 418 } 419 420 switch (devpriv->int816_mode) { 421 case INT_TYPE_AI1_DMA: 422 case INT_TYPE_AI3_DMA: 423 return interrupt_pcl816_ai_mode13_dma(irq, d); 424 case INT_TYPE_AI1_INT: 425 case INT_TYPE_AI3_INT: 426 return interrupt_pcl816_ai_mode13_int(irq, d); 427 } 428 429 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 430 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) | 431 (!devpriv->int816_mode)) { 432 if (devpriv->irq_was_now_closed) { 433 devpriv->irq_was_now_closed = 0; 434 // comedi_error(dev,"last IRQ.."); 435 return IRQ_HANDLED; 436 } 437 comedi_error(dev, "bad IRQ!"); 438 return IRQ_NONE; 439 } 440 comedi_error(dev, "IRQ from unknow source!"); 441 return IRQ_NONE; 442} 443 444/* 445============================================================================== 446 COMMAND MODE 447*/ 448static void pcl816_cmdtest_out(int e, struct comedi_cmd * cmd) 449{ 450 rt_printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e, 451 cmd->start_src, cmd->scan_begin_src, cmd->convert_src); 452 rt_printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e, 453 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg); 454 rt_printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src, 455 cmd->scan_end_src); 456 rt_printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e, 457 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len); 458} 459 460/* 461============================================================================== 462*/ 463static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, 464 struct comedi_cmd * cmd) 465{ 466 int err = 0; 467 int tmp, divisor1, divisor2; 468 469 DEBUG(rt_printk("pcl816 pcl812_ai_cmdtest\n"); 470 pcl816_cmdtest_out(-1, cmd);); 471 472 /* step 1: make sure trigger sources are trivially valid */ 473 tmp = cmd->start_src; 474 cmd->start_src &= TRIG_NOW; 475 if (!cmd->start_src || tmp != cmd->start_src) 476 err++; 477 478 tmp = cmd->scan_begin_src; 479 cmd->scan_begin_src &= TRIG_FOLLOW; 480 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 481 err++; 482 483 if (!cmd->convert_src & (TRIG_EXT | TRIG_TIMER)) 484 err++; 485 486 tmp = cmd->scan_end_src; 487 cmd->scan_end_src &= TRIG_COUNT; 488 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 489 err++; 490 491 tmp = cmd->stop_src; 492 cmd->stop_src &= TRIG_COUNT | TRIG_NONE; 493 if (!cmd->stop_src || tmp != cmd->stop_src) 494 err++; 495 496 if (err) { 497 return 1; 498 } 499 500 /* step 2: make sure trigger sources are unique and mutually compatible */ 501 502 if (cmd->start_src != TRIG_NOW) { 503 cmd->start_src = TRIG_NOW; 504 err++; 505 } 506 507 if (cmd->scan_begin_src != TRIG_FOLLOW) { 508 cmd->scan_begin_src = TRIG_FOLLOW; 509 err++; 510 } 511 512 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) { 513 cmd->convert_src = TRIG_TIMER; 514 err++; 515 } 516 517 if (cmd->scan_end_src != TRIG_COUNT) { 518 cmd->scan_end_src = TRIG_COUNT; 519 err++; 520 } 521 522 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) 523 err++; 524 525 if (err) { 526 return 2; 527 } 528 529 /* step 3: make sure arguments are trivially compatible */ 530 if (cmd->start_arg != 0) { 531 cmd->start_arg = 0; 532 err++; 533 } 534 535 if (cmd->scan_begin_arg != 0) { 536 cmd->scan_begin_arg = 0; 537 err++; 538 } 539 if (cmd->convert_src == TRIG_TIMER) { 540 if (cmd->convert_arg < this_board->ai_ns_min) { 541 cmd->convert_arg = this_board->ai_ns_min; 542 err++; 543 } 544 } else { /* TRIG_EXT */ 545 if (cmd->convert_arg != 0) { 546 cmd->convert_arg = 0; 547 err++; 548 } 549 } 550 551 if (!cmd->chanlist_len) { 552 cmd->chanlist_len = 1; 553 err++; 554 } 555 if (cmd->chanlist_len > this_board->n_aichan) { 556 cmd->chanlist_len = this_board->n_aichan; 557 err++; 558 } 559 if (cmd->scan_end_arg != cmd->chanlist_len) { 560 cmd->scan_end_arg = cmd->chanlist_len; 561 err++; 562 } 563 if (cmd->stop_src == TRIG_COUNT) { 564 if (!cmd->stop_arg) { 565 cmd->stop_arg = 1; 566 err++; 567 } 568 } else { /* TRIG_NONE */ 569 if (cmd->stop_arg != 0) { 570 cmd->stop_arg = 0; 571 err++; 572 } 573 } 574 575 if (err) { 576 return 3; 577 } 578 579 /* step 4: fix up any arguments */ 580 if (cmd->convert_src == TRIG_TIMER) { 581 tmp = cmd->convert_arg; 582 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, 583 &divisor1, &divisor2, &cmd->convert_arg, 584 cmd->flags & TRIG_ROUND_MASK); 585 if (cmd->convert_arg < this_board->ai_ns_min) 586 cmd->convert_arg = this_board->ai_ns_min; 587 if (tmp != cmd->convert_arg) 588 err++; 589 } 590 591 if (err) { 592 return 4; 593 } 594 595 return 0; 596} 597 598static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s) 599{ 600 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq; 601 struct comedi_cmd *cmd = &s->async->cmd; 602 603 if (cmd->start_src != TRIG_NOW) 604 return -EINVAL; 605 if (cmd->scan_begin_src != TRIG_FOLLOW) 606 return -EINVAL; 607 if (cmd->scan_end_src != TRIG_COUNT) 608 return -EINVAL; 609 if (cmd->scan_end_arg != cmd->chanlist_len) 610 return -EINVAL; 611// if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; 612 if (devpriv->irq_blocked) 613 return -EBUSY; 614 615 if (cmd->convert_src == TRIG_TIMER) { 616 if (cmd->convert_arg < this_board->ai_ns_min) 617 cmd->convert_arg = this_board->ai_ns_min; 618 619 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1, 620 &divisor2, &cmd->convert_arg, 621 cmd->flags & TRIG_ROUND_MASK); 622 if (divisor1 == 1) { // PCL816 crash if any divisor is set to 1 623 divisor1 = 2; 624 divisor2 /= 2; 625 } 626 if (divisor2 == 1) { 627 divisor2 = 2; 628 divisor1 /= 2; 629 } 630 } 631 632 start_pacer(dev, -1, 0, 0); // stop pacer 633 634 if (!check_and_setup_channel_list(dev, s, cmd->chanlist, 635 cmd->chanlist_len)) 636 return -EINVAL; 637 comedi_udelay(1); 638 639 devpriv->ai_act_scan = 0; 640 s->async->cur_chan = 0; 641 devpriv->irq_blocked = 1; 642 devpriv->ai_poll_ptr = 0; 643 devpriv->irq_was_now_closed = 0; 644 645 if (cmd->stop_src == TRIG_COUNT) { 646 devpriv->ai_scans = cmd->stop_arg; 647 devpriv->ai_neverending = 0; 648 } else { 649 devpriv->ai_scans = 0; 650 devpriv->ai_neverending = 1; 651 } 652 653 if ((cmd->flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan? 654 printk("pl816: You wankt WAKE_EOS but I dont want handle it"); 655 // devpriv->ai_eos=1; 656 //if (devpriv->ai_n_chan==1) 657 // devpriv->dma=0; // DMA is useless for this situation 658 } 659 660 if (devpriv->dma) { 661 bytes = devpriv->hwdmasize[0]; 662 if (!devpriv->ai_neverending) { 663 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); // how many 664 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fill 665 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved 666 devpriv->dma_runs_to_end--; 667 if (devpriv->dma_runs_to_end >= 0) 668 bytes = devpriv->hwdmasize[0]; 669 } else 670 devpriv->dma_runs_to_end = -1; 671 672 devpriv->next_dma_buf = 0; 673 set_dma_mode(devpriv->dma, DMA_MODE_READ); 674 dma_flags = claim_dma_lock(); 675 clear_dma_ff(devpriv->dma); 676 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]); 677 set_dma_count(devpriv->dma, bytes); 678 release_dma_lock(dma_flags); 679 enable_dma(devpriv->dma); 680 } 681 682 start_pacer(dev, 1, divisor1, divisor2); 683 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7); 684 685 switch (cmd->convert_src) { 686 case TRIG_TIMER: 687 devpriv->int816_mode = INT_TYPE_AI1_DMA; 688 outb(0x32, dev->iobase + PCL816_CONTROL); // Pacer+IRQ+DMA 689 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq and DMA to card 690 break; 691 692 default: 693 devpriv->int816_mode = INT_TYPE_AI3_DMA; 694 outb(0x34, dev->iobase + PCL816_CONTROL); // Ext trig+IRQ+DMA 695 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq to card 696 break; 697 } 698 699 DPRINTK("pcl816 END: pcl812_ai_cmd()\n"); 700 return 0; 701} 702 703static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s) 704{ 705 unsigned long flags; 706 unsigned int top1, top2, i; 707 708 if (!devpriv->dma) 709 return 0; // poll is valid only for DMA transfer 710 711 comedi_spin_lock_irqsave(&dev->spinlock, flags); 712 713 for (i = 0; i < 20; i++) { 714 top1 = get_dma_residue(devpriv->dma); // where is now DMA 715 top2 = get_dma_residue(devpriv->dma); 716 if (top1 == top2) 717 break; 718 } 719 if (top1 != top2) { 720 comedi_spin_unlock_irqrestore(&dev->spinlock, flags); 721 return 0; 722 } 723 724 top1 = devpriv->hwdmasize[0] - top1; // where is now DMA in buffer 725 top1 >>= 1; // sample position 726 top2 = top1 - devpriv->ai_poll_ptr; 727 if (top2 < 1) { // no new samples 728 comedi_spin_unlock_irqrestore(&dev->spinlock, flags); 729 return 0; 730 } 731 732 transfer_from_dma_buf(dev, s, 733 (short *) devpriv->dmabuf[devpriv->next_dma_buf], 734 devpriv->ai_poll_ptr, top2); 735 736 devpriv->ai_poll_ptr = top1; // new buffer position 737 comedi_spin_unlock_irqrestore(&dev->spinlock, flags); 738 739 return s->async->buf_write_count - s->async->buf_read_count; 740} 741 742/* 743============================================================================== 744 cancel any mode 1-4 AI 745*/ 746static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s) 747{ 748// DEBUG(rt_printk("pcl816_ai_cancel()\n");) 749 750 if (devpriv->irq_blocked > 0) { 751 switch (devpriv->int816_mode) { 752#ifdef unused 753 case INT_TYPE_AI1_DMA_RTC: 754 case INT_TYPE_AI3_DMA_RTC: 755 set_rtc_irq_bit(0); // stop RTC 756 del_timer(&devpriv->rtc_irq_timer); 757#endif 758 case INT_TYPE_AI1_DMA: 759 case INT_TYPE_AI3_DMA: 760 disable_dma(devpriv->dma); 761 case INT_TYPE_AI1_INT: 762 case INT_TYPE_AI3_INT: 763 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */ 764 comedi_udelay(1); 765 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */ 766 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */ 767 outb(0x70, dev->iobase + PCL816_CTRCTL); 768 outb(0, dev->iobase + PCL816_AD_LO); 769 inb(dev->iobase + PCL816_AD_LO); 770 inb(dev->iobase + PCL816_AD_HI); 771 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 772 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */ 773 devpriv->irq_blocked = 0; 774 devpriv->irq_was_now_closed = devpriv->int816_mode; 775 devpriv->int816_mode = 0; 776 devpriv->last_int_sub = s; 777// s->busy = 0; 778 break; 779 } 780 } 781 782 DEBUG(rt_printk("comedi: pcl816_ai_cancel() successful\n"); 783 ) 784 return 0; 785} 786 787/* 788============================================================================== 789 chech for PCL816 790*/ 791static int pcl816_check(unsigned long iobase) 792{ 793 outb(0x00, iobase + PCL816_MUX); 794 comedi_udelay(1); 795 if (inb(iobase + PCL816_MUX) != 0x00) 796 return 1; //there isn't card 797 outb(0x55, iobase + PCL816_MUX); 798 comedi_udelay(1); 799 if (inb(iobase + PCL816_MUX) != 0x55) 800 return 1; //there isn't card 801 outb(0x00, iobase + PCL816_MUX); 802 comedi_udelay(1); 803 outb(0x18, iobase + PCL816_CONTROL); 804 comedi_udelay(1); 805 if (inb(iobase + PCL816_CONTROL) != 0x18) 806 return 1; //there isn't card 807 return 0; // ok, card exist 808} 809 810/* 811============================================================================== 812 reset whole PCL-816 cards 813*/ 814static void pcl816_reset(struct comedi_device * dev) 815{ 816// outb (0, dev->iobase + PCL818_DA_LO); // DAC=0V 817// outb (0, dev->iobase + PCL818_DA_HI); 818// comedi_udelay (1); 819// outb (0, dev->iobase + PCL818_DO_HI); // DO=$0000 820// outb (0, dev->iobase + PCL818_DO_LO); 821// comedi_udelay (1); 822 outb(0, dev->iobase + PCL816_CONTROL); 823 outb(0, dev->iobase + PCL816_MUX); 824 outb(0, dev->iobase + PCL816_CLRINT); 825 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */ 826 outb(0x70, dev->iobase + PCL816_CTRCTL); 827 outb(0x30, dev->iobase + PCL816_CTRCTL); 828 outb(0, dev->iobase + PCL816_RANGE); 829} 830 831/* 832============================================================================== 833 Start/stop pacer onboard pacer 834*/ 835static void 836start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1, 837 unsigned int divisor2) 838{ 839 outb(0x32, dev->iobase + PCL816_CTRCTL); 840 outb(0xff, dev->iobase + PCL816_CTR0); 841 outb(0x00, dev->iobase + PCL816_CTR0); 842 comedi_udelay(1); 843 outb(0xb4, dev->iobase + PCL816_CTRCTL); // set counter 2 as mode 3 844 outb(0x74, dev->iobase + PCL816_CTRCTL); // set counter 1 as mode 3 845 comedi_udelay(1); 846 847 if (mode == 1) { 848 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1, 849 divisor2); 850 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2); 851 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2); 852 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1); 853 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1); 854 } 855 856 /* clear pending interrupts (just in case) */ 857// outb(0, dev->iobase + PCL816_CLRINT); 858} 859 860/* 861============================================================================== 862 Check if channel list from user is builded correctly 863 If it's ok, then program scan/gain logic 864*/ 865static int 866check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s, 867 unsigned int *chanlist, int chanlen) 868{ 869 unsigned int chansegment[16]; 870 unsigned int i, nowmustbechan, seglen, segpos; 871 872 // correct channel and range number check itself comedi/range.c 873 if (chanlen < 1) { 874 comedi_error(dev, "range/channel list is empty!"); 875 return 0; 876 } 877 878 if (chanlen > 1) { 879 chansegment[0] = chanlist[0]; // first channel is everytime ok 880 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) { 881 // build part of chanlist 882 DEBUG(rt_printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]), 883 CR_RANGE(chanlist[i])); 884 ) 885 if (chanlist[0] == chanlist[i]) 886 break; // we detect loop, this must by finish 887 nowmustbechan = 888 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen; 889 if (nowmustbechan != CR_CHAN(chanlist[i])) { 890 // channel list isn't continous :-( 891 rt_printk 892 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n", 893 dev->minor, i, CR_CHAN(chanlist[i]), 894 nowmustbechan, CR_CHAN(chanlist[0])); 895 return 0; 896 } 897 chansegment[i] = chanlist[i]; // well, this is next correct channel in list 898 } 899 900 for (i = 0, segpos = 0; i < chanlen; i++) { // check whole chanlist 901 DEBUG(rt_printk("%d %d=%d %d\n", 902 CR_CHAN(chansegment[i % seglen]), 903 CR_RANGE(chansegment[i % seglen]), 904 CR_CHAN(chanlist[i]), 905 CR_RANGE(chanlist[i])); 906 ) 907 if (chanlist[i] != chansegment[i % seglen]) { 908 rt_printk 909 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n", 910 dev->minor, i, CR_CHAN(chansegment[i]), 911 CR_RANGE(chansegment[i]), 912 CR_AREF(chansegment[i]), 913 CR_CHAN(chanlist[i % seglen]), 914 CR_RANGE(chanlist[i % seglen]), 915 CR_AREF(chansegment[i % seglen])); 916 return 0; // chan/gain list is strange 917 } 918 } 919 } else { 920 seglen = 1; 921 } 922 923 devpriv->ai_act_chanlist_len = seglen; 924 devpriv->ai_act_chanlist_pos = 0; 925 926 for (i = 0; i < seglen; i++) { // store range list to card 927 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]); 928 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX); 929 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */ 930 } 931 932 comedi_udelay(1); 933 934 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */ 935 936 return 1; // we can serve this with MUX logic 937} 938 939#ifdef unused 940/* 941============================================================================== 942 Enable(1)/disable(0) periodic interrupts from RTC 943*/ 944static int set_rtc_irq_bit(unsigned char bit) 945{ 946 unsigned char val; 947 unsigned long flags; 948 949 if (bit == 1) { 950 RTC_timer_lock++; 951 if (RTC_timer_lock > 1) 952 return 0; 953 } else { 954 RTC_timer_lock--; 955 if (RTC_timer_lock < 0) 956 RTC_timer_lock = 0; 957 if (RTC_timer_lock > 0) 958 return 0; 959 } 960 961 save_flags(flags); 962 cli(); 963 val = CMOS_READ(RTC_CONTROL); 964 if (bit) { 965 val |= RTC_PIE; 966 } else { 967 val &= ~RTC_PIE; 968 } 969 CMOS_WRITE(val, RTC_CONTROL); 970 CMOS_READ(RTC_INTR_FLAGS); 971 restore_flags(flags); 972 return 0; 973} 974#endif 975 976/* 977============================================================================== 978 Free any resources that we have claimed 979*/ 980static void free_resources(struct comedi_device * dev) 981{ 982 //rt_printk("free_resource()\n"); 983 if (dev->private) { 984 pcl816_ai_cancel(dev, devpriv->sub_ai); 985 pcl816_reset(dev); 986 if (devpriv->dma) 987 free_dma(devpriv->dma); 988 if (devpriv->dmabuf[0]) 989 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]); 990 if (devpriv->dmabuf[1]) 991 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]); 992#ifdef unused 993 if (devpriv->rtc_irq) 994 comedi_free_irq(devpriv->rtc_irq, dev); 995 if ((devpriv->dma_rtc) && (RTC_lock == 1)) { 996 if (devpriv->rtc_iobase) 997 release_region(devpriv->rtc_iobase, 998 devpriv->rtc_iosize); 999 } 1000#endif 1001 } 1002 1003 if (dev->irq) 1004 free_irq(dev->irq, dev); 1005 if (dev->iobase) 1006 release_region(dev->iobase, this_board->io_range); 1007 //rt_printk("free_resource() end\n"); 1008} 1009 1010/* 1011============================================================================== 1012 1013 Initialization 1014 1015*/ 1016static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it) 1017{ 1018 int ret; 1019 unsigned long iobase; 1020 unsigned int irq, dma; 1021 unsigned long pages; 1022 //int i; 1023 struct comedi_subdevice *s; 1024 1025 /* claim our I/O space */ 1026 iobase = it->options[0]; 1027 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor, 1028 this_board->name, iobase); 1029 1030 if (!request_region(iobase, this_board->io_range, "pcl816")) { 1031 rt_printk("I/O port conflict\n"); 1032 return -EIO; 1033 } 1034 1035 dev->iobase = iobase; 1036 1037 if (pcl816_check(iobase)) { 1038 rt_printk(", I cann't detect board. FAIL!\n"); 1039 return -EIO; 1040 } 1041 1042 if ((ret = alloc_private(dev, sizeof(struct pcl816_private))) < 0) 1043 return ret; /* Can't alloc mem */ 1044 1045 /* set up some name stuff */ 1046 dev->board_name = this_board->name; 1047 1048 /* grab our IRQ */ 1049 irq = 0; 1050 if (this_board->IRQbits != 0) { /* board support IRQ */ 1051 irq = it->options[1]; 1052 if (irq) { /* we want to use IRQ */ 1053 if (((1 << irq) & this_board->IRQbits) == 0) { 1054 rt_printk 1055 (", IRQ %u is out of allowed range, DISABLING IT", 1056 irq); 1057 irq = 0; /* Bad IRQ */ 1058 } else { 1059 if (comedi_request_irq(irq, interrupt_pcl816, 0, 1060 "pcl816", dev)) { 1061 rt_printk 1062 (", unable to allocate IRQ %u, DISABLING IT", 1063 irq); 1064 irq = 0; /* Can't use IRQ */ 1065 } else { 1066 rt_printk(", irq=%u", irq); 1067 } 1068 } 1069 } 1070 } 1071 1072 dev->irq = irq; 1073 if (irq) { 1074 devpriv->irq_free = 1; 1075 } /* 1=we have allocated irq */ 1076 else { 1077 devpriv->irq_free = 0; 1078 } 1079 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */ 1080 devpriv->int816_mode = 0; /* mode of irq */ 1081 1082#ifdef unused 1083 /* grab RTC for DMA operations */ 1084 devpriv->dma_rtc = 0; 1085 if (it->options[2] > 0) { // we want to use DMA 1086 if (RTC_lock == 0) { 1087 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT, 1088 "pcl816 (RTC)")) 1089 goto no_rtc; 1090 } 1091 devpriv->rtc_iobase = RTC_PORT(0); 1092 devpriv->rtc_iosize = RTC_IO_EXTENT; 1093 RTC_lock++; 1094#ifdef UNTESTED_CODE 1095 if (!comedi_request_irq(RTC_IRQ, 1096 interrupt_pcl816_ai_mode13_dma_rtc, 0, 1097 "pcl816 DMA (RTC)", dev)) { 1098 devpriv->dma_rtc = 1; 1099 devpriv->rtc_irq = RTC_IRQ; 1100 rt_printk(", dma_irq=%u", devpriv->rtc_irq); 1101 } else { 1102 RTC_lock--; 1103 if (RTC_lock == 0) { 1104 if (devpriv->rtc_iobase) 1105 release_region(devpriv->rtc_iobase, 1106 devpriv->rtc_iosize); 1107 } 1108 devpriv->rtc_iobase = 0; 1109 devpriv->rtc_iosize = 0; 1110 } 1111#else 1112 printk("pcl816: RTC code missing"); 1113#endif 1114 1115 } 1116 1117 no_rtc: 1118#endif 1119 /* grab our DMA */ 1120 dma = 0; 1121 devpriv->dma = dma; 1122 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0)) 1123 goto no_dma; /* if we haven't IRQ, we can't use DMA */ 1124 1125 if (this_board->DMAbits != 0) { /* board support DMA */ 1126 dma = it->options[2]; 1127 if (dma < 1) 1128 goto no_dma; /* DMA disabled */ 1129 1130 if (((1 << dma) & this_board->DMAbits) == 0) { 1131 rt_printk(", DMA is out of allowed range, FAIL!\n"); 1132 return -EINVAL; /* Bad DMA */ 1133 } 1134 ret = request_dma(dma, "pcl816"); 1135 if (ret) { 1136 rt_printk(", unable to allocate DMA %u, FAIL!\n", dma); 1137 return -EBUSY; /* DMA isn't free */ 1138 } 1139 1140 devpriv->dma = dma; 1141 rt_printk(", dma=%u", dma); 1142 pages = 2; /* we need 16KB */ 1143 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages); 1144 1145 if (!devpriv->dmabuf[0]) { 1146 rt_printk(", unable to allocate DMA buffer, FAIL!\n"); 1147 /* maybe experiment with try_to_free_pages() will help .... */ 1148 return -EBUSY; /* no buffer :-( */ 1149 } 1150 devpriv->dmapages[0] = pages; 1151 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]); 1152 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE; 1153 //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); 1154 1155 if (devpriv->dma_rtc == 0) { // we must do duble buff :-( 1156 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages); 1157 if (!devpriv->dmabuf[1]) { 1158 rt_printk 1159 (", unable to allocate DMA buffer, FAIL!\n"); 1160 return -EBUSY; 1161 } 1162 devpriv->dmapages[1] = pages; 1163 devpriv->hwdmaptr[1] = 1164 virt_to_bus((void *)devpriv->dmabuf[1]); 1165 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE; 1166 } 1167 } 1168 1169 no_dma: 1170 1171/* if (this_board->n_aochan > 0) 1172 subdevs[1] = COMEDI_SUBD_AO; 1173 if (this_board->n_dichan > 0) 1174 subdevs[2] = COMEDI_SUBD_DI; 1175 if (this_board->n_dochan > 0) 1176 subdevs[3] = COMEDI_SUBD_DO; 1177*/ 1178 if ((ret = alloc_subdevices(dev, 1)) < 0) 1179 return ret; 1180 1181 s = dev->subdevices + 0; 1182 if (this_board->n_aichan > 0) { 1183 s->type = COMEDI_SUBD_AI; 1184 devpriv->sub_ai = s; 1185 dev->read_subdev = s; 1186 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 1187 s->n_chan = this_board->n_aichan; 1188 s->subdev_flags |= SDF_DIFF; 1189 //printk (", %dchans DIFF DAC - %d", s->n_chan, i); 1190 s->maxdata = this_board->ai_maxdata; 1191 s->len_chanlist = this_board->ai_chanlist; 1192 s->range_table = this_board->ai_range_type; 1193 s->cancel = pcl816_ai_cancel; 1194 s->do_cmdtest = pcl816_ai_cmdtest; 1195 s->do_cmd = pcl816_ai_cmd; 1196 s->poll = pcl816_ai_poll; 1197 s->insn_read = pcl816_ai_insn_read; 1198 } else { 1199 s->type = COMEDI_SUBD_UNUSED; 1200 } 1201 1202#if 0 1203case COMEDI_SUBD_AO: 1204 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 1205 s->n_chan = this_board->n_aochan; 1206 s->maxdata = this_board->ao_maxdata; 1207 s->len_chanlist = this_board->ao_chanlist; 1208 s->range_table = this_board->ao_range_type; 1209 break; 1210 1211case COMEDI_SUBD_DI: 1212 s->subdev_flags = SDF_READABLE; 1213 s->n_chan = this_board->n_dichan; 1214 s->maxdata = 1; 1215 s->len_chanlist = this_board->n_dichan; 1216 s->range_table = &range_digital; 1217 break; 1218 1219case COMEDI_SUBD_DO: 1220 s->subdev_flags = SDF_WRITABLE; 1221 s->n_chan = this_board->n_dochan; 1222 s->maxdata = 1; 1223 s->len_chanlist = this_board->n_dochan; 1224 s->range_table = &range_digital; 1225 break; 1226#endif 1227 1228 pcl816_reset(dev); 1229 1230 rt_printk("\n"); 1231 1232 return 0; 1233} 1234 1235/* 1236============================================================================== 1237 Removes device 1238 */ 1239static int pcl816_detach(struct comedi_device * dev) 1240{ 1241 DEBUG(rt_printk("comedi%d: pcl816: remove\n", dev->minor); 1242 ) 1243 free_resources(dev); 1244#ifdef unused 1245 if (devpriv->dma_rtc) 1246 RTC_lock--; 1247#endif 1248 return 0; 1249} 1250