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