xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision 9e884bb1)
1f1d8a071SWilliam Breathitt Gray // SPDX-License-Identifier: GPL-2.0
2f1d8a071SWilliam Breathitt Gray /*
3f1d8a071SWilliam Breathitt Gray  * Counter driver for the ACCES 104-QUAD-8
4f1d8a071SWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
5f1d8a071SWilliam Breathitt Gray  *
6f1d8a071SWilliam Breathitt Gray  * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
7f1d8a071SWilliam Breathitt Gray  */
8f1d8a071SWilliam Breathitt Gray #include <linux/bitops.h>
9f1d8a071SWilliam Breathitt Gray #include <linux/counter.h>
10f1d8a071SWilliam Breathitt Gray #include <linux/device.h>
11f1d8a071SWilliam Breathitt Gray #include <linux/errno.h>
12f1d8a071SWilliam Breathitt Gray #include <linux/io.h>
13f1d8a071SWilliam Breathitt Gray #include <linux/ioport.h>
147aa2ba0dSWilliam Breathitt Gray #include <linux/interrupt.h>
15f1d8a071SWilliam Breathitt Gray #include <linux/isa.h>
16f1d8a071SWilliam Breathitt Gray #include <linux/kernel.h>
17c95cc0d9SWilliam Breathitt Gray #include <linux/list.h>
18f1d8a071SWilliam Breathitt Gray #include <linux/module.h>
19f1d8a071SWilliam Breathitt Gray #include <linux/moduleparam.h>
20f1d8a071SWilliam Breathitt Gray #include <linux/types.h>
2109db4678SWilliam Breathitt Gray #include <linux/spinlock.h>
22f1d8a071SWilliam Breathitt Gray 
23f1d8a071SWilliam Breathitt Gray #define QUAD8_EXTENT 32
24f1d8a071SWilliam Breathitt Gray 
25f1d8a071SWilliam Breathitt Gray static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
26f1d8a071SWilliam Breathitt Gray static unsigned int num_quad8;
27af383bb1SWilliam Breathitt Gray module_param_hw_array(base, uint, ioport, &num_quad8, 0);
28f1d8a071SWilliam Breathitt Gray MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
29f1d8a071SWilliam Breathitt Gray 
307aa2ba0dSWilliam Breathitt Gray static unsigned int irq[max_num_isa_dev(QUAD8_EXTENT)];
317aa2ba0dSWilliam Breathitt Gray module_param_hw_array(irq, uint, irq, NULL, 0);
327aa2ba0dSWilliam Breathitt Gray MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers");
337aa2ba0dSWilliam Breathitt Gray 
34f1d8a071SWilliam Breathitt Gray #define QUAD8_NUM_COUNTERS 8
35f1d8a071SWilliam Breathitt Gray 
36f1d8a071SWilliam Breathitt Gray /**
37e357e81fSWilliam Breathitt Gray  * struct quad8 - device private data structure
3894a853ecSWilliam Breathitt Gray  * @lock:		lock to prevent clobbering device states during R/W ops
39f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
40954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
41f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
42f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
43f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
44f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
45f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
46f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
477aa2ba0dSWilliam Breathitt Gray  * @irq_trigger:	array of current IRQ trigger function configurations
48f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
49f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
50954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
51e357e81fSWilliam Breathitt Gray  * @base:		base port address of the device
52f1d8a071SWilliam Breathitt Gray  */
53e357e81fSWilliam Breathitt Gray struct quad8 {
5409db4678SWilliam Breathitt Gray 	spinlock_t lock;
55de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
56f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
57f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
58f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
59f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
60f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
61f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
627aa2ba0dSWilliam Breathitt Gray 	unsigned int irq_trigger[QUAD8_NUM_COUNTERS];
63f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
64f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
65954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
66f1d8a071SWilliam Breathitt Gray 	unsigned int base;
67f1d8a071SWilliam Breathitt Gray };
68f1d8a071SWilliam Breathitt Gray 
697aa2ba0dSWilliam Breathitt Gray #define QUAD8_REG_INTERRUPT_STATUS 0x10
70f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_CHAN_OP 0x11
717aa2ba0dSWilliam Breathitt Gray #define QUAD8_REG_INDEX_INTERRUPT 0x12
72f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
73954ab5ccSWilliam Breathitt Gray #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
74f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
75f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
76f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
77f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
78f1d8a071SWilliam Breathitt Gray /* Error flag */
79f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
80f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
81f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
82f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
83f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
84f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
85f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
86f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
87f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
88f1d8a071SWilliam Breathitt Gray /* Index Control Register */
89f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
90f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
91f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
92f1d8a071SWilliam Breathitt Gray /* Reset Counter */
93f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
94f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
95f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
96f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
97f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
98f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
99f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
100f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
101f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
102de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
103de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
104f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
1057aa2ba0dSWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC 0x04
106f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
107f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
108f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
109f1d8a071SWilliam Breathitt Gray 
110f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
111493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
112493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
113f1d8a071SWilliam Breathitt Gray {
114aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
115f1d8a071SWilliam Breathitt Gray 	unsigned int state;
116f1d8a071SWilliam Breathitt Gray 
117f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
118f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
119f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
120f1d8a071SWilliam Breathitt Gray 
121f1d8a071SWilliam Breathitt Gray 	state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
122f1d8a071SWilliam Breathitt Gray 		& BIT(signal->id - 16);
123f1d8a071SWilliam Breathitt Gray 
124493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
125f1d8a071SWilliam Breathitt Gray 
126f1d8a071SWilliam Breathitt Gray 	return 0;
127f1d8a071SWilliam Breathitt Gray }
128f1d8a071SWilliam Breathitt Gray 
129f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
130aaec1a0fSWilliam Breathitt Gray 			    struct counter_count *count, u64 *val)
131f1d8a071SWilliam Breathitt Gray {
132aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
133f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
134f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
135f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
136f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
13709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
138f1d8a071SWilliam Breathitt Gray 	int i;
139f1d8a071SWilliam Breathitt Gray 
140f1d8a071SWilliam Breathitt Gray 	flags = inb(base_offset + 1);
141f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
142f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
143f1d8a071SWilliam Breathitt Gray 
144f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
145d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
146f1d8a071SWilliam Breathitt Gray 
14709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
148fc069262SSyed Nayyar Waris 
149f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
150f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
151f1d8a071SWilliam Breathitt Gray 	     base_offset + 1);
152f1d8a071SWilliam Breathitt Gray 
153f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
154d49e6ee2SWilliam Breathitt Gray 		*val |= (unsigned long)inb(base_offset) << (8 * i);
155f1d8a071SWilliam Breathitt Gray 
15609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
157fc069262SSyed Nayyar Waris 
158f1d8a071SWilliam Breathitt Gray 	return 0;
159f1d8a071SWilliam Breathitt Gray }
160f1d8a071SWilliam Breathitt Gray 
161f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
162aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count, u64 val)
163f1d8a071SWilliam Breathitt Gray {
164aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
165f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
16609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
167f1d8a071SWilliam Breathitt Gray 	int i;
168f1d8a071SWilliam Breathitt Gray 
169f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
170d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
171e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
172f1d8a071SWilliam Breathitt Gray 
17309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
174fc069262SSyed Nayyar Waris 
175f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
176f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
177f1d8a071SWilliam Breathitt Gray 
178f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
179f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
180d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
181f1d8a071SWilliam Breathitt Gray 
182f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
183f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
184f1d8a071SWilliam Breathitt Gray 
185f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
186f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
187f1d8a071SWilliam Breathitt Gray 
188f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
189d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
190f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
191d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
192f1d8a071SWilliam Breathitt Gray 
193f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
194f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
195f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
196f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
197f1d8a071SWilliam Breathitt Gray 
19809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
199fc069262SSyed Nayyar Waris 
200f1d8a071SWilliam Breathitt Gray 	return 0;
201f1d8a071SWilliam Breathitt Gray }
202f1d8a071SWilliam Breathitt Gray 
203394a0150SWilliam Breathitt Gray static const enum counter_function quad8_count_functions_list[] = {
204aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_PULSE_DIRECTION,
205aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X1_A,
206aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X2_A,
207aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X4,
208f1d8a071SWilliam Breathitt Gray };
209f1d8a071SWilliam Breathitt Gray 
210aaec1a0fSWilliam Breathitt Gray static int quad8_function_read(struct counter_device *counter,
211aaec1a0fSWilliam Breathitt Gray 			       struct counter_count *count,
212aaec1a0fSWilliam Breathitt Gray 			       enum counter_function *function)
213f1d8a071SWilliam Breathitt Gray {
214aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
215f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
21609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
217f1d8a071SWilliam Breathitt Gray 
21809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
219fc069262SSyed Nayyar Waris 
220fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
221fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
222f1d8a071SWilliam Breathitt Gray 		case 0:
223aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
224f1d8a071SWilliam Breathitt Gray 			break;
225f1d8a071SWilliam Breathitt Gray 		case 1:
226aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
227f1d8a071SWilliam Breathitt Gray 			break;
228f1d8a071SWilliam Breathitt Gray 		case 2:
229aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
230f1d8a071SWilliam Breathitt Gray 			break;
231f1d8a071SWilliam Breathitt Gray 		}
232f1d8a071SWilliam Breathitt Gray 	else
233aaec1a0fSWilliam Breathitt Gray 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
234f1d8a071SWilliam Breathitt Gray 
23509db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
236fc069262SSyed Nayyar Waris 
237f1d8a071SWilliam Breathitt Gray 	return 0;
238f1d8a071SWilliam Breathitt Gray }
239f1d8a071SWilliam Breathitt Gray 
240aaec1a0fSWilliam Breathitt Gray static int quad8_function_write(struct counter_device *counter,
241aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
242aaec1a0fSWilliam Breathitt Gray 				enum counter_function function)
243f1d8a071SWilliam Breathitt Gray {
244aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
245f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
246f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
247f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
248f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
249f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * id + 1;
25009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
251fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
252fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
253fc069262SSyed Nayyar Waris 
25409db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
255fc069262SSyed Nayyar Waris 
256fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
257fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
258f1d8a071SWilliam Breathitt Gray 
259aaec1a0fSWilliam Breathitt Gray 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
260f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
261f1d8a071SWilliam Breathitt Gray 
262f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
263f1d8a071SWilliam Breathitt Gray 		*scale = 0;
264f1d8a071SWilliam Breathitt Gray 
265f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
266f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
267f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
268f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
269f1d8a071SWilliam Breathitt Gray 			outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
270f1d8a071SWilliam Breathitt Gray 		}
271f1d8a071SWilliam Breathitt Gray 	} else {
272f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
273f1d8a071SWilliam Breathitt Gray 
274f1d8a071SWilliam Breathitt Gray 		switch (function) {
275aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
276f1d8a071SWilliam Breathitt Gray 			*scale = 0;
277f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
278f1d8a071SWilliam Breathitt Gray 			break;
279aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
280f1d8a071SWilliam Breathitt Gray 			*scale = 1;
281f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
282f1d8a071SWilliam Breathitt Gray 			break;
283aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X4:
284f1d8a071SWilliam Breathitt Gray 			*scale = 2;
285f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
286f1d8a071SWilliam Breathitt Gray 			break;
287b11eed15SWilliam Breathitt Gray 		default:
288b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
28909db4678SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
290b11eed15SWilliam Breathitt Gray 			return -EINVAL;
291f1d8a071SWilliam Breathitt Gray 		}
292f1d8a071SWilliam Breathitt Gray 	}
293f1d8a071SWilliam Breathitt Gray 
294f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
295f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
296f1d8a071SWilliam Breathitt Gray 
29709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
298fc069262SSyed Nayyar Waris 
299f1d8a071SWilliam Breathitt Gray 	return 0;
300f1d8a071SWilliam Breathitt Gray }
301f1d8a071SWilliam Breathitt Gray 
302aaec1a0fSWilliam Breathitt Gray static int quad8_direction_read(struct counter_device *counter,
303aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
304aaec1a0fSWilliam Breathitt Gray 				enum counter_count_direction *direction)
305f1d8a071SWilliam Breathitt Gray {
306aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
307f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
308f1d8a071SWilliam Breathitt Gray 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
309f1d8a071SWilliam Breathitt Gray 
310f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
311f1d8a071SWilliam Breathitt Gray 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
312f1d8a071SWilliam Breathitt Gray 
313f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
314f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
315aaec1a0fSWilliam Breathitt Gray 
316aaec1a0fSWilliam Breathitt Gray 	return 0;
317f1d8a071SWilliam Breathitt Gray }
318f1d8a071SWilliam Breathitt Gray 
3196a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
320aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
321aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
322f1d8a071SWilliam Breathitt Gray };
323f1d8a071SWilliam Breathitt Gray 
3246a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
325aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
326aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
327aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
328aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
329f1d8a071SWilliam Breathitt Gray };
330f1d8a071SWilliam Breathitt Gray 
331aaec1a0fSWilliam Breathitt Gray static int quad8_action_read(struct counter_device *counter,
332aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count,
333aaec1a0fSWilliam Breathitt Gray 			     struct counter_synapse *synapse,
334aaec1a0fSWilliam Breathitt Gray 			     enum counter_synapse_action *action)
335f1d8a071SWilliam Breathitt Gray {
336aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
337f1d8a071SWilliam Breathitt Gray 	int err;
338aaec1a0fSWilliam Breathitt Gray 	enum counter_function function;
339f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
340f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
341f1d8a071SWilliam Breathitt Gray 
342f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
343f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
344f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
345aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
346f1d8a071SWilliam Breathitt Gray 		else
347aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_NONE;
348f1d8a071SWilliam Breathitt Gray 
349f1d8a071SWilliam Breathitt Gray 		return 0;
350f1d8a071SWilliam Breathitt Gray 	}
351f1d8a071SWilliam Breathitt Gray 
352aaec1a0fSWilliam Breathitt Gray 	err = quad8_function_read(counter, count, &function);
353f1d8a071SWilliam Breathitt Gray 	if (err)
354f1d8a071SWilliam Breathitt Gray 		return err;
355f1d8a071SWilliam Breathitt Gray 
356f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
357aaec1a0fSWilliam Breathitt Gray 	*action = COUNTER_SYNAPSE_ACTION_NONE;
358f1d8a071SWilliam Breathitt Gray 
359f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
360f1d8a071SWilliam Breathitt Gray 	switch (function) {
361aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_PULSE_DIRECTION:
362f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
363aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
364b11eed15SWilliam Breathitt Gray 		return 0;
365aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
366f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
367aaec1a0fSWilliam Breathitt Gray 			err = quad8_direction_read(counter, count, &direction);
368aaec1a0fSWilliam Breathitt Gray 			if (err)
369aaec1a0fSWilliam Breathitt Gray 				return err;
370f1d8a071SWilliam Breathitt Gray 
371f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
372aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
373f1d8a071SWilliam Breathitt Gray 			else
374aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
375f1d8a071SWilliam Breathitt Gray 		}
376b11eed15SWilliam Breathitt Gray 		return 0;
377aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
378f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
379aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
380b11eed15SWilliam Breathitt Gray 		return 0;
381aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X4:
382aaec1a0fSWilliam Breathitt Gray 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
383f1d8a071SWilliam Breathitt Gray 		return 0;
384b11eed15SWilliam Breathitt Gray 	default:
385b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
386b11eed15SWilliam Breathitt Gray 		return -EINVAL;
387b11eed15SWilliam Breathitt Gray 	}
388f1d8a071SWilliam Breathitt Gray }
389f1d8a071SWilliam Breathitt Gray 
3907aa2ba0dSWilliam Breathitt Gray enum {
3917aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY = 0,
3927aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_COMPARE = 1,
3937aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_CARRY_BORROW = 2,
3947aa2ba0dSWilliam Breathitt Gray 	QUAD8_EVENT_INDEX = 3,
3957aa2ba0dSWilliam Breathitt Gray };
3967aa2ba0dSWilliam Breathitt Gray 
3977aa2ba0dSWilliam Breathitt Gray static int quad8_events_configure(struct counter_device *counter)
3987aa2ba0dSWilliam Breathitt Gray {
399aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
4007aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_enabled = 0;
4017aa2ba0dSWilliam Breathitt Gray 	unsigned long irqflags;
402c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
403c95cc0d9SWilliam Breathitt Gray 	unsigned int next_irq_trigger;
4047aa2ba0dSWilliam Breathitt Gray 	unsigned long ior_cfg;
4057aa2ba0dSWilliam Breathitt Gray 	unsigned long base_offset;
4067aa2ba0dSWilliam Breathitt Gray 
4077aa2ba0dSWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
4087aa2ba0dSWilliam Breathitt Gray 
409c95cc0d9SWilliam Breathitt Gray 	list_for_each_entry(event_node, &counter->events_list, l) {
410c95cc0d9SWilliam Breathitt Gray 		switch (event_node->event) {
411c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW:
412c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY;
413c95cc0d9SWilliam Breathitt Gray 			break;
414c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_THRESHOLD:
415c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_COMPARE;
416c95cc0d9SWilliam Breathitt Gray 			break;
417c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
418c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_CARRY_BORROW;
419c95cc0d9SWilliam Breathitt Gray 			break;
420c95cc0d9SWilliam Breathitt Gray 		case COUNTER_EVENT_INDEX:
421c95cc0d9SWilliam Breathitt Gray 			next_irq_trigger = QUAD8_EVENT_INDEX;
422c95cc0d9SWilliam Breathitt Gray 			break;
423c95cc0d9SWilliam Breathitt Gray 		default:
424c95cc0d9SWilliam Breathitt Gray 			/* should never reach this path */
425c95cc0d9SWilliam Breathitt Gray 			spin_unlock_irqrestore(&priv->lock, irqflags);
426c95cc0d9SWilliam Breathitt Gray 			return -EINVAL;
4277aa2ba0dSWilliam Breathitt Gray 		}
4287aa2ba0dSWilliam Breathitt Gray 
429c95cc0d9SWilliam Breathitt Gray 		/* Skip configuration if it is the same as previously set */
430c95cc0d9SWilliam Breathitt Gray 		if (priv->irq_trigger[event_node->channel] == next_irq_trigger)
431c95cc0d9SWilliam Breathitt Gray 			continue;
432c95cc0d9SWilliam Breathitt Gray 
433c95cc0d9SWilliam Breathitt Gray 		/* Save new IRQ function configuration */
434c95cc0d9SWilliam Breathitt Gray 		priv->irq_trigger[event_node->channel] = next_irq_trigger;
435c95cc0d9SWilliam Breathitt Gray 
436c95cc0d9SWilliam Breathitt Gray 		/* Load configuration to I/O Control Register */
437c95cc0d9SWilliam Breathitt Gray 		ior_cfg = priv->ab_enable[event_node->channel] |
438c95cc0d9SWilliam Breathitt Gray 			  priv->preset_enable[event_node->channel] << 1 |
439c95cc0d9SWilliam Breathitt Gray 			  priv->irq_trigger[event_node->channel] << 3;
440c95cc0d9SWilliam Breathitt Gray 		base_offset = priv->base + 2 * event_node->channel + 1;
441c95cc0d9SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
4427aa2ba0dSWilliam Breathitt Gray 
4437aa2ba0dSWilliam Breathitt Gray 		/* Enable IRQ line */
444c95cc0d9SWilliam Breathitt Gray 		irq_enabled |= BIT(event_node->channel);
4457aa2ba0dSWilliam Breathitt Gray 	}
4467aa2ba0dSWilliam Breathitt Gray 
4477aa2ba0dSWilliam Breathitt Gray 	outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT);
4487aa2ba0dSWilliam Breathitt Gray 
4497aa2ba0dSWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
4507aa2ba0dSWilliam Breathitt Gray 
4517aa2ba0dSWilliam Breathitt Gray 	return 0;
4527aa2ba0dSWilliam Breathitt Gray }
4537aa2ba0dSWilliam Breathitt Gray 
4547aa2ba0dSWilliam Breathitt Gray static int quad8_watch_validate(struct counter_device *counter,
4557aa2ba0dSWilliam Breathitt Gray 				const struct counter_watch *watch)
4567aa2ba0dSWilliam Breathitt Gray {
457c95cc0d9SWilliam Breathitt Gray 	struct counter_event_node *event_node;
4587aa2ba0dSWilliam Breathitt Gray 
4597aa2ba0dSWilliam Breathitt Gray 	if (watch->channel > QUAD8_NUM_COUNTERS - 1)
4607aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4617aa2ba0dSWilliam Breathitt Gray 
4627aa2ba0dSWilliam Breathitt Gray 	switch (watch->event) {
4637aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW:
4647aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_THRESHOLD:
4657aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
4667aa2ba0dSWilliam Breathitt Gray 	case COUNTER_EVENT_INDEX:
467c95cc0d9SWilliam Breathitt Gray 		list_for_each_entry(event_node, &counter->next_events_list, l)
468c95cc0d9SWilliam Breathitt Gray 			if (watch->channel == event_node->channel &&
469c95cc0d9SWilliam Breathitt Gray 				watch->event != event_node->event)
4707aa2ba0dSWilliam Breathitt Gray 				return -EINVAL;
4717aa2ba0dSWilliam Breathitt Gray 		return 0;
4727aa2ba0dSWilliam Breathitt Gray 	default:
4737aa2ba0dSWilliam Breathitt Gray 		return -EINVAL;
4747aa2ba0dSWilliam Breathitt Gray 	}
4757aa2ba0dSWilliam Breathitt Gray }
4767aa2ba0dSWilliam Breathitt Gray 
47717aa207eSYueHaibing static const struct counter_ops quad8_ops = {
478f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
479f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
480f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
481aaec1a0fSWilliam Breathitt Gray 	.function_read = quad8_function_read,
482aaec1a0fSWilliam Breathitt Gray 	.function_write = quad8_function_write,
4837aa2ba0dSWilliam Breathitt Gray 	.action_read = quad8_action_read,
4847aa2ba0dSWilliam Breathitt Gray 	.events_configure = quad8_events_configure,
4857aa2ba0dSWilliam Breathitt Gray 	.watch_validate = quad8_watch_validate,
486f1d8a071SWilliam Breathitt Gray };
487f1d8a071SWilliam Breathitt Gray 
488e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
489e357e81fSWilliam Breathitt Gray 	"negative",
490e357e81fSWilliam Breathitt Gray 	"positive"
491e357e81fSWilliam Breathitt Gray };
492e357e81fSWilliam Breathitt Gray 
493f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
494aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
495aaec1a0fSWilliam Breathitt Gray 				    u32 *index_polarity)
496f1d8a071SWilliam Breathitt Gray {
497aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
498f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
499f1d8a071SWilliam Breathitt Gray 
500f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
501f1d8a071SWilliam Breathitt Gray 
502f1d8a071SWilliam Breathitt Gray 	return 0;
503f1d8a071SWilliam Breathitt Gray }
504f1d8a071SWilliam Breathitt Gray 
505f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
506aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
507aaec1a0fSWilliam Breathitt Gray 				    u32 index_polarity)
508f1d8a071SWilliam Breathitt Gray {
509aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
510f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
511f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
51209db4678SWilliam Breathitt Gray 	unsigned long irqflags;
513fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
514fc069262SSyed Nayyar Waris 
51509db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
516fc069262SSyed Nayyar Waris 
517fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
518f1d8a071SWilliam Breathitt Gray 
519f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
520f1d8a071SWilliam Breathitt Gray 
521f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
522f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
523f1d8a071SWilliam Breathitt Gray 
52409db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
525fc069262SSyed Nayyar Waris 
526f1d8a071SWilliam Breathitt Gray 	return 0;
527f1d8a071SWilliam Breathitt Gray }
528f1d8a071SWilliam Breathitt Gray 
529e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
530e357e81fSWilliam Breathitt Gray 	"non-synchronous",
531e357e81fSWilliam Breathitt Gray 	"synchronous"
532e357e81fSWilliam Breathitt Gray };
533e357e81fSWilliam Breathitt Gray 
534f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
535aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
536aaec1a0fSWilliam Breathitt Gray 				      u32 *synchronous_mode)
537f1d8a071SWilliam Breathitt Gray {
538aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
539f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
540f1d8a071SWilliam Breathitt Gray 
541f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
542f1d8a071SWilliam Breathitt Gray 
543f1d8a071SWilliam Breathitt Gray 	return 0;
544f1d8a071SWilliam Breathitt Gray }
545f1d8a071SWilliam Breathitt Gray 
546f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
547aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
548aaec1a0fSWilliam Breathitt Gray 				      u32 synchronous_mode)
549f1d8a071SWilliam Breathitt Gray {
550aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
551f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
552f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
55309db4678SWilliam Breathitt Gray 	unsigned long irqflags;
554fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
555fc069262SSyed Nayyar Waris 
55609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
557fc069262SSyed Nayyar Waris 
558fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
559f1d8a071SWilliam Breathitt Gray 
560f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
561fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
56209db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
563f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
564fc069262SSyed Nayyar Waris 	}
565f1d8a071SWilliam Breathitt Gray 
566f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
567f1d8a071SWilliam Breathitt Gray 
568f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
569f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
570f1d8a071SWilliam Breathitt Gray 
57109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
572fc069262SSyed Nayyar Waris 
573f1d8a071SWilliam Breathitt Gray 	return 0;
574f1d8a071SWilliam Breathitt Gray }
575f1d8a071SWilliam Breathitt Gray 
576aaec1a0fSWilliam Breathitt Gray static int quad8_count_floor_read(struct counter_device *counter,
577aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count, u64 *floor)
578f1d8a071SWilliam Breathitt Gray {
579f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
580aaec1a0fSWilliam Breathitt Gray 	*floor = 0;
581aaec1a0fSWilliam Breathitt Gray 
582aaec1a0fSWilliam Breathitt Gray 	return 0;
583f1d8a071SWilliam Breathitt Gray }
584f1d8a071SWilliam Breathitt Gray 
585aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_read(struct counter_device *counter,
586aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count,
587aaec1a0fSWilliam Breathitt Gray 				 enum counter_count_mode *cnt_mode)
588f1d8a071SWilliam Breathitt Gray {
589aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
590f1d8a071SWilliam Breathitt Gray 
591f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
592f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
593f1d8a071SWilliam Breathitt Gray 	case 0:
594f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
595f1d8a071SWilliam Breathitt Gray 		break;
596f1d8a071SWilliam Breathitt Gray 	case 1:
597f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
598f1d8a071SWilliam Breathitt Gray 		break;
599f1d8a071SWilliam Breathitt Gray 	case 2:
600f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
601f1d8a071SWilliam Breathitt Gray 		break;
602f1d8a071SWilliam Breathitt Gray 	case 3:
603f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
604f1d8a071SWilliam Breathitt Gray 		break;
605f1d8a071SWilliam Breathitt Gray 	}
606f1d8a071SWilliam Breathitt Gray 
607f1d8a071SWilliam Breathitt Gray 	return 0;
608f1d8a071SWilliam Breathitt Gray }
609f1d8a071SWilliam Breathitt Gray 
610aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_write(struct counter_device *counter,
611aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count,
612aaec1a0fSWilliam Breathitt Gray 				  enum counter_count_mode cnt_mode)
613f1d8a071SWilliam Breathitt Gray {
614aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
615aaec1a0fSWilliam Breathitt Gray 	unsigned int count_mode;
616f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
617f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
61809db4678SWilliam Breathitt Gray 	unsigned long irqflags;
619f1d8a071SWilliam Breathitt Gray 
620f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
621f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
622f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
623aaec1a0fSWilliam Breathitt Gray 		count_mode = 0;
624f1d8a071SWilliam Breathitt Gray 		break;
625f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
626aaec1a0fSWilliam Breathitt Gray 		count_mode = 1;
627f1d8a071SWilliam Breathitt Gray 		break;
628f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
629aaec1a0fSWilliam Breathitt Gray 		count_mode = 2;
630f1d8a071SWilliam Breathitt Gray 		break;
631f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
632aaec1a0fSWilliam Breathitt Gray 		count_mode = 3;
633f1d8a071SWilliam Breathitt Gray 		break;
634b11eed15SWilliam Breathitt Gray 	default:
635b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
636b11eed15SWilliam Breathitt Gray 		return -EINVAL;
637f1d8a071SWilliam Breathitt Gray 	}
638f1d8a071SWilliam Breathitt Gray 
63909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
640fc069262SSyed Nayyar Waris 
641aaec1a0fSWilliam Breathitt Gray 	priv->count_mode[count->id] = count_mode;
642f1d8a071SWilliam Breathitt Gray 
643f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
644aaec1a0fSWilliam Breathitt Gray 	mode_cfg = count_mode << 1;
645f1d8a071SWilliam Breathitt Gray 
646f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
647f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
648f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
649f1d8a071SWilliam Breathitt Gray 
650f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
651f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
652f1d8a071SWilliam Breathitt Gray 
65309db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
654fc069262SSyed Nayyar Waris 
655f1d8a071SWilliam Breathitt Gray 	return 0;
656f1d8a071SWilliam Breathitt Gray }
657f1d8a071SWilliam Breathitt Gray 
658aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_read(struct counter_device *counter,
659aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u8 *enable)
660f1d8a071SWilliam Breathitt Gray {
661aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
662f1d8a071SWilliam Breathitt Gray 
663aaec1a0fSWilliam Breathitt Gray 	*enable = priv->ab_enable[count->id];
664aaec1a0fSWilliam Breathitt Gray 
665aaec1a0fSWilliam Breathitt Gray 	return 0;
666f1d8a071SWilliam Breathitt Gray }
667f1d8a071SWilliam Breathitt Gray 
668aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_write(struct counter_device *counter,
669aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u8 enable)
670f1d8a071SWilliam Breathitt Gray {
671aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
672f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
67309db4678SWilliam Breathitt Gray 	unsigned long irqflags;
674f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
675f1d8a071SWilliam Breathitt Gray 
67609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
677fc069262SSyed Nayyar Waris 
678aaec1a0fSWilliam Breathitt Gray 	priv->ab_enable[count->id] = enable;
679f1d8a071SWilliam Breathitt Gray 
6807aa2ba0dSWilliam Breathitt Gray 	ior_cfg = enable | priv->preset_enable[count->id] << 1 |
6817aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
682f1d8a071SWilliam Breathitt Gray 
683f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
684f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
685f1d8a071SWilliam Breathitt Gray 
68609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
687fc069262SSyed Nayyar Waris 
688aaec1a0fSWilliam Breathitt Gray 	return 0;
689f1d8a071SWilliam Breathitt Gray }
690f1d8a071SWilliam Breathitt Gray 
691e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
692e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
693e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
694e357e81fSWilliam Breathitt Gray };
695e357e81fSWilliam Breathitt Gray 
696f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
697aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count, u32 *noise_error)
698f1d8a071SWilliam Breathitt Gray {
699aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
700f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
701f1d8a071SWilliam Breathitt Gray 
702f1d8a071SWilliam Breathitt Gray 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
703f1d8a071SWilliam Breathitt Gray 
704f1d8a071SWilliam Breathitt Gray 	return 0;
705f1d8a071SWilliam Breathitt Gray }
706f1d8a071SWilliam Breathitt Gray 
707aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_read(struct counter_device *counter,
708aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u64 *preset)
709f1d8a071SWilliam Breathitt Gray {
710aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
711f1d8a071SWilliam Breathitt Gray 
712aaec1a0fSWilliam Breathitt Gray 	*preset = priv->preset[count->id];
713aaec1a0fSWilliam Breathitt Gray 
714aaec1a0fSWilliam Breathitt Gray 	return 0;
715f1d8a071SWilliam Breathitt Gray }
716f1d8a071SWilliam Breathitt Gray 
717e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
718e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
719fc069262SSyed Nayyar Waris {
720e357e81fSWilliam Breathitt Gray 	const unsigned int base_offset = priv->base + 2 * id;
721fc069262SSyed Nayyar Waris 	int i;
722fc069262SSyed Nayyar Waris 
723e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
724fc069262SSyed Nayyar Waris 
725fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
726fc069262SSyed Nayyar Waris 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
727fc069262SSyed Nayyar Waris 
728fc069262SSyed Nayyar Waris 	/* Set Preset Register */
729fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
730fc069262SSyed Nayyar Waris 		outb(preset >> (8 * i), base_offset);
731fc069262SSyed Nayyar Waris }
732fc069262SSyed Nayyar Waris 
733aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_write(struct counter_device *counter,
734aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 preset)
735f1d8a071SWilliam Breathitt Gray {
736aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
73709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
738f1d8a071SWilliam Breathitt Gray 
739f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
740f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
741e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
742f1d8a071SWilliam Breathitt Gray 
74309db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
744f1d8a071SWilliam Breathitt Gray 
745fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
746f1d8a071SWilliam Breathitt Gray 
74709db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
748f1d8a071SWilliam Breathitt Gray 
749aaec1a0fSWilliam Breathitt Gray 	return 0;
750f1d8a071SWilliam Breathitt Gray }
751f1d8a071SWilliam Breathitt Gray 
752aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_read(struct counter_device *counter,
753aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 *ceiling)
754f1d8a071SWilliam Breathitt Gray {
755aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
75609db4678SWilliam Breathitt Gray 	unsigned long irqflags;
757fc069262SSyed Nayyar Waris 
75809db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
759f1d8a071SWilliam Breathitt Gray 
760f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
761f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
762f1d8a071SWilliam Breathitt Gray 	case 1:
763f1d8a071SWilliam Breathitt Gray 	case 3:
764aaec1a0fSWilliam Breathitt Gray 		*ceiling = priv->preset[count->id];
765aaec1a0fSWilliam Breathitt Gray 		break;
766aaec1a0fSWilliam Breathitt Gray 	default:
767f1d8a071SWilliam Breathitt Gray 		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
768aaec1a0fSWilliam Breathitt Gray 		*ceiling = 0x1FFFFFF;
769aaec1a0fSWilliam Breathitt Gray 		break;
770f1d8a071SWilliam Breathitt Gray 	}
771f1d8a071SWilliam Breathitt Gray 
77209db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
773aaec1a0fSWilliam Breathitt Gray 
774aaec1a0fSWilliam Breathitt Gray 	return 0;
775aaec1a0fSWilliam Breathitt Gray }
776aaec1a0fSWilliam Breathitt Gray 
777aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_write(struct counter_device *counter,
778aaec1a0fSWilliam Breathitt Gray 				     struct counter_count *count, u64 ceiling)
779f1d8a071SWilliam Breathitt Gray {
780aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
78109db4678SWilliam Breathitt Gray 	unsigned long irqflags;
782fc069262SSyed Nayyar Waris 
783fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
784fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
785e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
786fc069262SSyed Nayyar Waris 
78709db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
788f1d8a071SWilliam Breathitt Gray 
789f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
790f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
791f1d8a071SWilliam Breathitt Gray 	case 1:
792f1d8a071SWilliam Breathitt Gray 	case 3:
793fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
79409db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
795aaec1a0fSWilliam Breathitt Gray 		return 0;
796f1d8a071SWilliam Breathitt Gray 	}
797f1d8a071SWilliam Breathitt Gray 
79809db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
799fc069262SSyed Nayyar Waris 
800728246e8SWilliam Breathitt Gray 	return -EINVAL;
801f1d8a071SWilliam Breathitt Gray }
802f1d8a071SWilliam Breathitt Gray 
803aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_read(struct counter_device *counter,
804aaec1a0fSWilliam Breathitt Gray 					  struct counter_count *count,
805aaec1a0fSWilliam Breathitt Gray 					  u8 *preset_enable)
806f1d8a071SWilliam Breathitt Gray {
807aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
808f1d8a071SWilliam Breathitt Gray 
809aaec1a0fSWilliam Breathitt Gray 	*preset_enable = !priv->preset_enable[count->id];
810aaec1a0fSWilliam Breathitt Gray 
811aaec1a0fSWilliam Breathitt Gray 	return 0;
812f1d8a071SWilliam Breathitt Gray }
813f1d8a071SWilliam Breathitt Gray 
814aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_write(struct counter_device *counter,
815aaec1a0fSWilliam Breathitt Gray 					   struct counter_count *count,
816aaec1a0fSWilliam Breathitt Gray 					   u8 preset_enable)
817f1d8a071SWilliam Breathitt Gray {
818aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
819f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
82009db4678SWilliam Breathitt Gray 	unsigned long irqflags;
821f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
822f1d8a071SWilliam Breathitt Gray 
823f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
824f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
825f1d8a071SWilliam Breathitt Gray 
82609db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
827fc069262SSyed Nayyar Waris 
828f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
829f1d8a071SWilliam Breathitt Gray 
8307aa2ba0dSWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1 |
8317aa2ba0dSWilliam Breathitt Gray 		  priv->irq_trigger[count->id] << 3;
832f1d8a071SWilliam Breathitt Gray 
833f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
834f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
835f1d8a071SWilliam Breathitt Gray 
83609db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
837fc069262SSyed Nayyar Waris 
838aaec1a0fSWilliam Breathitt Gray 	return 0;
839f1d8a071SWilliam Breathitt Gray }
840f1d8a071SWilliam Breathitt Gray 
841aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_read(struct counter_device *counter,
842954ab5ccSWilliam Breathitt Gray 					 struct counter_signal *signal,
843aaec1a0fSWilliam Breathitt Gray 					 u8 *cable_fault)
844954ab5ccSWilliam Breathitt Gray {
845aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
846954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
84709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
848708d9893SSyed Nayyar Waris 	bool disabled;
849954ab5ccSWilliam Breathitt Gray 	unsigned int status;
850954ab5ccSWilliam Breathitt Gray 
85109db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
852708d9893SSyed Nayyar Waris 
853708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
854708d9893SSyed Nayyar Waris 
855708d9893SSyed Nayyar Waris 	if (disabled) {
85609db4678SWilliam Breathitt Gray 		spin_unlock_irqrestore(&priv->lock, irqflags);
857954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
858708d9893SSyed Nayyar Waris 	}
859954ab5ccSWilliam Breathitt Gray 
860954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
861954ab5ccSWilliam Breathitt Gray 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
862954ab5ccSWilliam Breathitt Gray 
86309db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
864708d9893SSyed Nayyar Waris 
865954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
866aaec1a0fSWilliam Breathitt Gray 	*cable_fault = !(status & BIT(channel_id));
867954ab5ccSWilliam Breathitt Gray 
868aaec1a0fSWilliam Breathitt Gray 	return 0;
869954ab5ccSWilliam Breathitt Gray }
870954ab5ccSWilliam Breathitt Gray 
871aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
872aaec1a0fSWilliam Breathitt Gray 						struct counter_signal *signal,
873aaec1a0fSWilliam Breathitt Gray 						u8 *enable)
874954ab5ccSWilliam Breathitt Gray {
875aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
876954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
877954ab5ccSWilliam Breathitt Gray 
878aaec1a0fSWilliam Breathitt Gray 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
879aaec1a0fSWilliam Breathitt Gray 
880aaec1a0fSWilliam Breathitt Gray 	return 0;
881954ab5ccSWilliam Breathitt Gray }
882954ab5ccSWilliam Breathitt Gray 
883aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
884aaec1a0fSWilliam Breathitt Gray 						 struct counter_signal *signal,
885aaec1a0fSWilliam Breathitt Gray 						 u8 enable)
886954ab5ccSWilliam Breathitt Gray {
887aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
888954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
88909db4678SWilliam Breathitt Gray 	unsigned long irqflags;
890954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
891954ab5ccSWilliam Breathitt Gray 
89209db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
893708d9893SSyed Nayyar Waris 
894954ab5ccSWilliam Breathitt Gray 	if (enable)
895954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
896954ab5ccSWilliam Breathitt Gray 	else
897954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
898954ab5ccSWilliam Breathitt Gray 
899954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
900954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
901954ab5ccSWilliam Breathitt Gray 
902954ab5ccSWilliam Breathitt Gray 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
903954ab5ccSWilliam Breathitt Gray 
90409db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
905708d9893SSyed Nayyar Waris 
906aaec1a0fSWilliam Breathitt Gray 	return 0;
907954ab5ccSWilliam Breathitt Gray }
908954ab5ccSWilliam Breathitt Gray 
909aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
910aaec1a0fSWilliam Breathitt Gray 					   struct counter_signal *signal,
911aaec1a0fSWilliam Breathitt Gray 					   u8 *prescaler)
912de65d055SWilliam Breathitt Gray {
913aea8334bSUwe Kleine-König 	const struct quad8 *const priv = counter_priv(counter);
914de65d055SWilliam Breathitt Gray 
915aaec1a0fSWilliam Breathitt Gray 	*prescaler = priv->fck_prescaler[signal->id / 2];
916aaec1a0fSWilliam Breathitt Gray 
917aaec1a0fSWilliam Breathitt Gray 	return 0;
918de65d055SWilliam Breathitt Gray }
919de65d055SWilliam Breathitt Gray 
920aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
921aaec1a0fSWilliam Breathitt Gray 					    struct counter_signal *signal,
922aaec1a0fSWilliam Breathitt Gray 					    u8 prescaler)
923de65d055SWilliam Breathitt Gray {
924aea8334bSUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
925de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
926de65d055SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id;
92709db4678SWilliam Breathitt Gray 	unsigned long irqflags;
928de65d055SWilliam Breathitt Gray 
92909db4678SWilliam Breathitt Gray 	spin_lock_irqsave(&priv->lock, irqflags);
930d5ed76adSSyed Nayyar Waris 
931de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
932de65d055SWilliam Breathitt Gray 
933de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
934de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
935de65d055SWilliam Breathitt Gray 
936de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
937de65d055SWilliam Breathitt Gray 	outb(prescaler, base_offset);
938de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
939de65d055SWilliam Breathitt Gray 	     base_offset + 1);
940de65d055SWilliam Breathitt Gray 
94109db4678SWilliam Breathitt Gray 	spin_unlock_irqrestore(&priv->lock, irqflags);
942d5ed76adSSyed Nayyar Waris 
943aaec1a0fSWilliam Breathitt Gray 	return 0;
944de65d055SWilliam Breathitt Gray }
945de65d055SWilliam Breathitt Gray 
946aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_signal_ext[] = {
947aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
948aaec1a0fSWilliam Breathitt Gray 				 NULL),
949aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
950aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_read,
951aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_write),
952aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
953aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_read,
954aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_write)
955de65d055SWilliam Breathitt Gray };
956de65d055SWilliam Breathitt Gray 
957aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
958aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
959aaec1a0fSWilliam Breathitt Gray 
960aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_index_ext[] = {
961aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
962aaec1a0fSWilliam Breathitt Gray 				 quad8_index_polarity_set,
963aaec1a0fSWilliam Breathitt Gray 				 quad8_index_pol_enum),
964aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
965aaec1a0fSWilliam Breathitt Gray 				 quad8_synchronous_mode_set,
966aaec1a0fSWilliam Breathitt Gray 				 quad8_synch_mode_enum),
967f1d8a071SWilliam Breathitt Gray };
968f1d8a071SWilliam Breathitt Gray 
969f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
970f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
971de65d055SWilliam Breathitt Gray 	.name = (_name),			\
972de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
973de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
974f1d8a071SWilliam Breathitt Gray }
975f1d8a071SWilliam Breathitt Gray 
976f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
977f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
978f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
979f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
980f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
981f1d8a071SWilliam Breathitt Gray }
982f1d8a071SWilliam Breathitt Gray 
983f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
984f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
985f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
986f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
987f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
988f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
989f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
990f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
991f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
992f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
993f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
994f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
995f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
996f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
997f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
998f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
999f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
1000f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
1001f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
1002f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
1003f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
1004f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
1005f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
1006f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
1007f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
1008f1d8a071SWilliam Breathitt Gray };
1009f1d8a071SWilliam Breathitt Gray 
1010f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
1011f1d8a071SWilliam Breathitt Gray 	{								\
1012f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1013f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1014f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
1015f1d8a071SWilliam Breathitt Gray 	},								\
1016f1d8a071SWilliam Breathitt Gray 	{								\
1017f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
1018f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
1019f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
1020f1d8a071SWilliam Breathitt Gray 	},								\
1021f1d8a071SWilliam Breathitt Gray 	{								\
1022f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
1023f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
1024f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
1025f1d8a071SWilliam Breathitt Gray 	}								\
1026f1d8a071SWilliam Breathitt Gray }
1027f1d8a071SWilliam Breathitt Gray 
1028f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
1029f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
1030f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
1031f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
1032f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
1033f1d8a071SWilliam Breathitt Gray };
1034f1d8a071SWilliam Breathitt Gray 
1035aaec1a0fSWilliam Breathitt Gray static const enum counter_count_mode quad8_cnt_modes[] = {
1036aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NORMAL,
1037aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_RANGE_LIMIT,
1038aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NON_RECYCLE,
1039aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_MODULO_N,
1040aaec1a0fSWilliam Breathitt Gray };
1041aaec1a0fSWilliam Breathitt Gray 
1042aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
1043aaec1a0fSWilliam Breathitt Gray 
1044aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
1045aaec1a0fSWilliam Breathitt Gray 
1046aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_count_ext[] = {
1047aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
1048aaec1a0fSWilliam Breathitt Gray 			     quad8_count_ceiling_write),
1049aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
1050aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
1051aaec1a0fSWilliam Breathitt Gray 				quad8_count_mode_available),
1052aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_DIRECTION(quad8_direction_read),
1053aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
1054aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
1055aaec1a0fSWilliam Breathitt Gray 				quad8_error_noise_enum),
1056aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
1057aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
1058aaec1a0fSWilliam Breathitt Gray 				   quad8_count_preset_enable_write),
1059f1d8a071SWilliam Breathitt Gray };
1060f1d8a071SWilliam Breathitt Gray 
1061f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1062f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1063f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1064f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1065f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1066f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1067f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1068f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1069f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1070f1d8a071SWilliam Breathitt Gray }
1071f1d8a071SWilliam Breathitt Gray 
1072f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1073f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1074f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1075f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1076f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1077f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1078f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1079f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1080f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1081f1d8a071SWilliam Breathitt Gray };
1082f1d8a071SWilliam Breathitt Gray 
10837aa2ba0dSWilliam Breathitt Gray static irqreturn_t quad8_irq_handler(int irq, void *private)
10847aa2ba0dSWilliam Breathitt Gray {
1085*9e884bb1SUwe Kleine-König 	struct counter_device *counter = private;
1086*9e884bb1SUwe Kleine-König 	struct quad8 *const priv = counter_priv(counter);
10877aa2ba0dSWilliam Breathitt Gray 	const unsigned long base = priv->base;
10887aa2ba0dSWilliam Breathitt Gray 	unsigned long irq_status;
10897aa2ba0dSWilliam Breathitt Gray 	unsigned long channel;
10907aa2ba0dSWilliam Breathitt Gray 	u8 event;
10917aa2ba0dSWilliam Breathitt Gray 
10927aa2ba0dSWilliam Breathitt Gray 	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
10937aa2ba0dSWilliam Breathitt Gray 	if (!irq_status)
10947aa2ba0dSWilliam Breathitt Gray 		return IRQ_NONE;
10957aa2ba0dSWilliam Breathitt Gray 
10967aa2ba0dSWilliam Breathitt Gray 	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
10977aa2ba0dSWilliam Breathitt Gray 		switch (priv->irq_trigger[channel]) {
10987aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY:
10997aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW;
11007aa2ba0dSWilliam Breathitt Gray 				break;
11017aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_COMPARE:
11027aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_THRESHOLD;
11037aa2ba0dSWilliam Breathitt Gray 				break;
11047aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_CARRY_BORROW:
11057aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
11067aa2ba0dSWilliam Breathitt Gray 				break;
11077aa2ba0dSWilliam Breathitt Gray 		case QUAD8_EVENT_INDEX:
11087aa2ba0dSWilliam Breathitt Gray 			event = COUNTER_EVENT_INDEX;
11097aa2ba0dSWilliam Breathitt Gray 				break;
11107aa2ba0dSWilliam Breathitt Gray 		default:
11117aa2ba0dSWilliam Breathitt Gray 			/* should never reach this path */
11127aa2ba0dSWilliam Breathitt Gray 			WARN_ONCE(true, "invalid interrupt trigger function %u configured for channel %lu\n",
11137aa2ba0dSWilliam Breathitt Gray 				  priv->irq_trigger[channel], channel);
11147aa2ba0dSWilliam Breathitt Gray 			continue;
11157aa2ba0dSWilliam Breathitt Gray 		}
11167aa2ba0dSWilliam Breathitt Gray 
1117*9e884bb1SUwe Kleine-König 		counter_push_event(counter, event, channel);
11187aa2ba0dSWilliam Breathitt Gray 	}
11197aa2ba0dSWilliam Breathitt Gray 
11207aa2ba0dSWilliam Breathitt Gray 	/* Clear pending interrupts on device */
11217aa2ba0dSWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
11227aa2ba0dSWilliam Breathitt Gray 
11237aa2ba0dSWilliam Breathitt Gray 	return IRQ_HANDLED;
11247aa2ba0dSWilliam Breathitt Gray }
11257aa2ba0dSWilliam Breathitt Gray 
1126f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1127f1d8a071SWilliam Breathitt Gray {
1128*9e884bb1SUwe Kleine-König 	struct counter_device *counter;
1129e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1130f1d8a071SWilliam Breathitt Gray 	int i, j;
1131f1d8a071SWilliam Breathitt Gray 	unsigned int base_offset;
11327aa2ba0dSWilliam Breathitt Gray 	int err;
1133f1d8a071SWilliam Breathitt Gray 
1134f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1135f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1136f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1137f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1138f1d8a071SWilliam Breathitt Gray 	}
1139f1d8a071SWilliam Breathitt Gray 
1140*9e884bb1SUwe Kleine-König 	counter = devm_counter_alloc(dev, sizeof(*priv));
1141*9e884bb1SUwe Kleine-König 	if (!counter)
1142f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
1143*9e884bb1SUwe Kleine-König 	priv = counter_priv(counter);
1144f1d8a071SWilliam Breathitt Gray 
1145f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
1146*9e884bb1SUwe Kleine-König 	counter->name = dev_name(dev);
1147*9e884bb1SUwe Kleine-König 	counter->parent = dev;
1148*9e884bb1SUwe Kleine-König 	counter->ops = &quad8_ops;
1149*9e884bb1SUwe Kleine-König 	counter->counts = quad8_counts;
1150*9e884bb1SUwe Kleine-König 	counter->num_counts = ARRAY_SIZE(quad8_counts);
1151*9e884bb1SUwe Kleine-König 	counter->signals = quad8_signals;
1152*9e884bb1SUwe Kleine-König 	counter->num_signals = ARRAY_SIZE(quad8_signals);
1153e357e81fSWilliam Breathitt Gray 	priv->base = base[id];
1154f1d8a071SWilliam Breathitt Gray 
115509db4678SWilliam Breathitt Gray 	spin_lock_init(&priv->lock);
1156fc069262SSyed Nayyar Waris 
11577aa2ba0dSWilliam Breathitt Gray 	/* Reset Index/Interrupt Register */
11587aa2ba0dSWilliam Breathitt Gray 	outb(0x00, base[id] + QUAD8_REG_INDEX_INTERRUPT);
1159f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1160f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1161f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1162f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1163f1d8a071SWilliam Breathitt Gray 		base_offset = base[id] + 2 * i;
1164f1d8a071SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1165f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1166de65d055SWilliam Breathitt Gray 		/* Reset filter clock factor */
1167de65d055SWilliam Breathitt Gray 		outb(0, base_offset);
1168de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1169de65d055SWilliam Breathitt Gray 		     base_offset + 1);
1170de65d055SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1171de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1172f1d8a071SWilliam Breathitt Gray 		/* Reset Preset Register */
1173f1d8a071SWilliam Breathitt Gray 		for (j = 0; j < 3; j++)
1174f1d8a071SWilliam Breathitt Gray 			outb(0x00, base_offset);
1175f1d8a071SWilliam Breathitt Gray 		/* Reset Borrow, Carry, Compare, and Sign flags */
1176f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1177f1d8a071SWilliam Breathitt Gray 		/* Reset Error flag */
1178f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1179f1d8a071SWilliam Breathitt Gray 		/* Binary encoding; Normal count; non-quadrature mode */
1180f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_CMR, base_offset + 1);
1181f1d8a071SWilliam Breathitt Gray 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1182f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR, base_offset + 1);
1183f1d8a071SWilliam Breathitt Gray 		/* Disable index function; negative index polarity */
1184f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IDR, base_offset + 1);
1185f1d8a071SWilliam Breathitt Gray 	}
1186954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1187954ab5ccSWilliam Breathitt Gray 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
11887aa2ba0dSWilliam Breathitt Gray 	/* Enable all counters and enable interrupt function */
11897aa2ba0dSWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP);
11907aa2ba0dSWilliam Breathitt Gray 
11917aa2ba0dSWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED,
1192*9e884bb1SUwe Kleine-König 			       counter->name, counter);
11937aa2ba0dSWilliam Breathitt Gray 	if (err)
11947aa2ba0dSWilliam Breathitt Gray 		return err;
1195f1d8a071SWilliam Breathitt Gray 
1196*9e884bb1SUwe Kleine-König 	err = devm_counter_add(dev, counter);
1197*9e884bb1SUwe Kleine-König 	if (err < 0)
1198*9e884bb1SUwe Kleine-König 		return dev_err_probe(dev, err, "Failed to add counter\n");
1199*9e884bb1SUwe Kleine-König 
1200*9e884bb1SUwe Kleine-König 	return 0;
1201f1d8a071SWilliam Breathitt Gray }
1202f1d8a071SWilliam Breathitt Gray 
1203f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1204f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1205f1d8a071SWilliam Breathitt Gray 	.driver = {
1206f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1207f1d8a071SWilliam Breathitt Gray 	}
1208f1d8a071SWilliam Breathitt Gray };
1209f1d8a071SWilliam Breathitt Gray 
1210f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1211f1d8a071SWilliam Breathitt Gray 
1212f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1213e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1214f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1215