Merge existing fixes from regulator/for-5.14
[linux-2.6-microblaze.git] / drivers / 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, himask, lomask;
335
336         switch (data[0]) {
337         case INSN_CONFIG_DIGITAL_TRIG:
338                 if (data[1] != 0)
339                         return -EINVAL;
340                 shift = data[3];
341                 if (shift < 32) {
342                         oldmask = (1U << shift) - 1;
343                         himask = data[4] << shift;
344                         lomask = data[5] << shift;
345                 } else {
346                         oldmask = 0xffffffffu;
347                         himask = 0;
348                         lomask = 0;
349                 }
350                 switch (data[2]) {
351                 case COMEDI_DIGITAL_TRIG_DISABLE:
352                         devpriv->ctrl = 0;
353                         devpriv->mode1 = 0;
354                         devpriv->mode2 = 0;
355                         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
356                         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
357                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
358                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
359                         break;
360                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
361                         if (devpriv->ctrl != APCI1564_DI_IRQ_ENA) {
362                                 /* switching to 'OR' mode */
363                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA;
364                                 /* wipe old channels */
365                                 devpriv->mode1 = 0;
366                                 devpriv->mode2 = 0;
367                         } else {
368                                 /* preserve unspecified channels */
369                                 devpriv->mode1 &= oldmask;
370                                 devpriv->mode2 &= oldmask;
371                         }
372                         /* configure specified channels */
373                         devpriv->mode1 |= himask;
374                         devpriv->mode2 |= lomask;
375                         break;
376                 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
377                         if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA |
378                                               APCI1564_DI_IRQ_MODE)) {
379                                 /* switching to 'AND' mode */
380                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA |
381                                                 APCI1564_DI_IRQ_MODE;
382                                 /* wipe old channels */
383                                 devpriv->mode1 = 0;
384                                 devpriv->mode2 = 0;
385                         } else {
386                                 /* preserve unspecified channels */
387                                 devpriv->mode1 &= oldmask;
388                                 devpriv->mode2 &= oldmask;
389                         }
390                         /* configure specified channels */
391                         devpriv->mode1 |= himask;
392                         devpriv->mode2 |= lomask;
393                         break;
394                 default:
395                         return -EINVAL;
396                 }
397
398                 /* ensure the mode bits are in-range for channels [19:4] */
399                 devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK;
400                 devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK;
401                 break;
402         default:
403                 return -EINVAL;
404         }
405         return insn->n;
406 }
407
408 static int apci1564_cos_insn_bits(struct comedi_device *dev,
409                                   struct comedi_subdevice *s,
410                                   struct comedi_insn *insn,
411                                   unsigned int *data)
412 {
413         data[1] = s->state;
414
415         return 0;
416 }
417
418 static int apci1564_cos_cmdtest(struct comedi_device *dev,
419                                 struct comedi_subdevice *s,
420                                 struct comedi_cmd *cmd)
421 {
422         int err = 0;
423
424         /* Step 1 : check if triggers are trivially valid */
425
426         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
427         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
428         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
429         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
430         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
431
432         if (err)
433                 return 1;
434
435         /* Step 2a : make sure trigger sources are unique */
436         /* Step 2b : and mutually compatible */
437
438         /* Step 3: check if arguments are trivially valid */
439
440         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
441         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
442         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
443         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
444                                            cmd->chanlist_len);
445         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
446
447         if (err)
448                 return 3;
449
450         /* Step 4: fix up any arguments */
451
452         /* Step 5: check channel list if it exists */
453
454         return 0;
455 }
456
457 /*
458  * Change-Of-State (COS) 'do_cmd' operation
459  *
460  * Enable the COS interrupt as configured by apci1564_cos_insn_config().
461  */
462 static int apci1564_cos_cmd(struct comedi_device *dev,
463                             struct comedi_subdevice *s)
464 {
465         struct apci1564_private *devpriv = dev->private;
466
467         if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) {
468                 dev_warn(dev->class_dev,
469                          "Interrupts disabled due to mode configuration!\n");
470                 return -EINVAL;
471         }
472
473         outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
474         outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
475         outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
476
477         return 0;
478 }
479
480 static int apci1564_cos_cancel(struct comedi_device *dev,
481                                struct comedi_subdevice *s)
482 {
483         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
484         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
485         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
486         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
487
488         return 0;
489 }
490
491 static int apci1564_timer_insn_config(struct comedi_device *dev,
492                                       struct comedi_subdevice *s,
493                                       struct comedi_insn *insn,
494                                       unsigned int *data)
495 {
496         struct apci1564_private *devpriv = dev->private;
497         unsigned int val;
498
499         switch (data[0]) {
500         case INSN_CONFIG_ARM:
501                 if (data[1] > s->maxdata)
502                         return -EINVAL;
503                 outl(data[1], devpriv->timer + ADDI_TCW_RELOAD_REG);
504                 outl(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_TIMER_ENA,
505                      devpriv->timer + ADDI_TCW_CTRL_REG);
506                 break;
507         case INSN_CONFIG_DISARM:
508                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
509                 break;
510         case INSN_CONFIG_GET_COUNTER_STATUS:
511                 data[1] = 0;
512                 val = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
513                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
514                         data[1] |= COMEDI_COUNTER_ARMED;
515                 if (val & ADDI_TCW_CTRL_TIMER_ENA)
516                         data[1] |= COMEDI_COUNTER_COUNTING;
517                 val = inl(devpriv->timer + ADDI_TCW_STATUS_REG);
518                 if (val & ADDI_TCW_STATUS_OVERFLOW)
519                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
520                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
521                           COMEDI_COUNTER_TERMINAL_COUNT;
522                 break;
523         case INSN_CONFIG_SET_CLOCK_SRC:
524                 if (data[2] > s->maxdata)
525                         return -EINVAL;
526                 outl(data[1], devpriv->timer + ADDI_TCW_TIMEBASE_REG);
527                 outl(data[2], devpriv->timer + ADDI_TCW_RELOAD_REG);
528                 break;
529         case INSN_CONFIG_GET_CLOCK_SRC:
530                 data[1] = inl(devpriv->timer + ADDI_TCW_TIMEBASE_REG);
531                 data[2] = inl(devpriv->timer + ADDI_TCW_RELOAD_REG);
532                 break;
533         default:
534                 return -EINVAL;
535         }
536
537         return insn->n;
538 }
539
540 static int apci1564_timer_insn_write(struct comedi_device *dev,
541                                      struct comedi_subdevice *s,
542                                      struct comedi_insn *insn,
543                                      unsigned int *data)
544 {
545         struct apci1564_private *devpriv = dev->private;
546
547         /* just write the last to the reload register */
548         if (insn->n) {
549                 unsigned int val = data[insn->n - 1];
550
551                 outl(val, devpriv->timer + ADDI_TCW_RELOAD_REG);
552         }
553
554         return insn->n;
555 }
556
557 static int apci1564_timer_insn_read(struct comedi_device *dev,
558                                     struct comedi_subdevice *s,
559                                     struct comedi_insn *insn,
560                                     unsigned int *data)
561 {
562         struct apci1564_private *devpriv = dev->private;
563         int i;
564
565         /* return the actual value of the timer */
566         for (i = 0; i < insn->n; i++)
567                 data[i] = inl(devpriv->timer + ADDI_TCW_VAL_REG);
568
569         return insn->n;
570 }
571
572 static int apci1564_counter_insn_config(struct comedi_device *dev,
573                                         struct comedi_subdevice *s,
574                                         struct comedi_insn *insn,
575                                         unsigned int *data)
576 {
577         struct apci1564_private *devpriv = dev->private;
578         unsigned int chan = CR_CHAN(insn->chanspec);
579         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
580         unsigned int val;
581
582         switch (data[0]) {
583         case INSN_CONFIG_ARM:
584                 val = inl(iobase + ADDI_TCW_CTRL_REG);
585                 val |= ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA;
586                 outl(data[1], iobase + ADDI_TCW_RELOAD_REG);
587                 outl(val, iobase + ADDI_TCW_CTRL_REG);
588                 break;
589         case INSN_CONFIG_DISARM:
590                 val = inl(iobase + ADDI_TCW_CTRL_REG);
591                 val &= ~(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA);
592                 outl(val, iobase + ADDI_TCW_CTRL_REG);
593                 break;
594         case INSN_CONFIG_SET_COUNTER_MODE:
595                 /*
596                  * FIXME: The counter operation is not described in the
597                  * datasheet. For now just write the raw data[1] value to
598                  * the control register.
599                  */
600                 outl(data[1], iobase + ADDI_TCW_CTRL_REG);
601                 break;
602         case INSN_CONFIG_GET_COUNTER_STATUS:
603                 data[1] = 0;
604                 val = inl(iobase + ADDI_TCW_CTRL_REG);
605                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
606                         data[1] |= COMEDI_COUNTER_ARMED;
607                 if (val & ADDI_TCW_CTRL_CNTR_ENA)
608                         data[1] |= COMEDI_COUNTER_COUNTING;
609                 val = inl(iobase + ADDI_TCW_STATUS_REG);
610                 if (val & ADDI_TCW_STATUS_OVERFLOW)
611                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
612                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
613                           COMEDI_COUNTER_TERMINAL_COUNT;
614                 break;
615         default:
616                 return -EINVAL;
617         }
618
619         return insn->n;
620 }
621
622 static int apci1564_counter_insn_write(struct comedi_device *dev,
623                                        struct comedi_subdevice *s,
624                                        struct comedi_insn *insn,
625                                        unsigned int *data)
626 {
627         struct apci1564_private *devpriv = dev->private;
628         unsigned int chan = CR_CHAN(insn->chanspec);
629         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
630
631         /* just write the last to the reload register */
632         if (insn->n) {
633                 unsigned int val = data[insn->n - 1];
634
635                 outl(val, iobase + ADDI_TCW_RELOAD_REG);
636         }
637
638         return insn->n;
639 }
640
641 static int apci1564_counter_insn_read(struct comedi_device *dev,
642                                       struct comedi_subdevice *s,
643                                       struct comedi_insn *insn,
644                                       unsigned int *data)
645 {
646         struct apci1564_private *devpriv = dev->private;
647         unsigned int chan = CR_CHAN(insn->chanspec);
648         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
649         int i;
650
651         /* return the actual value of the counter */
652         for (i = 0; i < insn->n; i++)
653                 data[i] = inl(iobase + ADDI_TCW_VAL_REG);
654
655         return insn->n;
656 }
657
658 static int apci1564_auto_attach(struct comedi_device *dev,
659                                 unsigned long context_unused)
660 {
661         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
662         struct apci1564_private *devpriv;
663         struct comedi_subdevice *s;
664         unsigned int val;
665         int ret;
666
667         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
668         if (!devpriv)
669                 return -ENOMEM;
670
671         ret = comedi_pci_enable(dev);
672         if (ret)
673                 return ret;
674
675         /* read the EEPROM register and check the I/O map revision */
676         devpriv->eeprom = pci_resource_start(pcidev, 0);
677         val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
678         if (APCI1564_EEPROM_TO_REV(val) == 0) {
679                 /* PLD Revision 1.0 I/O Mapping */
680                 dev->iobase = pci_resource_start(pcidev, 1) +
681                               APCI1564_REV1_MAIN_IOBASE;
682                 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
683         } else {
684                 /* PLD Revision 2.x I/O Mapping */
685                 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
686                 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
687                 devpriv->counters = pci_resource_start(pcidev, 1);
688         }
689
690         apci1564_reset(dev);
691
692         if (pcidev->irq > 0) {
693                 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
694                                   dev->board_name, dev);
695                 if (ret == 0)
696                         dev->irq = pcidev->irq;
697         }
698
699         ret = comedi_alloc_subdevices(dev, 7);
700         if (ret)
701                 return ret;
702
703         /*  Allocate and Initialise DI Subdevice Structures */
704         s = &dev->subdevices[0];
705         s->type         = COMEDI_SUBD_DI;
706         s->subdev_flags = SDF_READABLE;
707         s->n_chan       = 32;
708         s->maxdata      = 1;
709         s->range_table  = &range_digital;
710         s->insn_bits    = apci1564_di_insn_bits;
711
712         /*  Allocate and Initialise DO Subdevice Structures */
713         s = &dev->subdevices[1];
714         s->type         = COMEDI_SUBD_DO;
715         s->subdev_flags = SDF_WRITABLE;
716         s->n_chan       = 32;
717         s->maxdata      = 1;
718         s->range_table  = &range_digital;
719         s->insn_bits    = apci1564_do_insn_bits;
720
721         /* Change-Of-State (COS) interrupt subdevice */
722         s = &dev->subdevices[2];
723         if (dev->irq) {
724                 dev->read_subdev = s;
725                 s->type         = COMEDI_SUBD_DI;
726                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL;
727                 s->n_chan       = 1;
728                 s->maxdata      = 1;
729                 s->range_table  = &range_digital;
730                 s->len_chanlist = 1;
731                 s->insn_config  = apci1564_cos_insn_config;
732                 s->insn_bits    = apci1564_cos_insn_bits;
733                 s->do_cmdtest   = apci1564_cos_cmdtest;
734                 s->do_cmd       = apci1564_cos_cmd;
735                 s->cancel       = apci1564_cos_cancel;
736         } else {
737                 s->type         = COMEDI_SUBD_UNUSED;
738         }
739
740         /* Timer subdevice */
741         s = &dev->subdevices[3];
742         s->type         = COMEDI_SUBD_TIMER;
743         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
744         s->n_chan       = 1;
745         s->maxdata      = 0x0fff;
746         s->range_table  = &range_digital;
747         s->insn_config  = apci1564_timer_insn_config;
748         s->insn_write   = apci1564_timer_insn_write;
749         s->insn_read    = apci1564_timer_insn_read;
750
751         /* Counter subdevice */
752         s = &dev->subdevices[4];
753         if (devpriv->counters) {
754                 s->type         = COMEDI_SUBD_COUNTER;
755                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
756                 s->n_chan       = 3;
757                 s->maxdata      = 0xffffffff;
758                 s->range_table  = &range_digital;
759                 s->insn_config  = apci1564_counter_insn_config;
760                 s->insn_write   = apci1564_counter_insn_write;
761                 s->insn_read    = apci1564_counter_insn_read;
762         } else {
763                 s->type         = COMEDI_SUBD_UNUSED;
764         }
765
766         /* Initialize the watchdog subdevice */
767         s = &dev->subdevices[5];
768         ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_IOBASE);
769         if (ret)
770                 return ret;
771
772         /* Initialize the diagnostic status subdevice */
773         s = &dev->subdevices[6];
774         s->type         = COMEDI_SUBD_DI;
775         s->subdev_flags = SDF_READABLE;
776         s->n_chan       = 2;
777         s->maxdata      = 1;
778         s->range_table  = &range_digital;
779         s->insn_bits    = apci1564_diag_insn_bits;
780
781         return 0;
782 }
783
784 static void apci1564_detach(struct comedi_device *dev)
785 {
786         if (dev->iobase)
787                 apci1564_reset(dev);
788         comedi_pci_detach(dev);
789 }
790
791 static struct comedi_driver apci1564_driver = {
792         .driver_name    = "addi_apci_1564",
793         .module         = THIS_MODULE,
794         .auto_attach    = apci1564_auto_attach,
795         .detach         = apci1564_detach,
796 };
797
798 static int apci1564_pci_probe(struct pci_dev *dev,
799                               const struct pci_device_id *id)
800 {
801         return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
802 }
803
804 static const struct pci_device_id apci1564_pci_table[] = {
805         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
806         { 0 }
807 };
808 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
809
810 static struct pci_driver apci1564_pci_driver = {
811         .name           = "addi_apci_1564",
812         .id_table       = apci1564_pci_table,
813         .probe          = apci1564_pci_probe,
814         .remove         = comedi_pci_auto_unconfig,
815 };
816 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
817
818 MODULE_AUTHOR("Comedi https://www.comedi.org");
819 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
820 MODULE_LICENSE("GPL");