cb5d26a567d390ca3d79fd2c94a090d49a5c5d25
[linux-2.6-block.git] / drivers / staging / comedi / drivers / addi_apci_2032.c
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/module.h>
26 #include <linux/pci.h>
27 #include <linux/interrupt.h>
28
29 #include "../comedidev.h"
30 #include "addi_watchdog.h"
31 #include "comedi_fc.h"
32
33 /*
34  * PCI bar 1 I/O Register map
35  */
36 #define APCI2032_DO_REG                 0x00
37 #define APCI2032_INT_CTRL_REG           0x04
38 #define APCI2032_INT_CTRL_VCC_ENA       (1 << 0)
39 #define APCI2032_INT_CTRL_CC_ENA        (1 << 1)
40 #define APCI2032_INT_STATUS_REG         0x08
41 #define APCI2032_INT_STATUS_VCC         (1 << 0)
42 #define APCI2032_INT_STATUS_CC          (1 << 1)
43 #define APCI2032_STATUS_REG             0x0c
44 #define APCI2032_STATUS_IRQ             (1 << 0)
45 #define APCI2032_WDOG_REG               0x10
46
47 struct apci2032_int_private {
48         spinlock_t spinlock;
49         unsigned int stop_count;
50         bool active;
51         unsigned char enabled_isns;
52 };
53
54 static int apci2032_do_insn_bits(struct comedi_device *dev,
55                                  struct comedi_subdevice *s,
56                                  struct comedi_insn *insn,
57                                  unsigned int *data)
58 {
59         unsigned int mask = data[0];
60         unsigned int bits = data[1];
61
62         s->state = inl(dev->iobase + APCI2032_DO_REG);
63         if (mask) {
64                 s->state &= ~mask;
65                 s->state |= (bits & mask);
66
67                 outl(s->state, dev->iobase + APCI2032_DO_REG);
68         }
69
70         data[1] = s->state;
71
72         return insn->n;
73 }
74
75 static int apci2032_int_insn_bits(struct comedi_device *dev,
76                                   struct comedi_subdevice *s,
77                                   struct comedi_insn *insn,
78                                   unsigned int *data)
79 {
80         data[1] = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
81         return insn->n;
82 }
83
84 static void apci2032_int_stop(struct comedi_device *dev,
85                               struct comedi_subdevice *s)
86 {
87         struct apci2032_int_private *subpriv = s->private;
88
89         subpriv->active = false;
90         subpriv->enabled_isns = 0;
91         outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
92 }
93
94 static bool apci2032_int_start(struct comedi_device *dev,
95                                struct comedi_subdevice *s,
96                                unsigned char enabled_isns)
97 {
98         struct apci2032_int_private *subpriv = s->private;
99         struct comedi_cmd *cmd = &s->async->cmd;
100         bool do_event;
101
102         subpriv->enabled_isns = enabled_isns;
103         subpriv->stop_count = cmd->stop_arg;
104         if (cmd->stop_src == TRIG_COUNT && subpriv->stop_count == 0) {
105                 /* An empty acquisition! */
106                 s->async->events |= COMEDI_CB_EOA;
107                 subpriv->active = false;
108                 do_event = true;
109         } else {
110                 subpriv->active = true;
111                 outl(enabled_isns, dev->iobase + APCI2032_INT_CTRL_REG);
112                 do_event = false;
113         }
114
115         return do_event;
116 }
117
118 static int apci2032_int_cmdtest(struct comedi_device *dev,
119                                 struct comedi_subdevice *s,
120                                 struct comedi_cmd *cmd)
121 {
122         int err = 0;
123
124         /* Step 1 : check if triggers are trivially valid */
125
126         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
127         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
128         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
129         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
130         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
131
132         if (err)
133                 return 1;
134
135         /* Step 2a : make sure trigger sources are unique */
136         err |= cfc_check_trigger_is_unique(cmd->stop_src);
137
138         /* Step 2b : and mutually compatible */
139
140         if (err)
141                 return 2;
142
143         /* Step 3: check if arguments are trivially valid */
144
145         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
146         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
147         err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
148         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
149         if (cmd->stop_src == TRIG_NONE)
150                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
151
152         if (err)
153                 return 3;
154
155         /* step 4: ignored */
156
157         if (err)
158                 return 4;
159
160         return 0;
161 }
162
163 static int apci2032_int_cmd(struct comedi_device *dev,
164                             struct comedi_subdevice *s)
165 {
166         struct comedi_cmd *cmd = &s->async->cmd;
167         struct apci2032_int_private *subpriv = s->private;
168         unsigned char enabled_isns;
169         unsigned int n;
170         unsigned long flags;
171         bool do_event;
172
173         enabled_isns = 0;
174         for (n = 0; n < cmd->chanlist_len; n++)
175                 enabled_isns |= 1 << CR_CHAN(cmd->chanlist[n]);
176
177         spin_lock_irqsave(&subpriv->spinlock, flags);
178         do_event = apci2032_int_start(dev, s, enabled_isns);
179         spin_unlock_irqrestore(&subpriv->spinlock, flags);
180
181         if (do_event)
182                 comedi_event(dev, s);
183
184         return 0;
185 }
186
187 static int apci2032_int_cancel(struct comedi_device *dev,
188                                struct comedi_subdevice *s)
189 {
190         struct apci2032_int_private *subpriv = s->private;
191         unsigned long flags;
192
193         spin_lock_irqsave(&subpriv->spinlock, flags);
194         if (subpriv->active)
195                 apci2032_int_stop(dev, s);
196         spin_unlock_irqrestore(&subpriv->spinlock, flags);
197
198         return 0;
199 }
200
201 static irqreturn_t apci2032_interrupt(int irq, void *d)
202 {
203         struct comedi_device *dev = d;
204         struct comedi_subdevice *s = dev->read_subdev;
205         struct apci2032_int_private *subpriv;
206         unsigned int val;
207         bool do_event = false;
208
209         if (!dev->attached)
210                 return IRQ_NONE;
211
212         /* Check if VCC OR CC interrupt has occurred */
213         val = inl(dev->iobase + APCI2032_STATUS_REG) & APCI2032_STATUS_IRQ;
214         if (!val)
215                 return IRQ_NONE;
216
217         subpriv = s->private;
218         spin_lock(&subpriv->spinlock);
219
220         val = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
221         /* Disable triggered interrupt sources. */
222         outl(~val & 3, dev->iobase + APCI2032_INT_CTRL_REG);
223         /*
224          * Note: We don't reenable the triggered interrupt sources because they
225          * are level-sensitive, hardware error status interrupt sources and
226          * they'd keep triggering interrupts repeatedly.
227          */
228
229         if (subpriv->active && (val & subpriv->enabled_isns) != 0) {
230                 unsigned short bits;
231                 unsigned int n, len;
232                 unsigned int *chanlist;
233
234                 /* Bits in scan data correspond to indices in channel list. */
235                 bits = 0;
236                 len = s->async->cmd.chanlist_len;
237                 chanlist = &s->async->cmd.chanlist[0];
238                 for (n = 0; n < len; n++)
239                         if ((val & (1U << CR_CHAN(chanlist[n]))) != 0)
240                                 bits |= 1U << n;
241
242                 if (comedi_buf_put(s->async, bits)) {
243                         s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
244                         if (s->async->cmd.stop_src == TRIG_COUNT &&
245                             subpriv->stop_count > 0) {
246                                 subpriv->stop_count--;
247                                 if (subpriv->stop_count == 0) {
248                                         /* end of acquisition */
249                                         s->async->events |= COMEDI_CB_EOA;
250                                         apci2032_int_stop(dev, s);
251                                 }
252                         }
253                 } else {
254                         apci2032_int_stop(dev, s);
255                         s->async->events |= COMEDI_CB_OVERFLOW;
256                 }
257                 do_event = true;
258         }
259
260         spin_unlock(&subpriv->spinlock);
261         if (do_event)
262                 comedi_event(dev, s);
263
264         return IRQ_HANDLED;
265 }
266
267 static int apci2032_reset(struct comedi_device *dev)
268 {
269         outl(0x0, dev->iobase + APCI2032_DO_REG);
270         outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
271
272         addi_watchdog_reset(dev->iobase + APCI2032_WDOG_REG);
273
274         return 0;
275 }
276
277 static int apci2032_auto_attach(struct comedi_device *dev,
278                                 unsigned long context_unused)
279 {
280         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
281         struct comedi_subdevice *s;
282         int ret;
283
284         ret = comedi_pci_enable(dev);
285         if (ret)
286                 return ret;
287         dev->iobase = pci_resource_start(pcidev, 1);
288         apci2032_reset(dev);
289
290         if (pcidev->irq > 0) {
291                 ret = request_irq(pcidev->irq, apci2032_interrupt,
292                                   IRQF_SHARED, dev->board_name, dev);
293                 if (ret == 0)
294                         dev->irq = pcidev->irq;
295         }
296
297         ret = comedi_alloc_subdevices(dev, 3);
298         if (ret)
299                 return ret;
300
301         /* Initialize the digital output subdevice */
302         s = &dev->subdevices[0];
303         s->type         = COMEDI_SUBD_DO;
304         s->subdev_flags = SDF_WRITEABLE;
305         s->n_chan       = 32;
306         s->maxdata      = 1;
307         s->range_table  = &range_digital;
308         s->insn_bits    = apci2032_do_insn_bits;
309
310         /* Initialize the watchdog subdevice */
311         s = &dev->subdevices[1];
312         ret = addi_watchdog_init(s, dev->iobase + APCI2032_WDOG_REG);
313         if (ret)
314                 return ret;
315
316         /* Initialize the interrupt subdevice */
317         s = &dev->subdevices[2];
318         s->type         = COMEDI_SUBD_DI;
319         s->subdev_flags = SDF_READABLE;
320         s->n_chan       = 2;
321         s->maxdata      = 1;
322         s->range_table  = &range_digital;
323         s->insn_bits    = apci2032_int_insn_bits;
324         if (dev->irq) {
325                 struct apci2032_int_private *subpriv;
326
327                 dev->read_subdev = s;
328                 subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
329                 if (!subpriv)
330                         return -ENOMEM;
331                 spin_lock_init(&subpriv->spinlock);
332                 s->private      = subpriv;
333                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
334                 s->len_chanlist = 2;
335                 s->do_cmdtest   = apci2032_int_cmdtest;
336                 s->do_cmd       = apci2032_int_cmd;
337                 s->cancel       = apci2032_int_cancel;
338         }
339
340         return 0;
341 }
342
343 static void apci2032_detach(struct comedi_device *dev)
344 {
345         if (dev->iobase)
346                 apci2032_reset(dev);
347         if (dev->irq)
348                 free_irq(dev->irq, dev);
349         if (dev->read_subdev)
350                 kfree(dev->read_subdev->private);
351         comedi_pci_disable(dev);
352 }
353
354 static struct comedi_driver apci2032_driver = {
355         .driver_name    = "addi_apci_2032",
356         .module         = THIS_MODULE,
357         .auto_attach    = apci2032_auto_attach,
358         .detach         = apci2032_detach,
359 };
360
361 static int apci2032_pci_probe(struct pci_dev *dev,
362                               const struct pci_device_id *id)
363 {
364         return comedi_pci_auto_config(dev, &apci2032_driver, id->driver_data);
365 }
366
367 static DEFINE_PCI_DEVICE_TABLE(apci2032_pci_table) = {
368         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1004) },
369         { 0 }
370 };
371 MODULE_DEVICE_TABLE(pci, apci2032_pci_table);
372
373 static struct pci_driver apci2032_pci_driver = {
374         .name           = "addi_apci_2032",
375         .id_table       = apci2032_pci_table,
376         .probe          = apci2032_pci_probe,
377         .remove         = comedi_pci_auto_unconfig,
378 };
379 module_comedi_pci_driver(apci2032_driver, apci2032_pci_driver);
380
381 MODULE_AUTHOR("Comedi http://www.comedi.org");
382 MODULE_DESCRIPTION("Comedi low-level driver");
383 MODULE_LICENSE("GPL");