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