Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / staging / comedi / drivers / addi_apci_1564.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * addi_apci_1564.c
4  * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
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
15 /*
16  * Driver: addi_apci_1564
17  * Description: ADDI-DATA APCI-1564 Digital I/O board
18  * Devices: [ADDI-DATA] APCI-1564 (addi_apci_1564)
19  * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
20  * Updated: Thu, 02 Jun 2016 13:12:46 -0700
21  * Status: untested
22  *
23  * Configuration Options: not applicable, uses comedi PCI auto config
24  *
25  * This board has the following features:
26  *   - 32 optically isolated digital inputs (24V), 16 of which can
27  *     generate change-of-state (COS) interrupts (channels 4 to 19)
28  *   - 32 optically isolated digital outputs (10V to 36V)
29  *   - 1 8-bit watchdog for resetting the outputs
30  *   - 1 12-bit timer
31  *   - 3 32-bit counters
32  *   - 2 diagnostic inputs
33  *
34  * The COS, timer, and counter subdevices all use the dev->read_subdev to
35  * return the interrupt status. The sample data is updated and returned when
36  * any of these subdevices generate an interrupt. The sample data format is:
37  *
38  *    Bit   Description
39  *   -----  ------------------------------------------
40  *    31    COS interrupt
41  *    30    timer interrupt
42  *    29    counter 2 interrupt
43  *    28    counter 1 interrupt
44  *    27    counter 0 interrupt
45  *   26:20  not used
46  *   19:4   COS digital input state (channels 19 to 4)
47  *    3:0   not used
48  *
49  * The COS interrupts must be configured using an INSN_CONFIG_DIGITAL_TRIG
50  * instruction before they can be enabled by an async command. The COS
51  * interrupts will stay active until canceled.
52  *
53  * The timer subdevice does not use an async command. All control is handled
54  * by the (*insn_config).
55  *
56  * FIXME: The format of the ADDI_TCW_TIMEBASE_REG is not descibed in the
57  * datasheet I have. The INSN_CONFIG_SET_CLOCK_SRC currently just writes
58  * the raw data[1] to this register along with the raw data[2] value to the
59  * ADDI_TCW_RELOAD_REG. If anyone tests this and can determine the actual
60  * timebase/reload operation please let me know.
61  *
62  * The counter subdevice also does not use an async command. All control is
63  * handled by the (*insn_config).
64  *
65  * FIXME: The operation of the counters is not really described in the
66  * datasheet I have. The (*insn_config) needs more work.
67  */
68
69 #include <linux/module.h>
70 #include <linux/interrupt.h>
71
72 #include "../comedi_pci.h"
73 #include "addi_tcw.h"
74 #include "addi_watchdog.h"
75
76 /*
77  * PCI BAR 0
78  *
79  * PLD Revision 1.0 I/O Mapping
80  *   0x00         93C76 EEPROM
81  *   0x04 - 0x18  Timer 12-Bit
82  *
83  * PLD Revision 2.x I/O Mapping
84  *   0x00         93C76 EEPROM
85  *   0x04 - 0x14  Digital Input
86  *   0x18 - 0x25  Digital Output
87  *   0x28 - 0x44  Watchdog 8-Bit
88  *   0x48 - 0x64  Timer 12-Bit
89  */
90 #define APCI1564_EEPROM_REG                     0x00
91 #define APCI1564_EEPROM_VCC_STATUS              BIT(8)
92 #define APCI1564_EEPROM_TO_REV(x)               (((x) >> 4) & 0xf)
93 #define APCI1564_EEPROM_DI                      BIT(3)
94 #define APCI1564_EEPROM_DO                      BIT(2)
95 #define APCI1564_EEPROM_CS                      BIT(1)
96 #define APCI1564_EEPROM_CLK                     BIT(0)
97 #define APCI1564_REV1_TIMER_IOBASE              0x04
98 #define APCI1564_REV2_MAIN_IOBASE               0x04
99 #define APCI1564_REV2_TIMER_IOBASE              0x48
100
101 /*
102  * PCI BAR 1
103  *
104  * PLD Revision 1.0 I/O Mapping
105  *   0x00 - 0x10  Digital Input
106  *   0x14 - 0x20  Digital Output
107  *   0x24 - 0x3c  Watchdog 8-Bit
108  *
109  * PLD Revision 2.x I/O Mapping
110  *   0x00         Counter_0
111  *   0x20         Counter_1
112  *   0x30         Counter_3
113  */
114 #define APCI1564_REV1_MAIN_IOBASE               0x00
115
116 /*
117  * dev->iobase Register Map
118  *   PLD Revision 1.0 - PCI BAR 1 + 0x00
119  *   PLD Revision 2.x - PCI BAR 0 + 0x04
120  */
121 #define APCI1564_DI_REG                         0x00
122 #define APCI1564_DI_INT_MODE1_REG               0x04
123 #define APCI1564_DI_INT_MODE2_REG               0x08
124 #define APCI1564_DI_INT_MODE_MASK               0x000ffff0 /* chans [19:4] */
125 #define APCI1564_DI_INT_STATUS_REG              0x0c
126 #define APCI1564_DI_IRQ_REG                     0x10
127 #define APCI1564_DI_IRQ_ENA                     BIT(2)
128 #define APCI1564_DI_IRQ_MODE                    BIT(1)  /* 1=AND, 0=OR */
129 #define APCI1564_DO_REG                         0x14
130 #define APCI1564_DO_INT_CTRL_REG                0x18
131 #define APCI1564_DO_INT_CTRL_CC_INT_ENA         BIT(1)
132 #define APCI1564_DO_INT_CTRL_VCC_INT_ENA        BIT(0)
133 #define APCI1564_DO_INT_STATUS_REG              0x1c
134 #define APCI1564_DO_INT_STATUS_CC               BIT(1)
135 #define APCI1564_DO_INT_STATUS_VCC              BIT(0)
136 #define APCI1564_DO_IRQ_REG                     0x20
137 #define APCI1564_DO_IRQ_INTR                    BIT(0)
138 #define APCI1564_WDOG_IOBASE                    0x24
139
140 /*
141  * devpriv->timer Register Map (see addi_tcw.h for register/bit defines)
142  *   PLD Revision 1.0 - PCI BAR 0 + 0x04
143  *   PLD Revision 2.x - PCI BAR 0 + 0x48
144  */
145
146 /*
147  * devpriv->counters Register Map (see addi_tcw.h for register/bit defines)
148  *   PLD Revision 2.x - PCI BAR 1 + 0x00
149  */
150 #define APCI1564_COUNTER(x)                     ((x) * 0x20)
151
152 /*
153  * The dev->read_subdev is used to return the interrupt events along with
154  * the state of the interrupt capable inputs.
155  */
156 #define APCI1564_EVENT_COS                      BIT(31)
157 #define APCI1564_EVENT_TIMER                    BIT(30)
158 #define APCI1564_EVENT_COUNTER(x)               BIT(27 + (x)) /* counter 0-2 */
159 #define APCI1564_EVENT_MASK                     0xfff0000f /* all but [19:4] */
160
161 struct apci1564_private {
162         unsigned long eeprom;   /* base address of EEPROM register */
163         unsigned long timer;    /* base address of 12-bit timer */
164         unsigned long counters; /* base address of 32-bit counters */
165         unsigned int mode1;     /* rising-edge/high level channels */
166         unsigned int mode2;     /* falling-edge/low level channels */
167         unsigned int ctrl;      /* interrupt mode OR (edge) . AND (level) */
168 };
169
170 static int apci1564_reset(struct comedi_device *dev)
171 {
172         struct apci1564_private *devpriv = dev->private;
173
174         /* Disable the input interrupts and reset status register */
175         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
176         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
177         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
178         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
179
180         /* Reset the output channels and disable interrupts */
181         outl(0x0, dev->iobase + APCI1564_DO_REG);
182         outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
183
184         /* Reset the watchdog registers */
185         addi_watchdog_reset(dev->iobase + APCI1564_WDOG_IOBASE);
186
187         /* Reset the timer registers */
188         outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
189         outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
190
191         if (devpriv->counters) {
192                 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
193
194                 /* Reset the counter registers */
195                 outl(0x0, iobase + APCI1564_COUNTER(0));
196                 outl(0x0, iobase + APCI1564_COUNTER(1));
197                 outl(0x0, iobase + APCI1564_COUNTER(2));
198         }
199
200         return 0;
201 }
202
203 static irqreturn_t apci1564_interrupt(int irq, void *d)
204 {
205         struct comedi_device *dev = d;
206         struct apci1564_private *devpriv = dev->private;
207         struct comedi_subdevice *s = dev->read_subdev;
208         unsigned int status;
209         unsigned int ctrl;
210         unsigned int chan;
211
212         s->state &= ~APCI1564_EVENT_MASK;
213
214         status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
215         if (status & APCI1564_DI_IRQ_ENA) {
216                 /* get the COS interrupt state and set the event flag */
217                 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
218                 s->state &= APCI1564_DI_INT_MODE_MASK;
219                 s->state |= APCI1564_EVENT_COS;
220
221                 /* clear the interrupt */
222                 outl(status & ~APCI1564_DI_IRQ_ENA,
223                      dev->iobase + APCI1564_DI_IRQ_REG);
224                 outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
225         }
226
227         status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
228         if (status & ADDI_TCW_IRQ) {
229                 s->state |= APCI1564_EVENT_TIMER;
230
231                 /* clear the interrupt */
232                 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
233                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
234                 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
235         }
236
237         if (devpriv->counters) {
238                 for (chan = 0; chan < 3; chan++) {
239                         unsigned long iobase;
240
241                         iobase = devpriv->counters + APCI1564_COUNTER(chan);
242
243                         status = inl(iobase + ADDI_TCW_IRQ_REG);
244                         if (status & ADDI_TCW_IRQ) {
245                                 s->state |= APCI1564_EVENT_COUNTER(chan);
246
247                                 /* clear the interrupt */
248                                 ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
249                                 outl(0x0, iobase + ADDI_TCW_CTRL_REG);
250                                 outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
251                         }
252                 }
253         }
254
255         if (s->state & APCI1564_EVENT_MASK) {
256                 comedi_buf_write_samples(s, &s->state, 1);
257                 comedi_handle_events(dev, s);
258         }
259
260         return IRQ_HANDLED;
261 }
262
263 static int apci1564_di_insn_bits(struct comedi_device *dev,
264                                  struct comedi_subdevice *s,
265                                  struct comedi_insn *insn,
266                                  unsigned int *data)
267 {
268         data[1] = inl(dev->iobase + APCI1564_DI_REG);
269
270         return insn->n;
271 }
272
273 static int apci1564_do_insn_bits(struct comedi_device *dev,
274                                  struct comedi_subdevice *s,
275                                  struct comedi_insn *insn,
276                                  unsigned int *data)
277 {
278         s->state = inl(dev->iobase + APCI1564_DO_REG);
279
280         if (comedi_dio_update_state(s, data))
281                 outl(s->state, dev->iobase + APCI1564_DO_REG);
282
283         data[1] = s->state;
284
285         return insn->n;
286 }
287
288 static int apci1564_diag_insn_bits(struct comedi_device *dev,
289                                    struct comedi_subdevice *s,
290                                    struct comedi_insn *insn,
291                                    unsigned int *data)
292 {
293         data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
294
295         return insn->n;
296 }
297
298 /*
299  * Change-Of-State (COS) interrupt configuration
300  *
301  * Channels 4 to 19 are interruptible. These channels can be configured
302  * to generate interrupts based on AND/OR logic for the desired channels.
303  *
304  *      OR logic
305  *              - reacts to rising or falling edges
306  *              - interrupt is generated when any enabled channel
307  *                meet the desired interrupt condition
308  *
309  *      AND logic
310  *              - reacts to changes in level of the selected inputs
311  *              - interrupt is generated when all enabled channels
312  *                meet the desired interrupt condition
313  *              - after an interrupt, a change in level must occur on
314  *                the selected inputs to release the IRQ logic
315  *
316  * The COS interrupt must be configured before it can be enabled.
317  *
318  *      data[0] : INSN_CONFIG_DIGITAL_TRIG
319  *      data[1] : trigger number (= 0)
320  *      data[2] : configuration operation:
321  *                COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
322  *                COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
323  *                COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
324  *      data[3] : left-shift for data[4] and data[5]
325  *      data[4] : rising-edge/high level channels
326  *      data[5] : falling-edge/low level channels
327  */
328 static int apci1564_cos_insn_config(struct comedi_device *dev,
329                                     struct comedi_subdevice *s,
330                                     struct comedi_insn *insn,
331                                     unsigned int *data)
332 {
333         struct apci1564_private *devpriv = dev->private;
334         unsigned int shift, oldmask;
335
336         switch (data[0]) {
337         case INSN_CONFIG_DIGITAL_TRIG:
338                 if (data[1] != 0)
339                         return -EINVAL;
340                 shift = data[3];
341                 oldmask = (1U << shift) - 1;
342                 switch (data[2]) {
343                 case COMEDI_DIGITAL_TRIG_DISABLE:
344                         devpriv->ctrl = 0;
345                         devpriv->mode1 = 0;
346                         devpriv->mode2 = 0;
347                         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
348                         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
349                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
350                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
351                         break;
352                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
353                         if (devpriv->ctrl != APCI1564_DI_IRQ_ENA) {
354                                 /* switching to 'OR' mode */
355                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA;
356                                 /* wipe old channels */
357                                 devpriv->mode1 = 0;
358                                 devpriv->mode2 = 0;
359                         } else {
360                                 /* preserve unspecified channels */
361                                 devpriv->mode1 &= oldmask;
362                                 devpriv->mode2 &= oldmask;
363                         }
364                         /* configure specified channels */
365                         devpriv->mode1 |= data[4] << shift;
366                         devpriv->mode2 |= data[5] << shift;
367                         break;
368                 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
369                         if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA |
370                                               APCI1564_DI_IRQ_MODE)) {
371                                 /* switching to 'AND' mode */
372                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA |
373                                                 APCI1564_DI_IRQ_MODE;
374                                 /* wipe old channels */
375                                 devpriv->mode1 = 0;
376                                 devpriv->mode2 = 0;
377                         } else {
378                                 /* preserve unspecified channels */
379                                 devpriv->mode1 &= oldmask;
380                                 devpriv->mode2 &= oldmask;
381                         }
382                         /* configure specified channels */
383                         devpriv->mode1 |= data[4] << shift;
384                         devpriv->mode2 |= data[5] << shift;
385                         break;
386                 default:
387                         return -EINVAL;
388                 }
389
390                 /* ensure the mode bits are in-range for channels [19:4] */
391                 devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK;
392                 devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK;
393                 break;
394         default:
395                 return -EINVAL;
396         }
397         return insn->n;
398 }
399
400 static int apci1564_cos_insn_bits(struct comedi_device *dev,
401                                   struct comedi_subdevice *s,
402                                   struct comedi_insn *insn,
403                                   unsigned int *data)
404 {
405         data[1] = s->state;
406
407         return 0;
408 }
409
410 static int apci1564_cos_cmdtest(struct comedi_device *dev,
411                                 struct comedi_subdevice *s,
412                                 struct comedi_cmd *cmd)
413 {
414         int err = 0;
415
416         /* Step 1 : check if triggers are trivially valid */
417
418         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
419         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
420         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
421         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
422         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
423
424         if (err)
425                 return 1;
426
427         /* Step 2a : make sure trigger sources are unique */
428         /* Step 2b : and mutually compatible */
429
430         /* Step 3: check if arguments are trivially valid */
431
432         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
433         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
434         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
435         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
436                                            cmd->chanlist_len);
437         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
438
439         if (err)
440                 return 3;
441
442         /* Step 4: fix up any arguments */
443
444         /* Step 5: check channel list if it exists */
445
446         return 0;
447 }
448
449 /*
450  * Change-Of-State (COS) 'do_cmd' operation
451  *
452  * Enable the COS interrupt as configured by apci1564_cos_insn_config().
453  */
454 static int apci1564_cos_cmd(struct comedi_device *dev,
455                             struct comedi_subdevice *s)
456 {
457         struct apci1564_private *devpriv = dev->private;
458
459         if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) {
460                 dev_warn(dev->class_dev,
461                          "Interrupts disabled due to mode configuration!\n");
462                 return -EINVAL;
463         }
464
465         outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
466         outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
467         outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
468
469         return 0;
470 }
471
472 static int apci1564_cos_cancel(struct comedi_device *dev,
473                                struct comedi_subdevice *s)
474 {
475         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
476         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
477         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
478         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
479
480         return 0;
481 }
482
483 static int apci1564_timer_insn_config(struct comedi_device *dev,
484                                       struct comedi_subdevice *s,
485                                       struct comedi_insn *insn,
486                                       unsigned int *data)
487 {
488         struct apci1564_private *devpriv = dev->private;
489         unsigned int val;
490
491         switch (data[0]) {
492         case INSN_CONFIG_ARM:
493                 if (data[1] > s->maxdata)
494                         return -EINVAL;
495                 outl(data[1], devpriv->timer + ADDI_TCW_RELOAD_REG);
496                 outl(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_TIMER_ENA,
497                      devpriv->timer + ADDI_TCW_CTRL_REG);
498                 break;
499         case INSN_CONFIG_DISARM:
500                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
501                 break;
502         case INSN_CONFIG_GET_COUNTER_STATUS:
503                 data[1] = 0;
504                 val = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
505                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
506                         data[1] |= COMEDI_COUNTER_ARMED;
507                 if (val & ADDI_TCW_CTRL_TIMER_ENA)
508                         data[1] |= COMEDI_COUNTER_COUNTING;
509                 val = inl(devpriv->timer + ADDI_TCW_STATUS_REG);
510                 if (val & ADDI_TCW_STATUS_OVERFLOW)
511                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
512                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
513                           COMEDI_COUNTER_TERMINAL_COUNT;
514                 break;
515         case INSN_CONFIG_SET_CLOCK_SRC:
516                 if (data[2] > s->maxdata)
517                         return -EINVAL;
518                 outl(data[1], devpriv->timer + ADDI_TCW_TIMEBASE_REG);
519                 outl(data[2], devpriv->timer + ADDI_TCW_RELOAD_REG);
520                 break;
521         case INSN_CONFIG_GET_CLOCK_SRC:
522                 data[1] = inl(devpriv->timer + ADDI_TCW_TIMEBASE_REG);
523                 data[2] = inl(devpriv->timer + ADDI_TCW_RELOAD_REG);
524                 break;
525         default:
526                 return -EINVAL;
527         }
528
529         return insn->n;
530 }
531
532 static int apci1564_timer_insn_write(struct comedi_device *dev,
533                                      struct comedi_subdevice *s,
534                                      struct comedi_insn *insn,
535                                      unsigned int *data)
536 {
537         struct apci1564_private *devpriv = dev->private;
538
539         /* just write the last last to the reload register */
540         if (insn->n) {
541                 unsigned int val = data[insn->n - 1];
542
543                 outl(val, devpriv->timer + ADDI_TCW_RELOAD_REG);
544         }
545
546         return insn->n;
547 }
548
549 static int apci1564_timer_insn_read(struct comedi_device *dev,
550                                     struct comedi_subdevice *s,
551                                     struct comedi_insn *insn,
552                                     unsigned int *data)
553 {
554         struct apci1564_private *devpriv = dev->private;
555         int i;
556
557         /* return the actual value of the timer */
558         for (i = 0; i < insn->n; i++)
559                 data[i] = inl(devpriv->timer + ADDI_TCW_VAL_REG);
560
561         return insn->n;
562 }
563
564 static int apci1564_counter_insn_config(struct comedi_device *dev,
565                                         struct comedi_subdevice *s,
566                                         struct comedi_insn *insn,
567                                         unsigned int *data)
568 {
569         struct apci1564_private *devpriv = dev->private;
570         unsigned int chan = CR_CHAN(insn->chanspec);
571         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
572         unsigned int val;
573
574         switch (data[0]) {
575         case INSN_CONFIG_ARM:
576                 val = inl(iobase + ADDI_TCW_CTRL_REG);
577                 val |= ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA;
578                 outl(data[1], iobase + ADDI_TCW_RELOAD_REG);
579                 outl(val, iobase + ADDI_TCW_CTRL_REG);
580                 break;
581         case INSN_CONFIG_DISARM:
582                 val = inl(iobase + ADDI_TCW_CTRL_REG);
583                 val &= ~(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA);
584                 outl(val, iobase + ADDI_TCW_CTRL_REG);
585                 break;
586         case INSN_CONFIG_SET_COUNTER_MODE:
587                 /*
588                  * FIXME: The counter operation is not described in the
589                  * datasheet. For now just write the raw data[1] value to
590                  * the control register.
591                  */
592                 outl(data[1], iobase + ADDI_TCW_CTRL_REG);
593                 break;
594         case INSN_CONFIG_GET_COUNTER_STATUS:
595                 data[1] = 0;
596                 val = inl(iobase + ADDI_TCW_CTRL_REG);
597                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
598                         data[1] |= COMEDI_COUNTER_ARMED;
599                 if (val & ADDI_TCW_CTRL_CNTR_ENA)
600                         data[1] |= COMEDI_COUNTER_COUNTING;
601                 val = inl(iobase + ADDI_TCW_STATUS_REG);
602                 if (val & ADDI_TCW_STATUS_OVERFLOW)
603                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
604                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
605                           COMEDI_COUNTER_TERMINAL_COUNT;
606                 break;
607         default:
608                 return -EINVAL;
609         }
610
611         return insn->n;
612 }
613
614 static int apci1564_counter_insn_write(struct comedi_device *dev,
615                                        struct comedi_subdevice *s,
616                                        struct comedi_insn *insn,
617                                        unsigned int *data)
618 {
619         struct apci1564_private *devpriv = dev->private;
620         unsigned int chan = CR_CHAN(insn->chanspec);
621         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
622
623         /* just write the last last to the reload register */
624         if (insn->n) {
625                 unsigned int val = data[insn->n - 1];
626
627                 outl(val, iobase + ADDI_TCW_RELOAD_REG);
628         }
629
630         return insn->n;
631 }
632
633 static int apci1564_counter_insn_read(struct comedi_device *dev,
634                                       struct comedi_subdevice *s,
635                                       struct comedi_insn *insn,
636                                       unsigned int *data)
637 {
638         struct apci1564_private *devpriv = dev->private;
639         unsigned int chan = CR_CHAN(insn->chanspec);
640         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
641         int i;
642
643         /* return the actual value of the counter */
644         for (i = 0; i < insn->n; i++)
645                 data[i] = inl(iobase + ADDI_TCW_VAL_REG);
646
647         return insn->n;
648 }
649
650 static int apci1564_auto_attach(struct comedi_device *dev,
651                                 unsigned long context_unused)
652 {
653         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
654         struct apci1564_private *devpriv;
655         struct comedi_subdevice *s;
656         unsigned int val;
657         int ret;
658
659         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
660         if (!devpriv)
661                 return -ENOMEM;
662
663         ret = comedi_pci_enable(dev);
664         if (ret)
665                 return ret;
666
667         /* read the EEPROM register and check the I/O map revision */
668         devpriv->eeprom = pci_resource_start(pcidev, 0);
669         val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
670         if (APCI1564_EEPROM_TO_REV(val) == 0) {
671                 /* PLD Revision 1.0 I/O Mapping */
672                 dev->iobase = pci_resource_start(pcidev, 1) +
673                               APCI1564_REV1_MAIN_IOBASE;
674                 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
675         } else {
676                 /* PLD Revision 2.x I/O Mapping */
677                 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
678                 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
679                 devpriv->counters = pci_resource_start(pcidev, 1);
680         }
681
682         apci1564_reset(dev);
683
684         if (pcidev->irq > 0) {
685                 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
686                                   dev->board_name, dev);
687                 if (ret == 0)
688                         dev->irq = pcidev->irq;
689         }
690
691         ret = comedi_alloc_subdevices(dev, 7);
692         if (ret)
693                 return ret;
694
695         /*  Allocate and Initialise DI Subdevice Structures */
696         s = &dev->subdevices[0];
697         s->type         = COMEDI_SUBD_DI;
698         s->subdev_flags = SDF_READABLE;
699         s->n_chan       = 32;
700         s->maxdata      = 1;
701         s->range_table  = &range_digital;
702         s->insn_bits    = apci1564_di_insn_bits;
703
704         /*  Allocate and Initialise DO Subdevice Structures */
705         s = &dev->subdevices[1];
706         s->type         = COMEDI_SUBD_DO;
707         s->subdev_flags = SDF_WRITABLE;
708         s->n_chan       = 32;
709         s->maxdata      = 1;
710         s->range_table  = &range_digital;
711         s->insn_bits    = apci1564_do_insn_bits;
712
713         /* Change-Of-State (COS) interrupt subdevice */
714         s = &dev->subdevices[2];
715         if (dev->irq) {
716                 dev->read_subdev = s;
717                 s->type         = COMEDI_SUBD_DI;
718                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL;
719                 s->n_chan       = 1;
720                 s->maxdata      = 1;
721                 s->range_table  = &range_digital;
722                 s->len_chanlist = 1;
723                 s->insn_config  = apci1564_cos_insn_config;
724                 s->insn_bits    = apci1564_cos_insn_bits;
725                 s->do_cmdtest   = apci1564_cos_cmdtest;
726                 s->do_cmd       = apci1564_cos_cmd;
727                 s->cancel       = apci1564_cos_cancel;
728         } else {
729                 s->type         = COMEDI_SUBD_UNUSED;
730         }
731
732         /* Timer subdevice */
733         s = &dev->subdevices[3];
734         s->type         = COMEDI_SUBD_TIMER;
735         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
736         s->n_chan       = 1;
737         s->maxdata      = 0x0fff;
738         s->range_table  = &range_digital;
739         s->insn_config  = apci1564_timer_insn_config;
740         s->insn_write   = apci1564_timer_insn_write;
741         s->insn_read    = apci1564_timer_insn_read;
742
743         /* Counter subdevice */
744         s = &dev->subdevices[4];
745         if (devpriv->counters) {
746                 s->type         = COMEDI_SUBD_COUNTER;
747                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
748                 s->n_chan       = 3;
749                 s->maxdata      = 0xffffffff;
750                 s->range_table  = &range_digital;
751                 s->insn_config  = apci1564_counter_insn_config;
752                 s->insn_write   = apci1564_counter_insn_write;
753                 s->insn_read    = apci1564_counter_insn_read;
754         } else {
755                 s->type         = COMEDI_SUBD_UNUSED;
756         }
757
758         /* Initialize the watchdog subdevice */
759         s = &dev->subdevices[5];
760         ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_IOBASE);
761         if (ret)
762                 return ret;
763
764         /* Initialize the diagnostic status subdevice */
765         s = &dev->subdevices[6];
766         s->type         = COMEDI_SUBD_DI;
767         s->subdev_flags = SDF_READABLE;
768         s->n_chan       = 2;
769         s->maxdata      = 1;
770         s->range_table  = &range_digital;
771         s->insn_bits    = apci1564_diag_insn_bits;
772
773         return 0;
774 }
775
776 static void apci1564_detach(struct comedi_device *dev)
777 {
778         if (dev->iobase)
779                 apci1564_reset(dev);
780         comedi_pci_detach(dev);
781 }
782
783 static struct comedi_driver apci1564_driver = {
784         .driver_name    = "addi_apci_1564",
785         .module         = THIS_MODULE,
786         .auto_attach    = apci1564_auto_attach,
787         .detach         = apci1564_detach,
788 };
789
790 static int apci1564_pci_probe(struct pci_dev *dev,
791                               const struct pci_device_id *id)
792 {
793         return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
794 }
795
796 static const struct pci_device_id apci1564_pci_table[] = {
797         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
798         { 0 }
799 };
800 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
801
802 static struct pci_driver apci1564_pci_driver = {
803         .name           = "addi_apci_1564",
804         .id_table       = apci1564_pci_table,
805         .probe          = apci1564_pci_probe,
806         .remove         = comedi_pci_auto_unconfig,
807 };
808 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
809
810 MODULE_AUTHOR("Comedi http://www.comedi.org");
811 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
812 MODULE_LICENSE("GPL");