Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
[linux-2.6.git] / drivers / staging / comedi / drivers / amplc_pc236.c
blob4e889b82cbf2a3827010d2d7a37fd046e6c240cf
1 /*
2 comedi/drivers/amplc_pc236.c
3 Driver for Amplicon PC36AT and PCI236 DIO boards.
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
21 Driver: amplc_pc236
22 Description: Amplicon PC36AT, PCI236
23 Author: Ian Abbott <abbotti@mev.co.uk>
24 Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
25 Updated: Wed, 01 Apr 2009 15:41:25 +0100
26 Status: works
28 Configuration options - PC36AT:
29 [0] - I/O port base address
30 [1] - IRQ (optional)
32 Configuration options - PCI236:
33 [0] - PCI bus of device (optional)
34 [1] - PCI slot of device (optional)
35 If bus/slot is not specified, the first available PCI device will be
36 used.
38 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
39 as subdevice 0.
41 Subdevice 1 pretends to be a digital input device, but it always returns
42 0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
43 a rising edge on port C bit 3 acts as an external trigger, which can be
44 used to wake up tasks. This is like the comedi_parport device, but the
45 only way to physically disable the interrupt on the PC36AT is to remove
46 the IRQ jumper. If no interrupt is connected, then subdevice 1 is
47 unused.
50 #include <linux/pci.h>
51 #include <linux/interrupt.h>
53 #include "../comedidev.h"
55 #include "comedi_fc.h"
56 #include "8255.h"
57 #include "plx9052.h"
59 #define PC236_DRIVER_NAME "amplc_pc236"
61 #define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
62 #define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
64 /* PCI236 PCI configuration register information */
65 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
66 #define PCI_DEVICE_ID_INVALID 0xffff
68 /* PC36AT / PCI236 registers */
70 #define PC236_IO_SIZE 4
71 #define PC236_LCR_IO_SIZE 128
73 /* Disable, and clear, interrupts */
74 #define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1POL | \
75 PLX9052_INTCSR_LI2POL | \
76 PLX9052_INTCSR_LI1SEL | \
77 PLX9052_INTCSR_LI1CLRINT)
79 /* Enable, and clear, interrupts */
80 #define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB | \
81 PLX9052_INTCSR_LI1POL | \
82 PLX9052_INTCSR_LI2POL | \
83 PLX9052_INTCSR_PCIENAB | \
84 PLX9052_INTCSR_LI1SEL | \
85 PLX9052_INTCSR_LI1CLRINT)
88 * Board descriptions for Amplicon PC36AT and PCI236.
91 enum pc236_bustype { isa_bustype, pci_bustype };
92 enum pc236_model { pc36at_model, pci236_model, anypci_model };
94 struct pc236_board {
95 const char *name;
96 unsigned short devid;
97 enum pc236_bustype bustype;
98 enum pc236_model model;
100 static const struct pc236_board pc236_boards[] = {
101 #if DO_ISA
103 .name = "pc36at",
104 .bustype = isa_bustype,
105 .model = pc36at_model,
107 #endif
108 #if DO_PCI
110 .name = "pci236",
111 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
112 .bustype = pci_bustype,
113 .model = pci236_model,
116 .name = PC236_DRIVER_NAME,
117 .devid = PCI_DEVICE_ID_INVALID,
118 .bustype = pci_bustype,
119 .model = anypci_model, /* wildcard */
121 #endif
124 /* this structure is for data unique to this hardware driver. If
125 several hardware drivers keep similar information in this structure,
126 feel free to suggest moving the variable to the struct comedi_device struct.
128 struct pc236_private {
129 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
130 int enable_irq;
133 /* test if ISA supported and this is an ISA board */
134 static inline bool is_isa_board(const struct pc236_board *board)
136 return DO_ISA && board->bustype == isa_bustype;
139 /* test if PCI supported and this is a PCI board */
140 static inline bool is_pci_board(const struct pc236_board *board)
142 return DO_PCI && board->bustype == pci_bustype;
146 * This function looks for a board matching the supplied PCI device.
148 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
150 unsigned int i;
152 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
153 if (is_pci_board(&pc236_boards[i]) &&
154 pci_dev->device == pc236_boards[i].devid)
155 return &pc236_boards[i];
156 return NULL;
160 * This function looks for a PCI device matching the requested board name,
161 * bus and slot.
163 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
164 struct comedi_devconfig *it)
166 const struct pc236_board *thisboard = comedi_board(dev);
167 struct pci_dev *pci_dev = NULL;
168 int bus = it->options[0];
169 int slot = it->options[1];
171 for_each_pci_dev(pci_dev) {
172 if (bus || slot) {
173 if (bus != pci_dev->bus->number ||
174 slot != PCI_SLOT(pci_dev->devfn))
175 continue;
177 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
178 continue;
180 if (thisboard->model == anypci_model) {
181 /* Wildcard board matches any supported PCI board. */
182 const struct pc236_board *foundboard;
184 foundboard = pc236_find_pci_board(pci_dev);
185 if (foundboard == NULL)
186 continue;
187 /* Replace wildcard board_ptr. */
188 dev->board_ptr = foundboard;
189 } else {
190 /* Match specific model name. */
191 if (pci_dev->device != thisboard->devid)
192 continue;
194 return pci_dev;
196 dev_err(dev->class_dev,
197 "No supported board found! (req. bus %d, slot %d)\n",
198 bus, slot);
199 return NULL;
203 * This function is called to mark the interrupt as disabled (no command
204 * configured on subdevice 1) and to physically disable the interrupt
205 * (not possible on the PC36AT, except by removing the IRQ jumper!).
207 static void pc236_intr_disable(struct comedi_device *dev)
209 const struct pc236_board *thisboard = comedi_board(dev);
210 struct pc236_private *devpriv = dev->private;
211 unsigned long flags;
213 spin_lock_irqsave(&dev->spinlock, flags);
214 devpriv->enable_irq = 0;
215 if (is_pci_board(thisboard))
216 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
217 spin_unlock_irqrestore(&dev->spinlock, flags);
221 * This function is called to mark the interrupt as enabled (a command
222 * configured on subdevice 1) and to physically enable the interrupt
223 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
225 static void pc236_intr_enable(struct comedi_device *dev)
227 const struct pc236_board *thisboard = comedi_board(dev);
228 struct pc236_private *devpriv = dev->private;
229 unsigned long flags;
231 spin_lock_irqsave(&dev->spinlock, flags);
232 devpriv->enable_irq = 1;
233 if (is_pci_board(thisboard))
234 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
235 spin_unlock_irqrestore(&dev->spinlock, flags);
239 * This function is called when an interrupt occurs to check whether
240 * the interrupt has been marked as enabled and was generated by the
241 * board. If so, the function prepares the hardware for the next
242 * interrupt.
243 * Returns 0 if the interrupt should be ignored.
245 static int pc236_intr_check(struct comedi_device *dev)
247 const struct pc236_board *thisboard = comedi_board(dev);
248 struct pc236_private *devpriv = dev->private;
249 int retval = 0;
250 unsigned long flags;
251 unsigned int intcsr;
253 spin_lock_irqsave(&dev->spinlock, flags);
254 if (devpriv->enable_irq) {
255 retval = 1;
256 if (is_pci_board(thisboard)) {
257 intcsr = inl(devpriv->lcr_iobase + PLX9052_INTCSR);
258 if (!(intcsr & PLX9052_INTCSR_LI1STAT)) {
259 retval = 0;
260 } else {
261 /* Clear interrupt and keep it enabled. */
262 outl(PCI236_INTR_ENABLE,
263 devpriv->lcr_iobase + PLX9052_INTCSR);
267 spin_unlock_irqrestore(&dev->spinlock, flags);
269 return retval;
273 * Input from subdevice 1.
274 * Copied from the comedi_parport driver.
276 static int pc236_intr_insn(struct comedi_device *dev,
277 struct comedi_subdevice *s, struct comedi_insn *insn,
278 unsigned int *data)
280 data[1] = 0;
281 return insn->n;
285 * Subdevice 1 command test.
286 * Copied from the comedi_parport driver.
288 static int pc236_intr_cmdtest(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_cmd *cmd)
292 int err = 0;
294 /* Step 1 : check if triggers are trivially valid */
296 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
297 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
298 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
299 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
300 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
302 if (err)
303 return 1;
305 /* Step 2a : make sure trigger sources are unique */
306 /* Step 2b : and mutually compatible */
308 if (err)
309 return 2;
311 /* Step 3: check it arguments are trivially valid */
313 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
314 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
315 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
316 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, 1);
317 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
319 if (err)
320 return 3;
322 /* step 4: ignored */
324 if (err)
325 return 4;
327 return 0;
331 * Subdevice 1 command.
333 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
335 pc236_intr_enable(dev);
337 return 0;
341 * Subdevice 1 cancel command.
343 static int pc236_intr_cancel(struct comedi_device *dev,
344 struct comedi_subdevice *s)
346 pc236_intr_disable(dev);
348 return 0;
352 * Interrupt service routine.
353 * Based on the comedi_parport driver.
355 static irqreturn_t pc236_interrupt(int irq, void *d)
357 struct comedi_device *dev = d;
358 struct comedi_subdevice *s = &dev->subdevices[1];
359 int handled;
361 handled = pc236_intr_check(dev);
362 if (dev->attached && handled) {
363 comedi_buf_put(s->async, 0);
364 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
365 comedi_event(dev, s);
367 return IRQ_RETVAL(handled);
370 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
372 const struct pc236_board *thisboard = comedi_board(dev);
373 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
374 char tmpbuf[60];
375 int tmplen;
377 if (is_isa_board(thisboard))
378 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
379 "(base %#lx) ", dev->iobase);
380 else if (is_pci_board(thisboard))
381 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
382 "(pci %s) ", pci_name(pcidev));
383 else
384 tmplen = 0;
385 if (irq)
386 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
387 "(irq %u%s) ", irq,
388 (dev->irq ? "" : " UNAVAILABLE"));
389 else
390 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
391 "(no irq) ");
392 dev_info(dev->class_dev, "%s %sattached\n",
393 dev->board_name, tmpbuf);
396 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
397 unsigned int irq, unsigned long req_irq_flags)
399 const struct pc236_board *thisboard = comedi_board(dev);
400 struct comedi_subdevice *s;
401 int ret;
403 dev->board_name = thisboard->name;
404 dev->iobase = iobase;
406 ret = comedi_alloc_subdevices(dev, 2);
407 if (ret)
408 return ret;
410 s = &dev->subdevices[0];
411 /* digital i/o subdevice (8255) */
412 ret = subdev_8255_init(dev, s, NULL, iobase);
413 if (ret < 0) {
414 dev_err(dev->class_dev, "error! out of memory!\n");
415 return ret;
417 s = &dev->subdevices[1];
418 dev->read_subdev = s;
419 s->type = COMEDI_SUBD_UNUSED;
420 pc236_intr_disable(dev);
421 if (irq) {
422 if (request_irq(irq, pc236_interrupt, req_irq_flags,
423 PC236_DRIVER_NAME, dev) >= 0) {
424 dev->irq = irq;
425 s->type = COMEDI_SUBD_DI;
426 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
427 s->n_chan = 1;
428 s->maxdata = 1;
429 s->range_table = &range_digital;
430 s->insn_bits = pc236_intr_insn;
431 s->do_cmdtest = pc236_intr_cmdtest;
432 s->do_cmd = pc236_intr_cmd;
433 s->cancel = pc236_intr_cancel;
436 pc236_report_attach(dev, irq);
437 return 1;
440 static int pc236_pci_common_attach(struct comedi_device *dev,
441 struct pci_dev *pci_dev)
443 struct pc236_private *devpriv = dev->private;
444 unsigned long iobase;
445 int ret;
447 comedi_set_hw_dev(dev, &pci_dev->dev);
449 ret = comedi_pci_enable(dev);
450 if (ret)
451 return ret;
453 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
454 iobase = pci_resource_start(pci_dev, 2);
455 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
459 * Attach is called by the Comedi core to configure the driver
460 * for a particular board. If you specified a board_name array
461 * in the driver structure, dev->board_ptr contains that
462 * address.
464 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
466 const struct pc236_board *thisboard = comedi_board(dev);
467 struct pc236_private *devpriv;
468 int ret;
470 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
471 if (!devpriv)
472 return -ENOMEM;
473 dev->private = devpriv;
475 /* Process options according to bus type. */
476 if (is_isa_board(thisboard)) {
477 ret = comedi_request_region(dev, it->options[0], PC236_IO_SIZE);
478 if (ret)
479 return ret;
481 return pc236_common_attach(dev, dev->iobase, it->options[1], 0);
482 } else if (is_pci_board(thisboard)) {
483 struct pci_dev *pci_dev;
485 pci_dev = pc236_find_pci_dev(dev, it);
486 if (!pci_dev)
487 return -EIO;
488 return pc236_pci_common_attach(dev, pci_dev);
489 } else {
490 dev_err(dev->class_dev, PC236_DRIVER_NAME
491 ": BUG! cannot determine board type!\n");
492 return -EINVAL;
497 * The auto_attach hook is called at PCI probe time via
498 * comedi_pci_auto_config(). dev->board_ptr is NULL on entry.
499 * There should be a board entry matching the supplied PCI device.
501 static int pc236_auto_attach(struct comedi_device *dev,
502 unsigned long context_unused)
504 struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
505 struct pc236_private *devpriv;
507 if (!DO_PCI)
508 return -EINVAL;
510 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
511 pci_name(pci_dev));
513 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
514 if (!devpriv)
515 return -ENOMEM;
516 dev->private = devpriv;
518 dev->board_ptr = pc236_find_pci_board(pci_dev);
519 if (dev->board_ptr == NULL) {
520 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
521 return -EINVAL;
524 * Need to 'get' the PCI device to match the 'put' in pc236_detach().
525 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
526 * support for manual attachment of PCI devices via pc236_attach()
527 * has been removed.
529 pci_dev_get(pci_dev);
530 return pc236_pci_common_attach(dev, pci_dev);
533 static void pc236_detach(struct comedi_device *dev)
535 const struct pc236_board *thisboard = comedi_board(dev);
537 if (!thisboard)
538 return;
539 if (dev->iobase)
540 pc236_intr_disable(dev);
541 if (is_isa_board(thisboard)) {
542 comedi_legacy_detach(dev);
543 } else if (is_pci_board(thisboard)) {
544 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
545 if (dev->irq)
546 free_irq(dev->irq, dev);
547 comedi_pci_disable(dev);
548 if (pcidev)
549 pci_dev_put(pcidev);
554 * The struct comedi_driver structure tells the Comedi core module
555 * which functions to call to configure/deconfigure (attach/detach)
556 * the board, and also about the kernel module that contains
557 * the device code.
559 static struct comedi_driver amplc_pc236_driver = {
560 .driver_name = PC236_DRIVER_NAME,
561 .module = THIS_MODULE,
562 .attach = pc236_attach,
563 .auto_attach = pc236_auto_attach,
564 .detach = pc236_detach,
565 .board_name = &pc236_boards[0].name,
566 .offset = sizeof(struct pc236_board),
567 .num_names = ARRAY_SIZE(pc236_boards),
570 #if DO_PCI
571 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
572 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
576 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
578 static int amplc_pc236_pci_probe(struct pci_dev *dev,
579 const struct pci_device_id *id)
581 return comedi_pci_auto_config(dev, &amplc_pc236_driver,
582 id->driver_data);
585 static struct pci_driver amplc_pc236_pci_driver = {
586 .name = PC236_DRIVER_NAME,
587 .id_table = pc236_pci_table,
588 .probe = &amplc_pc236_pci_probe,
589 .remove = comedi_pci_auto_unconfig,
592 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
593 #else
594 module_comedi_driver(amplc_pc236_driver);
595 #endif
597 MODULE_AUTHOR("Comedi http://www.comedi.org");
598 MODULE_DESCRIPTION("Comedi low-level driver");
599 MODULE_LICENSE("GPL");