1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#include <linux/module.h>
33#include <linux/gfp.h>
34#include <linux/delay.h>
35#include <linux/io.h>
36#include <linux/interrupt.h>
37
38#include "../comedidev.h"
39
40#include "comedi_isadma.h"
41#include "comedi_8254.h"
42
43
44
45
46#define PCL816_DO_DI_LSB_REG 0x00
47#define PCL816_DO_DI_MSB_REG 0x01
48#define PCL816_TIMER_BASE 0x04
49#define PCL816_AI_LSB_REG 0x08
50#define PCL816_AI_MSB_REG 0x09
51#define PCL816_RANGE_REG 0x09
52#define PCL816_CLRINT_REG 0x0a
53#define PCL816_MUX_REG 0x0b
54#define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
55#define PCL816_CTRL_REG 0x0c
56#define PCL816_CTRL_SOFT_TRIG BIT(0)
57#define PCL816_CTRL_PACER_TRIG BIT(1)
58#define PCL816_CTRL_EXT_TRIG BIT(2)
59#define PCL816_CTRL_POE BIT(3)
60#define PCL816_CTRL_DMAEN BIT(4)
61#define PCL816_CTRL_INTEN BIT(5)
62#define PCL816_CTRL_DMASRC_SLOT(x) (((x) & 0x3) << 6)
63#define PCL816_STATUS_REG 0x0d
64#define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
65#define PCL816_STATUS_INTSRC_SLOT(x) (((x) & 0x3) << 4)
66#define PCL816_STATUS_INTSRC_DMA PCL816_STATUS_INTSRC_SLOT(3)
67#define PCL816_STATUS_INTSRC_MASK PCL816_STATUS_INTSRC_SLOT(3)
68#define PCL816_STATUS_INTACT BIT(6)
69#define PCL816_STATUS_DRDY BIT(7)
70
71#define MAGIC_DMA_WORD 0x5a5a
72
73static const struct comedi_lrange range_pcl816 = {
74 8, {
75 BIP_RANGE(10),
76 BIP_RANGE(5),
77 BIP_RANGE(2.5),
78 BIP_RANGE(1.25),
79 UNI_RANGE(10),
80 UNI_RANGE(5),
81 UNI_RANGE(2.5),
82 UNI_RANGE(1.25)
83 }
84};
85
86struct pcl816_board {
87 const char *name;
88 int ai_maxdata;
89 int ai_chanlist;
90};
91
92static const struct pcl816_board boardtypes[] = {
93 {
94 .name = "pcl816",
95 .ai_maxdata = 0xffff,
96 .ai_chanlist = 1024,
97 }, {
98 .name = "pcl814b",
99 .ai_maxdata = 0x3fff,
100 .ai_chanlist = 1024,
101 },
102};
103
104struct pcl816_private {
105 struct comedi_isadma *dma;
106 unsigned int ai_poll_ptr;
107 unsigned int ai_cmd_running:1;
108 unsigned int ai_cmd_canceled:1;
109};
110
111static void pcl816_ai_setup_dma(struct comedi_device *dev,
112 struct comedi_subdevice *s,
113 unsigned int unread_samples)
114{
115 struct pcl816_private *devpriv = dev->private;
116 struct comedi_isadma *dma = devpriv->dma;
117 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
118 unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
119 unsigned int nsamples;
120
121 comedi_isadma_disable(dma->chan);
122
123
124
125
126
127 nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
128 if (nsamples > unread_samples) {
129 nsamples -= unread_samples;
130 desc->size = comedi_samples_to_bytes(s, nsamples);
131 comedi_isadma_program(desc);
132 }
133}
134
135static void pcl816_ai_set_chan_range(struct comedi_device *dev,
136 unsigned int chan,
137 unsigned int range)
138{
139 outb(chan, dev->iobase + PCL816_MUX_REG);
140 outb(range, dev->iobase + PCL816_RANGE_REG);
141}
142
143static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
144 unsigned int first_chan,
145 unsigned int last_chan)
146{
147 outb(PCL816_MUX_SCAN(first_chan, last_chan),
148 dev->iobase + PCL816_MUX_REG);
149}
150
151static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
152 unsigned int *chanlist,
153 unsigned int seglen)
154{
155 unsigned int first_chan = CR_CHAN(chanlist[0]);
156 unsigned int last_chan;
157 unsigned int range;
158 unsigned int i;
159
160
161 for (i = 0; i < seglen; i++) {
162 last_chan = CR_CHAN(chanlist[i]);
163 range = CR_RANGE(chanlist[i]);
164
165 pcl816_ai_set_chan_range(dev, last_chan, range);
166 }
167
168 udelay(1);
169
170 pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
171}
172
173static void pcl816_ai_clear_eoc(struct comedi_device *dev)
174{
175
176 outb(0, dev->iobase + PCL816_CLRINT_REG);
177}
178
179static void pcl816_ai_soft_trig(struct comedi_device *dev)
180{
181
182 outb(0, dev->iobase + PCL816_AI_LSB_REG);
183}
184
185static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
186 struct comedi_subdevice *s)
187{
188 unsigned int val;
189
190 val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
191 val |= inb(dev->iobase + PCL816_AI_LSB_REG);
192
193 return val & s->maxdata;
194}
195
196static int pcl816_ai_eoc(struct comedi_device *dev,
197 struct comedi_subdevice *s,
198 struct comedi_insn *insn,
199 unsigned long context)
200{
201 unsigned int status;
202
203 status = inb(dev->iobase + PCL816_STATUS_REG);
204 if ((status & PCL816_STATUS_DRDY) == 0)
205 return 0;
206 return -EBUSY;
207}
208
209static bool pcl816_ai_next_chan(struct comedi_device *dev,
210 struct comedi_subdevice *s)
211{
212 struct comedi_cmd *cmd = &s->async->cmd;
213
214 if (cmd->stop_src == TRIG_COUNT &&
215 s->async->scans_done >= cmd->stop_arg) {
216 s->async->events |= COMEDI_CB_EOA;
217 return false;
218 }
219
220 return true;
221}
222
223static void transfer_from_dma_buf(struct comedi_device *dev,
224 struct comedi_subdevice *s,
225 unsigned short *ptr,
226 unsigned int bufptr, unsigned int len)
227{
228 unsigned short val;
229 int i;
230
231 for (i = 0; i < len; i++) {
232 val = ptr[bufptr++];
233 comedi_buf_write_samples(s, &val, 1);
234
235 if (!pcl816_ai_next_chan(dev, s))
236 return;
237 }
238}
239
240static irqreturn_t pcl816_interrupt(int irq, void *d)
241{
242 struct comedi_device *dev = d;
243 struct comedi_subdevice *s = dev->read_subdev;
244 struct pcl816_private *devpriv = dev->private;
245 struct comedi_isadma *dma = devpriv->dma;
246 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
247 unsigned int nsamples;
248 unsigned int bufptr;
249
250 if (!dev->attached || !devpriv->ai_cmd_running) {
251 pcl816_ai_clear_eoc(dev);
252 return IRQ_HANDLED;
253 }
254
255 if (devpriv->ai_cmd_canceled) {
256 devpriv->ai_cmd_canceled = 0;
257 pcl816_ai_clear_eoc(dev);
258 return IRQ_HANDLED;
259 }
260
261 nsamples = comedi_bytes_to_samples(s, desc->size) -
262 devpriv->ai_poll_ptr;
263 bufptr = devpriv->ai_poll_ptr;
264 devpriv->ai_poll_ptr = 0;
265
266
267 dma->cur_dma = 1 - dma->cur_dma;
268 pcl816_ai_setup_dma(dev, s, nsamples);
269
270 transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
271
272 pcl816_ai_clear_eoc(dev);
273
274 comedi_handle_events(dev, s);
275 return IRQ_HANDLED;
276}
277
278static int check_channel_list(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 unsigned int *chanlist,
281 unsigned int chanlen)
282{
283 unsigned int chansegment[16];
284 unsigned int i, nowmustbechan, seglen, segpos;
285
286
287 if (chanlen < 1) {
288 dev_err(dev->class_dev, "range/channel list is empty!\n");
289 return 0;
290 }
291
292 if (chanlen > 1) {
293
294 chansegment[0] = chanlist[0];
295 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
296
297 if (chanlist[0] == chanlist[i])
298 break;
299 nowmustbechan =
300 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
301 if (nowmustbechan != CR_CHAN(chanlist[i])) {
302
303 dev_dbg(dev->class_dev,
304 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
305 i, CR_CHAN(chanlist[i]), nowmustbechan,
306 CR_CHAN(chanlist[0]));
307 return 0;
308 }
309
310 chansegment[i] = chanlist[i];
311 }
312
313
314 for (i = 0, segpos = 0; i < chanlen; i++) {
315 if (chanlist[i] != chansegment[i % seglen]) {
316 dev_dbg(dev->class_dev,
317 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
318 i, CR_CHAN(chansegment[i]),
319 CR_RANGE(chansegment[i]),
320 CR_AREF(chansegment[i]),
321 CR_CHAN(chanlist[i % seglen]),
322 CR_RANGE(chanlist[i % seglen]),
323 CR_AREF(chansegment[i % seglen]));
324 return 0;
325 }
326 }
327 } else {
328 seglen = 1;
329 }
330
331 return seglen;
332}
333
334static int pcl816_ai_cmdtest(struct comedi_device *dev,
335 struct comedi_subdevice *s, struct comedi_cmd *cmd)
336{
337 int err = 0;
338
339
340
341 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
342 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
343 err |= comedi_check_trigger_src(&cmd->convert_src,
344 TRIG_EXT | TRIG_TIMER);
345 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
346 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
347
348 if (err)
349 return 1;
350
351
352
353 err |= comedi_check_trigger_is_unique(cmd->convert_src);
354 err |= comedi_check_trigger_is_unique(cmd->stop_src);
355
356
357
358 if (err)
359 return 2;
360
361
362
363 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
364 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
365
366 if (cmd->convert_src == TRIG_TIMER)
367 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
368 else
369 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
370
371 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
372 cmd->chanlist_len);
373
374 if (cmd->stop_src == TRIG_COUNT)
375 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
376 else
377 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
378
379 if (err)
380 return 3;
381
382
383 if (cmd->convert_src == TRIG_TIMER) {
384 unsigned int arg = cmd->convert_arg;
385
386 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
387 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
388 }
389
390 if (err)
391 return 4;
392
393
394
395 if (cmd->chanlist) {
396 if (!check_channel_list(dev, s, cmd->chanlist,
397 cmd->chanlist_len))
398 return 5;
399 }
400
401 return 0;
402}
403
404static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
405{
406 struct pcl816_private *devpriv = dev->private;
407 struct comedi_isadma *dma = devpriv->dma;
408 struct comedi_cmd *cmd = &s->async->cmd;
409 unsigned int ctrl;
410 unsigned int seglen;
411
412 if (devpriv->ai_cmd_running)
413 return -EBUSY;
414
415 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
416 if (seglen < 1)
417 return -EINVAL;
418 pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
419 udelay(1);
420
421 devpriv->ai_cmd_running = 1;
422 devpriv->ai_poll_ptr = 0;
423 devpriv->ai_cmd_canceled = 0;
424
425
426 dma->cur_dma = 0;
427 pcl816_ai_setup_dma(dev, s, 0);
428
429 comedi_8254_set_mode(dev->pacer, 0, I8254_MODE1 | I8254_BINARY);
430 comedi_8254_write(dev->pacer, 0, 0x0ff);
431 udelay(1);
432 comedi_8254_update_divisors(dev->pacer);
433 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
434
435 ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN |
436 PCL816_CTRL_DMASRC_SLOT(0);
437 if (cmd->convert_src == TRIG_TIMER)
438 ctrl |= PCL816_CTRL_PACER_TRIG;
439 else
440 ctrl |= PCL816_CTRL_EXT_TRIG;
441
442 outb(ctrl, dev->iobase + PCL816_CTRL_REG);
443 outb((dma->chan << 4) | dev->irq,
444 dev->iobase + PCL816_STATUS_REG);
445
446 return 0;
447}
448
449static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
450{
451 struct pcl816_private *devpriv = dev->private;
452 struct comedi_isadma *dma = devpriv->dma;
453 struct comedi_isadma_desc *desc;
454 unsigned long flags;
455 unsigned int poll;
456 int ret;
457
458 spin_lock_irqsave(&dev->spinlock, flags);
459
460 poll = comedi_isadma_poll(dma);
461 poll = comedi_bytes_to_samples(s, poll);
462 if (poll > devpriv->ai_poll_ptr) {
463 desc = &dma->desc[dma->cur_dma];
464 transfer_from_dma_buf(dev, s, desc->virt_addr,
465 devpriv->ai_poll_ptr,
466 poll - devpriv->ai_poll_ptr);
467
468 devpriv->ai_poll_ptr = poll;
469
470 comedi_handle_events(dev, s);
471
472 ret = comedi_buf_n_bytes_ready(s);
473 } else {
474
475 ret = 0;
476 }
477 spin_unlock_irqrestore(&dev->spinlock, flags);
478
479 return ret;
480}
481
482static int pcl816_ai_cancel(struct comedi_device *dev,
483 struct comedi_subdevice *s)
484{
485 struct pcl816_private *devpriv = dev->private;
486
487 if (!devpriv->ai_cmd_running)
488 return 0;
489
490 outb(0, dev->iobase + PCL816_CTRL_REG);
491 pcl816_ai_clear_eoc(dev);
492
493 comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
494
495 devpriv->ai_cmd_running = 0;
496 devpriv->ai_cmd_canceled = 1;
497
498 return 0;
499}
500
501static int pcl816_ai_insn_read(struct comedi_device *dev,
502 struct comedi_subdevice *s,
503 struct comedi_insn *insn,
504 unsigned int *data)
505{
506 unsigned int chan = CR_CHAN(insn->chanspec);
507 unsigned int range = CR_RANGE(insn->chanspec);
508 int ret = 0;
509 int i;
510
511 outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
512
513 pcl816_ai_set_chan_range(dev, chan, range);
514 pcl816_ai_set_chan_scan(dev, chan, chan);
515
516 for (i = 0; i < insn->n; i++) {
517 pcl816_ai_clear_eoc(dev);
518 pcl816_ai_soft_trig(dev);
519
520 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
521 if (ret)
522 break;
523
524 data[i] = pcl816_ai_get_sample(dev, s);
525 }
526 outb(0, dev->iobase + PCL816_CTRL_REG);
527 pcl816_ai_clear_eoc(dev);
528
529 return ret ? ret : insn->n;
530}
531
532static int pcl816_di_insn_bits(struct comedi_device *dev,
533 struct comedi_subdevice *s,
534 struct comedi_insn *insn,
535 unsigned int *data)
536{
537 data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
538 (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
539
540 return insn->n;
541}
542
543static int pcl816_do_insn_bits(struct comedi_device *dev,
544 struct comedi_subdevice *s,
545 struct comedi_insn *insn,
546 unsigned int *data)
547{
548 if (comedi_dio_update_state(s, data)) {
549 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
550 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
551 }
552
553 data[1] = s->state;
554
555 return insn->n;
556}
557
558static void pcl816_reset(struct comedi_device *dev)
559{
560 outb(0, dev->iobase + PCL816_CTRL_REG);
561 pcl816_ai_set_chan_range(dev, 0, 0);
562 pcl816_ai_clear_eoc(dev);
563
564
565 outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
566 outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
567}
568
569static void pcl816_alloc_irq_and_dma(struct comedi_device *dev,
570 struct comedi_devconfig *it)
571{
572 struct pcl816_private *devpriv = dev->private;
573 unsigned int irq_num = it->options[1];
574 unsigned int dma_chan = it->options[2];
575
576
577 if (!(irq_num >= 2 && irq_num <= 7) ||
578 !(dma_chan == 3 || dma_chan == 1))
579 return;
580
581 if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev))
582 return;
583
584
585 devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
586 PAGE_SIZE * 4, COMEDI_ISADMA_READ);
587 if (!devpriv->dma)
588 free_irq(irq_num, dev);
589 else
590 dev->irq = irq_num;
591}
592
593static void pcl816_free_dma(struct comedi_device *dev)
594{
595 struct pcl816_private *devpriv = dev->private;
596
597 if (devpriv)
598 comedi_isadma_free(devpriv->dma);
599}
600
601static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
602{
603 const struct pcl816_board *board = dev->board_ptr;
604 struct pcl816_private *devpriv;
605 struct comedi_subdevice *s;
606 int ret;
607
608 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
609 if (!devpriv)
610 return -ENOMEM;
611
612 ret = comedi_request_region(dev, it->options[0], 0x10);
613 if (ret)
614 return ret;
615
616
617 pcl816_alloc_irq_and_dma(dev, it);
618
619 dev->pacer = comedi_8254_init(dev->iobase + PCL816_TIMER_BASE,
620 I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
621 if (!dev->pacer)
622 return -ENOMEM;
623
624 ret = comedi_alloc_subdevices(dev, 4);
625 if (ret)
626 return ret;
627
628 s = &dev->subdevices[0];
629 s->type = COMEDI_SUBD_AI;
630 s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
631 s->n_chan = 16;
632 s->maxdata = board->ai_maxdata;
633 s->range_table = &range_pcl816;
634 s->insn_read = pcl816_ai_insn_read;
635 if (dev->irq) {
636 dev->read_subdev = s;
637 s->subdev_flags |= SDF_CMD_READ;
638 s->len_chanlist = board->ai_chanlist;
639 s->do_cmdtest = pcl816_ai_cmdtest;
640 s->do_cmd = pcl816_ai_cmd;
641 s->poll = pcl816_ai_poll;
642 s->cancel = pcl816_ai_cancel;
643 }
644
645
646 s = &dev->subdevices[1];
647 s->type = COMEDI_SUBD_UNUSED;
648
649
650 s = &dev->subdevices[2];
651 s->type = COMEDI_SUBD_DI;
652 s->subdev_flags = SDF_READABLE;
653 s->n_chan = 16;
654 s->maxdata = 1;
655 s->range_table = &range_digital;
656 s->insn_bits = pcl816_di_insn_bits;
657
658
659 s = &dev->subdevices[3];
660 s->type = COMEDI_SUBD_DO;
661 s->subdev_flags = SDF_WRITABLE;
662 s->n_chan = 16;
663 s->maxdata = 1;
664 s->range_table = &range_digital;
665 s->insn_bits = pcl816_do_insn_bits;
666
667 pcl816_reset(dev);
668
669 return 0;
670}
671
672static void pcl816_detach(struct comedi_device *dev)
673{
674 if (dev->private) {
675 pcl816_ai_cancel(dev, dev->read_subdev);
676 pcl816_reset(dev);
677 }
678 pcl816_free_dma(dev);
679 comedi_legacy_detach(dev);
680}
681
682static struct comedi_driver pcl816_driver = {
683 .driver_name = "pcl816",
684 .module = THIS_MODULE,
685 .attach = pcl816_attach,
686 .detach = pcl816_detach,
687 .board_name = &boardtypes[0].name,
688 .num_names = ARRAY_SIZE(boardtypes),
689 .offset = sizeof(struct pcl816_board),
690};
691module_comedi_driver(pcl816_driver);
692
693MODULE_AUTHOR("Comedi http://www.comedi.org");
694MODULE_DESCRIPTION("Comedi low-level driver");
695MODULE_LICENSE("GPL");
696