Version:  2.0.40 2.2.26 2.4.37 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17

Linux/drivers/staging/comedi/drivers/addi_apci_1564.c

  1 #include <linux/module.h>
  2 #include <linux/pci.h>
  3 #include <linux/interrupt.h>
  4 #include <linux/sched.h>
  5 
  6 #include "../comedidev.h"
  7 #include "comedi_fc.h"
  8 #include "amcc_s5933.h"
  9 #include "addi_watchdog.h"
 10 
 11 struct apci1564_private {
 12         unsigned int amcc_iobase;       /* base of AMCC I/O registers */
 13         unsigned int mode1;             /* riding-edge/high level channels */
 14         unsigned int mode2;             /* falling-edge/low level channels */
 15         unsigned int ctrl;              /* interrupt mode OR (edge) . AND (level) */
 16         unsigned char timer_select_mode;
 17         unsigned char mode_select_register;
 18         struct task_struct *tsk_current;
 19 };
 20 
 21 #include "addi-data/hwdrv_apci1564.c"
 22 
 23 static int apci1564_reset(struct comedi_device *dev)
 24 {
 25         struct apci1564_private *devpriv = dev->private;
 26 
 27         /* Disable the input interrupts and reset status register */
 28         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
 29         inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
 30         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
 31         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
 32 
 33         /* Reset the output channels and disable interrupts */
 34         outl(0x0, devpriv->amcc_iobase + APCI1564_DO_REG);
 35         outl(0x0, devpriv->amcc_iobase + APCI1564_DO_INT_CTRL_REG);
 36 
 37         /* Reset the watchdog registers */
 38         addi_watchdog_reset(devpriv->amcc_iobase + APCI1564_WDOG_REG);
 39 
 40         /* Reset the timer registers */
 41         outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
 42         outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_RELOAD_REG);
 43 
 44         /* Reset the counter registers */
 45         outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER1));
 46         outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER2));
 47         outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER3));
 48         outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER4));
 49 
 50         return 0;
 51 }
 52 
 53 static irqreturn_t apci1564_interrupt(int irq, void *d)
 54 {
 55         struct comedi_device *dev = d;
 56         struct apci1564_private *devpriv = dev->private;
 57         struct comedi_subdevice *s = dev->read_subdev;
 58         unsigned int status;
 59         unsigned int ctrl;
 60         unsigned int chan;
 61 
 62         /* check interrupt is from this device */
 63         if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
 64              INTCSR_INTR_ASSERTED) == 0)
 65                 return IRQ_NONE;
 66 
 67         status = inl(devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
 68         if (status & APCI1564_DI_INT_ENABLE) {
 69                 /* disable the interrupt */
 70                 outl(status & APCI1564_DI_INT_DISABLE,
 71                      devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
 72 
 73                 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG)
 74                                & 0xffff;
 75                 comedi_buf_put(s, s->state);
 76                 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
 77                 comedi_event(dev, s);
 78 
 79                 /* enable the interrupt */
 80                 outl(status, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
 81         }
 82 
 83         status = inl(devpriv->amcc_iobase + APCI1564_TIMER_IRQ_REG);
 84         if (status & 0x01) {
 85                 /*  Disable Timer Interrupt */
 86                 ctrl = inl(devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
 87                 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
 88 
 89                 /* Send a signal to from kernel to user space */
 90                 send_sig(SIGIO, devpriv->tsk_current, 0);
 91 
 92                 /*  Enable Timer Interrupt */
 93                 outl(ctrl, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
 94         }
 95 
 96         for (chan = 0; chan < 4; chan++) {
 97                 status = inl(dev->iobase + APCI1564_TCW_IRQ_REG(chan));
 98                 if (status & 0x01) {
 99                         /*  Disable Counter Interrupt */
100                         ctrl = inl(dev->iobase + APCI1564_TCW_CTRL_REG(chan));
101                         outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(chan));
102 
103                         /* Send a signal to from kernel to user space */
104                         send_sig(SIGIO, devpriv->tsk_current, 0);
105 
106                         /*  Enable Counter Interrupt */
107                         outl(ctrl, dev->iobase + APCI1564_TCW_CTRL_REG(chan));
108                 }
109         }
110 
111         return IRQ_HANDLED;
112 }
113 
114 static int apci1564_di_insn_bits(struct comedi_device *dev,
115                                  struct comedi_subdevice *s,
116                                  struct comedi_insn *insn,
117                                  unsigned int *data)
118 {
119         struct apci1564_private *devpriv = dev->private;
120 
121         data[1] = inl(devpriv->amcc_iobase + APCI1564_DI_REG);
122 
123         return insn->n;
124 }
125 
126 static int apci1564_do_insn_bits(struct comedi_device *dev,
127                                  struct comedi_subdevice *s,
128                                  struct comedi_insn *insn,
129                                  unsigned int *data)
130 {
131         struct apci1564_private *devpriv = dev->private;
132 
133         s->state = inl(devpriv->amcc_iobase + APCI1564_DO_REG);
134 
135         if (comedi_dio_update_state(s, data))
136                 outl(s->state, devpriv->amcc_iobase + APCI1564_DO_REG);
137 
138         data[1] = s->state;
139 
140         return insn->n;
141 }
142 
143 static int apci1564_diag_insn_bits(struct comedi_device *dev,
144                                    struct comedi_subdevice *s,
145                                    struct comedi_insn *insn,
146                                    unsigned int *data)
147 {
148         struct apci1564_private *devpriv = dev->private;
149 
150         data[1] = inl(devpriv->amcc_iobase + APCI1564_DO_INT_STATUS_REG) & 3;
151 
152         return insn->n;
153 }
154 
155 /*
156  * Change-Of-State (COS) interrupt configuration
157  *
158  * Channels 0 to 15 are interruptible. These channels can be configured
159  * to generate interrupts based on AND/OR logic for the desired channels.
160  *
161  *      OR logic
162  *              - reacts to rising or falling edges
163  *              - interrupt is generated when any enabled channel
164  *                meet the desired interrupt condition
165  *
166  *      AND logic
167  *              - reacts to changes in level of the selected inputs
168  *              - interrupt is generated when all enabled channels
169  *                meet the desired interrupt condition
170  *              - after an interrupt, a change in level must occur on
171  *                the selected inputs to release the IRQ logic
172  *
173  * The COS interrupt must be configured before it can be enabled.
174  *
175  *      data[0] : INSN_CONFIG_DIGITAL_TRIG
176  *      data[1] : trigger number (= 0)
177  *      data[2] : configuration operation:
178  *                COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
179  *                COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
180  *                COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
181  *      data[3] : left-shift for data[4] and data[5]
182  *      data[4] : rising-edge/high level channels
183  *      data[5] : falling-edge/low level channels
184  */
185 static int apci1564_cos_insn_config(struct comedi_device *dev,
186                                     struct comedi_subdevice *s,
187                                     struct comedi_insn *insn,
188                                     unsigned int *data)
189 {
190         struct apci1564_private *devpriv = dev->private;
191         unsigned int shift, oldmask;
192 
193         switch (data[0]) {
194         case INSN_CONFIG_DIGITAL_TRIG:
195                 if (data[1] != 0)
196                         return -EINVAL;
197                 shift = data[3];
198                 oldmask = (1U << shift) - 1;
199                 switch (data[2]) {
200                 case COMEDI_DIGITAL_TRIG_DISABLE:
201                         devpriv->ctrl = 0;
202                         devpriv->mode1 = 0;
203                         devpriv->mode2 = 0;
204                         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
205                         inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
206                         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
207                         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
208                         break;
209                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
210                         if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
211                                               APCI1564_DI_INT_OR)) {
212                                 /* switching to 'OR' mode */
213                                 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
214                                                 APCI1564_DI_INT_OR;
215                                 /* wipe old channels */
216                                 devpriv->mode1 = 0;
217                                 devpriv->mode2 = 0;
218                         } else {
219                                 /* preserve unspecified channels */
220                                 devpriv->mode1 &= oldmask;
221                                 devpriv->mode2 &= oldmask;
222                         }
223                         /* configure specified channels */
224                         devpriv->mode1 |= data[4] << shift;
225                         devpriv->mode2 |= data[5] << shift;
226                         break;
227                 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
228                         if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
229                                               APCI1564_DI_INT_AND)) {
230                                 /* switching to 'AND' mode */
231                                 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
232                                                 APCI1564_DI_INT_AND;
233                                 /* wipe old channels */
234                                 devpriv->mode1 = 0;
235                                 devpriv->mode2 = 0;
236                         } else {
237                                 /* preserve unspecified channels */
238                                 devpriv->mode1 &= oldmask;
239                                 devpriv->mode2 &= oldmask;
240                         }
241                         /* configure specified channels */
242                         devpriv->mode1 |= data[4] << shift;
243                         devpriv->mode2 |= data[5] << shift;
244                         break;
245                 default:
246                         return -EINVAL;
247                 }
248                 break;
249         default:
250                 return -EINVAL;
251         }
252         return insn->n;
253 }
254 
255 static int apci1564_cos_insn_bits(struct comedi_device *dev,
256                                   struct comedi_subdevice *s,
257                                   struct comedi_insn *insn,
258                                   unsigned int *data)
259 {
260         data[1] = s->state;
261 
262         return 0;
263 }
264 
265 static int apci1564_cos_cmdtest(struct comedi_device *dev,
266                                 struct comedi_subdevice *s,
267                                 struct comedi_cmd *cmd)
268 {
269         int err = 0;
270 
271         /* Step 1 : check if triggers are trivially valid */
272 
273         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
274         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
275         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
276         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
277         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
278 
279         if (err)
280                 return 1;
281 
282         /* Step 2a : make sure trigger sources are unique */
283         /* Step 2b : and mutually compatible */
284 
285         if (err)
286                 return 2;
287 
288         /* Step 3: check if arguments are trivially valid */
289 
290         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
291         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
292         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
293         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
294         err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
295 
296         if (err)
297                 return 3;
298 
299         /* step 4: ignored */
300 
301         if (err)
302                 return 4;
303 
304         return 0;
305 }
306 
307 /*
308  * Change-Of-State (COS) 'do_cmd' operation
309  *
310  * Enable the COS interrupt as configured by apci1564_cos_insn_config().
311  */
312 static int apci1564_cos_cmd(struct comedi_device *dev,
313                             struct comedi_subdevice *s)
314 {
315         struct apci1564_private *devpriv = dev->private;
316 
317         if (!devpriv->ctrl) {
318                 dev_warn(dev->class_dev,
319                         "Interrupts disabled due to mode configuration!\n");
320                 return -EINVAL;
321         }
322 
323         outl(devpriv->mode1, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
324         outl(devpriv->mode2, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
325         outl(devpriv->ctrl, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
326 
327         return 0;
328 }
329 
330 static int apci1564_cos_cancel(struct comedi_device *dev,
331                                struct comedi_subdevice *s)
332 {
333         struct apci1564_private *devpriv = dev->private;
334 
335         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
336         inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
337         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
338         outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
339 
340         return 0;
341 }
342 
343 static int apci1564_auto_attach(struct comedi_device *dev,
344                                       unsigned long context_unused)
345 {
346         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
347         struct apci1564_private *devpriv;
348         struct comedi_subdevice *s;
349         int ret;
350 
351         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
352         if (!devpriv)
353                 return -ENOMEM;
354 
355         ret = comedi_pci_enable(dev);
356         if (ret)
357                 return ret;
358 
359         dev->iobase = pci_resource_start(pcidev, 1);
360         devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
361 
362         apci1564_reset(dev);
363 
364         if (pcidev->irq > 0) {
365                 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
366                                   dev->board_name, dev);
367                 if (ret == 0)
368                         dev->irq = pcidev->irq;
369         }
370 
371         ret = comedi_alloc_subdevices(dev, 6);
372         if (ret)
373                 return ret;
374 
375         /*  Allocate and Initialise DI Subdevice Structures */
376         s = &dev->subdevices[0];
377         s->type = COMEDI_SUBD_DI;
378         s->subdev_flags = SDF_READABLE;
379         s->n_chan = 32;
380         s->maxdata = 1;
381         s->range_table = &range_digital;
382         s->insn_bits = apci1564_di_insn_bits;
383 
384         /*  Allocate and Initialise DO Subdevice Structures */
385         s = &dev->subdevices[1];
386         s->type = COMEDI_SUBD_DO;
387         s->subdev_flags = SDF_WRITEABLE;
388         s->n_chan = 32;
389         s->maxdata = 1;
390         s->range_table = &range_digital;
391         s->insn_config = apci1564_do_config;
392         s->insn_bits = apci1564_do_insn_bits;
393 
394         /* Change-Of-State (COS) interrupt subdevice */
395         s = &dev->subdevices[2];
396         if (dev->irq) {
397                 dev->read_subdev = s;
398                 s->type = COMEDI_SUBD_DI;
399                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
400                 s->n_chan = 1;
401                 s->maxdata = 1;
402                 s->range_table = &range_digital;
403                 s->len_chanlist = 1;
404                 s->insn_config = apci1564_cos_insn_config;
405                 s->insn_bits = apci1564_cos_insn_bits;
406                 s->do_cmdtest = apci1564_cos_cmdtest;
407                 s->do_cmd = apci1564_cos_cmd;
408                 s->cancel = apci1564_cos_cancel;
409         } else {
410                 s->type = COMEDI_SUBD_UNUSED;
411         }
412 
413         /*  Allocate and Initialise Timer Subdevice Structures */
414         s = &dev->subdevices[3];
415         s->type = COMEDI_SUBD_TIMER;
416         s->subdev_flags = SDF_WRITEABLE;
417         s->n_chan = 1;
418         s->maxdata = 0;
419         s->len_chanlist = 1;
420         s->range_table = &range_digital;
421         s->insn_write = apci1564_timer_write;
422         s->insn_read = apci1564_timer_read;
423         s->insn_config = apci1564_timer_config;
424 
425         /* Initialize the watchdog subdevice */
426         s = &dev->subdevices[4];
427         ret = addi_watchdog_init(s, devpriv->amcc_iobase + APCI1564_WDOG_REG);
428         if (ret)
429                 return ret;
430 
431         /* Initialize the diagnostic status subdevice */
432         s = &dev->subdevices[5];
433         s->type = COMEDI_SUBD_DI;
434         s->subdev_flags = SDF_READABLE;
435         s->n_chan = 2;
436         s->maxdata = 1;
437         s->range_table = &range_digital;
438         s->insn_bits = apci1564_diag_insn_bits;
439 
440         return 0;
441 }
442 
443 static void apci1564_detach(struct comedi_device *dev)
444 {
445         if (dev->iobase)
446                 apci1564_reset(dev);
447         if (dev->irq)
448                 free_irq(dev->irq, dev);
449         comedi_pci_disable(dev);
450 }
451 
452 static struct comedi_driver apci1564_driver = {
453         .driver_name    = "addi_apci_1564",
454         .module         = THIS_MODULE,
455         .auto_attach    = apci1564_auto_attach,
456         .detach         = apci1564_detach,
457 };
458 
459 static int apci1564_pci_probe(struct pci_dev *dev,
460                               const struct pci_device_id *id)
461 {
462         return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
463 }
464 
465 static const struct pci_device_id apci1564_pci_table[] = {
466         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
467         { 0 }
468 };
469 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
470 
471 static struct pci_driver apci1564_pci_driver = {
472         .name           = "addi_apci_1564",
473         .id_table       = apci1564_pci_table,
474         .probe          = apci1564_pci_probe,
475         .remove         = comedi_pci_auto_unconfig,
476 };
477 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
478 
479 MODULE_AUTHOR("Comedi http://www.comedi.org");
480 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
481 MODULE_LICENSE("GPL");
482 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us