]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/staging/comedi/drivers/amplc_pc236.c
staging: comedi: amplc_pc236: add helper functions to check bus type
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / amplc_pc236.c
1 /*
2 comedi/drivers/amplc_pc236.c
3 Driver for Amplicon PC36AT and PCI236 DIO boards.
4
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 */
25 /*
26 Driver: amplc_pc236
27 Description: Amplicon PC36AT, PCI236
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
30 Updated: Wed, 01 Apr 2009 15:41:25 +0100
31 Status: works
32
33 Configuration options - PC36AT:
34 [0] - I/O port base address
35 [1] - IRQ (optional)
36
37 Configuration options - PCI236:
38 [0] - PCI bus of device (optional)
39 [1] - PCI slot of device (optional)
40 If bus/slot is not specified, the first available PCI device will be
41 used.
42
43 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
44 as subdevice 0.
45
46 Subdevice 1 pretends to be a digital input device, but it always returns
47 0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
48 a rising edge on port C bit 3 acts as an external trigger, which can be
49 used to wake up tasks. This is like the comedi_parport device, but the
50 only way to physically disable the interrupt on the PC36AT is to remove
51 the IRQ jumper. If no interrupt is connected, then subdevice 1 is
52 unused.
53 */
54
55 #include <linux/interrupt.h>
56
57 #include "../comedidev.h"
58
59 #include "8255.h"
60 #include "plx9052.h"
61
62 #define PC236_DRIVER_NAME "amplc_pc236"
63
64 /* PCI236 PCI configuration register information */
65 #define PCI_VENDOR_ID_AMPLICON 0x14dc
66 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
67 #define PCI_DEVICE_ID_INVALID 0xffff
68
69 /* PC36AT / PCI236 registers */
70
71 #define PC236_IO_SIZE 4
72 #define PC236_LCR_IO_SIZE 128
73
74 /*
75 * INTCSR values for PCI236.
76 */
77 /* Disable interrupt, also clear any interrupt there */
78 #define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
79 | PLX9052_INTCSR_LI1POL_HIGH \
80 | PLX9052_INTCSR_LI2POL_HIGH \
81 | PLX9052_INTCSR_PCIENAB_DISABLED \
82 | PLX9052_INTCSR_LI1SEL_EDGE \
83 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
84 /* Enable interrupt, also clear any interrupt there. */
85 #define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
86 | PLX9052_INTCSR_LI1POL_HIGH \
87 | PLX9052_INTCSR_LI2POL_HIGH \
88 | PLX9052_INTCSR_PCIENAB_ENABLED \
89 | PLX9052_INTCSR_LI1SEL_EDGE \
90 | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
91
92 /*
93 * Board descriptions for Amplicon PC36AT and PCI236.
94 */
95
96 enum pc236_bustype { isa_bustype, pci_bustype };
97 enum pc236_model { pc36at_model, pci236_model, anypci_model };
98
99 struct pc236_board {
100 const char *name;
101 unsigned short devid;
102 enum pc236_bustype bustype;
103 enum pc236_model model;
104 };
105 static const struct pc236_board pc236_boards[] = {
106 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
107 {
108 .name = "pc36at",
109 .bustype = isa_bustype,
110 .model = pc36at_model,
111 },
112 #endif
113 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_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, /* wildcard */
125 },
126 #endif
127 };
128
129 /* this structure is for data unique to this hardware driver. If
130 several hardware drivers keep similar information in this structure,
131 feel free to suggest moving the variable to the struct comedi_device struct.
132 */
133 struct pc236_private {
134 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
135 int enable_irq;
136 };
137
138 /* test if ISA supported and this is an ISA board */
139 static inline bool is_isa_board(const struct pc236_board *board)
140 {
141 return IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
142 && board->bustype == isa_bustype;
143 }
144
145 /* test if PCI supported and this is a PCI board */
146 static inline bool is_pci_board(const struct pc236_board *board)
147 {
148 return IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
149 && board->bustype == pci_bustype;
150 }
151
152 /*
153 * This function looks for a board matching the supplied PCI device.
154 */
155 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
156 {
157 unsigned int i;
158
159 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
160 if (is_pci_board(&pc236_boards[i]) &&
161 pci_dev->device == pc236_boards[i].devid)
162 return &pc236_boards[i];
163 return NULL;
164 }
165
166 /*
167 * This function looks for a PCI device matching the requested board name,
168 * bus and slot.
169 */
170 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
171 struct comedi_devconfig *it)
172 {
173 const struct pc236_board *thisboard = comedi_board(dev);
174 struct pci_dev *pci_dev = NULL;
175 int bus = it->options[0];
176 int slot = it->options[1];
177
178 for_each_pci_dev(pci_dev) {
179 if (bus || slot) {
180 if (bus != pci_dev->bus->number ||
181 slot != PCI_SLOT(pci_dev->devfn))
182 continue;
183 }
184 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
185 continue;
186
187 if (thisboard->model == anypci_model) {
188 /* Wildcard board matches any supported PCI board. */
189 const struct pc236_board *foundboard;
190
191 foundboard = pc236_find_pci_board(pci_dev);
192 if (foundboard == NULL)
193 continue;
194 /* Replace wildcard board_ptr. */
195 dev->board_ptr = foundboard;
196 } else {
197 /* Match specific model name. */
198 if (pci_dev->device != thisboard->devid)
199 continue;
200 }
201 return pci_dev;
202 }
203 dev_err(dev->class_dev,
204 "No supported board found! (req. bus %d, slot %d)\n",
205 bus, slot);
206 return NULL;
207 }
208
209 /*
210 * This function checks and requests an I/O region, reporting an error
211 * if there is a conflict.
212 */
213 static int pc236_request_region(struct comedi_device *dev, unsigned long from,
214 unsigned long extent)
215 {
216 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
217 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
218 from, extent);
219 return -EIO;
220 }
221 return 0;
222 }
223
224 /*
225 * This function is called to mark the interrupt as disabled (no command
226 * configured on subdevice 1) and to physically disable the interrupt
227 * (not possible on the PC36AT, except by removing the IRQ jumper!).
228 */
229 static void pc236_intr_disable(struct comedi_device *dev)
230 {
231 struct pc236_private *devpriv = dev->private;
232 unsigned long flags;
233
234 spin_lock_irqsave(&dev->spinlock, flags);
235 devpriv->enable_irq = 0;
236 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
237 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
238 spin_unlock_irqrestore(&dev->spinlock, flags);
239 }
240
241 /*
242 * This function is called to mark the interrupt as enabled (a command
243 * configured on subdevice 1) and to physically enable the interrupt
244 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
245 */
246 static void pc236_intr_enable(struct comedi_device *dev)
247 {
248 struct pc236_private *devpriv = dev->private;
249 unsigned long flags;
250
251 spin_lock_irqsave(&dev->spinlock, flags);
252 devpriv->enable_irq = 1;
253 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
254 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
255 spin_unlock_irqrestore(&dev->spinlock, flags);
256 }
257
258 /*
259 * This function is called when an interrupt occurs to check whether
260 * the interrupt has been marked as enabled and was generated by the
261 * board. If so, the function prepares the hardware for the next
262 * interrupt.
263 * Returns 0 if the interrupt should be ignored.
264 */
265 static int pc236_intr_check(struct comedi_device *dev)
266 {
267 struct pc236_private *devpriv = dev->private;
268 int retval = 0;
269 unsigned long flags;
270
271 spin_lock_irqsave(&dev->spinlock, flags);
272 if (devpriv->enable_irq) {
273 retval = 1;
274 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
275 devpriv->lcr_iobase) {
276 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
277 & PLX9052_INTCSR_LI1STAT_MASK)
278 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
279 retval = 0;
280 } else {
281 /* Clear interrupt and keep it enabled. */
282 outl(PCI236_INTR_ENABLE,
283 devpriv->lcr_iobase + PLX9052_INTCSR);
284 }
285 }
286 }
287 spin_unlock_irqrestore(&dev->spinlock, flags);
288
289 return retval;
290 }
291
292 /*
293 * Input from subdevice 1.
294 * Copied from the comedi_parport driver.
295 */
296 static int pc236_intr_insn(struct comedi_device *dev,
297 struct comedi_subdevice *s, struct comedi_insn *insn,
298 unsigned int *data)
299 {
300 data[1] = 0;
301 return insn->n;
302 }
303
304 /*
305 * Subdevice 1 command test.
306 * Copied from the comedi_parport driver.
307 */
308 static int pc236_intr_cmdtest(struct comedi_device *dev,
309 struct comedi_subdevice *s,
310 struct comedi_cmd *cmd)
311 {
312 int err = 0;
313 int tmp;
314
315 /* step 1 */
316
317 tmp = cmd->start_src;
318 cmd->start_src &= TRIG_NOW;
319 if (!cmd->start_src || tmp != cmd->start_src)
320 err++;
321
322 tmp = cmd->scan_begin_src;
323 cmd->scan_begin_src &= TRIG_EXT;
324 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
325 err++;
326
327 tmp = cmd->convert_src;
328 cmd->convert_src &= TRIG_FOLLOW;
329 if (!cmd->convert_src || tmp != cmd->convert_src)
330 err++;
331
332 tmp = cmd->scan_end_src;
333 cmd->scan_end_src &= TRIG_COUNT;
334 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
335 err++;
336
337 tmp = cmd->stop_src;
338 cmd->stop_src &= TRIG_NONE;
339 if (!cmd->stop_src || tmp != cmd->stop_src)
340 err++;
341
342 if (err)
343 return 1;
344
345 /* step 2: ignored */
346
347 if (err)
348 return 2;
349
350 /* step 3: */
351
352 if (cmd->start_arg != 0) {
353 cmd->start_arg = 0;
354 err++;
355 }
356 if (cmd->scan_begin_arg != 0) {
357 cmd->scan_begin_arg = 0;
358 err++;
359 }
360 if (cmd->convert_arg != 0) {
361 cmd->convert_arg = 0;
362 err++;
363 }
364 if (cmd->scan_end_arg != 1) {
365 cmd->scan_end_arg = 1;
366 err++;
367 }
368 if (cmd->stop_arg != 0) {
369 cmd->stop_arg = 0;
370 err++;
371 }
372
373 if (err)
374 return 3;
375
376 /* step 4: ignored */
377
378 if (err)
379 return 4;
380
381 return 0;
382 }
383
384 /*
385 * Subdevice 1 command.
386 */
387 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
388 {
389 pc236_intr_enable(dev);
390
391 return 0;
392 }
393
394 /*
395 * Subdevice 1 cancel command.
396 */
397 static int pc236_intr_cancel(struct comedi_device *dev,
398 struct comedi_subdevice *s)
399 {
400 pc236_intr_disable(dev);
401
402 return 0;
403 }
404
405 /*
406 * Interrupt service routine.
407 * Based on the comedi_parport driver.
408 */
409 static irqreturn_t pc236_interrupt(int irq, void *d)
410 {
411 struct comedi_device *dev = d;
412 struct comedi_subdevice *s = dev->subdevices + 1;
413 int handled;
414
415 handled = pc236_intr_check(dev);
416 if (dev->attached && handled) {
417 comedi_buf_put(s->async, 0);
418 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
419 comedi_event(dev, s);
420 }
421 return IRQ_RETVAL(handled);
422 }
423
424 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
425 {
426 const struct pc236_board *thisboard = comedi_board(dev);
427 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
428 char tmpbuf[60];
429 int tmplen;
430
431 if (is_isa_board(thisboard))
432 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
433 "(base %#lx) ", dev->iobase);
434 else if (is_pci_board(thisboard))
435 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
436 "(pci %s) ", pci_name(pcidev));
437 else
438 tmplen = 0;
439 if (irq)
440 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
441 "(irq %u%s) ", irq,
442 (dev->irq ? "" : " UNAVAILABLE"));
443 else
444 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
445 "(no irq) ");
446 dev_info(dev->class_dev, "%s %sattached\n",
447 dev->board_name, tmpbuf);
448 }
449
450 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
451 unsigned int irq, unsigned long req_irq_flags)
452 {
453 const struct pc236_board *thisboard = comedi_board(dev);
454 struct comedi_subdevice *s;
455 int ret;
456
457 dev->board_name = thisboard->name;
458 dev->iobase = iobase;
459
460 ret = comedi_alloc_subdevices(dev, 2);
461 if (ret)
462 return ret;
463
464 s = dev->subdevices + 0;
465 /* digital i/o subdevice (8255) */
466 ret = subdev_8255_init(dev, s, NULL, iobase);
467 if (ret < 0) {
468 dev_err(dev->class_dev, "error! out of memory!\n");
469 return ret;
470 }
471 s = dev->subdevices + 1;
472 dev->read_subdev = s;
473 s->type = COMEDI_SUBD_UNUSED;
474 pc236_intr_disable(dev);
475 if (irq) {
476 if (request_irq(irq, pc236_interrupt, req_irq_flags,
477 PC236_DRIVER_NAME, dev) >= 0) {
478 dev->irq = irq;
479 s->type = COMEDI_SUBD_DI;
480 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
481 s->n_chan = 1;
482 s->maxdata = 1;
483 s->range_table = &range_digital;
484 s->insn_bits = pc236_intr_insn;
485 s->do_cmdtest = pc236_intr_cmdtest;
486 s->do_cmd = pc236_intr_cmd;
487 s->cancel = pc236_intr_cancel;
488 }
489 }
490 pc236_report_attach(dev, irq);
491 return 1;
492 }
493
494 static int pc236_pci_common_attach(struct comedi_device *dev,
495 struct pci_dev *pci_dev)
496 {
497 struct pc236_private *devpriv = dev->private;
498 unsigned long iobase;
499 int ret;
500
501 comedi_set_hw_dev(dev, &pci_dev->dev);
502
503 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
504 if (ret < 0) {
505 dev_err(dev->class_dev,
506 "error! cannot enable PCI device and request regions!\n");
507 return ret;
508 }
509 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
510 iobase = pci_resource_start(pci_dev, 2);
511 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
512 }
513
514 /*
515 * Attach is called by the Comedi core to configure the driver
516 * for a particular board. If you specified a board_name array
517 * in the driver structure, dev->board_ptr contains that
518 * address.
519 */
520 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
521 {
522 const struct pc236_board *thisboard = comedi_board(dev);
523 int ret;
524
525 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach\n");
526 ret = alloc_private(dev, sizeof(struct pc236_private));
527 if (ret < 0) {
528 dev_err(dev->class_dev, "error! out of memory!\n");
529 return ret;
530 }
531 /* Process options according to bus type. */
532 if (is_isa_board(thisboard)) {
533 unsigned long iobase = it->options[0];
534 unsigned int irq = it->options[1];
535 ret = pc236_request_region(dev, iobase, PC236_IO_SIZE);
536 if (ret < 0)
537 return ret;
538 return pc236_common_attach(dev, iobase, irq, 0);
539 } else if (is_pci_board(thisboard)) {
540 struct pci_dev *pci_dev;
541
542 pci_dev = pc236_find_pci_dev(dev, it);
543 if (!pci_dev)
544 return -EIO;
545 return pc236_pci_common_attach(dev, pci_dev);
546 } else {
547 dev_err(dev->class_dev, PC236_DRIVER_NAME
548 ": BUG! cannot determine board type!\n");
549 return -EINVAL;
550 }
551 }
552
553 /*
554 * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
555 * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
556 * be a board entry matching the supplied PCI device.
557 */
558 static int __devinit pc236_attach_pci(struct comedi_device *dev,
559 struct pci_dev *pci_dev)
560 {
561 int ret;
562
563 if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI))
564 return -EINVAL;
565
566 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
567 pci_name(pci_dev));
568 ret = alloc_private(dev, sizeof(struct pc236_private));
569 if (ret < 0) {
570 dev_err(dev->class_dev, "error! out of memory!\n");
571 return ret;
572 }
573 dev->board_ptr = pc236_find_pci_board(pci_dev);
574 if (dev->board_ptr == NULL) {
575 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
576 return -EINVAL;
577 }
578 return pc236_pci_common_attach(dev, pci_dev);
579 }
580
581 static void pc236_detach(struct comedi_device *dev)
582 {
583 struct pc236_private *devpriv = dev->private;
584 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
585
586 if (devpriv)
587 pc236_intr_disable(dev);
588 if (dev->irq)
589 free_irq(dev->irq, dev);
590 if (dev->subdevices)
591 subdev_8255_cleanup(dev, dev->subdevices + 0);
592 if (pcidev) {
593 if (dev->iobase)
594 comedi_pci_disable(pcidev);
595 pci_dev_put(pcidev);
596 } else {
597 if (dev->iobase)
598 release_region(dev->iobase, PC236_IO_SIZE);
599 }
600 }
601
602 /*
603 * The struct comedi_driver structure tells the Comedi core module
604 * which functions to call to configure/deconfigure (attach/detach)
605 * the board, and also about the kernel module that contains
606 * the device code.
607 */
608 static struct comedi_driver amplc_pc236_driver = {
609 .driver_name = PC236_DRIVER_NAME,
610 .module = THIS_MODULE,
611 .attach = pc236_attach,
612 .attach_pci = pc236_attach_pci,
613 .detach = pc236_detach,
614 .board_name = &pc236_boards[0].name,
615 .offset = sizeof(struct pc236_board),
616 .num_names = ARRAY_SIZE(pc236_boards),
617 };
618
619 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
620 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
621 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
622 {0}
623 };
624
625 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
626
627 static int __devinit amplc_pc236_pci_probe(struct pci_dev *dev,
628 const struct pci_device_id *ent)
629 {
630 return comedi_pci_auto_config(dev, &amplc_pc236_driver);
631 }
632
633 static void __devexit amplc_pc236_pci_remove(struct pci_dev *dev)
634 {
635 comedi_pci_auto_unconfig(dev);
636 }
637
638 static struct pci_driver amplc_pc236_pci_driver = {
639 .name = PC236_DRIVER_NAME,
640 .id_table = pc236_pci_table,
641 .probe = &amplc_pc236_pci_probe,
642 .remove = __devexit_p(&amplc_pc236_pci_remove)
643 };
644
645 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
646 #else
647 module_comedi_driver(amplc_pc236_driver);
648 #endif
649
650 MODULE_AUTHOR("Comedi http://www.comedi.org");
651 MODULE_DESCRIPTION("Comedi low-level driver");
652 MODULE_LICENSE("GPL");