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#include <linux/interrupt.h>
56
57#include "../comedidev.h"
58
59#include "comedi_pci.h"
60
61#include "8255.h"
62#include "plx9052.h"
63
64#define PC236_DRIVER_NAME "amplc_pc236"
65
66
67#define PCI_VENDOR_ID_AMPLICON 0x14dc
68#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
69#define PCI_DEVICE_ID_INVALID 0xffff
70
71
72
73#define PC236_IO_SIZE 4
74#define PC236_LCR_IO_SIZE 128
75
76
77
78
79
80#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
81 | PLX9052_INTCSR_LI1POL_HIGH \
82 | PLX9052_INTCSR_LI2POL_HIGH \
83 | PLX9052_INTCSR_PCIENAB_DISABLED \
84 | PLX9052_INTCSR_LI1SEL_EDGE \
85 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
86
87#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
88 | PLX9052_INTCSR_LI1POL_HIGH \
89 | PLX9052_INTCSR_LI2POL_HIGH \
90 | PLX9052_INTCSR_PCIENAB_ENABLED \
91 | PLX9052_INTCSR_LI1SEL_EDGE \
92 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
93
94
95
96
97
98enum pc236_bustype { isa_bustype, pci_bustype };
99enum pc236_model { pc36at_model, pci236_model, anypci_model };
100
101struct pc236_board {
102 const char *name;
103 const char *fancy_name;
104 unsigned short devid;
105 enum pc236_bustype bustype;
106 enum pc236_model model;
107};
108static const struct pc236_board pc236_boards[] = {
109 {
110 .name = "pc36at",
111 .fancy_name = "PC36AT",
112 .bustype = isa_bustype,
113 .model = pc36at_model,
114 },
115#ifdef CONFIG_COMEDI_PCI
116 {
117 .name = "pci236",
118 .fancy_name = "PCI236",
119 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
120 .bustype = pci_bustype,
121 .model = pci236_model,
122 },
123#endif
124#ifdef CONFIG_COMEDI_PCI
125 {
126 .name = PC236_DRIVER_NAME,
127 .fancy_name = PC236_DRIVER_NAME,
128 .devid = PCI_DEVICE_ID_INVALID,
129 .bustype = pci_bustype,
130 .model = anypci_model,
131 },
132#endif
133};
134
135#ifdef CONFIG_COMEDI_PCI
136static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
137 {
138 PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236,
139 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
140 0}
141};
142
143MODULE_DEVICE_TABLE(pci, pc236_pci_table);
144#endif
145
146
147
148
149#define thisboard ((const struct pc236_board *)dev->board_ptr)
150
151
152
153
154struct pc236_private {
155#ifdef CONFIG_COMEDI_PCI
156
157 struct pci_dev *pci_dev;
158 unsigned long lcr_iobase;
159#endif
160 int enable_irq;
161};
162
163#define devpriv ((struct pc236_private *)dev->private)
164
165
166
167
168
169
170
171static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
172static int pc236_detach(struct comedi_device *dev);
173static struct comedi_driver driver_amplc_pc236 = {
174 .driver_name = PC236_DRIVER_NAME,
175 .module = THIS_MODULE,
176 .attach = pc236_attach,
177 .detach = pc236_detach,
178 .board_name = &pc236_boards[0].name,
179 .offset = sizeof(struct pc236_board),
180 .num_names = ARRAY_SIZE(pc236_boards),
181};
182
183#ifdef CONFIG_COMEDI_PCI
184COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
185#else
186COMEDI_INITCLEANUP(driver_amplc_pc236);
187#endif
188
189static int pc236_request_region(unsigned minor, unsigned long from,
190 unsigned long extent);
191static void pc236_intr_disable(struct comedi_device *dev);
192static void pc236_intr_enable(struct comedi_device *dev);
193static int pc236_intr_check(struct comedi_device *dev);
194static int pc236_intr_insn(struct comedi_device *dev,
195 struct comedi_subdevice *s, struct comedi_insn *insn,
196 unsigned int *data);
197static int pc236_intr_cmdtest(struct comedi_device *dev,
198 struct comedi_subdevice *s,
199 struct comedi_cmd *cmd);
200static int pc236_intr_cmd(struct comedi_device *dev,
201 struct comedi_subdevice *s);
202static int pc236_intr_cancel(struct comedi_device *dev,
203 struct comedi_subdevice *s);
204static irqreturn_t pc236_interrupt(int irq, void *d);
205
206
207
208
209
210#ifdef CONFIG_COMEDI_PCI
211static int
212pc236_find_pci(struct comedi_device *dev, int bus, int slot,
213 struct pci_dev **pci_dev_p)
214{
215 struct pci_dev *pci_dev = NULL;
216
217 *pci_dev_p = NULL;
218
219
220 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
221 pci_dev != NULL;
222 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
223 PCI_ANY_ID, pci_dev)) {
224
225 if (bus || slot) {
226 if (bus != pci_dev->bus->number
227 || slot != PCI_SLOT(pci_dev->devfn))
228 continue;
229 }
230 if (thisboard->model == anypci_model) {
231
232 int i;
233
234 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
235 if (pc236_boards[i].bustype != pci_bustype)
236 continue;
237 if (pci_dev->device == pc236_boards[i].devid) {
238
239 dev->board_ptr = &pc236_boards[i];
240 break;
241 }
242 }
243 if (i == ARRAY_SIZE(pc236_boards))
244 continue;
245 } else {
246
247 if (pci_dev->device != thisboard->devid)
248 continue;
249 }
250
251
252 *pci_dev_p = pci_dev;
253 return 0;
254 }
255
256 if (bus || slot) {
257 printk(KERN_ERR
258 "comedi%d: error! no %s found at pci %02x:%02x!\n",
259 dev->minor, thisboard->name, bus, slot);
260 } else {
261 printk(KERN_ERR "comedi%d: error! no %s found!\n",
262 dev->minor, thisboard->name);
263 }
264 return -EIO;
265}
266#endif
267
268
269
270
271
272
273
274static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
275{
276 struct comedi_subdevice *s;
277 unsigned long iobase = 0;
278 unsigned int irq = 0;
279#ifdef CONFIG_COMEDI_PCI
280 struct pci_dev *pci_dev = NULL;
281 int bus = 0, slot = 0;
282#endif
283 int share_irq = 0;
284 int ret;
285
286 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
287 PC236_DRIVER_NAME);
288
289
290
291
292 ret = alloc_private(dev, sizeof(struct pc236_private));
293 if (ret < 0) {
294 printk(KERN_ERR "comedi%d: error! out of memory!\n",
295 dev->minor);
296 return ret;
297 }
298
299 switch (thisboard->bustype) {
300 case isa_bustype:
301 iobase = it->options[0];
302 irq = it->options[1];
303 share_irq = 0;
304 break;
305#ifdef CONFIG_COMEDI_PCI
306 case pci_bustype:
307 bus = it->options[0];
308 slot = it->options[1];
309 share_irq = 1;
310
311 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
312 if (ret < 0)
313 return ret;
314 devpriv->pci_dev = pci_dev;
315 break;
316#endif
317 default:
318 printk(KERN_ERR
319 "comedi%d: %s: BUG! cannot determine board type!\n",
320 dev->minor, PC236_DRIVER_NAME);
321 return -EINVAL;
322 break;
323 }
324
325
326
327
328 dev->board_name = thisboard->name;
329
330
331#ifdef CONFIG_COMEDI_PCI
332 if (pci_dev) {
333
334 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
335 if (ret < 0) {
336 printk(KERN_ERR
337 "comedi%d: error! cannot enable PCI device and request regions!\n",
338 dev->minor);
339 return ret;
340 }
341 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
342 iobase = pci_resource_start(pci_dev, 2);
343 irq = pci_dev->irq;
344 } else
345#endif
346 {
347 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
348 if (ret < 0) {
349 return ret;
350 }
351 }
352 dev->iobase = iobase;
353
354
355
356
357
358 ret = alloc_subdevices(dev, 2);
359 if (ret < 0) {
360 printk(KERN_ERR "comedi%d: error! out of memory!\n",
361 dev->minor);
362 return ret;
363 }
364
365 s = dev->subdevices + 0;
366
367 ret = subdev_8255_init(dev, s, NULL, iobase);
368 if (ret < 0) {
369 printk(KERN_ERR "comedi%d: error! out of memory!\n",
370 dev->minor);
371 return ret;
372 }
373 s = dev->subdevices + 1;
374 dev->read_subdev = s;
375 s->type = COMEDI_SUBD_UNUSED;
376 pc236_intr_disable(dev);
377 if (irq) {
378 unsigned long flags = share_irq ? IRQF_SHARED : 0;
379
380 if (request_irq(irq, pc236_interrupt, flags,
381 PC236_DRIVER_NAME, dev) >= 0) {
382 dev->irq = irq;
383 s->type = COMEDI_SUBD_DI;
384 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
385 s->n_chan = 1;
386 s->maxdata = 1;
387 s->range_table = &range_digital;
388 s->insn_bits = pc236_intr_insn;
389 s->do_cmdtest = pc236_intr_cmdtest;
390 s->do_cmd = pc236_intr_cmd;
391 s->cancel = pc236_intr_cancel;
392 }
393 }
394 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
395 if (thisboard->bustype == isa_bustype) {
396 printk("(base %#lx) ", iobase);
397 } else {
398#ifdef CONFIG_COMEDI_PCI
399 printk("(pci %s) ", pci_name(pci_dev));
400#endif
401 }
402 if (irq) {
403 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
404 } else {
405 printk("(no irq) ");
406 }
407
408 printk("attached\n");
409
410 return 1;
411}
412
413
414
415
416
417
418
419
420
421static int pc236_detach(struct comedi_device *dev)
422{
423 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
424 PC236_DRIVER_NAME);
425 if (devpriv) {
426 pc236_intr_disable(dev);
427 }
428 if (dev->irq)
429 free_irq(dev->irq, dev);
430 if (dev->subdevices) {
431 subdev_8255_cleanup(dev, dev->subdevices + 0);
432 }
433 if (devpriv) {
434#ifdef CONFIG_COMEDI_PCI
435 if (devpriv->pci_dev) {
436 if (dev->iobase) {
437 comedi_pci_disable(devpriv->pci_dev);
438 }
439 pci_dev_put(devpriv->pci_dev);
440 } else
441#endif
442 {
443 if (dev->iobase) {
444 release_region(dev->iobase, PC236_IO_SIZE);
445 }
446 }
447 }
448 if (dev->board_name) {
449 printk(KERN_INFO "comedi%d: %s removed\n",
450 dev->minor, dev->board_name);
451 }
452 return 0;
453}
454
455
456
457
458
459static int pc236_request_region(unsigned minor, unsigned long from,
460 unsigned long extent)
461{
462 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
463 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
464 minor, from, extent);
465 return -EIO;
466 }
467 return 0;
468}
469
470
471
472
473
474
475static void pc236_intr_disable(struct comedi_device *dev)
476{
477 unsigned long flags;
478
479 spin_lock_irqsave(&dev->spinlock, flags);
480 devpriv->enable_irq = 0;
481#ifdef CONFIG_COMEDI_PCI
482 if (devpriv->lcr_iobase)
483 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
484#endif
485 spin_unlock_irqrestore(&dev->spinlock, flags);
486}
487
488
489
490
491
492
493static void pc236_intr_enable(struct comedi_device *dev)
494{
495 unsigned long flags;
496
497 spin_lock_irqsave(&dev->spinlock, flags);
498 devpriv->enable_irq = 1;
499#ifdef CONFIG_COMEDI_PCI
500 if (devpriv->lcr_iobase)
501 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
502#endif
503 spin_unlock_irqrestore(&dev->spinlock, flags);
504}
505
506
507
508
509
510
511
512
513static int pc236_intr_check(struct comedi_device *dev)
514{
515 int retval = 0;
516 unsigned long flags;
517
518 spin_lock_irqsave(&dev->spinlock, flags);
519 if (devpriv->enable_irq) {
520 retval = 1;
521#ifdef CONFIG_COMEDI_PCI
522 if (devpriv->lcr_iobase) {
523 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
524 & PLX9052_INTCSR_LI1STAT_MASK)
525 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
526 retval = 0;
527 } else {
528
529 outl(PCI236_INTR_ENABLE,
530 devpriv->lcr_iobase + PLX9052_INTCSR);
531 }
532 }
533#endif
534 }
535 spin_unlock_irqrestore(&dev->spinlock, flags);
536
537 return retval;
538}
539
540
541
542
543
544static int pc236_intr_insn(struct comedi_device *dev,
545 struct comedi_subdevice *s, struct comedi_insn *insn,
546 unsigned int *data)
547{
548 data[1] = 0;
549 return 2;
550}
551
552
553
554
555
556static int pc236_intr_cmdtest(struct comedi_device *dev,
557 struct comedi_subdevice *s,
558 struct comedi_cmd *cmd)
559{
560 int err = 0;
561 int tmp;
562
563
564
565 tmp = cmd->start_src;
566 cmd->start_src &= TRIG_NOW;
567 if (!cmd->start_src || tmp != cmd->start_src)
568 err++;
569
570 tmp = cmd->scan_begin_src;
571 cmd->scan_begin_src &= TRIG_EXT;
572 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
573 err++;
574
575 tmp = cmd->convert_src;
576 cmd->convert_src &= TRIG_FOLLOW;
577 if (!cmd->convert_src || tmp != cmd->convert_src)
578 err++;
579
580 tmp = cmd->scan_end_src;
581 cmd->scan_end_src &= TRIG_COUNT;
582 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
583 err++;
584
585 tmp = cmd->stop_src;
586 cmd->stop_src &= TRIG_NONE;
587 if (!cmd->stop_src || tmp != cmd->stop_src)
588 err++;
589
590 if (err)
591 return 1;
592
593
594
595 if (err)
596 return 2;
597
598
599
600 if (cmd->start_arg != 0) {
601 cmd->start_arg = 0;
602 err++;
603 }
604 if (cmd->scan_begin_arg != 0) {
605 cmd->scan_begin_arg = 0;
606 err++;
607 }
608 if (cmd->convert_arg != 0) {
609 cmd->convert_arg = 0;
610 err++;
611 }
612 if (cmd->scan_end_arg != 1) {
613 cmd->scan_end_arg = 1;
614 err++;
615 }
616 if (cmd->stop_arg != 0) {
617 cmd->stop_arg = 0;
618 err++;
619 }
620
621 if (err)
622 return 3;
623
624
625
626 if (err)
627 return 4;
628
629 return 0;
630}
631
632
633
634
635static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
636{
637 pc236_intr_enable(dev);
638
639 return 0;
640}
641
642
643
644
645static int pc236_intr_cancel(struct comedi_device *dev,
646 struct comedi_subdevice *s)
647{
648 pc236_intr_disable(dev);
649
650 return 0;
651}
652
653
654
655
656
657static irqreturn_t pc236_interrupt(int irq, void *d)
658{
659 struct comedi_device *dev = d;
660 struct comedi_subdevice *s = dev->subdevices + 1;
661 int handled;
662
663 handled = pc236_intr_check(dev);
664 if (dev->attached && handled) {
665 comedi_buf_put(s->async, 0);
666 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
667 comedi_event(dev, s);
668 }
669 return IRQ_RETVAL(handled);
670}
671