staging: comedi: drivers: replace SDF_WRITEABLE with SDF_WRITABLE
[deliverable/linux.git] / drivers / staging / comedi / drivers / addi_apci_1564.c
1 /*
2 * addi_apci_1564.c
3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
4 *
5 * ADDI-DATA GmbH
6 * Dieselstrasse 3
7 * D-77833 Ottersweier
8 * Tel: +19(0)7223/9493-0
9 * Fax: +49(0)7223/9493-92
10 * http://www.addi-data.com
11 * info@addi-data.com
12 *
13 * This program is free software; you can redistribute it and/or modify it under
14 * the terms of the GNU General Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at your option) any later
16 * version.
17 *
18 * This program is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 * details.
22 */
23
24 #include <linux/module.h>
25 #include <linux/pci.h>
26 #include <linux/interrupt.h>
27 #include <linux/sched.h>
28
29 #include "../comedidev.h"
30 #include "comedi_fc.h"
31 #include "amcc_s5933.h"
32 #include "addi_watchdog.h"
33
34 struct apci1564_private {
35 unsigned int amcc_iobase; /* base of AMCC I/O registers */
36 unsigned int mode1; /* riding-edge/high level channels */
37 unsigned int mode2; /* falling-edge/low level channels */
38 unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
39 unsigned char timer_select_mode;
40 struct task_struct *tsk_current;
41 };
42
43 #include "addi-data/hwdrv_apci1564.c"
44
45 static int apci1564_reset(struct comedi_device *dev)
46 {
47 struct apci1564_private *devpriv = dev->private;
48
49 /* Disable the input interrupts and reset status register */
50 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
51 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
52 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
53 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
54
55 /* Reset the output channels and disable interrupts */
56 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_REG);
57 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_INT_CTRL_REG);
58
59 /* Reset the watchdog registers */
60 addi_watchdog_reset(devpriv->amcc_iobase + APCI1564_WDOG_REG);
61
62 /* Reset the timer registers */
63 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
64 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_RELOAD_REG);
65
66 /* Reset the counter registers */
67 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(0));
68 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(1));
69 outl(0x0, dev->iobase + APCI1564_COUNTER_CTRL_REG(2));
70
71 return 0;
72 }
73
74 static irqreturn_t apci1564_interrupt(int irq, void *d)
75 {
76 struct comedi_device *dev = d;
77 struct apci1564_private *devpriv = dev->private;
78 struct comedi_subdevice *s = dev->read_subdev;
79 unsigned int status;
80 unsigned int ctrl;
81 unsigned int chan;
82
83 /* check interrupt is from this device */
84 if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
85 INTCSR_INTR_ASSERTED) == 0)
86 return IRQ_NONE;
87
88 status = inl(devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
89 if (status & APCI1564_DI_INT_ENABLE) {
90 /* disable the interrupt */
91 outl(status & APCI1564_DI_INT_DISABLE,
92 devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
93
94 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG)
95 & 0xffff;
96 comedi_buf_write_samples(s, &s->state, 1);
97 comedi_handle_events(dev, s);
98
99 /* enable the interrupt */
100 outl(status, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
101 }
102
103 status = inl(devpriv->amcc_iobase + APCI1564_TIMER_IRQ_REG);
104 if (status & 0x01) {
105 /* Disable Timer Interrupt */
106 ctrl = inl(devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
107 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
108
109 /* Send a signal to from kernel to user space */
110 send_sig(SIGIO, devpriv->tsk_current, 0);
111
112 /* Enable Timer Interrupt */
113 outl(ctrl, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
114 }
115
116 for (chan = 0; chan < 4; chan++) {
117 status = inl(dev->iobase + APCI1564_COUNTER_IRQ_REG(chan));
118 if (status & 0x01) {
119 /* Disable Counter Interrupt */
120 ctrl = inl(dev->iobase +
121 APCI1564_COUNTER_CTRL_REG(chan));
122 outl(0x0, dev->iobase +
123 APCI1564_COUNTER_CTRL_REG(chan));
124
125 /* Send a signal to from kernel to user space */
126 send_sig(SIGIO, devpriv->tsk_current, 0);
127
128 /* Enable Counter Interrupt */
129 outl(ctrl, dev->iobase +
130 APCI1564_COUNTER_CTRL_REG(chan));
131 }
132 }
133
134 return IRQ_HANDLED;
135 }
136
137 static int apci1564_di_insn_bits(struct comedi_device *dev,
138 struct comedi_subdevice *s,
139 struct comedi_insn *insn,
140 unsigned int *data)
141 {
142 struct apci1564_private *devpriv = dev->private;
143
144 data[1] = inl(devpriv->amcc_iobase + APCI1564_DI_REG);
145
146 return insn->n;
147 }
148
149 static int apci1564_do_insn_bits(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
152 unsigned int *data)
153 {
154 struct apci1564_private *devpriv = dev->private;
155
156 s->state = inl(devpriv->amcc_iobase + APCI1564_DO_REG);
157
158 if (comedi_dio_update_state(s, data))
159 outl(s->state, devpriv->amcc_iobase + APCI1564_DO_REG);
160
161 data[1] = s->state;
162
163 return insn->n;
164 }
165
166 static int apci1564_diag_insn_bits(struct comedi_device *dev,
167 struct comedi_subdevice *s,
168 struct comedi_insn *insn,
169 unsigned int *data)
170 {
171 struct apci1564_private *devpriv = dev->private;
172
173 data[1] = inl(devpriv->amcc_iobase + APCI1564_DO_INT_STATUS_REG) & 3;
174
175 return insn->n;
176 }
177
178 /*
179 * Change-Of-State (COS) interrupt configuration
180 *
181 * Channels 0 to 15 are interruptible. These channels can be configured
182 * to generate interrupts based on AND/OR logic for the desired channels.
183 *
184 * OR logic
185 * - reacts to rising or falling edges
186 * - interrupt is generated when any enabled channel
187 * meet the desired interrupt condition
188 *
189 * AND logic
190 * - reacts to changes in level of the selected inputs
191 * - interrupt is generated when all enabled channels
192 * meet the desired interrupt condition
193 * - after an interrupt, a change in level must occur on
194 * the selected inputs to release the IRQ logic
195 *
196 * The COS interrupt must be configured before it can be enabled.
197 *
198 * data[0] : INSN_CONFIG_DIGITAL_TRIG
199 * data[1] : trigger number (= 0)
200 * data[2] : configuration operation:
201 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
202 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
203 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
204 * data[3] : left-shift for data[4] and data[5]
205 * data[4] : rising-edge/high level channels
206 * data[5] : falling-edge/low level channels
207 */
208 static int apci1564_cos_insn_config(struct comedi_device *dev,
209 struct comedi_subdevice *s,
210 struct comedi_insn *insn,
211 unsigned int *data)
212 {
213 struct apci1564_private *devpriv = dev->private;
214 unsigned int shift, oldmask;
215
216 switch (data[0]) {
217 case INSN_CONFIG_DIGITAL_TRIG:
218 if (data[1] != 0)
219 return -EINVAL;
220 shift = data[3];
221 oldmask = (1U << shift) - 1;
222 switch (data[2]) {
223 case COMEDI_DIGITAL_TRIG_DISABLE:
224 devpriv->ctrl = 0;
225 devpriv->mode1 = 0;
226 devpriv->mode2 = 0;
227 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
228 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
229 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
230 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
231 break;
232 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
233 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
234 APCI1564_DI_INT_OR)) {
235 /* switching to 'OR' mode */
236 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
237 APCI1564_DI_INT_OR;
238 /* wipe old channels */
239 devpriv->mode1 = 0;
240 devpriv->mode2 = 0;
241 } else {
242 /* preserve unspecified channels */
243 devpriv->mode1 &= oldmask;
244 devpriv->mode2 &= oldmask;
245 }
246 /* configure specified channels */
247 devpriv->mode1 |= data[4] << shift;
248 devpriv->mode2 |= data[5] << shift;
249 break;
250 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
251 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
252 APCI1564_DI_INT_AND)) {
253 /* switching to 'AND' mode */
254 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
255 APCI1564_DI_INT_AND;
256 /* wipe old channels */
257 devpriv->mode1 = 0;
258 devpriv->mode2 = 0;
259 } else {
260 /* preserve unspecified channels */
261 devpriv->mode1 &= oldmask;
262 devpriv->mode2 &= oldmask;
263 }
264 /* configure specified channels */
265 devpriv->mode1 |= data[4] << shift;
266 devpriv->mode2 |= data[5] << shift;
267 break;
268 default:
269 return -EINVAL;
270 }
271 break;
272 default:
273 return -EINVAL;
274 }
275 return insn->n;
276 }
277
278 static int apci1564_cos_insn_bits(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 struct comedi_insn *insn,
281 unsigned int *data)
282 {
283 data[1] = s->state;
284
285 return 0;
286 }
287
288 static int apci1564_cos_cmdtest(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_cmd *cmd)
291 {
292 int err = 0;
293
294 /* Step 1 : check if triggers are trivially valid */
295
296 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
297 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
298 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
299 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
300 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
301
302 if (err)
303 return 1;
304
305 /* Step 2a : make sure trigger sources are unique */
306 /* Step 2b : and mutually compatible */
307
308 /* Step 3: check if arguments are trivially valid */
309
310 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
311 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
312 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
313 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
314 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
315
316 if (err)
317 return 3;
318
319 /* Step 4: fix up any arguments */
320
321 /* Step 5: check channel list if it exists */
322
323 return 0;
324 }
325
326 /*
327 * Change-Of-State (COS) 'do_cmd' operation
328 *
329 * Enable the COS interrupt as configured by apci1564_cos_insn_config().
330 */
331 static int apci1564_cos_cmd(struct comedi_device *dev,
332 struct comedi_subdevice *s)
333 {
334 struct apci1564_private *devpriv = dev->private;
335
336 if (!devpriv->ctrl) {
337 dev_warn(dev->class_dev,
338 "Interrupts disabled due to mode configuration!\n");
339 return -EINVAL;
340 }
341
342 outl(devpriv->mode1, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
343 outl(devpriv->mode2, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
344 outl(devpriv->ctrl, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
345
346 return 0;
347 }
348
349 static int apci1564_cos_cancel(struct comedi_device *dev,
350 struct comedi_subdevice *s)
351 {
352 struct apci1564_private *devpriv = dev->private;
353
354 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
355 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
356 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
357 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
358
359 return 0;
360 }
361
362 static int apci1564_auto_attach(struct comedi_device *dev,
363 unsigned long context_unused)
364 {
365 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
366 struct apci1564_private *devpriv;
367 struct comedi_subdevice *s;
368 int ret;
369
370 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
371 if (!devpriv)
372 return -ENOMEM;
373
374 ret = comedi_pci_enable(dev);
375 if (ret)
376 return ret;
377
378 dev->iobase = pci_resource_start(pcidev, 1);
379 devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
380
381 apci1564_reset(dev);
382
383 if (pcidev->irq > 0) {
384 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
385 dev->board_name, dev);
386 if (ret == 0)
387 dev->irq = pcidev->irq;
388 }
389
390 ret = comedi_alloc_subdevices(dev, 6);
391 if (ret)
392 return ret;
393
394 /* Allocate and Initialise DI Subdevice Structures */
395 s = &dev->subdevices[0];
396 s->type = COMEDI_SUBD_DI;
397 s->subdev_flags = SDF_READABLE;
398 s->n_chan = 32;
399 s->maxdata = 1;
400 s->range_table = &range_digital;
401 s->insn_bits = apci1564_di_insn_bits;
402
403 /* Allocate and Initialise DO Subdevice Structures */
404 s = &dev->subdevices[1];
405 s->type = COMEDI_SUBD_DO;
406 s->subdev_flags = SDF_WRITABLE;
407 s->n_chan = 32;
408 s->maxdata = 1;
409 s->range_table = &range_digital;
410 s->insn_bits = apci1564_do_insn_bits;
411
412 /* Change-Of-State (COS) interrupt subdevice */
413 s = &dev->subdevices[2];
414 if (dev->irq) {
415 dev->read_subdev = s;
416 s->type = COMEDI_SUBD_DI;
417 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
418 s->n_chan = 1;
419 s->maxdata = 1;
420 s->range_table = &range_digital;
421 s->len_chanlist = 1;
422 s->insn_config = apci1564_cos_insn_config;
423 s->insn_bits = apci1564_cos_insn_bits;
424 s->do_cmdtest = apci1564_cos_cmdtest;
425 s->do_cmd = apci1564_cos_cmd;
426 s->cancel = apci1564_cos_cancel;
427 } else {
428 s->type = COMEDI_SUBD_UNUSED;
429 }
430
431 /* Allocate and Initialise Timer Subdevice Structures */
432 s = &dev->subdevices[3];
433 s->type = COMEDI_SUBD_TIMER;
434 s->subdev_flags = SDF_WRITABLE;
435 s->n_chan = 3;
436 s->maxdata = 0;
437 s->range_table = &range_digital;
438 s->insn_write = apci1564_timer_write;
439 s->insn_read = apci1564_timer_read;
440 s->insn_config = apci1564_timer_config;
441
442 /* Initialize the watchdog subdevice */
443 s = &dev->subdevices[4];
444 ret = addi_watchdog_init(s, devpriv->amcc_iobase + APCI1564_WDOG_REG);
445 if (ret)
446 return ret;
447
448 /* Initialize the diagnostic status subdevice */
449 s = &dev->subdevices[5];
450 s->type = COMEDI_SUBD_DI;
451 s->subdev_flags = SDF_READABLE;
452 s->n_chan = 2;
453 s->maxdata = 1;
454 s->range_table = &range_digital;
455 s->insn_bits = apci1564_diag_insn_bits;
456
457 return 0;
458 }
459
460 static void apci1564_detach(struct comedi_device *dev)
461 {
462 if (dev->iobase)
463 apci1564_reset(dev);
464 comedi_pci_detach(dev);
465 }
466
467 static struct comedi_driver apci1564_driver = {
468 .driver_name = "addi_apci_1564",
469 .module = THIS_MODULE,
470 .auto_attach = apci1564_auto_attach,
471 .detach = apci1564_detach,
472 };
473
474 static int apci1564_pci_probe(struct pci_dev *dev,
475 const struct pci_device_id *id)
476 {
477 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
478 }
479
480 static const struct pci_device_id apci1564_pci_table[] = {
481 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
482 { 0 }
483 };
484 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
485
486 static struct pci_driver apci1564_pci_driver = {
487 .name = "addi_apci_1564",
488 .id_table = apci1564_pci_table,
489 .probe = apci1564_pci_probe,
490 .remove = comedi_pci_auto_unconfig,
491 };
492 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
493
494 MODULE_AUTHOR("Comedi http://www.comedi.org");
495 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
496 MODULE_LICENSE("GPL");
This page took 0.053795 seconds and 5 git commands to generate.