xref: /openbmc/linux/drivers/comedi/drivers/addi_apci_1564.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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  #include <linux/comedi/comedi_pci.h>
72  
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  
apci1564_reset(struct comedi_device * dev)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  
apci1564_interrupt(int irq,void * d)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  
apci1564_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_diag_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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   */
apci1564_cos_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_cos_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_cos_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)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   */
apci1564_cos_cmd(struct comedi_device * dev,struct comedi_subdevice * s)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  
apci1564_cos_cancel(struct comedi_device * dev,struct comedi_subdevice * s)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  
apci1564_timer_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_timer_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_timer_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_counter_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_counter_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_counter_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  
apci1564_auto_attach(struct comedi_device * dev,unsigned long context_unused)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  
apci1564_detach(struct comedi_device * dev)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  
apci1564_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)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");
821