1/*
2 * addi_apci_1032.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/module.h>
26#include <linux/pci.h>
27#include <linux/interrupt.h>
28
29#include "../comedidev.h"
30#include "comedi_fc.h"
31#include "amcc_s5933.h"
32
33/*
34 * I/O Register Map
35 */
36#define APCI1032_DI_REG			0x00
37#define APCI1032_MODE1_REG		0x04
38#define APCI1032_MODE2_REG		0x08
39#define APCI1032_STATUS_REG		0x0c
40#define APCI1032_CTRL_REG		0x10
41#define APCI1032_CTRL_INT_OR		(0 << 1)
42#define APCI1032_CTRL_INT_AND		(1 << 1)
43#define APCI1032_CTRL_INT_ENA		(1 << 2)
44
45struct apci1032_private {
46	unsigned long amcc_iobase;	/* base of AMCC I/O registers */
47	unsigned int mode1;	/* rising-edge/high level channels */
48	unsigned int mode2;	/* falling-edge/low level channels */
49	unsigned int ctrl;	/* interrupt mode OR (edge) . AND (level) */
50};
51
52static int apci1032_reset(struct comedi_device *dev)
53{
54	/* disable the interrupts */
55	outl(0x0, dev->iobase + APCI1032_CTRL_REG);
56	/* Reset the interrupt status register */
57	inl(dev->iobase + APCI1032_STATUS_REG);
58	/* Disable the and/or interrupt */
59	outl(0x0, dev->iobase + APCI1032_MODE1_REG);
60	outl(0x0, dev->iobase + APCI1032_MODE2_REG);
61
62	return 0;
63}
64
65/*
66 * Change-Of-State (COS) interrupt configuration
67 *
68 * Channels 0 to 15 are interruptible. These channels can be configured
69 * to generate interrupts based on AND/OR logic for the desired channels.
70 *
71 *	OR logic
72 *		- reacts to rising or falling edges
73 *		- interrupt is generated when any enabled channel
74 *		  meet the desired interrupt condition
75 *
76 *	AND logic
77 *		- reacts to changes in level of the selected inputs
78 *		- interrupt is generated when all enabled channels
79 *		  meet the desired interrupt condition
80 *		- after an interrupt, a change in level must occur on
81 *		  the selected inputs to release the IRQ logic
82 *
83 * The COS interrupt must be configured before it can be enabled.
84 *
85 *	data[0] : INSN_CONFIG_DIGITAL_TRIG
86 *	data[1] : trigger number (= 0)
87 *	data[2] : configuration operation:
88 *	          COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
89 *	          COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
90 *	          COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
91 *	data[3] : left-shift for data[4] and data[5]
92 *	data[4] : rising-edge/high level channels
93 *	data[5] : falling-edge/low level channels
94 */
95static int apci1032_cos_insn_config(struct comedi_device *dev,
96				    struct comedi_subdevice *s,
97				    struct comedi_insn *insn,
98				    unsigned int *data)
99{
100	struct apci1032_private *devpriv = dev->private;
101	unsigned int shift, oldmask;
102
103	switch (data[0]) {
104	case INSN_CONFIG_DIGITAL_TRIG:
105		if (data[1] != 0)
106			return -EINVAL;
107		shift = data[3];
108		oldmask = (1U << shift) - 1;
109		switch (data[2]) {
110		case COMEDI_DIGITAL_TRIG_DISABLE:
111			devpriv->ctrl = 0;
112			devpriv->mode1 = 0;
113			devpriv->mode2 = 0;
114			apci1032_reset(dev);
115			break;
116		case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
117			if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
118					      APCI1032_CTRL_INT_OR)) {
119				/* switching to 'OR' mode */
120				devpriv->ctrl = APCI1032_CTRL_INT_ENA |
121						APCI1032_CTRL_INT_OR;
122				/* wipe old channels */
123				devpriv->mode1 = 0;
124				devpriv->mode2 = 0;
125			} else {
126				/* preserve unspecified channels */
127				devpriv->mode1 &= oldmask;
128				devpriv->mode2 &= oldmask;
129			}
130			/* configure specified channels */
131			devpriv->mode1 |= data[4] << shift;
132			devpriv->mode2 |= data[5] << shift;
133			break;
134		case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
135			if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
136					      APCI1032_CTRL_INT_AND)) {
137				/* switching to 'AND' mode */
138				devpriv->ctrl = APCI1032_CTRL_INT_ENA |
139						APCI1032_CTRL_INT_AND;
140				/* wipe old channels */
141				devpriv->mode1 = 0;
142				devpriv->mode2 = 0;
143			} else {
144				/* preserve unspecified channels */
145				devpriv->mode1 &= oldmask;
146				devpriv->mode2 &= oldmask;
147			}
148			/* configure specified channels */
149			devpriv->mode1 |= data[4] << shift;
150			devpriv->mode2 |= data[5] << shift;
151			break;
152		default:
153			return -EINVAL;
154		}
155		break;
156	default:
157		return -EINVAL;
158	}
159
160	return insn->n;
161}
162
163static int apci1032_cos_insn_bits(struct comedi_device *dev,
164				  struct comedi_subdevice *s,
165				  struct comedi_insn *insn,
166				  unsigned int *data)
167{
168	data[1] = s->state;
169
170	return 0;
171}
172
173static int apci1032_cos_cmdtest(struct comedi_device *dev,
174				struct comedi_subdevice *s,
175				struct comedi_cmd *cmd)
176{
177	int err = 0;
178
179	/* Step 1 : check if triggers are trivially valid */
180
181	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
182	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
183	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
184	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
185	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
186
187	if (err)
188		return 1;
189
190	/* Step 2a : make sure trigger sources are unique */
191	/* Step 2b : and mutually compatible */
192
193	/* Step 3: check if arguments are trivially valid */
194
195	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
196	err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
197	err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
198	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
199	err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
200
201	if (err)
202		return 3;
203
204	/* Step 4: fix up any arguments */
205
206	/* Step 5: check channel list if it exists */
207
208	return 0;
209}
210
211/*
212 * Change-Of-State (COS) 'do_cmd' operation
213 *
214 * Enable the COS interrupt as configured by apci1032_cos_insn_config().
215 */
216static int apci1032_cos_cmd(struct comedi_device *dev,
217			    struct comedi_subdevice *s)
218{
219	struct apci1032_private *devpriv = dev->private;
220
221	if (!devpriv->ctrl) {
222		dev_warn(dev->class_dev,
223			"Interrupts disabled due to mode configuration!\n");
224		return -EINVAL;
225	}
226
227	outl(devpriv->mode1, dev->iobase + APCI1032_MODE1_REG);
228	outl(devpriv->mode2, dev->iobase + APCI1032_MODE2_REG);
229	outl(devpriv->ctrl, dev->iobase + APCI1032_CTRL_REG);
230
231	return 0;
232}
233
234static int apci1032_cos_cancel(struct comedi_device *dev,
235			       struct comedi_subdevice *s)
236{
237	return apci1032_reset(dev);
238}
239
240static irqreturn_t apci1032_interrupt(int irq, void *d)
241{
242	struct comedi_device *dev = d;
243	struct apci1032_private *devpriv = dev->private;
244	struct comedi_subdevice *s = dev->read_subdev;
245	unsigned int ctrl;
246
247	/* check interrupt is from this device */
248	if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
249	     INTCSR_INTR_ASSERTED) == 0)
250		return IRQ_NONE;
251
252	/* check interrupt is enabled */
253	ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
254	if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
255		return IRQ_HANDLED;
256
257	/* disable the interrupt */
258	outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
259
260	s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
261	comedi_buf_put(s, s->state);
262	s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
263	comedi_event(dev, s);
264
265	/* enable the interrupt */
266	outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
267
268	return IRQ_HANDLED;
269}
270
271static int apci1032_di_insn_bits(struct comedi_device *dev,
272				 struct comedi_subdevice *s,
273				 struct comedi_insn *insn,
274				 unsigned int *data)
275{
276	data[1] = inl(dev->iobase + APCI1032_DI_REG);
277
278	return insn->n;
279}
280
281static int apci1032_auto_attach(struct comedi_device *dev,
282					  unsigned long context_unused)
283{
284	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
285	struct apci1032_private *devpriv;
286	struct comedi_subdevice *s;
287	int ret;
288
289	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
290	if (!devpriv)
291		return -ENOMEM;
292
293	ret = comedi_pci_enable(dev);
294	if (ret)
295		return ret;
296
297	devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
298	dev->iobase = pci_resource_start(pcidev, 1);
299	apci1032_reset(dev);
300	if (pcidev->irq > 0) {
301		ret = request_irq(pcidev->irq, apci1032_interrupt, IRQF_SHARED,
302				  dev->board_name, dev);
303		if (ret == 0)
304			dev->irq = pcidev->irq;
305	}
306
307	ret = comedi_alloc_subdevices(dev, 2);
308	if (ret)
309		return ret;
310
311	/*  Allocate and Initialise DI Subdevice Structures */
312	s = &dev->subdevices[0];
313	s->type		= COMEDI_SUBD_DI;
314	s->subdev_flags	= SDF_READABLE;
315	s->n_chan	= 32;
316	s->maxdata	= 1;
317	s->range_table	= &range_digital;
318	s->insn_bits	= apci1032_di_insn_bits;
319
320	/* Change-Of-State (COS) interrupt subdevice */
321	s = &dev->subdevices[1];
322	if (dev->irq) {
323		dev->read_subdev = s;
324		s->type		= COMEDI_SUBD_DI;
325		s->subdev_flags	= SDF_READABLE | SDF_CMD_READ;
326		s->n_chan	= 1;
327		s->maxdata	= 1;
328		s->range_table	= &range_digital;
329		s->insn_config	= apci1032_cos_insn_config;
330		s->insn_bits	= apci1032_cos_insn_bits;
331		s->len_chanlist	= 1;
332		s->do_cmdtest	= apci1032_cos_cmdtest;
333		s->do_cmd	= apci1032_cos_cmd;
334		s->cancel	= apci1032_cos_cancel;
335	} else {
336		s->type		= COMEDI_SUBD_UNUSED;
337	}
338
339	return 0;
340}
341
342static void apci1032_detach(struct comedi_device *dev)
343{
344	if (dev->iobase)
345		apci1032_reset(dev);
346	comedi_pci_detach(dev);
347}
348
349static struct comedi_driver apci1032_driver = {
350	.driver_name	= "addi_apci_1032",
351	.module		= THIS_MODULE,
352	.auto_attach	= apci1032_auto_attach,
353	.detach		= apci1032_detach,
354};
355
356static int apci1032_pci_probe(struct pci_dev *dev,
357			      const struct pci_device_id *id)
358{
359	return comedi_pci_auto_config(dev, &apci1032_driver, id->driver_data);
360}
361
362static const struct pci_device_id apci1032_pci_table[] = {
363	{ PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
364	{ 0 }
365};
366MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
367
368static struct pci_driver apci1032_pci_driver = {
369	.name		= "addi_apci_1032",
370	.id_table	= apci1032_pci_table,
371	.probe		= apci1032_pci_probe,
372	.remove		= comedi_pci_auto_unconfig,
373};
374module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
375
376MODULE_AUTHOR("Comedi http://www.comedi.org");
377MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
378MODULE_LICENSE("GPL");
379