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
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * You should also find the complete GPL in the COPYING file accompanying
32 #include <linux/pci.h>
33 #include <linux/interrupt.h>
35 #include "../comedidev.h"
36 #include "addi_watchdog.h"
37 #include "comedi_fc.h"
40 * PCI bar 1 I/O Register map
42 #define APCI2032_DO_REG 0x00
43 #define APCI2032_INT_CTRL_REG 0x04
44 #define APCI2032_INT_CTRL_VCC_ENA (1 << 0)
45 #define APCI2032_INT_CTRL_CC_ENA (1 << 1)
46 #define APCI2032_INT_STATUS_REG 0x08
47 #define APCI2032_INT_STATUS_VCC (1 << 0)
48 #define APCI2032_INT_STATUS_CC (1 << 1)
49 #define APCI2032_STATUS_REG 0x0c
50 #define APCI2032_STATUS_IRQ (1 << 0)
51 #define APCI2032_WDOG_REG 0x10
53 struct apci2032_int_private
{
55 unsigned int stop_count
;
57 unsigned char enabled_isns
;
60 static int apci2032_do_insn_bits(struct comedi_device
*dev
,
61 struct comedi_subdevice
*s
,
62 struct comedi_insn
*insn
,
65 unsigned int mask
= data
[0];
66 unsigned int bits
= data
[1];
68 s
->state
= inl(dev
->iobase
+ APCI2032_DO_REG
);
71 s
->state
|= (bits
& mask
);
73 outl(s
->state
, dev
->iobase
+ APCI2032_DO_REG
);
81 static int apci2032_int_insn_bits(struct comedi_device
*dev
,
82 struct comedi_subdevice
*s
,
83 struct comedi_insn
*insn
,
86 data
[1] = inl(dev
->iobase
+ APCI2032_INT_STATUS_REG
) & 3;
90 static void apci2032_int_stop(struct comedi_device
*dev
,
91 struct comedi_subdevice
*s
)
93 struct apci2032_int_private
*subpriv
= s
->private;
95 subpriv
->active
= false;
96 subpriv
->enabled_isns
= 0;
97 outl(0x0, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
100 static bool apci2032_int_start(struct comedi_device
*dev
,
101 struct comedi_subdevice
*s
,
102 unsigned char enabled_isns
)
104 struct apci2032_int_private
*subpriv
= s
->private;
105 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
108 subpriv
->enabled_isns
= enabled_isns
;
109 subpriv
->stop_count
= cmd
->stop_arg
;
110 if (cmd
->stop_src
== TRIG_COUNT
&& subpriv
->stop_count
== 0) {
111 /* An empty acquisition! */
112 s
->async
->events
|= COMEDI_CB_EOA
;
113 subpriv
->active
= false;
116 subpriv
->active
= true;
117 outl(enabled_isns
, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
124 static int apci2032_int_cmdtest(struct comedi_device
*dev
,
125 struct comedi_subdevice
*s
,
126 struct comedi_cmd
*cmd
)
130 /* Step 1 : check if triggers are trivially valid */
132 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
133 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_EXT
);
134 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_NOW
);
135 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
136 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
141 /* Step 2a : make sure trigger sources are unique */
142 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
144 /* Step 2b : and mutually compatible */
149 /* Step 3: check if arguments are trivially valid */
151 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
152 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
153 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
154 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
155 if (cmd
->stop_src
== TRIG_NONE
)
156 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
161 /* step 4: ignored */
169 static int apci2032_int_cmd(struct comedi_device
*dev
,
170 struct comedi_subdevice
*s
)
172 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
173 struct apci2032_int_private
*subpriv
= s
->private;
174 unsigned char enabled_isns
;
180 for (n
= 0; n
< cmd
->chanlist_len
; n
++)
181 enabled_isns
|= 1 << CR_CHAN(cmd
->chanlist
[n
]);
183 spin_lock_irqsave(&subpriv
->spinlock
, flags
);
184 do_event
= apci2032_int_start(dev
, s
, enabled_isns
);
185 spin_unlock_irqrestore(&subpriv
->spinlock
, flags
);
188 comedi_event(dev
, s
);
193 static int apci2032_int_cancel(struct comedi_device
*dev
,
194 struct comedi_subdevice
*s
)
196 struct apci2032_int_private
*subpriv
= s
->private;
199 spin_lock_irqsave(&subpriv
->spinlock
, flags
);
201 apci2032_int_stop(dev
, s
);
202 spin_unlock_irqrestore(&subpriv
->spinlock
, flags
);
207 static irqreturn_t
apci2032_interrupt(int irq
, void *d
)
209 struct comedi_device
*dev
= d
;
210 struct comedi_subdevice
*s
= dev
->read_subdev
;
211 struct apci2032_int_private
*subpriv
;
213 bool do_event
= false;
218 /* Check if VCC OR CC interrupt has occurred */
219 val
= inl(dev
->iobase
+ APCI2032_STATUS_REG
) & APCI2032_STATUS_IRQ
;
223 subpriv
= s
->private;
224 spin_lock(&subpriv
->spinlock
);
226 val
= inl(dev
->iobase
+ APCI2032_INT_STATUS_REG
) & 3;
227 /* Disable triggered interrupt sources. */
228 outl(~val
& 3, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
230 * Note: We don't reenable the triggered interrupt sources because they
231 * are level-sensitive, hardware error status interrupt sources and
232 * they'd keep triggering interrupts repeatedly.
235 if (subpriv
->active
&& (val
& subpriv
->enabled_isns
) != 0) {
238 unsigned int *chanlist
;
240 /* Bits in scan data correspond to indices in channel list. */
242 len
= s
->async
->cmd
.chanlist_len
;
243 chanlist
= &s
->async
->cmd
.chanlist
[0];
244 for (n
= 0; n
< len
; n
++)
245 if ((val
& (1U << CR_CHAN(chanlist
[n
]))) != 0)
248 if (comedi_buf_put(s
->async
, bits
)) {
249 s
->async
->events
|= COMEDI_CB_BLOCK
| COMEDI_CB_EOS
;
250 if (s
->async
->cmd
.stop_src
== TRIG_COUNT
&&
251 subpriv
->stop_count
> 0) {
252 subpriv
->stop_count
--;
253 if (subpriv
->stop_count
== 0) {
254 /* end of acquisition */
255 s
->async
->events
|= COMEDI_CB_EOA
;
256 apci2032_int_stop(dev
, s
);
260 apci2032_int_stop(dev
, s
);
261 s
->async
->events
|= COMEDI_CB_OVERFLOW
;
266 spin_unlock(&subpriv
->spinlock
);
268 comedi_event(dev
, s
);
273 static int apci2032_reset(struct comedi_device
*dev
)
275 outl(0x0, dev
->iobase
+ APCI2032_DO_REG
);
276 outl(0x0, dev
->iobase
+ APCI2032_INT_CTRL_REG
);
278 addi_watchdog_reset(dev
->iobase
+ APCI2032_WDOG_REG
);
283 static int apci2032_auto_attach(struct comedi_device
*dev
,
284 unsigned long context_unused
)
286 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
287 struct comedi_subdevice
*s
;
290 ret
= comedi_pci_enable(dev
);
293 dev
->iobase
= pci_resource_start(pcidev
, 1);
296 if (pcidev
->irq
> 0) {
297 ret
= request_irq(pcidev
->irq
, apci2032_interrupt
,
298 IRQF_SHARED
, dev
->board_name
, dev
);
300 dev
->irq
= pcidev
->irq
;
303 ret
= comedi_alloc_subdevices(dev
, 3);
307 /* Initialize the digital output subdevice */
308 s
= &dev
->subdevices
[0];
309 s
->type
= COMEDI_SUBD_DO
;
310 s
->subdev_flags
= SDF_WRITEABLE
;
313 s
->range_table
= &range_digital
;
314 s
->insn_bits
= apci2032_do_insn_bits
;
316 /* Initialize the watchdog subdevice */
317 s
= &dev
->subdevices
[1];
318 ret
= addi_watchdog_init(s
, dev
->iobase
+ APCI2032_WDOG_REG
);
322 /* Initialize the interrupt subdevice */
323 s
= &dev
->subdevices
[2];
324 s
->type
= COMEDI_SUBD_DI
;
325 s
->subdev_flags
= SDF_READABLE
;
328 s
->range_table
= &range_digital
;
329 s
->insn_bits
= apci2032_int_insn_bits
;
331 struct apci2032_int_private
*subpriv
;
333 dev
->read_subdev
= s
;
334 subpriv
= kzalloc(sizeof(*subpriv
), GFP_KERNEL
);
337 spin_lock_init(&subpriv
->spinlock
);
338 s
->private = subpriv
;
339 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
341 s
->do_cmdtest
= apci2032_int_cmdtest
;
342 s
->do_cmd
= apci2032_int_cmd
;
343 s
->cancel
= apci2032_int_cancel
;
349 static void apci2032_detach(struct comedi_device
*dev
)
354 free_irq(dev
->irq
, dev
);
355 if (dev
->read_subdev
)
356 kfree(dev
->read_subdev
->private);
357 comedi_spriv_free(dev
, 1);
358 comedi_pci_disable(dev
);
361 static struct comedi_driver apci2032_driver
= {
362 .driver_name
= "addi_apci_2032",
363 .module
= THIS_MODULE
,
364 .auto_attach
= apci2032_auto_attach
,
365 .detach
= apci2032_detach
,
368 static int apci2032_pci_probe(struct pci_dev
*dev
,
369 const struct pci_device_id
*id
)
371 return comedi_pci_auto_config(dev
, &apci2032_driver
, id
->driver_data
);
374 static DEFINE_PCI_DEVICE_TABLE(apci2032_pci_table
) = {
375 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA
, 0x1004) },
378 MODULE_DEVICE_TABLE(pci
, apci2032_pci_table
);
380 static struct pci_driver apci2032_pci_driver
= {
381 .name
= "addi_apci_2032",
382 .id_table
= apci2032_pci_table
,
383 .probe
= apci2032_pci_probe
,
384 .remove
= comedi_pci_auto_unconfig
,
386 module_comedi_pci_driver(apci2032_driver
, apci2032_pci_driver
);
388 MODULE_AUTHOR("Comedi http://www.comedi.org");
389 MODULE_DESCRIPTION("Comedi low-level driver");
390 MODULE_LICENSE("GPL");