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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66#include <linux/module.h>
67#include <linux/interrupt.h>
68#include <linux/comedi/comedidev.h>
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87#define PCMUIO_PORT_REG(x) (0x00 + (x))
88#define PCMUIO_INT_PENDING_REG 0x06
89#define PCMUIO_PAGE_LOCK_REG 0x07
90#define PCMUIO_LOCK_PORT(x) ((1 << (x)) & 0x3f)
91#define PCMUIO_PAGE(x) (((x) & 0x3) << 6)
92#define PCMUIO_PAGE_MASK PCMUIO_PAGE(3)
93#define PCMUIO_PAGE_POL 1
94#define PCMUIO_PAGE_ENAB 2
95#define PCMUIO_PAGE_INT_ID 3
96#define PCMUIO_PAGE_REG(x) (0x08 + (x))
97
98#define PCMUIO_ASIC_IOSIZE 0x10
99#define PCMUIO_MAX_ASICS 2
100
101struct pcmuio_board {
102 const char *name;
103 const int num_asics;
104};
105
106static const struct pcmuio_board pcmuio_boards[] = {
107 {
108 .name = "pcmuio48",
109 .num_asics = 1,
110 }, {
111 .name = "pcmuio96",
112 .num_asics = 2,
113 },
114};
115
116struct pcmuio_asic {
117 spinlock_t pagelock;
118 spinlock_t spinlock;
119 unsigned int enabled_mask;
120 unsigned int active:1;
121};
122
123struct pcmuio_private {
124 struct pcmuio_asic asics[PCMUIO_MAX_ASICS];
125 unsigned int irq2;
126};
127
128static inline unsigned long pcmuio_asic_iobase(struct comedi_device *dev,
129 int asic)
130{
131 return dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
132}
133
134static inline int pcmuio_subdevice_to_asic(struct comedi_subdevice *s)
135{
136
137
138
139
140 return s->index / 2;
141}
142
143static inline int pcmuio_subdevice_to_port(struct comedi_subdevice *s)
144{
145
146
147
148
149 return (s->index % 2) ? 3 : 0;
150}
151
152static void pcmuio_write(struct comedi_device *dev, unsigned int val,
153 int asic, int page, int port)
154{
155 struct pcmuio_private *devpriv = dev->private;
156 struct pcmuio_asic *chip = &devpriv->asics[asic];
157 unsigned long iobase = pcmuio_asic_iobase(dev, asic);
158 unsigned long flags;
159
160 spin_lock_irqsave(&chip->pagelock, flags);
161 if (page == 0) {
162
163 outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0));
164 outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1));
165 outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2));
166 } else {
167 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
168 outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0));
169 outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1));
170 outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2));
171 }
172 spin_unlock_irqrestore(&chip->pagelock, flags);
173}
174
175static unsigned int pcmuio_read(struct comedi_device *dev,
176 int asic, int page, int port)
177{
178 struct pcmuio_private *devpriv = dev->private;
179 struct pcmuio_asic *chip = &devpriv->asics[asic];
180 unsigned long iobase = pcmuio_asic_iobase(dev, asic);
181 unsigned long flags;
182 unsigned int val;
183
184 spin_lock_irqsave(&chip->pagelock, flags);
185 if (page == 0) {
186
187 val = inb(iobase + PCMUIO_PORT_REG(port + 0));
188 val |= (inb(iobase + PCMUIO_PORT_REG(port + 1)) << 8);
189 val |= (inb(iobase + PCMUIO_PORT_REG(port + 2)) << 16);
190 } else {
191 outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
192 val = inb(iobase + PCMUIO_PAGE_REG(0));
193 val |= (inb(iobase + PCMUIO_PAGE_REG(1)) << 8);
194 val |= (inb(iobase + PCMUIO_PAGE_REG(2)) << 16);
195 }
196 spin_unlock_irqrestore(&chip->pagelock, flags);
197
198 return val;
199}
200
201
202
203
204
205
206
207
208
209
210
211static int pcmuio_dio_insn_bits(struct comedi_device *dev,
212 struct comedi_subdevice *s,
213 struct comedi_insn *insn,
214 unsigned int *data)
215{
216 int asic = pcmuio_subdevice_to_asic(s);
217 int port = pcmuio_subdevice_to_port(s);
218 unsigned int chanmask = (1 << s->n_chan) - 1;
219 unsigned int mask;
220 unsigned int val;
221
222 mask = comedi_dio_update_state(s, data);
223 if (mask) {
224
225
226
227
228
229
230
231
232 val = ~s->state & chanmask;
233 val &= s->io_bits;
234 pcmuio_write(dev, val, asic, 0, port);
235 }
236
237
238 val = pcmuio_read(dev, asic, 0, port);
239
240
241 data[1] = ~val & chanmask;
242
243 return insn->n;
244}
245
246static int pcmuio_dio_insn_config(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 struct comedi_insn *insn,
249 unsigned int *data)
250{
251 int asic = pcmuio_subdevice_to_asic(s);
252 int port = pcmuio_subdevice_to_port(s);
253 int ret;
254
255 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
256 if (ret)
257 return ret;
258
259 if (data[0] == INSN_CONFIG_DIO_INPUT)
260 pcmuio_write(dev, s->io_bits, asic, 0, port);
261
262 return insn->n;
263}
264
265static void pcmuio_reset(struct comedi_device *dev)
266{
267 const struct pcmuio_board *board = dev->board_ptr;
268 int asic;
269
270 for (asic = 0; asic < board->num_asics; ++asic) {
271
272 pcmuio_write(dev, 0, asic, 0, 0);
273 pcmuio_write(dev, 0, asic, 0, 3);
274
275
276 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0);
277 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
278 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
279 }
280}
281
282
283static void pcmuio_stop_intr(struct comedi_device *dev,
284 struct comedi_subdevice *s)
285{
286 struct pcmuio_private *devpriv = dev->private;
287 int asic = pcmuio_subdevice_to_asic(s);
288 struct pcmuio_asic *chip = &devpriv->asics[asic];
289
290 chip->enabled_mask = 0;
291 chip->active = 0;
292 s->async->inttrig = NULL;
293
294
295 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
296}
297
298static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
299 struct comedi_subdevice *s,
300 unsigned int triggered)
301{
302 struct pcmuio_private *devpriv = dev->private;
303 int asic = pcmuio_subdevice_to_asic(s);
304 struct pcmuio_asic *chip = &devpriv->asics[asic];
305 struct comedi_cmd *cmd = &s->async->cmd;
306 unsigned int val = 0;
307 unsigned long flags;
308 unsigned int i;
309
310 spin_lock_irqsave(&chip->spinlock, flags);
311
312 if (!chip->active)
313 goto done;
314
315 if (!(triggered & chip->enabled_mask))
316 goto done;
317
318 for (i = 0; i < cmd->chanlist_len; i++) {
319 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
320
321 if (triggered & (1 << chan))
322 val |= (1 << i);
323 }
324
325 comedi_buf_write_samples(s, &val, 1);
326
327 if (cmd->stop_src == TRIG_COUNT &&
328 s->async->scans_done >= cmd->stop_arg)
329 s->async->events |= COMEDI_CB_EOA;
330
331done:
332 spin_unlock_irqrestore(&chip->spinlock, flags);
333
334 comedi_handle_events(dev, s);
335}
336
337static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
338{
339
340 struct comedi_subdevice *s = &dev->subdevices[asic * 2];
341 unsigned long iobase = pcmuio_asic_iobase(dev, asic);
342 unsigned int val;
343
344
345 val = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07;
346 if (!val)
347 return 0;
348
349
350 val = pcmuio_read(dev, asic, PCMUIO_PAGE_INT_ID, 0);
351 pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
352
353
354 pcmuio_handle_intr_subdev(dev, s, val);
355
356 return 1;
357}
358
359static irqreturn_t pcmuio_interrupt(int irq, void *d)
360{
361 struct comedi_device *dev = d;
362 struct pcmuio_private *devpriv = dev->private;
363 int handled = 0;
364
365 if (irq == dev->irq)
366 handled += pcmuio_handle_asic_interrupt(dev, 0);
367 if (irq == devpriv->irq2)
368 handled += pcmuio_handle_asic_interrupt(dev, 1);
369
370 return handled ? IRQ_HANDLED : IRQ_NONE;
371}
372
373
374static void pcmuio_start_intr(struct comedi_device *dev,
375 struct comedi_subdevice *s)
376{
377 struct pcmuio_private *devpriv = dev->private;
378 int asic = pcmuio_subdevice_to_asic(s);
379 struct pcmuio_asic *chip = &devpriv->asics[asic];
380 struct comedi_cmd *cmd = &s->async->cmd;
381 unsigned int bits = 0;
382 unsigned int pol_bits = 0;
383 int i;
384
385 chip->enabled_mask = 0;
386 chip->active = 1;
387 if (cmd->chanlist) {
388 for (i = 0; i < cmd->chanlist_len; i++) {
389 unsigned int chanspec = cmd->chanlist[i];
390 unsigned int chan = CR_CHAN(chanspec);
391 unsigned int range = CR_RANGE(chanspec);
392 unsigned int aref = CR_AREF(chanspec);
393
394 bits |= (1 << chan);
395 pol_bits |= ((aref || range) ? 1 : 0) << chan;
396 }
397 }
398 bits &= ((1 << s->n_chan) - 1);
399 chip->enabled_mask = bits;
400
401
402 pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0);
403 pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0);
404}
405
406static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
407{
408 struct pcmuio_private *devpriv = dev->private;
409 int asic = pcmuio_subdevice_to_asic(s);
410 struct pcmuio_asic *chip = &devpriv->asics[asic];
411 unsigned long flags;
412
413 spin_lock_irqsave(&chip->spinlock, flags);
414 if (chip->active)
415 pcmuio_stop_intr(dev, s);
416 spin_unlock_irqrestore(&chip->spinlock, flags);
417
418 return 0;
419}
420
421static int pcmuio_inttrig_start_intr(struct comedi_device *dev,
422 struct comedi_subdevice *s,
423 unsigned int trig_num)
424{
425 struct pcmuio_private *devpriv = dev->private;
426 struct comedi_cmd *cmd = &s->async->cmd;
427 int asic = pcmuio_subdevice_to_asic(s);
428 struct pcmuio_asic *chip = &devpriv->asics[asic];
429 unsigned long flags;
430
431 if (trig_num != cmd->start_arg)
432 return -EINVAL;
433
434 spin_lock_irqsave(&chip->spinlock, flags);
435 s->async->inttrig = NULL;
436 if (chip->active)
437 pcmuio_start_intr(dev, s);
438
439 spin_unlock_irqrestore(&chip->spinlock, flags);
440
441 return 1;
442}
443
444
445
446
447static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
448{
449 struct pcmuio_private *devpriv = dev->private;
450 struct comedi_cmd *cmd = &s->async->cmd;
451 int asic = pcmuio_subdevice_to_asic(s);
452 struct pcmuio_asic *chip = &devpriv->asics[asic];
453 unsigned long flags;
454
455 spin_lock_irqsave(&chip->spinlock, flags);
456 chip->active = 1;
457
458
459 if (cmd->start_src == TRIG_INT)
460 s->async->inttrig = pcmuio_inttrig_start_intr;
461 else
462 pcmuio_start_intr(dev, s);
463
464 spin_unlock_irqrestore(&chip->spinlock, flags);
465
466 return 0;
467}
468
469static int pcmuio_cmdtest(struct comedi_device *dev,
470 struct comedi_subdevice *s,
471 struct comedi_cmd *cmd)
472{
473 int err = 0;
474
475
476
477 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
478 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
479 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
480 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
481 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
482
483 if (err)
484 return 1;
485
486
487
488 err |= comedi_check_trigger_is_unique(cmd->start_src);
489 err |= comedi_check_trigger_is_unique(cmd->stop_src);
490
491
492
493 if (err)
494 return 2;
495
496
497
498 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
499 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
500 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
501 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
502 cmd->chanlist_len);
503
504 if (cmd->stop_src == TRIG_COUNT)
505 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
506 else
507 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
508
509 if (err)
510 return 3;
511
512
513
514
515
516 return 0;
517}
518
519static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
520{
521 const struct pcmuio_board *board = dev->board_ptr;
522 struct comedi_subdevice *s;
523 struct pcmuio_private *devpriv;
524 int ret;
525 int i;
526
527 ret = comedi_request_region(dev, it->options[0],
528 board->num_asics * PCMUIO_ASIC_IOSIZE);
529 if (ret)
530 return ret;
531
532 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
533 if (!devpriv)
534 return -ENOMEM;
535
536 for (i = 0; i < PCMUIO_MAX_ASICS; ++i) {
537 struct pcmuio_asic *chip = &devpriv->asics[i];
538
539 spin_lock_init(&chip->pagelock);
540 spin_lock_init(&chip->spinlock);
541 }
542
543 pcmuio_reset(dev);
544
545 if (it->options[1]) {
546
547 ret = request_irq(it->options[1], pcmuio_interrupt, 0,
548 dev->board_name, dev);
549 if (ret == 0)
550 dev->irq = it->options[1];
551 }
552
553 if (board->num_asics == 2) {
554 if (it->options[2] == dev->irq) {
555
556 devpriv->irq2 = it->options[2];
557 } else if (it->options[2]) {
558
559 ret = request_irq(it->options[2], pcmuio_interrupt, 0,
560 dev->board_name, dev);
561 if (ret == 0)
562 devpriv->irq2 = it->options[2];
563 }
564 }
565
566 ret = comedi_alloc_subdevices(dev, board->num_asics * 2);
567 if (ret)
568 return ret;
569
570 for (i = 0; i < dev->n_subdevices; ++i) {
571 s = &dev->subdevices[i];
572 s->type = COMEDI_SUBD_DIO;
573 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
574 s->n_chan = 24;
575 s->maxdata = 1;
576 s->range_table = &range_digital;
577 s->insn_bits = pcmuio_dio_insn_bits;
578 s->insn_config = pcmuio_dio_insn_config;
579
580
581 if ((i == 0 && dev->irq) || (i == 2 && devpriv->irq2)) {
582
583 dev->read_subdev = s;
584 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL |
585 SDF_PACKED;
586 s->len_chanlist = s->n_chan;
587 s->cancel = pcmuio_cancel;
588 s->do_cmd = pcmuio_cmd;
589 s->do_cmdtest = pcmuio_cmdtest;
590 }
591 }
592
593 return 0;
594}
595
596static void pcmuio_detach(struct comedi_device *dev)
597{
598 struct pcmuio_private *devpriv = dev->private;
599
600 if (devpriv) {
601 pcmuio_reset(dev);
602
603
604 if (devpriv->irq2 && devpriv->irq2 != dev->irq)
605 free_irq(devpriv->irq2, dev);
606 }
607 comedi_legacy_detach(dev);
608}
609
610static struct comedi_driver pcmuio_driver = {
611 .driver_name = "pcmuio",
612 .module = THIS_MODULE,
613 .attach = pcmuio_attach,
614 .detach = pcmuio_detach,
615 .board_name = &pcmuio_boards[0].name,
616 .offset = sizeof(struct pcmuio_board),
617 .num_names = ARRAY_SIZE(pcmuio_boards),
618};
619module_comedi_driver(pcmuio_driver);
620
621MODULE_AUTHOR("Comedi https://www.comedi.org");
622MODULE_DESCRIPTION("Comedi low-level driver");
623MODULE_LICENSE("GPL");
624