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