addi_apci_2032.c revision 588ba6dc5fb4bdca47a3da38c2718fbb82d3eee1
1/*
2 * addi_apci_2032.c
3 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
4 * Project manager: Eric Stolz
5 *
6 *	ADDI-DATA GmbH
7 *	Dieselstrasse 3
8 *	D-77833 Ottersweier
9 *	Tel: +19(0)7223/9493-0
10 *	Fax: +49(0)7223/9493-92
11 *	http://www.addi-data.com
12 *	info@addi-data.com
13 *
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.
18 *
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
22 * more details.
23 */
24
25#include <linux/pci.h>
26#include <linux/interrupt.h>
27
28#include "../comedidev.h"
29#include "addi_watchdog.h"
30#include "comedi_fc.h"
31
32/*
33 * PCI bar 1 I/O Register map
34 */
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
45
46struct apci2032_int_private {
47	spinlock_t spinlock;
48	unsigned int stop_count;
49	bool active;
50	unsigned char enabled_isns;
51};
52
53static int apci2032_do_insn_bits(struct comedi_device *dev,
54				 struct comedi_subdevice *s,
55				 struct comedi_insn *insn,
56				 unsigned int *data)
57{
58	unsigned int mask = data[0];
59	unsigned int bits = data[1];
60
61	s->state = inl(dev->iobase + APCI2032_DO_REG);
62	if (mask) {
63		s->state &= ~mask;
64		s->state |= (bits & mask);
65
66		outl(s->state, dev->iobase + APCI2032_DO_REG);
67	}
68
69	data[1] = s->state;
70
71	return insn->n;
72}
73
74static int apci2032_int_insn_bits(struct comedi_device *dev,
75				  struct comedi_subdevice *s,
76				  struct comedi_insn *insn,
77				  unsigned int *data)
78{
79	data[1] = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
80	return insn->n;
81}
82
83static void apci2032_int_stop(struct comedi_device *dev,
84			      struct comedi_subdevice *s)
85{
86	struct apci2032_int_private *subpriv = s->private;
87
88	subpriv->active = false;
89	subpriv->enabled_isns = 0;
90	outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
91}
92
93static bool apci2032_int_start(struct comedi_device *dev,
94			       struct comedi_subdevice *s,
95			       unsigned char enabled_isns)
96{
97	struct apci2032_int_private *subpriv = s->private;
98	struct comedi_cmd *cmd = &s->async->cmd;
99	bool do_event;
100
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;
107		do_event = true;
108	} else {
109		subpriv->active = true;
110		outl(enabled_isns, dev->iobase + APCI2032_INT_CTRL_REG);
111		do_event = false;
112	}
113
114	return do_event;
115}
116
117static int apci2032_int_cmdtest(struct comedi_device *dev,
118				struct comedi_subdevice *s,
119				struct comedi_cmd *cmd)
120{
121	int err = 0;
122
123	/* Step 1 : check if triggers are trivially valid */
124
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);
130
131	if (err)
132		return 1;
133
134	/* Step 2a : make sure trigger sources are unique */
135	err |= cfc_check_trigger_is_unique(cmd->stop_src);
136
137	/* Step 2b : and mutually compatible */
138
139	if (err)
140		return 2;
141
142	/* Step 3: check if arguments are trivially valid */
143
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);
150
151	if (err)
152		return 3;
153
154	/* step 4: ignored */
155
156	if (err)
157		return 4;
158
159	return 0;
160}
161
162static int apci2032_int_cmd(struct comedi_device *dev,
163			    struct comedi_subdevice *s)
164{
165	struct comedi_cmd *cmd = &s->async->cmd;
166	struct apci2032_int_private *subpriv = s->private;
167	unsigned char enabled_isns;
168	unsigned int n;
169	unsigned long flags;
170	bool do_event;
171
172	enabled_isns = 0;
173	for (n = 0; n < cmd->chanlist_len; n++)
174		enabled_isns |= 1 << CR_CHAN(cmd->chanlist[n]);
175
176	spin_lock_irqsave(&subpriv->spinlock, flags);
177	do_event = apci2032_int_start(dev, s, enabled_isns);
178	spin_unlock_irqrestore(&subpriv->spinlock, flags);
179
180	if (do_event)
181		comedi_event(dev, s);
182
183	return 0;
184}
185
186static int apci2032_int_cancel(struct comedi_device *dev,
187			       struct comedi_subdevice *s)
188{
189	struct apci2032_int_private *subpriv = s->private;
190	unsigned long flags;
191
192	spin_lock_irqsave(&subpriv->spinlock, flags);
193	if (subpriv->active)
194		apci2032_int_stop(dev, s);
195	spin_unlock_irqrestore(&subpriv->spinlock, flags);
196
197	return 0;
198}
199
200static irqreturn_t apci2032_interrupt(int irq, void *d)
201{
202	struct comedi_device *dev = d;
203	struct comedi_subdevice *s = dev->read_subdev;
204	struct apci2032_int_private *subpriv;
205	unsigned int val;
206	bool do_event = false;
207
208	if (!dev->attached)
209		return IRQ_NONE;
210
211	/* Check if VCC OR CC interrupt has occurred */
212	val = inl(dev->iobase + APCI2032_STATUS_REG) & APCI2032_STATUS_IRQ;
213	if (!val)
214		return IRQ_NONE;
215
216	subpriv = s->private;
217	spin_lock(&subpriv->spinlock);
218
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);
222	/*
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.
226	 */
227
228	if (subpriv->active && (val & subpriv->enabled_isns) != 0) {
229		unsigned short bits;
230		unsigned int n, len;
231		unsigned int *chanlist;
232
233		/* Bits in scan data correspond to indices in channel list. */
234		bits = 0;
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)
239				bits |= 1U << n;
240
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);
250				}
251			}
252		} else {
253			apci2032_int_stop(dev, s);
254			s->async->events |= COMEDI_CB_OVERFLOW;
255		}
256		do_event = true;
257	}
258
259	spin_unlock(&subpriv->spinlock);
260	if (do_event)
261		comedi_event(dev, s);
262
263	return IRQ_HANDLED;
264}
265
266static int apci2032_reset(struct comedi_device *dev)
267{
268	outl(0x0, dev->iobase + APCI2032_DO_REG);
269	outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
270
271	addi_watchdog_reset(dev->iobase + APCI2032_WDOG_REG);
272
273	return 0;
274}
275
276static int apci2032_auto_attach(struct comedi_device *dev,
277				unsigned long context_unused)
278{
279	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
280	struct comedi_subdevice *s;
281	int ret;
282
283	ret = comedi_pci_enable(dev);
284	if (ret)
285		return ret;
286	dev->iobase = pci_resource_start(pcidev, 1);
287	apci2032_reset(dev);
288
289	if (pcidev->irq > 0) {
290		ret = request_irq(pcidev->irq, apci2032_interrupt,
291				  IRQF_SHARED, dev->board_name, dev);
292		if (ret == 0)
293			dev->irq = pcidev->irq;
294	}
295
296	ret = comedi_alloc_subdevices(dev, 3);
297	if (ret)
298		return ret;
299
300	/* Initialize the digital output subdevice */
301	s = &dev->subdevices[0];
302	s->type		= COMEDI_SUBD_DO;
303	s->subdev_flags	= SDF_WRITEABLE;
304	s->n_chan	= 32;
305	s->maxdata	= 1;
306	s->range_table	= &range_digital;
307	s->insn_bits	= apci2032_do_insn_bits;
308
309	/* Initialize the watchdog subdevice */
310	s = &dev->subdevices[1];
311	ret = addi_watchdog_init(s, dev->iobase + APCI2032_WDOG_REG);
312	if (ret)
313		return ret;
314
315	/* Initialize the interrupt subdevice */
316	s = &dev->subdevices[2];
317	s->type		= COMEDI_SUBD_DI;
318	s->subdev_flags	= SDF_READABLE;
319	s->n_chan	= 2;
320	s->maxdata	= 1;
321	s->range_table	= &range_digital;
322	s->insn_bits	= apci2032_int_insn_bits;
323	if (dev->irq) {
324		struct apci2032_int_private *subpriv;
325
326		dev->read_subdev = s;
327		subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
328		if (!subpriv)
329			return -ENOMEM;
330		spin_lock_init(&subpriv->spinlock);
331		s->private	= subpriv;
332		s->subdev_flags	= SDF_READABLE | SDF_CMD_READ;
333		s->len_chanlist = 2;
334		s->do_cmdtest	= apci2032_int_cmdtest;
335		s->do_cmd	= apci2032_int_cmd;
336		s->cancel	= apci2032_int_cancel;
337	}
338
339	return 0;
340}
341
342static void apci2032_detach(struct comedi_device *dev)
343{
344	if (dev->iobase)
345		apci2032_reset(dev);
346	if (dev->irq)
347		free_irq(dev->irq, dev);
348	if (dev->read_subdev)
349		kfree(dev->read_subdev->private);
350	comedi_pci_disable(dev);
351}
352
353static 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,
358};
359
360static int apci2032_pci_probe(struct pci_dev *dev,
361			      const struct pci_device_id *id)
362{
363	return comedi_pci_auto_config(dev, &apci2032_driver, id->driver_data);
364}
365
366static DEFINE_PCI_DEVICE_TABLE(apci2032_pci_table) = {
367	{ PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1004) },
368	{ 0 }
369};
370MODULE_DEVICE_TABLE(pci, apci2032_pci_table);
371
372static 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,
377};
378module_comedi_pci_driver(apci2032_driver, apci2032_pci_driver);
379
380MODULE_AUTHOR("Comedi http://www.comedi.org");
381MODULE_DESCRIPTION("Comedi low-level driver");
382MODULE_LICENSE("GPL");
383