1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64#include <linux/module.h>
65#include <linux/interrupt.h>
66
67#include "../comedi_pci.h"
68#include "amcc_s5933.h"
69
70
71
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
83struct apci1032_private {
84 unsigned long amcc_iobase;
85 unsigned int mode1;
86 unsigned int mode2;
87 unsigned int ctrl;
88};
89
90static int apci1032_reset(struct comedi_device *dev)
91{
92
93 outl(0x0, dev->iobase + APCI1032_CTRL_REG);
94
95 inl(dev->iobase + APCI1032_STATUS_REG);
96
97 outl(0x0, dev->iobase + APCI1032_MODE1_REG);
98 outl(0x0, dev->iobase + APCI1032_MODE2_REG);
99
100 return 0;
101}
102
103static 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
136 devpriv->ctrl = APCI1032_CTRL_INT_ENA |
137 APCI1032_CTRL_INT_OR;
138
139 devpriv->mode1 = 0;
140 devpriv->mode2 = 0;
141 } else {
142
143 devpriv->mode1 &= oldmask;
144 devpriv->mode2 &= oldmask;
145 }
146
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
154 devpriv->ctrl = APCI1032_CTRL_INT_ENA |
155 APCI1032_CTRL_INT_AND;
156
157 devpriv->mode1 = 0;
158 devpriv->mode2 = 0;
159 } else {
160
161 devpriv->mode1 &= oldmask;
162 devpriv->mode2 &= oldmask;
163 }
164
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
179static 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
189static 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
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
207
208
209
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
222
223
224
225 return 0;
226}
227
228
229
230
231
232
233static 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
251static int apci1032_cos_cancel(struct comedi_device *dev,
252 struct comedi_subdevice *s)
253{
254 return apci1032_reset(dev);
255}
256
257static 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
266 if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
267 INTCSR_INTR_ASSERTED) == 0)
268 return IRQ_NONE;
269
270
271 ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
272 if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
273 return IRQ_HANDLED;
274
275
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
284 outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
285
286 return IRQ_HANDLED;
287}
288
289static 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
299static 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
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
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
360static void apci1032_detach(struct comedi_device *dev)
361{
362 if (dev->iobase)
363 apci1032_reset(dev);
364 comedi_pci_detach(dev);
365}
366
367static 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
374static 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
380static const struct pci_device_id apci1032_pci_table[] = {
381 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
382 { 0 }
383};
384MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
385
386static 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};
392module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
393
394MODULE_AUTHOR("Comedi https://www.comedi.org");
395MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
396MODULE_LICENSE("GPL");
397