3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
4 * Project manager: Eric Stolz
9 * Tel: +19(0)7223/9493-0
10 * Fax: +49(0)7223/9493-92
11 * http://www.addi-data.com
14 * This program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation; either version 2 of the License, or (at your
17 * option) any later version.
19 * This program is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
25 #include <linux/pci.h>
26 #include <linux/interrupt.h>
28 #include "../comedidev.h"
29 #include "addi_watchdog.h"
30 #include "comedi_fc.h"
33 * PCI bar 1 I/O Register map
35 #define APCI2032_DO_REG 0x00
36 #define APCI2032_INT_CTRL_REG 0x04
37 #define APCI2032_INT_CTRL_VCC_ENA (1 << 0)
38 #define APCI2032_INT_CTRL_CC_ENA (1 << 1)
39 #define APCI2032_INT_STATUS_REG 0x08
40 #define APCI2032_INT_STATUS_VCC (1 << 0)
41 #define APCI2032_INT_STATUS_CC (1 << 1)
42 #define APCI2032_STATUS_REG 0x0c
43 #define APCI2032_STATUS_IRQ (1 << 0)
44 #define APCI2032_WDOG_REG 0x10
46 struct apci2032_int_private
{
48 unsigned int stop_count
;
50 unsigned char enabled_isns
;
53 static int apci2032_do_insn_bits(struct comedi_device
*dev
,
54 struct comedi_subdevice
*s
,
55 struct comedi_insn
*insn
,
58 unsigned int mask
= data
[0];
59 unsigned int bits
= data
[1];
61 s
->state
= inl(dev
->iobase
+ APCI2032_DO_REG
);
64 s
->state
|= (bits
& mask
);
66 outl(s
->state
, dev
->iobase
+ APCI2032_DO_REG
);
74 static int apci2032_int_insn_bits(struct comedi_device
*dev
,
75 struct comedi_subdevice
*s
,
76 struct comedi_insn
*insn
,
79 data
[1] = inl(dev
->iobase
+ APCI2032_INT_STATUS_REG
) & 3;
83 static void apci2032_int_stop(struct comedi_device
*dev
,
84 struct comedi_subdevice
*s
)
86 struct apci2032_int_private
*subpriv
= s
->private;
88 subpriv
->active
= false;
89 subpriv
->enabled_isns
= 0;
90 outl(0x0, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
93 static bool apci2032_int_start(struct comedi_device
*dev
,
94 struct comedi_subdevice
*s
,
95 unsigned char enabled_isns
)
97 struct apci2032_int_private
*subpriv
= s
->private;
98 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
101 subpriv
->enabled_isns
= enabled_isns
;
102 subpriv
->stop_count
= cmd
->stop_arg
;
103 if (cmd
->stop_src
== TRIG_COUNT
&& subpriv
->stop_count
== 0) {
104 /* An empty acquisition! */
105 s
->async
->events
|= COMEDI_CB_EOA
;
106 subpriv
->active
= false;
109 subpriv
->active
= true;
110 outl(enabled_isns
, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
117 static int apci2032_int_cmdtest(struct comedi_device
*dev
,
118 struct comedi_subdevice
*s
,
119 struct comedi_cmd
*cmd
)
123 /* Step 1 : check if triggers are trivially valid */
125 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
126 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_EXT
);
127 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_NOW
);
128 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
129 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
134 /* Step 2a : make sure trigger sources are unique */
135 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
137 /* Step 2b : and mutually compatible */
142 /* Step 3: check if arguments are trivially valid */
144 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
145 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
146 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
147 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
148 if (cmd
->stop_src
== TRIG_NONE
)
149 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
154 /* step 4: ignored */
162 static int apci2032_int_cmd(struct comedi_device
*dev
,
163 struct comedi_subdevice
*s
)
165 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
166 struct apci2032_int_private
*subpriv
= s
->private;
167 unsigned char enabled_isns
;
173 for (n
= 0; n
< cmd
->chanlist_len
; n
++)
174 enabled_isns
|= 1 << CR_CHAN(cmd
->chanlist
[n
]);
176 spin_lock_irqsave(&subpriv
->spinlock
, flags
);
177 do_event
= apci2032_int_start(dev
, s
, enabled_isns
);
178 spin_unlock_irqrestore(&subpriv
->spinlock
, flags
);
181 comedi_event(dev
, s
);
186 static int apci2032_int_cancel(struct comedi_device
*dev
,
187 struct comedi_subdevice
*s
)
189 struct apci2032_int_private
*subpriv
= s
->private;
192 spin_lock_irqsave(&subpriv
->spinlock
, flags
);
194 apci2032_int_stop(dev
, s
);
195 spin_unlock_irqrestore(&subpriv
->spinlock
, flags
);
200 static irqreturn_t
apci2032_interrupt(int irq
, void *d
)
202 struct comedi_device
*dev
= d
;
203 struct comedi_subdevice
*s
= dev
->read_subdev
;
204 struct apci2032_int_private
*subpriv
;
206 bool do_event
= false;
211 /* Check if VCC OR CC interrupt has occurred */
212 val
= inl(dev
->iobase
+ APCI2032_STATUS_REG
) & APCI2032_STATUS_IRQ
;
216 subpriv
= s
->private;
217 spin_lock(&subpriv
->spinlock
);
219 val
= inl(dev
->iobase
+ APCI2032_INT_STATUS_REG
) & 3;
220 /* Disable triggered interrupt sources. */
221 outl(~val
& 3, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
223 * Note: We don't reenable the triggered interrupt sources because they
224 * are level-sensitive, hardware error status interrupt sources and
225 * they'd keep triggering interrupts repeatedly.
228 if (subpriv
->active
&& (val
& subpriv
->enabled_isns
) != 0) {
231 unsigned int *chanlist
;
233 /* Bits in scan data correspond to indices in channel list. */
235 len
= s
->async
->cmd
.chanlist_len
;
236 chanlist
= &s
->async
->cmd
.chanlist
[0];
237 for (n
= 0; n
< len
; n
++)
238 if ((val
& (1U << CR_CHAN(chanlist
[n
]))) != 0)
241 if (comedi_buf_put(s
->async
, bits
)) {
242 s
->async
->events
|= COMEDI_CB_BLOCK
| COMEDI_CB_EOS
;
243 if (s
->async
->cmd
.stop_src
== TRIG_COUNT
&&
244 subpriv
->stop_count
> 0) {
245 subpriv
->stop_count
--;
246 if (subpriv
->stop_count
== 0) {
247 /* end of acquisition */
248 s
->async
->events
|= COMEDI_CB_EOA
;
249 apci2032_int_stop(dev
, s
);
253 apci2032_int_stop(dev
, s
);
254 s
->async
->events
|= COMEDI_CB_OVERFLOW
;
259 spin_unlock(&subpriv
->spinlock
);
261 comedi_event(dev
, s
);
266 static int apci2032_reset(struct comedi_device
*dev
)
268 outl(0x0, dev
->iobase
+ APCI2032_DO_REG
);
269 outl(0x0, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
271 addi_watchdog_reset(dev
->iobase
+ APCI2032_WDOG_REG
);
276 static int apci2032_auto_attach(struct comedi_device
*dev
,
277 unsigned long context_unused
)
279 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
280 struct comedi_subdevice
*s
;
283 ret
= comedi_pci_enable(dev
);
286 dev
->iobase
= pci_resource_start(pcidev
, 1);
289 if (pcidev
->irq
> 0) {
290 ret
= request_irq(pcidev
->irq
, apci2032_interrupt
,
291 IRQF_SHARED
, dev
->board_name
, dev
);
293 dev
->irq
= pcidev
->irq
;
296 ret
= comedi_alloc_subdevices(dev
, 3);
300 /* Initialize the digital output subdevice */
301 s
= &dev
->subdevices
[0];
302 s
->type
= COMEDI_SUBD_DO
;
303 s
->subdev_flags
= SDF_WRITEABLE
;
306 s
->range_table
= &range_digital
;
307 s
->insn_bits
= apci2032_do_insn_bits
;
309 /* Initialize the watchdog subdevice */
310 s
= &dev
->subdevices
[1];
311 ret
= addi_watchdog_init(s
, dev
->iobase
+ APCI2032_WDOG_REG
);
315 /* Initialize the interrupt subdevice */
316 s
= &dev
->subdevices
[2];
317 s
->type
= COMEDI_SUBD_DI
;
318 s
->subdev_flags
= SDF_READABLE
;
321 s
->range_table
= &range_digital
;
322 s
->insn_bits
= apci2032_int_insn_bits
;
324 struct apci2032_int_private
*subpriv
;
326 dev
->read_subdev
= s
;
327 subpriv
= kzalloc(sizeof(*subpriv
), GFP_KERNEL
);
330 spin_lock_init(&subpriv
->spinlock
);
331 s
->private = subpriv
;
332 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
334 s
->do_cmdtest
= apci2032_int_cmdtest
;
335 s
->do_cmd
= apci2032_int_cmd
;
336 s
->cancel
= apci2032_int_cancel
;
342 static void apci2032_detach(struct comedi_device
*dev
)
347 free_irq(dev
->irq
, dev
);
348 if (dev
->read_subdev
)
349 kfree(dev
->read_subdev
->private);
350 comedi_pci_disable(dev
);
353 static struct comedi_driver apci2032_driver
= {
354 .driver_name
= "addi_apci_2032",
355 .module
= THIS_MODULE
,
356 .auto_attach
= apci2032_auto_attach
,
357 .detach
= apci2032_detach
,
360 static int apci2032_pci_probe(struct pci_dev
*dev
,
361 const struct pci_device_id
*id
)
363 return comedi_pci_auto_config(dev
, &apci2032_driver
, id
->driver_data
);
366 static DEFINE_PCI_DEVICE_TABLE(apci2032_pci_table
) = {
367 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA
, 0x1004) },
370 MODULE_DEVICE_TABLE(pci
, apci2032_pci_table
);
372 static struct pci_driver apci2032_pci_driver
= {
373 .name
= "addi_apci_2032",
374 .id_table
= apci2032_pci_table
,
375 .probe
= apci2032_pci_probe
,
376 .remove
= comedi_pci_auto_unconfig
,
378 module_comedi_pci_driver(apci2032_driver
, apci2032_pci_driver
);
380 MODULE_AUTHOR("Comedi http://www.comedi.org");
381 MODULE_DESCRIPTION("Comedi low-level driver");
382 MODULE_LICENSE("GPL");