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/pci.h>
56#include <linux/interrupt.h>
57
58#include "../comedidev.h"
59
60#include "comedi_fc.h"
61#include "8255.h"
62#include "plx9052.h"
63
64#define PC236_DRIVER_NAME "amplc_pc236"
65
66#define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
67#define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
68
69
70#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
71#define PCI_DEVICE_ID_INVALID 0xffff
72
73
74
75#define PC236_IO_SIZE 4
76#define PC236_LCR_IO_SIZE 128
77
78
79#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1POL | \
80 PLX9052_INTCSR_LI2POL | \
81 PLX9052_INTCSR_LI1SEL | \
82 PLX9052_INTCSR_LI1CLRINT)
83
84
85#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB | \
86 PLX9052_INTCSR_LI1POL | \
87 PLX9052_INTCSR_LI2POL | \
88 PLX9052_INTCSR_PCIENAB | \
89 PLX9052_INTCSR_LI1SEL | \
90 PLX9052_INTCSR_LI1CLRINT)
91
92
93
94
95
96enum pc236_bustype { isa_bustype, pci_bustype };
97enum pc236_model { pc36at_model, pci236_model, anypci_model };
98
99struct pc236_board {
100 const char *name;
101 unsigned short devid;
102 enum pc236_bustype bustype;
103 enum pc236_model model;
104};
105static const struct pc236_board pc236_boards[] = {
106#if DO_ISA
107 {
108 .name = "pc36at",
109 .bustype = isa_bustype,
110 .model = pc36at_model,
111 },
112#endif
113#if DO_PCI
114 {
115 .name = "pci236",
116 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
117 .bustype = pci_bustype,
118 .model = pci236_model,
119 },
120 {
121 .name = PC236_DRIVER_NAME,
122 .devid = PCI_DEVICE_ID_INVALID,
123 .bustype = pci_bustype,
124 .model = anypci_model,
125 },
126#endif
127};
128
129
130
131
132
133struct pc236_private {
134 unsigned long lcr_iobase;
135 int enable_irq;
136};
137
138
139static inline bool is_isa_board(const struct pc236_board *board)
140{
141 return DO_ISA && board->bustype == isa_bustype;
142}
143
144
145static inline bool is_pci_board(const struct pc236_board *board)
146{
147 return DO_PCI && board->bustype == pci_bustype;
148}
149
150
151
152
153static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
154{
155 unsigned int i;
156
157 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
158 if (is_pci_board(&pc236_boards[i]) &&
159 pci_dev->device == pc236_boards[i].devid)
160 return &pc236_boards[i];
161 return NULL;
162}
163
164
165
166
167
168static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
169 struct comedi_devconfig *it)
170{
171 const struct pc236_board *thisboard = comedi_board(dev);
172 struct pci_dev *pci_dev = NULL;
173 int bus = it->options[0];
174 int slot = it->options[1];
175
176 for_each_pci_dev(pci_dev) {
177 if (bus || slot) {
178 if (bus != pci_dev->bus->number ||
179 slot != PCI_SLOT(pci_dev->devfn))
180 continue;
181 }
182 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
183 continue;
184
185 if (thisboard->model == anypci_model) {
186
187 const struct pc236_board *foundboard;
188
189 foundboard = pc236_find_pci_board(pci_dev);
190 if (foundboard == NULL)
191 continue;
192
193 dev->board_ptr = foundboard;
194 } else {
195
196 if (pci_dev->device != thisboard->devid)
197 continue;
198 }
199 return pci_dev;
200 }
201 dev_err(dev->class_dev,
202 "No supported board found! (req. bus %d, slot %d)\n",
203 bus, slot);
204 return NULL;
205}
206
207
208
209
210
211
212static void pc236_intr_disable(struct comedi_device *dev)
213{
214 const struct pc236_board *thisboard = comedi_board(dev);
215 struct pc236_private *devpriv = dev->private;
216 unsigned long flags;
217
218 spin_lock_irqsave(&dev->spinlock, flags);
219 devpriv->enable_irq = 0;
220 if (is_pci_board(thisboard))
221 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
222 spin_unlock_irqrestore(&dev->spinlock, flags);
223}
224
225
226
227
228
229
230static void pc236_intr_enable(struct comedi_device *dev)
231{
232 const struct pc236_board *thisboard = comedi_board(dev);
233 struct pc236_private *devpriv = dev->private;
234 unsigned long flags;
235
236 spin_lock_irqsave(&dev->spinlock, flags);
237 devpriv->enable_irq = 1;
238 if (is_pci_board(thisboard))
239 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
240 spin_unlock_irqrestore(&dev->spinlock, flags);
241}
242
243
244
245
246
247
248
249
250static int pc236_intr_check(struct comedi_device *dev)
251{
252 const struct pc236_board *thisboard = comedi_board(dev);
253 struct pc236_private *devpriv = dev->private;
254 int retval = 0;
255 unsigned long flags;
256 unsigned int intcsr;
257
258 spin_lock_irqsave(&dev->spinlock, flags);
259 if (devpriv->enable_irq) {
260 retval = 1;
261 if (is_pci_board(thisboard)) {
262 intcsr = inl(devpriv->lcr_iobase + PLX9052_INTCSR);
263 if (!(intcsr & PLX9052_INTCSR_LI1STAT)) {
264 retval = 0;
265 } else {
266
267 outl(PCI236_INTR_ENABLE,
268 devpriv->lcr_iobase + PLX9052_INTCSR);
269 }
270 }
271 }
272 spin_unlock_irqrestore(&dev->spinlock, flags);
273
274 return retval;
275}
276
277
278
279
280
281static int pc236_intr_insn(struct comedi_device *dev,
282 struct comedi_subdevice *s, struct comedi_insn *insn,
283 unsigned int *data)
284{
285 data[1] = 0;
286 return insn->n;
287}
288
289
290
291
292
293static int pc236_intr_cmdtest(struct comedi_device *dev,
294 struct comedi_subdevice *s,
295 struct comedi_cmd *cmd)
296{
297 int err = 0;
298
299
300
301 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
302 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
303 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
304 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
305 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
306
307 if (err)
308 return 1;
309
310
311
312
313 if (err)
314 return 2;
315
316
317
318 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
319 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
320 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
321 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
322 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
323
324 if (err)
325 return 3;
326
327
328
329 if (err)
330 return 4;
331
332 return 0;
333}
334
335
336
337
338static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
339{
340 pc236_intr_enable(dev);
341
342 return 0;
343}
344
345
346
347
348static int pc236_intr_cancel(struct comedi_device *dev,
349 struct comedi_subdevice *s)
350{
351 pc236_intr_disable(dev);
352
353 return 0;
354}
355
356
357
358
359
360static irqreturn_t pc236_interrupt(int irq, void *d)
361{
362 struct comedi_device *dev = d;
363 struct comedi_subdevice *s = &dev->subdevices[1];
364 int handled;
365
366 handled = pc236_intr_check(dev);
367 if (dev->attached && handled) {
368 comedi_buf_put(s->async, 0);
369 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
370 comedi_event(dev, s);
371 }
372 return IRQ_RETVAL(handled);
373}
374
375static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
376{
377 const struct pc236_board *thisboard = comedi_board(dev);
378 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
379 char tmpbuf[60];
380 int tmplen;
381
382 if (is_isa_board(thisboard))
383 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
384 "(base %#lx) ", dev->iobase);
385 else if (is_pci_board(thisboard))
386 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
387 "(pci %s) ", pci_name(pcidev));
388 else
389 tmplen = 0;
390 if (irq)
391 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
392 "(irq %u%s) ", irq,
393 (dev->irq ? "" : " UNAVAILABLE"));
394 else
395 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
396 "(no irq) ");
397 dev_info(dev->class_dev, "%s %sattached\n",
398 dev->board_name, tmpbuf);
399}
400
401static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
402 unsigned int irq, unsigned long req_irq_flags)
403{
404 const struct pc236_board *thisboard = comedi_board(dev);
405 struct comedi_subdevice *s;
406 int ret;
407
408 dev->board_name = thisboard->name;
409 dev->iobase = iobase;
410
411 ret = comedi_alloc_subdevices(dev, 2);
412 if (ret)
413 return ret;
414
415 s = &dev->subdevices[0];
416
417 ret = subdev_8255_init(dev, s, NULL, iobase);
418 if (ret < 0) {
419 dev_err(dev->class_dev, "error! out of memory!\n");
420 return ret;
421 }
422 s = &dev->subdevices[1];
423 dev->read_subdev = s;
424 s->type = COMEDI_SUBD_UNUSED;
425 pc236_intr_disable(dev);
426 if (irq) {
427 if (request_irq(irq, pc236_interrupt, req_irq_flags,
428 PC236_DRIVER_NAME, dev) >= 0) {
429 dev->irq = irq;
430 s->type = COMEDI_SUBD_DI;
431 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
432 s->n_chan = 1;
433 s->maxdata = 1;
434 s->range_table = &range_digital;
435 s->insn_bits = pc236_intr_insn;
436 s->do_cmdtest = pc236_intr_cmdtest;
437 s->do_cmd = pc236_intr_cmd;
438 s->cancel = pc236_intr_cancel;
439 }
440 }
441 pc236_report_attach(dev, irq);
442 return 1;
443}
444
445static int pc236_pci_common_attach(struct comedi_device *dev,
446 struct pci_dev *pci_dev)
447{
448 struct pc236_private *devpriv = dev->private;
449 unsigned long iobase;
450 int ret;
451
452 comedi_set_hw_dev(dev, &pci_dev->dev);
453
454 ret = comedi_pci_enable(dev);
455 if (ret)
456 return ret;
457
458 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
459 iobase = pci_resource_start(pci_dev, 2);
460 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
461}
462
463
464
465
466
467
468
469static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
470{
471 const struct pc236_board *thisboard = comedi_board(dev);
472 struct pc236_private *devpriv;
473 int ret;
474
475 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
476 if (!devpriv)
477 return -ENOMEM;
478 dev->private = devpriv;
479
480
481 if (is_isa_board(thisboard)) {
482 ret = comedi_request_region(dev, it->options[0], PC236_IO_SIZE);
483 if (ret)
484 return ret;
485
486 return pc236_common_attach(dev, dev->iobase, it->options[1], 0);
487 } else if (is_pci_board(thisboard)) {
488 struct pci_dev *pci_dev;
489
490 pci_dev = pc236_find_pci_dev(dev, it);
491 if (!pci_dev)
492 return -EIO;
493 return pc236_pci_common_attach(dev, pci_dev);
494 } else {
495 dev_err(dev->class_dev, PC236_DRIVER_NAME
496 ": BUG! cannot determine board type!\n");
497 return -EINVAL;
498 }
499}
500
501
502
503
504
505
506static int pc236_auto_attach(struct comedi_device *dev,
507 unsigned long context_unused)
508{
509 struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
510 struct pc236_private *devpriv;
511
512 if (!DO_PCI)
513 return -EINVAL;
514
515 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
516 pci_name(pci_dev));
517
518 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
519 if (!devpriv)
520 return -ENOMEM;
521 dev->private = devpriv;
522
523 dev->board_ptr = pc236_find_pci_board(pci_dev);
524 if (dev->board_ptr == NULL) {
525 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
526 return -EINVAL;
527 }
528
529
530
531
532
533
534 pci_dev_get(pci_dev);
535 return pc236_pci_common_attach(dev, pci_dev);
536}
537
538static void pc236_detach(struct comedi_device *dev)
539{
540 const struct pc236_board *thisboard = comedi_board(dev);
541
542 if (!thisboard)
543 return;
544 if (dev->iobase)
545 pc236_intr_disable(dev);
546 comedi_spriv_free(dev, 0);
547 if (is_isa_board(thisboard)) {
548 comedi_legacy_detach(dev);
549 } else if (is_pci_board(thisboard)) {
550 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
551 if (dev->irq)
552 free_irq(dev->irq, dev);
553 comedi_pci_disable(dev);
554 if (pcidev)
555 pci_dev_put(pcidev);
556 }
557}
558
559
560
561
562
563
564
565static struct comedi_driver amplc_pc236_driver = {
566 .driver_name = PC236_DRIVER_NAME,
567 .module = THIS_MODULE,
568 .attach = pc236_attach,
569 .auto_attach = pc236_auto_attach,
570 .detach = pc236_detach,
571 .board_name = &pc236_boards[0].name,
572 .offset = sizeof(struct pc236_board),
573 .num_names = ARRAY_SIZE(pc236_boards),
574};
575
576#if DO_PCI
577static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
578 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
579 {0}
580};
581
582MODULE_DEVICE_TABLE(pci, pc236_pci_table);
583
584static int amplc_pc236_pci_probe(struct pci_dev *dev,
585 const struct pci_device_id *id)
586{
587 return comedi_pci_auto_config(dev, &lc_pc236_driver,
588 id->driver_data);
589}
590
591static struct pci_driver amplc_pc236_pci_driver = {
592 .name = PC236_DRIVER_NAME,
593 .id_table = pc236_pci_table,
594 .probe = &lc_pc236_pci_probe,
595 .remove = comedi_pci_auto_unconfig,
596};
597
598module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
599#else
600module_comedi_driver(amplc_pc236_driver);
601#endif
602
603MODULE_AUTHOR("Comedi http://www.comedi.org");
604MODULE_DESCRIPTION("Comedi low-level driver");
605MODULE_LICENSE("GPL");
606