Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
amplc_pc236.c
Go to the documentation of this file.
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 <[email protected]>
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 <[email protected]>
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 "comedi_fc.h"
60 #include "8255.h"
61 #include "plx9052.h"
62 
63 #define PC236_DRIVER_NAME "amplc_pc236"
64 
65 #define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
66 #define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
67 
68 /* PCI236 PCI configuration register information */
69 #define PCI_VENDOR_ID_AMPLICON 0x14dc
70 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
71 #define PCI_DEVICE_ID_INVALID 0xffff
72 
73 /* PC36AT / PCI236 registers */
74 
75 #define PC236_IO_SIZE 4
76 #define PC236_LCR_IO_SIZE 128
77 
78 /*
79  * INTCSR values for PCI236.
80  */
81 /* Disable interrupt, also clear any interrupt there */
82 #define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
83  | PLX9052_INTCSR_LI1POL_HIGH \
84  | PLX9052_INTCSR_LI2POL_HIGH \
85  | PLX9052_INTCSR_PCIENAB_DISABLED \
86  | PLX9052_INTCSR_LI1SEL_EDGE \
87  | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
88 /* Enable interrupt, also clear any interrupt there. */
89 #define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
90  | PLX9052_INTCSR_LI1POL_HIGH \
91  | PLX9052_INTCSR_LI2POL_HIGH \
92  | PLX9052_INTCSR_PCIENAB_ENABLED \
93  | PLX9052_INTCSR_LI1SEL_EDGE \
94  | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
95 
96 /*
97  * Board descriptions for Amplicon PC36AT and PCI236.
98  */
99 
102 
103 struct pc236_board {
104  const char *name;
105  unsigned short devid;
108 };
109 static const struct pc236_board pc236_boards[] = {
110 #if DO_ISA
111  {
112  .name = "pc36at",
113  .bustype = isa_bustype,
114  .model = pc36at_model,
115  },
116 #endif
117 #if DO_PCI
118  {
119  .name = "pci236",
121  .bustype = pci_bustype,
122  .model = pci236_model,
123  },
124  {
125  .name = PC236_DRIVER_NAME,
126  .devid = PCI_DEVICE_ID_INVALID,
127  .bustype = pci_bustype,
128  .model = anypci_model, /* wildcard */
129  },
130 #endif
131 };
132 
133 /* this structure is for data unique to this hardware driver. If
134  several hardware drivers keep similar information in this structure,
135  feel free to suggest moving the variable to the struct comedi_device struct.
136  */
138  unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
140 };
141 
142 /* test if ISA supported and this is an ISA board */
143 static inline bool is_isa_board(const struct pc236_board *board)
144 {
145  return DO_ISA && board->bustype == isa_bustype;
146 }
147 
148 /* test if PCI supported and this is a PCI board */
149 static inline bool is_pci_board(const struct pc236_board *board)
150 {
151  return DO_PCI && board->bustype == pci_bustype;
152 }
153 
154 /*
155  * This function looks for a board matching the supplied PCI device.
156  */
157 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
158 {
159  unsigned int i;
160 
161  for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
162  if (is_pci_board(&pc236_boards[i]) &&
163  pci_dev->device == pc236_boards[i].devid)
164  return &pc236_boards[i];
165  return NULL;
166 }
167 
168 /*
169  * This function looks for a PCI device matching the requested board name,
170  * bus and slot.
171  */
172 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
173  struct comedi_devconfig *it)
174 {
175  const struct pc236_board *thisboard = comedi_board(dev);
176  struct pci_dev *pci_dev = NULL;
177  int bus = it->options[0];
178  int slot = it->options[1];
179 
180  for_each_pci_dev(pci_dev) {
181  if (bus || slot) {
182  if (bus != pci_dev->bus->number ||
183  slot != PCI_SLOT(pci_dev->devfn))
184  continue;
185  }
186  if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
187  continue;
188 
189  if (thisboard->model == anypci_model) {
190  /* Wildcard board matches any supported PCI board. */
191  const struct pc236_board *foundboard;
192 
193  foundboard = pc236_find_pci_board(pci_dev);
194  if (foundboard == NULL)
195  continue;
196  /* Replace wildcard board_ptr. */
197  dev->board_ptr = foundboard;
198  } else {
199  /* Match specific model name. */
200  if (pci_dev->device != thisboard->devid)
201  continue;
202  }
203  return pci_dev;
204  }
205  dev_err(dev->class_dev,
206  "No supported board found! (req. bus %d, slot %d)\n",
207  bus, slot);
208  return NULL;
209 }
210 
211 /*
212  * This function checks and requests an I/O region, reporting an error
213  * if there is a conflict.
214  */
215 static int pc236_request_region(struct comedi_device *dev, unsigned long from,
216  unsigned long extent)
217 {
218  if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
219  dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
220  from, extent);
221  return -EIO;
222  }
223  return 0;
224 }
225 
226 /*
227  * This function is called to mark the interrupt as disabled (no command
228  * configured on subdevice 1) and to physically disable the interrupt
229  * (not possible on the PC36AT, except by removing the IRQ jumper!).
230  */
231 static void pc236_intr_disable(struct comedi_device *dev)
232 {
233  const struct pc236_board *thisboard = comedi_board(dev);
234  struct pc236_private *devpriv = dev->private;
235  unsigned long flags;
236 
237  spin_lock_irqsave(&dev->spinlock, flags);
238  devpriv->enable_irq = 0;
239  if (is_pci_board(thisboard))
241  spin_unlock_irqrestore(&dev->spinlock, flags);
242 }
243 
244 /*
245  * This function is called to mark the interrupt as enabled (a command
246  * configured on subdevice 1) and to physically enable the interrupt
247  * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
248  */
249 static void pc236_intr_enable(struct comedi_device *dev)
250 {
251  const struct pc236_board *thisboard = comedi_board(dev);
252  struct pc236_private *devpriv = dev->private;
253  unsigned long flags;
254 
255  spin_lock_irqsave(&dev->spinlock, flags);
256  devpriv->enable_irq = 1;
257  if (is_pci_board(thisboard))
259  spin_unlock_irqrestore(&dev->spinlock, flags);
260 }
261 
262 /*
263  * This function is called when an interrupt occurs to check whether
264  * the interrupt has been marked as enabled and was generated by the
265  * board. If so, the function prepares the hardware for the next
266  * interrupt.
267  * Returns 0 if the interrupt should be ignored.
268  */
269 static int pc236_intr_check(struct comedi_device *dev)
270 {
271  const struct pc236_board *thisboard = comedi_board(dev);
272  struct pc236_private *devpriv = dev->private;
273  int retval = 0;
274  unsigned long flags;
275 
276  spin_lock_irqsave(&dev->spinlock, flags);
277  if (devpriv->enable_irq) {
278  retval = 1;
279  if (is_pci_board(thisboard)) {
280  if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
283  retval = 0;
284  } else {
285  /* Clear interrupt and keep it enabled. */
287  devpriv->lcr_iobase + PLX9052_INTCSR);
288  }
289  }
290  }
291  spin_unlock_irqrestore(&dev->spinlock, flags);
292 
293  return retval;
294 }
295 
296 /*
297  * Input from subdevice 1.
298  * Copied from the comedi_parport driver.
299  */
300 static int pc236_intr_insn(struct comedi_device *dev,
301  struct comedi_subdevice *s, struct comedi_insn *insn,
302  unsigned int *data)
303 {
304  data[1] = 0;
305  return insn->n;
306 }
307 
308 /*
309  * Subdevice 1 command test.
310  * Copied from the comedi_parport driver.
311  */
312 static int pc236_intr_cmdtest(struct comedi_device *dev,
313  struct comedi_subdevice *s,
314  struct comedi_cmd *cmd)
315 {
316  int err = 0;
317 
318  /* Step 1 : check if triggers are trivially valid */
319 
320  err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
321  err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
322  err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
323  err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
324  err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
325 
326  if (err)
327  return 1;
328 
329  /* Step 2a : make sure trigger sources are unique */
330  /* Step 2b : and mutually compatible */
331 
332  if (err)
333  return 2;
334 
335  /* step 3: */
336 
337  if (cmd->start_arg != 0) {
338  cmd->start_arg = 0;
339  err++;
340  }
341  if (cmd->scan_begin_arg != 0) {
342  cmd->scan_begin_arg = 0;
343  err++;
344  }
345  if (cmd->convert_arg != 0) {
346  cmd->convert_arg = 0;
347  err++;
348  }
349  if (cmd->scan_end_arg != 1) {
350  cmd->scan_end_arg = 1;
351  err++;
352  }
353  if (cmd->stop_arg != 0) {
354  cmd->stop_arg = 0;
355  err++;
356  }
357 
358  if (err)
359  return 3;
360 
361  /* step 4: ignored */
362 
363  if (err)
364  return 4;
365 
366  return 0;
367 }
368 
369 /*
370  * Subdevice 1 command.
371  */
372 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
373 {
374  pc236_intr_enable(dev);
375 
376  return 0;
377 }
378 
379 /*
380  * Subdevice 1 cancel command.
381  */
382 static int pc236_intr_cancel(struct comedi_device *dev,
383  struct comedi_subdevice *s)
384 {
385  pc236_intr_disable(dev);
386 
387  return 0;
388 }
389 
390 /*
391  * Interrupt service routine.
392  * Based on the comedi_parport driver.
393  */
394 static irqreturn_t pc236_interrupt(int irq, void *d)
395 {
396  struct comedi_device *dev = d;
397  struct comedi_subdevice *s = &dev->subdevices[1];
398  int handled;
399 
400  handled = pc236_intr_check(dev);
401  if (dev->attached && handled) {
402  comedi_buf_put(s->async, 0);
403  s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
404  comedi_event(dev, s);
405  }
406  return IRQ_RETVAL(handled);
407 }
408 
409 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
410 {
411  const struct pc236_board *thisboard = comedi_board(dev);
412  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
413  char tmpbuf[60];
414  int tmplen;
415 
416  if (is_isa_board(thisboard))
417  tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
418  "(base %#lx) ", dev->iobase);
419  else if (is_pci_board(thisboard))
420  tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
421  "(pci %s) ", pci_name(pcidev));
422  else
423  tmplen = 0;
424  if (irq)
425  tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
426  "(irq %u%s) ", irq,
427  (dev->irq ? "" : " UNAVAILABLE"));
428  else
429  tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
430  "(no irq) ");
431  dev_info(dev->class_dev, "%s %sattached\n",
432  dev->board_name, tmpbuf);
433 }
434 
435 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
436  unsigned int irq, unsigned long req_irq_flags)
437 {
438  const struct pc236_board *thisboard = comedi_board(dev);
439  struct comedi_subdevice *s;
440  int ret;
441 
442  dev->board_name = thisboard->name;
443  dev->iobase = iobase;
444 
445  ret = comedi_alloc_subdevices(dev, 2);
446  if (ret)
447  return ret;
448 
449  s = &dev->subdevices[0];
450  /* digital i/o subdevice (8255) */
451  ret = subdev_8255_init(dev, s, NULL, iobase);
452  if (ret < 0) {
453  dev_err(dev->class_dev, "error! out of memory!\n");
454  return ret;
455  }
456  s = &dev->subdevices[1];
457  dev->read_subdev = s;
459  pc236_intr_disable(dev);
460  if (irq) {
461  if (request_irq(irq, pc236_interrupt, req_irq_flags,
462  PC236_DRIVER_NAME, dev) >= 0) {
463  dev->irq = irq;
464  s->type = COMEDI_SUBD_DI;
466  s->n_chan = 1;
467  s->maxdata = 1;
469  s->insn_bits = pc236_intr_insn;
470  s->do_cmdtest = pc236_intr_cmdtest;
471  s->do_cmd = pc236_intr_cmd;
472  s->cancel = pc236_intr_cancel;
473  }
474  }
475  pc236_report_attach(dev, irq);
476  return 1;
477 }
478 
479 static int pc236_pci_common_attach(struct comedi_device *dev,
480  struct pci_dev *pci_dev)
481 {
482  struct pc236_private *devpriv = dev->private;
483  unsigned long iobase;
484  int ret;
485 
486  comedi_set_hw_dev(dev, &pci_dev->dev);
487 
488  ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
489  if (ret < 0) {
490  dev_err(dev->class_dev,
491  "error! cannot enable PCI device and request regions!\n");
492  return ret;
493  }
494  devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
495  iobase = pci_resource_start(pci_dev, 2);
496  return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
497 }
498 
499 /*
500  * Attach is called by the Comedi core to configure the driver
501  * for a particular board. If you specified a board_name array
502  * in the driver structure, dev->board_ptr contains that
503  * address.
504  */
505 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
506 {
507  const struct pc236_board *thisboard = comedi_board(dev);
508  int ret;
509 
510  dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach\n");
511  ret = alloc_private(dev, sizeof(struct pc236_private));
512  if (ret < 0) {
513  dev_err(dev->class_dev, "error! out of memory!\n");
514  return ret;
515  }
516  /* Process options according to bus type. */
517  if (is_isa_board(thisboard)) {
518  unsigned long iobase = it->options[0];
519  unsigned int irq = it->options[1];
520  ret = pc236_request_region(dev, iobase, PC236_IO_SIZE);
521  if (ret < 0)
522  return ret;
523  return pc236_common_attach(dev, iobase, irq, 0);
524  } else if (is_pci_board(thisboard)) {
525  struct pci_dev *pci_dev;
526 
527  pci_dev = pc236_find_pci_dev(dev, it);
528  if (!pci_dev)
529  return -EIO;
530  return pc236_pci_common_attach(dev, pci_dev);
531  } else {
533  ": BUG! cannot determine board type!\n");
534  return -EINVAL;
535  }
536 }
537 
538 /*
539  * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
540  * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
541  * be a board entry matching the supplied PCI device.
542  */
543 static int __devinit pc236_attach_pci(struct comedi_device *dev,
544  struct pci_dev *pci_dev)
545 {
546  int ret;
547 
548  if (!DO_PCI)
549  return -EINVAL;
550 
551  dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
552  pci_name(pci_dev));
553  ret = alloc_private(dev, sizeof(struct pc236_private));
554  if (ret < 0) {
555  dev_err(dev->class_dev, "error! out of memory!\n");
556  return ret;
557  }
558  dev->board_ptr = pc236_find_pci_board(pci_dev);
559  if (dev->board_ptr == NULL) {
560  dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
561  return -EINVAL;
562  }
563  /*
564  * Need to 'get' the PCI device to match the 'put' in pc236_detach().
565  * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
566  * support for manual attachment of PCI devices via pc236_attach()
567  * has been removed.
568  */
569  pci_dev_get(pci_dev);
570  return pc236_pci_common_attach(dev, pci_dev);
571 }
572 
573 static void pc236_detach(struct comedi_device *dev)
574 {
575  const struct pc236_board *thisboard = comedi_board(dev);
576 
577  if (!thisboard)
578  return;
579  if (dev->iobase)
580  pc236_intr_disable(dev);
581  if (dev->irq)
582  free_irq(dev->irq, dev);
583  if (dev->subdevices)
584  subdev_8255_cleanup(dev, &dev->subdevices[0]);
585  if (is_isa_board(thisboard)) {
586  if (dev->iobase)
588  } else if (is_pci_board(thisboard)) {
589  struct pci_dev *pcidev = comedi_to_pci_dev(dev);
590  if (pcidev) {
591  if (dev->iobase)
592  comedi_pci_disable(pcidev);
593  pci_dev_put(pcidev);
594  }
595  }
596 }
597 
598 /*
599  * The struct comedi_driver structure tells the Comedi core module
600  * which functions to call to configure/deconfigure (attach/detach)
601  * the board, and also about the kernel module that contains
602  * the device code.
603  */
604 static struct comedi_driver amplc_pc236_driver = {
605  .driver_name = PC236_DRIVER_NAME,
606  .module = THIS_MODULE,
607  .attach = pc236_attach,
608  .attach_pci = pc236_attach_pci,
609  .detach = pc236_detach,
610  .board_name = &pc236_boards[0].name,
611  .offset = sizeof(struct pc236_board),
612  .num_names = ARRAY_SIZE(pc236_boards),
613 };
614 
615 #if DO_PCI
616 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
618  {0}
619 };
620 
621 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
622 
623 static int __devinit amplc_pc236_pci_probe(struct pci_dev *dev,
624  const struct pci_device_id *ent)
625 {
626  return comedi_pci_auto_config(dev, &amplc_pc236_driver);
627 }
628 
629 static void __devexit amplc_pc236_pci_remove(struct pci_dev *dev)
630 {
632 }
633 
634 static struct pci_driver amplc_pc236_pci_driver = {
636  .id_table = pc236_pci_table,
637  .probe = &amplc_pc236_pci_probe,
638  .remove = __devexit_p(&amplc_pc236_pci_remove)
639 };
640 
641 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
642 #else
643 module_comedi_driver(amplc_pc236_driver);
644 #endif
645 
646 MODULE_AUTHOR("Comedi http://www.comedi.org");
647 MODULE_DESCRIPTION("Comedi low-level driver");
648 MODULE_LICENSE("GPL");