xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision aaec1a0f)
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>
14f1d8a071SWilliam Breathitt Gray #include <linux/isa.h>
15f1d8a071SWilliam Breathitt Gray #include <linux/kernel.h>
16f1d8a071SWilliam Breathitt Gray #include <linux/module.h>
17f1d8a071SWilliam Breathitt Gray #include <linux/moduleparam.h>
18f1d8a071SWilliam Breathitt Gray #include <linux/types.h>
19f1d8a071SWilliam Breathitt Gray 
20f1d8a071SWilliam Breathitt Gray #define QUAD8_EXTENT 32
21f1d8a071SWilliam Breathitt Gray 
22f1d8a071SWilliam Breathitt Gray static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
23f1d8a071SWilliam Breathitt Gray static unsigned int num_quad8;
24af383bb1SWilliam Breathitt Gray module_param_hw_array(base, uint, ioport, &num_quad8, 0);
25f1d8a071SWilliam Breathitt Gray MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
26f1d8a071SWilliam Breathitt Gray 
27f1d8a071SWilliam Breathitt Gray #define QUAD8_NUM_COUNTERS 8
28f1d8a071SWilliam Breathitt Gray 
29f1d8a071SWilliam Breathitt Gray /**
30e357e81fSWilliam Breathitt Gray  * struct quad8 - device private data structure
3194a853ecSWilliam Breathitt Gray  * @lock:		lock to prevent clobbering device states during R/W ops
32f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
33954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
34f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
35f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
36f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
37f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
38f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
39f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
40f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
41f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
42954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
43e357e81fSWilliam Breathitt Gray  * @base:		base port address of the device
44f1d8a071SWilliam Breathitt Gray  */
45e357e81fSWilliam Breathitt Gray struct quad8 {
46fc069262SSyed Nayyar Waris 	struct mutex lock;
47f1d8a071SWilliam Breathitt Gray 	struct counter_device counter;
48de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
49f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
50f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
51f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
52f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
53f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
54f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
55f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
56f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
57954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
58f1d8a071SWilliam Breathitt Gray 	unsigned int base;
59f1d8a071SWilliam Breathitt Gray };
60f1d8a071SWilliam Breathitt Gray 
61f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_CHAN_OP 0x11
62f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
63954ab5ccSWilliam Breathitt Gray #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
64f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
65f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
66f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
67f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
68f1d8a071SWilliam Breathitt Gray /* Error flag */
69f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
70f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
71f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
72f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
73f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
74f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
75f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
76f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
77f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
78f1d8a071SWilliam Breathitt Gray /* Index Control Register */
79f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
80f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
81f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
82f1d8a071SWilliam Breathitt Gray /* Reset Counter */
83f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
84f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
85f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
86f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
87f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
88f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
89f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
90f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
91f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
92de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
93de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
94f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
95f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
96f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
97f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
98f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
99f1d8a071SWilliam Breathitt Gray 
100f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
101493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
102493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
103f1d8a071SWilliam Breathitt Gray {
104e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
105f1d8a071SWilliam Breathitt Gray 	unsigned int state;
106f1d8a071SWilliam Breathitt Gray 
107f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
108f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
109f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
110f1d8a071SWilliam Breathitt Gray 
111f1d8a071SWilliam Breathitt Gray 	state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
112f1d8a071SWilliam Breathitt Gray 		& BIT(signal->id - 16);
113f1d8a071SWilliam Breathitt Gray 
114493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
115f1d8a071SWilliam Breathitt Gray 
116f1d8a071SWilliam Breathitt Gray 	return 0;
117f1d8a071SWilliam Breathitt Gray }
118f1d8a071SWilliam Breathitt Gray 
119f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
120*aaec1a0fSWilliam Breathitt Gray 			    struct counter_count *count, u64 *val)
121f1d8a071SWilliam Breathitt Gray {
122e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
123f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
124f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
125f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
126f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
127f1d8a071SWilliam Breathitt Gray 	int i;
128f1d8a071SWilliam Breathitt Gray 
129f1d8a071SWilliam Breathitt Gray 	flags = inb(base_offset + 1);
130f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
131f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
132f1d8a071SWilliam Breathitt Gray 
133f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
134d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
135f1d8a071SWilliam Breathitt Gray 
136fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
137fc069262SSyed Nayyar Waris 
138f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
139f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
140f1d8a071SWilliam Breathitt Gray 	     base_offset + 1);
141f1d8a071SWilliam Breathitt Gray 
142f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
143d49e6ee2SWilliam Breathitt Gray 		*val |= (unsigned long)inb(base_offset) << (8 * i);
144f1d8a071SWilliam Breathitt Gray 
145fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
146fc069262SSyed Nayyar Waris 
147f1d8a071SWilliam Breathitt Gray 	return 0;
148f1d8a071SWilliam Breathitt Gray }
149f1d8a071SWilliam Breathitt Gray 
150f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
151*aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count, u64 val)
152f1d8a071SWilliam Breathitt Gray {
153e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
154f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
155f1d8a071SWilliam Breathitt Gray 	int i;
156f1d8a071SWilliam Breathitt Gray 
157f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
158d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
159e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
160f1d8a071SWilliam Breathitt Gray 
161fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
162fc069262SSyed Nayyar Waris 
163f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
164f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
165f1d8a071SWilliam Breathitt Gray 
166f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
167f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
168d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
169f1d8a071SWilliam Breathitt Gray 
170f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
171f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
172f1d8a071SWilliam Breathitt Gray 
173f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
174f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
175f1d8a071SWilliam Breathitt Gray 
176f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
177d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
178f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
179d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
180f1d8a071SWilliam Breathitt Gray 
181f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
182f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
183f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
184f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
185f1d8a071SWilliam Breathitt Gray 
186fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
187fc069262SSyed Nayyar Waris 
188f1d8a071SWilliam Breathitt Gray 	return 0;
189f1d8a071SWilliam Breathitt Gray }
190f1d8a071SWilliam Breathitt Gray 
191394a0150SWilliam Breathitt Gray static const enum counter_function quad8_count_functions_list[] = {
192*aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_PULSE_DIRECTION,
193*aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X1_A,
194*aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X2_A,
195*aaec1a0fSWilliam Breathitt Gray 	COUNTER_FUNCTION_QUADRATURE_X4,
196f1d8a071SWilliam Breathitt Gray };
197f1d8a071SWilliam Breathitt Gray 
198*aaec1a0fSWilliam Breathitt Gray static int quad8_function_read(struct counter_device *counter,
199*aaec1a0fSWilliam Breathitt Gray 			       struct counter_count *count,
200*aaec1a0fSWilliam Breathitt Gray 			       enum counter_function *function)
201f1d8a071SWilliam Breathitt Gray {
202e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
203f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
204f1d8a071SWilliam Breathitt Gray 
205fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
206fc069262SSyed Nayyar Waris 
207fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
208fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
209f1d8a071SWilliam Breathitt Gray 		case 0:
210*aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X1_A;
211f1d8a071SWilliam Breathitt Gray 			break;
212f1d8a071SWilliam Breathitt Gray 		case 1:
213*aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X2_A;
214f1d8a071SWilliam Breathitt Gray 			break;
215f1d8a071SWilliam Breathitt Gray 		case 2:
216*aaec1a0fSWilliam Breathitt Gray 			*function = COUNTER_FUNCTION_QUADRATURE_X4;
217f1d8a071SWilliam Breathitt Gray 			break;
218f1d8a071SWilliam Breathitt Gray 		}
219f1d8a071SWilliam Breathitt Gray 	else
220*aaec1a0fSWilliam Breathitt Gray 		*function = COUNTER_FUNCTION_PULSE_DIRECTION;
221f1d8a071SWilliam Breathitt Gray 
222fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
223fc069262SSyed Nayyar Waris 
224f1d8a071SWilliam Breathitt Gray 	return 0;
225f1d8a071SWilliam Breathitt Gray }
226f1d8a071SWilliam Breathitt Gray 
227*aaec1a0fSWilliam Breathitt Gray static int quad8_function_write(struct counter_device *counter,
228*aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
229*aaec1a0fSWilliam Breathitt Gray 				enum counter_function function)
230f1d8a071SWilliam Breathitt Gray {
231e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
232f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
233f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
234f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
235f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
236f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * id + 1;
237fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
238fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
239fc069262SSyed Nayyar Waris 
240fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
241fc069262SSyed Nayyar Waris 
242fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
243fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
244f1d8a071SWilliam Breathitt Gray 
245*aaec1a0fSWilliam Breathitt Gray 	if (function == COUNTER_FUNCTION_PULSE_DIRECTION) {
246f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
247f1d8a071SWilliam Breathitt Gray 
248f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
249f1d8a071SWilliam Breathitt Gray 		*scale = 0;
250f1d8a071SWilliam Breathitt Gray 
251f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
252f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
253f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
254f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
255f1d8a071SWilliam Breathitt Gray 			outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
256f1d8a071SWilliam Breathitt Gray 		}
257f1d8a071SWilliam Breathitt Gray 	} else {
258f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
259f1d8a071SWilliam Breathitt Gray 
260f1d8a071SWilliam Breathitt Gray 		switch (function) {
261*aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X1_A:
262f1d8a071SWilliam Breathitt Gray 			*scale = 0;
263f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
264f1d8a071SWilliam Breathitt Gray 			break;
265*aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X2_A:
266f1d8a071SWilliam Breathitt Gray 			*scale = 1;
267f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
268f1d8a071SWilliam Breathitt Gray 			break;
269*aaec1a0fSWilliam Breathitt Gray 		case COUNTER_FUNCTION_QUADRATURE_X4:
270f1d8a071SWilliam Breathitt Gray 			*scale = 2;
271f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
272f1d8a071SWilliam Breathitt Gray 			break;
273b11eed15SWilliam Breathitt Gray 		default:
274b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
275b11eed15SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
276b11eed15SWilliam Breathitt Gray 			return -EINVAL;
277f1d8a071SWilliam Breathitt Gray 		}
278f1d8a071SWilliam Breathitt Gray 	}
279f1d8a071SWilliam Breathitt Gray 
280f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
281f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
282f1d8a071SWilliam Breathitt Gray 
283fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
284fc069262SSyed Nayyar Waris 
285f1d8a071SWilliam Breathitt Gray 	return 0;
286f1d8a071SWilliam Breathitt Gray }
287f1d8a071SWilliam Breathitt Gray 
288*aaec1a0fSWilliam Breathitt Gray static int quad8_direction_read(struct counter_device *counter,
289*aaec1a0fSWilliam Breathitt Gray 				struct counter_count *count,
290*aaec1a0fSWilliam Breathitt Gray 				enum counter_count_direction *direction)
291f1d8a071SWilliam Breathitt Gray {
292e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
293f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
294f1d8a071SWilliam Breathitt Gray 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
295f1d8a071SWilliam Breathitt Gray 
296f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
297f1d8a071SWilliam Breathitt Gray 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
298f1d8a071SWilliam Breathitt Gray 
299f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
300f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
301*aaec1a0fSWilliam Breathitt Gray 
302*aaec1a0fSWilliam Breathitt Gray 	return 0;
303f1d8a071SWilliam Breathitt Gray }
304f1d8a071SWilliam Breathitt Gray 
3056a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
306*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
307*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
308f1d8a071SWilliam Breathitt Gray };
309f1d8a071SWilliam Breathitt Gray 
3106a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
311*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_NONE,
312*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
313*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
314*aaec1a0fSWilliam Breathitt Gray 	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
315f1d8a071SWilliam Breathitt Gray };
316f1d8a071SWilliam Breathitt Gray 
317*aaec1a0fSWilliam Breathitt Gray static int quad8_action_read(struct counter_device *counter,
318*aaec1a0fSWilliam Breathitt Gray 			     struct counter_count *count,
319*aaec1a0fSWilliam Breathitt Gray 			     struct counter_synapse *synapse,
320*aaec1a0fSWilliam Breathitt Gray 			     enum counter_synapse_action *action)
321f1d8a071SWilliam Breathitt Gray {
322e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
323f1d8a071SWilliam Breathitt Gray 	int err;
324*aaec1a0fSWilliam Breathitt Gray 	enum counter_function function;
325f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
326f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
327f1d8a071SWilliam Breathitt Gray 
328f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
329f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
330f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
331*aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
332f1d8a071SWilliam Breathitt Gray 		else
333*aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_NONE;
334f1d8a071SWilliam Breathitt Gray 
335f1d8a071SWilliam Breathitt Gray 		return 0;
336f1d8a071SWilliam Breathitt Gray 	}
337f1d8a071SWilliam Breathitt Gray 
338*aaec1a0fSWilliam Breathitt Gray 	err = quad8_function_read(counter, count, &function);
339f1d8a071SWilliam Breathitt Gray 	if (err)
340f1d8a071SWilliam Breathitt Gray 		return err;
341f1d8a071SWilliam Breathitt Gray 
342f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
343*aaec1a0fSWilliam Breathitt Gray 	*action = COUNTER_SYNAPSE_ACTION_NONE;
344f1d8a071SWilliam Breathitt Gray 
345f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
346f1d8a071SWilliam Breathitt Gray 	switch (function) {
347*aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_PULSE_DIRECTION:
348f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
349*aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
350b11eed15SWilliam Breathitt Gray 		return 0;
351*aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X1_A:
352f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
353*aaec1a0fSWilliam Breathitt Gray 			err = quad8_direction_read(counter, count, &direction);
354*aaec1a0fSWilliam Breathitt Gray 			if (err)
355*aaec1a0fSWilliam Breathitt Gray 				return err;
356f1d8a071SWilliam Breathitt Gray 
357f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
358*aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
359f1d8a071SWilliam Breathitt Gray 			else
360*aaec1a0fSWilliam Breathitt Gray 				*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
361f1d8a071SWilliam Breathitt Gray 		}
362b11eed15SWilliam Breathitt Gray 		return 0;
363*aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X2_A:
364f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
365*aaec1a0fSWilliam Breathitt Gray 			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
366b11eed15SWilliam Breathitt Gray 		return 0;
367*aaec1a0fSWilliam Breathitt Gray 	case COUNTER_FUNCTION_QUADRATURE_X4:
368*aaec1a0fSWilliam Breathitt Gray 		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
369f1d8a071SWilliam Breathitt Gray 		return 0;
370b11eed15SWilliam Breathitt Gray 	default:
371b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
372b11eed15SWilliam Breathitt Gray 		return -EINVAL;
373b11eed15SWilliam Breathitt Gray 	}
374f1d8a071SWilliam Breathitt Gray }
375f1d8a071SWilliam Breathitt Gray 
37617aa207eSYueHaibing static const struct counter_ops quad8_ops = {
377f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
378f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
379f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
380*aaec1a0fSWilliam Breathitt Gray 	.function_read = quad8_function_read,
381*aaec1a0fSWilliam Breathitt Gray 	.function_write = quad8_function_write,
382*aaec1a0fSWilliam Breathitt Gray 	.action_read = quad8_action_read
383f1d8a071SWilliam Breathitt Gray };
384f1d8a071SWilliam Breathitt Gray 
385e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
386e357e81fSWilliam Breathitt Gray 	"negative",
387e357e81fSWilliam Breathitt Gray 	"positive"
388e357e81fSWilliam Breathitt Gray };
389e357e81fSWilliam Breathitt Gray 
390f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
391*aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
392*aaec1a0fSWilliam Breathitt Gray 				    u32 *index_polarity)
393f1d8a071SWilliam Breathitt Gray {
394e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
395f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
396f1d8a071SWilliam Breathitt Gray 
397f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
398f1d8a071SWilliam Breathitt Gray 
399f1d8a071SWilliam Breathitt Gray 	return 0;
400f1d8a071SWilliam Breathitt Gray }
401f1d8a071SWilliam Breathitt Gray 
402f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
403*aaec1a0fSWilliam Breathitt Gray 				    struct counter_signal *signal,
404*aaec1a0fSWilliam Breathitt Gray 				    u32 index_polarity)
405f1d8a071SWilliam Breathitt Gray {
406e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
407f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
408f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
409fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
410fc069262SSyed Nayyar Waris 
411fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
412fc069262SSyed Nayyar Waris 
413fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
414f1d8a071SWilliam Breathitt Gray 
415f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
416f1d8a071SWilliam Breathitt Gray 
417f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
418f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
419f1d8a071SWilliam Breathitt Gray 
420fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
421fc069262SSyed Nayyar Waris 
422f1d8a071SWilliam Breathitt Gray 	return 0;
423f1d8a071SWilliam Breathitt Gray }
424f1d8a071SWilliam Breathitt Gray 
425e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
426e357e81fSWilliam Breathitt Gray 	"non-synchronous",
427e357e81fSWilliam Breathitt Gray 	"synchronous"
428e357e81fSWilliam Breathitt Gray };
429e357e81fSWilliam Breathitt Gray 
430f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
431*aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
432*aaec1a0fSWilliam Breathitt Gray 				      u32 *synchronous_mode)
433f1d8a071SWilliam Breathitt Gray {
434e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
435f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
436f1d8a071SWilliam Breathitt Gray 
437f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
438f1d8a071SWilliam Breathitt Gray 
439f1d8a071SWilliam Breathitt Gray 	return 0;
440f1d8a071SWilliam Breathitt Gray }
441f1d8a071SWilliam Breathitt Gray 
442f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
443*aaec1a0fSWilliam Breathitt Gray 				      struct counter_signal *signal,
444*aaec1a0fSWilliam Breathitt Gray 				      u32 synchronous_mode)
445f1d8a071SWilliam Breathitt Gray {
446e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
447f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
448f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
449fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
450fc069262SSyed Nayyar Waris 
451fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
452fc069262SSyed Nayyar Waris 
453fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
454f1d8a071SWilliam Breathitt Gray 
455f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
456fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
457fc069262SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
458f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
459fc069262SSyed Nayyar Waris 	}
460f1d8a071SWilliam Breathitt Gray 
461f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
462f1d8a071SWilliam Breathitt Gray 
463f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
464f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
465f1d8a071SWilliam Breathitt Gray 
466fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
467fc069262SSyed Nayyar Waris 
468f1d8a071SWilliam Breathitt Gray 	return 0;
469f1d8a071SWilliam Breathitt Gray }
470f1d8a071SWilliam Breathitt Gray 
471*aaec1a0fSWilliam Breathitt Gray static int quad8_count_floor_read(struct counter_device *counter,
472*aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count, u64 *floor)
473f1d8a071SWilliam Breathitt Gray {
474f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
475*aaec1a0fSWilliam Breathitt Gray 	*floor = 0;
476*aaec1a0fSWilliam Breathitt Gray 
477*aaec1a0fSWilliam Breathitt Gray 	return 0;
478f1d8a071SWilliam Breathitt Gray }
479f1d8a071SWilliam Breathitt Gray 
480*aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_read(struct counter_device *counter,
481*aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count,
482*aaec1a0fSWilliam Breathitt Gray 				 enum counter_count_mode *cnt_mode)
483f1d8a071SWilliam Breathitt Gray {
484e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
485f1d8a071SWilliam Breathitt Gray 
486f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
487f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
488f1d8a071SWilliam Breathitt Gray 	case 0:
489f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
490f1d8a071SWilliam Breathitt Gray 		break;
491f1d8a071SWilliam Breathitt Gray 	case 1:
492f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
493f1d8a071SWilliam Breathitt Gray 		break;
494f1d8a071SWilliam Breathitt Gray 	case 2:
495f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
496f1d8a071SWilliam Breathitt Gray 		break;
497f1d8a071SWilliam Breathitt Gray 	case 3:
498f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
499f1d8a071SWilliam Breathitt Gray 		break;
500f1d8a071SWilliam Breathitt Gray 	}
501f1d8a071SWilliam Breathitt Gray 
502f1d8a071SWilliam Breathitt Gray 	return 0;
503f1d8a071SWilliam Breathitt Gray }
504f1d8a071SWilliam Breathitt Gray 
505*aaec1a0fSWilliam Breathitt Gray static int quad8_count_mode_write(struct counter_device *counter,
506*aaec1a0fSWilliam Breathitt Gray 				  struct counter_count *count,
507*aaec1a0fSWilliam Breathitt Gray 				  enum counter_count_mode cnt_mode)
508f1d8a071SWilliam Breathitt Gray {
509e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
510*aaec1a0fSWilliam Breathitt Gray 	unsigned int count_mode;
511f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
512f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
513f1d8a071SWilliam Breathitt Gray 
514f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
515f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
516f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
517*aaec1a0fSWilliam Breathitt Gray 		count_mode = 0;
518f1d8a071SWilliam Breathitt Gray 		break;
519f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
520*aaec1a0fSWilliam Breathitt Gray 		count_mode = 1;
521f1d8a071SWilliam Breathitt Gray 		break;
522f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
523*aaec1a0fSWilliam Breathitt Gray 		count_mode = 2;
524f1d8a071SWilliam Breathitt Gray 		break;
525f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
526*aaec1a0fSWilliam Breathitt Gray 		count_mode = 3;
527f1d8a071SWilliam Breathitt Gray 		break;
528b11eed15SWilliam Breathitt Gray 	default:
529b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
530b11eed15SWilliam Breathitt Gray 		return -EINVAL;
531f1d8a071SWilliam Breathitt Gray 	}
532f1d8a071SWilliam Breathitt Gray 
533fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
534fc069262SSyed Nayyar Waris 
535*aaec1a0fSWilliam Breathitt Gray 	priv->count_mode[count->id] = count_mode;
536f1d8a071SWilliam Breathitt Gray 
537f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
538*aaec1a0fSWilliam Breathitt Gray 	mode_cfg = count_mode << 1;
539f1d8a071SWilliam Breathitt Gray 
540f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
541f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
542f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
543f1d8a071SWilliam Breathitt Gray 
544f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
545f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
546f1d8a071SWilliam Breathitt Gray 
547fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
548fc069262SSyed Nayyar Waris 
549f1d8a071SWilliam Breathitt Gray 	return 0;
550f1d8a071SWilliam Breathitt Gray }
551f1d8a071SWilliam Breathitt Gray 
552*aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_read(struct counter_device *counter,
553*aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u8 *enable)
554f1d8a071SWilliam Breathitt Gray {
555e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
556f1d8a071SWilliam Breathitt Gray 
557*aaec1a0fSWilliam Breathitt Gray 	*enable = priv->ab_enable[count->id];
558*aaec1a0fSWilliam Breathitt Gray 
559*aaec1a0fSWilliam Breathitt Gray 	return 0;
560f1d8a071SWilliam Breathitt Gray }
561f1d8a071SWilliam Breathitt Gray 
562*aaec1a0fSWilliam Breathitt Gray static int quad8_count_enable_write(struct counter_device *counter,
563*aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u8 enable)
564f1d8a071SWilliam Breathitt Gray {
565e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
566f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
567f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
568f1d8a071SWilliam Breathitt Gray 
569fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
570fc069262SSyed Nayyar Waris 
571*aaec1a0fSWilliam Breathitt Gray 	priv->ab_enable[count->id] = enable;
572f1d8a071SWilliam Breathitt Gray 
573*aaec1a0fSWilliam Breathitt Gray 	ior_cfg = enable | priv->preset_enable[count->id] << 1;
574f1d8a071SWilliam Breathitt Gray 
575f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
576f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
577f1d8a071SWilliam Breathitt Gray 
578fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
579fc069262SSyed Nayyar Waris 
580*aaec1a0fSWilliam Breathitt Gray 	return 0;
581f1d8a071SWilliam Breathitt Gray }
582f1d8a071SWilliam Breathitt Gray 
583e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
584e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
585e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
586e357e81fSWilliam Breathitt Gray };
587e357e81fSWilliam Breathitt Gray 
588f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
589*aaec1a0fSWilliam Breathitt Gray 				 struct counter_count *count, u32 *noise_error)
590f1d8a071SWilliam Breathitt Gray {
591e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
592f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
593f1d8a071SWilliam Breathitt Gray 
594f1d8a071SWilliam Breathitt Gray 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
595f1d8a071SWilliam Breathitt Gray 
596f1d8a071SWilliam Breathitt Gray 	return 0;
597f1d8a071SWilliam Breathitt Gray }
598f1d8a071SWilliam Breathitt Gray 
599*aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_read(struct counter_device *counter,
600*aaec1a0fSWilliam Breathitt Gray 				   struct counter_count *count, u64 *preset)
601f1d8a071SWilliam Breathitt Gray {
602e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
603f1d8a071SWilliam Breathitt Gray 
604*aaec1a0fSWilliam Breathitt Gray 	*preset = priv->preset[count->id];
605*aaec1a0fSWilliam Breathitt Gray 
606*aaec1a0fSWilliam Breathitt Gray 	return 0;
607f1d8a071SWilliam Breathitt Gray }
608f1d8a071SWilliam Breathitt Gray 
609e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
610e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
611fc069262SSyed Nayyar Waris {
612e357e81fSWilliam Breathitt Gray 	const unsigned int base_offset = priv->base + 2 * id;
613fc069262SSyed Nayyar Waris 	int i;
614fc069262SSyed Nayyar Waris 
615e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
616fc069262SSyed Nayyar Waris 
617fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
618fc069262SSyed Nayyar Waris 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
619fc069262SSyed Nayyar Waris 
620fc069262SSyed Nayyar Waris 	/* Set Preset Register */
621fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
622fc069262SSyed Nayyar Waris 		outb(preset >> (8 * i), base_offset);
623fc069262SSyed Nayyar Waris }
624fc069262SSyed Nayyar Waris 
625*aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_write(struct counter_device *counter,
626*aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 preset)
627f1d8a071SWilliam Breathitt Gray {
628e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
629f1d8a071SWilliam Breathitt Gray 
630f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
631f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
632e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
633f1d8a071SWilliam Breathitt Gray 
634fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
635f1d8a071SWilliam Breathitt Gray 
636fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
637f1d8a071SWilliam Breathitt Gray 
638fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
639f1d8a071SWilliam Breathitt Gray 
640*aaec1a0fSWilliam Breathitt Gray 	return 0;
641f1d8a071SWilliam Breathitt Gray }
642f1d8a071SWilliam Breathitt Gray 
643*aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_read(struct counter_device *counter,
644*aaec1a0fSWilliam Breathitt Gray 				    struct counter_count *count, u64 *ceiling)
645f1d8a071SWilliam Breathitt Gray {
646e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
647fc069262SSyed Nayyar Waris 
648fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
649f1d8a071SWilliam Breathitt Gray 
650f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
651f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
652f1d8a071SWilliam Breathitt Gray 	case 1:
653f1d8a071SWilliam Breathitt Gray 	case 3:
654*aaec1a0fSWilliam Breathitt Gray 		*ceiling = priv->preset[count->id];
655*aaec1a0fSWilliam Breathitt Gray 		break;
656*aaec1a0fSWilliam Breathitt Gray 	default:
657f1d8a071SWilliam Breathitt Gray 		/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
658*aaec1a0fSWilliam Breathitt Gray 		*ceiling = 0x1FFFFFF;
659*aaec1a0fSWilliam Breathitt Gray 		break;
660f1d8a071SWilliam Breathitt Gray 	}
661f1d8a071SWilliam Breathitt Gray 
662*aaec1a0fSWilliam Breathitt Gray 	mutex_unlock(&priv->lock);
663*aaec1a0fSWilliam Breathitt Gray 
664*aaec1a0fSWilliam Breathitt Gray 	return 0;
665*aaec1a0fSWilliam Breathitt Gray }
666*aaec1a0fSWilliam Breathitt Gray 
667*aaec1a0fSWilliam Breathitt Gray static int quad8_count_ceiling_write(struct counter_device *counter,
668*aaec1a0fSWilliam Breathitt Gray 				     struct counter_count *count, u64 ceiling)
669f1d8a071SWilliam Breathitt Gray {
670e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
671fc069262SSyed Nayyar Waris 
672fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
673fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
674e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
675fc069262SSyed Nayyar Waris 
676fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
677f1d8a071SWilliam Breathitt Gray 
678f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
679f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
680f1d8a071SWilliam Breathitt Gray 	case 1:
681f1d8a071SWilliam Breathitt Gray 	case 3:
682fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
683728246e8SWilliam Breathitt Gray 		mutex_unlock(&priv->lock);
684*aaec1a0fSWilliam Breathitt Gray 		return 0;
685f1d8a071SWilliam Breathitt Gray 	}
686f1d8a071SWilliam Breathitt Gray 
687fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
688fc069262SSyed Nayyar Waris 
689728246e8SWilliam Breathitt Gray 	return -EINVAL;
690f1d8a071SWilliam Breathitt Gray }
691f1d8a071SWilliam Breathitt Gray 
692*aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_read(struct counter_device *counter,
693*aaec1a0fSWilliam Breathitt Gray 					  struct counter_count *count,
694*aaec1a0fSWilliam Breathitt Gray 					  u8 *preset_enable)
695f1d8a071SWilliam Breathitt Gray {
696e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
697f1d8a071SWilliam Breathitt Gray 
698*aaec1a0fSWilliam Breathitt Gray 	*preset_enable = !priv->preset_enable[count->id];
699*aaec1a0fSWilliam Breathitt Gray 
700*aaec1a0fSWilliam Breathitt Gray 	return 0;
701f1d8a071SWilliam Breathitt Gray }
702f1d8a071SWilliam Breathitt Gray 
703*aaec1a0fSWilliam Breathitt Gray static int quad8_count_preset_enable_write(struct counter_device *counter,
704*aaec1a0fSWilliam Breathitt Gray 					   struct counter_count *count,
705*aaec1a0fSWilliam Breathitt Gray 					   u8 preset_enable)
706f1d8a071SWilliam Breathitt Gray {
707e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
708f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
709f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
710f1d8a071SWilliam Breathitt Gray 
711f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
712f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
713f1d8a071SWilliam Breathitt Gray 
714fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
715fc069262SSyed Nayyar Waris 
716f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
717f1d8a071SWilliam Breathitt Gray 
718*aaec1a0fSWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | preset_enable << 1;
719f1d8a071SWilliam Breathitt Gray 
720f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
721f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
722f1d8a071SWilliam Breathitt Gray 
723fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
724fc069262SSyed Nayyar Waris 
725*aaec1a0fSWilliam Breathitt Gray 	return 0;
726f1d8a071SWilliam Breathitt Gray }
727f1d8a071SWilliam Breathitt Gray 
728*aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_read(struct counter_device *counter,
729954ab5ccSWilliam Breathitt Gray 					 struct counter_signal *signal,
730*aaec1a0fSWilliam Breathitt Gray 					 u8 *cable_fault)
731954ab5ccSWilliam Breathitt Gray {
732e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
733954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
734708d9893SSyed Nayyar Waris 	bool disabled;
735954ab5ccSWilliam Breathitt Gray 	unsigned int status;
736954ab5ccSWilliam Breathitt Gray 
737708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
738708d9893SSyed Nayyar Waris 
739708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
740708d9893SSyed Nayyar Waris 
741708d9893SSyed Nayyar Waris 	if (disabled) {
742708d9893SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
743954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
744708d9893SSyed Nayyar Waris 	}
745954ab5ccSWilliam Breathitt Gray 
746954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
747954ab5ccSWilliam Breathitt Gray 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
748954ab5ccSWilliam Breathitt Gray 
749708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
750708d9893SSyed Nayyar Waris 
751954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
752*aaec1a0fSWilliam Breathitt Gray 	*cable_fault = !(status & BIT(channel_id));
753954ab5ccSWilliam Breathitt Gray 
754*aaec1a0fSWilliam Breathitt Gray 	return 0;
755954ab5ccSWilliam Breathitt Gray }
756954ab5ccSWilliam Breathitt Gray 
757*aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_read(struct counter_device *counter,
758*aaec1a0fSWilliam Breathitt Gray 						struct counter_signal *signal,
759*aaec1a0fSWilliam Breathitt Gray 						u8 *enable)
760954ab5ccSWilliam Breathitt Gray {
761e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
762954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
763954ab5ccSWilliam Breathitt Gray 
764*aaec1a0fSWilliam Breathitt Gray 	*enable = !!(priv->cable_fault_enable & BIT(channel_id));
765*aaec1a0fSWilliam Breathitt Gray 
766*aaec1a0fSWilliam Breathitt Gray 	return 0;
767954ab5ccSWilliam Breathitt Gray }
768954ab5ccSWilliam Breathitt Gray 
769*aaec1a0fSWilliam Breathitt Gray static int quad8_signal_cable_fault_enable_write(struct counter_device *counter,
770*aaec1a0fSWilliam Breathitt Gray 						 struct counter_signal *signal,
771*aaec1a0fSWilliam Breathitt Gray 						 u8 enable)
772954ab5ccSWilliam Breathitt Gray {
773e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
774954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
775954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
776954ab5ccSWilliam Breathitt Gray 
777708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
778708d9893SSyed Nayyar Waris 
779954ab5ccSWilliam Breathitt Gray 	if (enable)
780954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
781954ab5ccSWilliam Breathitt Gray 	else
782954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
783954ab5ccSWilliam Breathitt Gray 
784954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
785954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
786954ab5ccSWilliam Breathitt Gray 
787954ab5ccSWilliam Breathitt Gray 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
788954ab5ccSWilliam Breathitt Gray 
789708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
790708d9893SSyed Nayyar Waris 
791*aaec1a0fSWilliam Breathitt Gray 	return 0;
792954ab5ccSWilliam Breathitt Gray }
793954ab5ccSWilliam Breathitt Gray 
794*aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_read(struct counter_device *counter,
795*aaec1a0fSWilliam Breathitt Gray 					   struct counter_signal *signal,
796*aaec1a0fSWilliam Breathitt Gray 					   u8 *prescaler)
797de65d055SWilliam Breathitt Gray {
798e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
799de65d055SWilliam Breathitt Gray 
800*aaec1a0fSWilliam Breathitt Gray 	*prescaler = priv->fck_prescaler[signal->id / 2];
801*aaec1a0fSWilliam Breathitt Gray 
802*aaec1a0fSWilliam Breathitt Gray 	return 0;
803de65d055SWilliam Breathitt Gray }
804de65d055SWilliam Breathitt Gray 
805*aaec1a0fSWilliam Breathitt Gray static int quad8_signal_fck_prescaler_write(struct counter_device *counter,
806*aaec1a0fSWilliam Breathitt Gray 					    struct counter_signal *signal,
807*aaec1a0fSWilliam Breathitt Gray 					    u8 prescaler)
808de65d055SWilliam Breathitt Gray {
809e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
810de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
811de65d055SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id;
812de65d055SWilliam Breathitt Gray 
813d5ed76adSSyed Nayyar Waris 	mutex_lock(&priv->lock);
814d5ed76adSSyed Nayyar Waris 
815de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
816de65d055SWilliam Breathitt Gray 
817de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
818de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
819de65d055SWilliam Breathitt Gray 
820de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
821de65d055SWilliam Breathitt Gray 	outb(prescaler, base_offset);
822de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
823de65d055SWilliam Breathitt Gray 	     base_offset + 1);
824de65d055SWilliam Breathitt Gray 
825d5ed76adSSyed Nayyar Waris 	mutex_unlock(&priv->lock);
826d5ed76adSSyed Nayyar Waris 
827*aaec1a0fSWilliam Breathitt Gray 	return 0;
828de65d055SWilliam Breathitt Gray }
829de65d055SWilliam Breathitt Gray 
830*aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_signal_ext[] = {
831*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault", quad8_signal_cable_fault_read,
832*aaec1a0fSWilliam Breathitt Gray 				 NULL),
833*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_BOOL("cable_fault_enable",
834*aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_read,
835*aaec1a0fSWilliam Breathitt Gray 				 quad8_signal_cable_fault_enable_write),
836*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_U8("filter_clock_prescaler",
837*aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_read,
838*aaec1a0fSWilliam Breathitt Gray 			       quad8_signal_fck_prescaler_write)
839de65d055SWilliam Breathitt Gray };
840de65d055SWilliam Breathitt Gray 
841*aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_index_pol_enum, quad8_index_polarity_modes);
842*aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_synch_mode_enum, quad8_synchronous_modes);
843*aaec1a0fSWilliam Breathitt Gray 
844*aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_index_ext[] = {
845*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("index_polarity", quad8_index_polarity_get,
846*aaec1a0fSWilliam Breathitt Gray 				 quad8_index_polarity_set,
847*aaec1a0fSWilliam Breathitt Gray 				 quad8_index_pol_enum),
848*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_SIGNAL_ENUM("synchronous_mode", quad8_synchronous_mode_get,
849*aaec1a0fSWilliam Breathitt Gray 				 quad8_synchronous_mode_set,
850*aaec1a0fSWilliam Breathitt Gray 				 quad8_synch_mode_enum),
851f1d8a071SWilliam Breathitt Gray };
852f1d8a071SWilliam Breathitt Gray 
853f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
854f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
855de65d055SWilliam Breathitt Gray 	.name = (_name),			\
856de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
857de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
858f1d8a071SWilliam Breathitt Gray }
859f1d8a071SWilliam Breathitt Gray 
860f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
861f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
862f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
863f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
864f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
865f1d8a071SWilliam Breathitt Gray }
866f1d8a071SWilliam Breathitt Gray 
867f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
868f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
869f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
870f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
871f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
872f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
873f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
874f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
875f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
876f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
877f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
878f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
879f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
880f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
881f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
882f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
883f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
884f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
885f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
886f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
887f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
888f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
889f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
890f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
891f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
892f1d8a071SWilliam Breathitt Gray };
893f1d8a071SWilliam Breathitt Gray 
894f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
895f1d8a071SWilliam Breathitt Gray 	{								\
896f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
897f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
898f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
899f1d8a071SWilliam Breathitt Gray 	},								\
900f1d8a071SWilliam Breathitt Gray 	{								\
901f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
902f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
903f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
904f1d8a071SWilliam Breathitt Gray 	},								\
905f1d8a071SWilliam Breathitt Gray 	{								\
906f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
907f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
908f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
909f1d8a071SWilliam Breathitt Gray 	}								\
910f1d8a071SWilliam Breathitt Gray }
911f1d8a071SWilliam Breathitt Gray 
912f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
913f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
914f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
915f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
916f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
917f1d8a071SWilliam Breathitt Gray };
918f1d8a071SWilliam Breathitt Gray 
919*aaec1a0fSWilliam Breathitt Gray static const enum counter_count_mode quad8_cnt_modes[] = {
920*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NORMAL,
921*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_RANGE_LIMIT,
922*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_NON_RECYCLE,
923*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COUNT_MODE_MODULO_N,
924*aaec1a0fSWilliam Breathitt Gray };
925*aaec1a0fSWilliam Breathitt Gray 
926*aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_AVAILABLE(quad8_count_mode_available, quad8_cnt_modes);
927*aaec1a0fSWilliam Breathitt Gray 
928*aaec1a0fSWilliam Breathitt Gray static DEFINE_COUNTER_ENUM(quad8_error_noise_enum, quad8_noise_error_states);
929*aaec1a0fSWilliam Breathitt Gray 
930*aaec1a0fSWilliam Breathitt Gray static struct counter_comp quad8_count_ext[] = {
931*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_CEILING(quad8_count_ceiling_read,
932*aaec1a0fSWilliam Breathitt Gray 			     quad8_count_ceiling_write),
933*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_FLOOR(quad8_count_floor_read, NULL),
934*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_MODE(quad8_count_mode_read, quad8_count_mode_write,
935*aaec1a0fSWilliam Breathitt Gray 				quad8_count_mode_available),
936*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_DIRECTION(quad8_direction_read),
937*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_ENABLE(quad8_count_enable_read, quad8_count_enable_write),
938*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_COUNT_ENUM("error_noise", quad8_error_noise_get, NULL,
939*aaec1a0fSWilliam Breathitt Gray 				quad8_error_noise_enum),
940*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET(quad8_count_preset_read, quad8_count_preset_write),
941*aaec1a0fSWilliam Breathitt Gray 	COUNTER_COMP_PRESET_ENABLE(quad8_count_preset_enable_read,
942*aaec1a0fSWilliam Breathitt Gray 				   quad8_count_preset_enable_write),
943f1d8a071SWilliam Breathitt Gray };
944f1d8a071SWilliam Breathitt Gray 
945f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
946f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
947f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
948f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
949f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
950f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
951f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
952f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
953f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
954f1d8a071SWilliam Breathitt Gray }
955f1d8a071SWilliam Breathitt Gray 
956f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
957f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
958f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
959f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
960f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
961f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
962f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
963f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
964f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
965f1d8a071SWilliam Breathitt Gray };
966f1d8a071SWilliam Breathitt Gray 
967f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
968f1d8a071SWilliam Breathitt Gray {
969e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
970f1d8a071SWilliam Breathitt Gray 	int i, j;
971f1d8a071SWilliam Breathitt Gray 	unsigned int base_offset;
972f1d8a071SWilliam Breathitt Gray 
973f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
974f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
975f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
976f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
977f1d8a071SWilliam Breathitt Gray 	}
978f1d8a071SWilliam Breathitt Gray 
979e357e81fSWilliam Breathitt Gray 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
980e357e81fSWilliam Breathitt Gray 	if (!priv)
981f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
982f1d8a071SWilliam Breathitt Gray 
983f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
984e357e81fSWilliam Breathitt Gray 	priv->counter.name = dev_name(dev);
985e357e81fSWilliam Breathitt Gray 	priv->counter.parent = dev;
986e357e81fSWilliam Breathitt Gray 	priv->counter.ops = &quad8_ops;
987e357e81fSWilliam Breathitt Gray 	priv->counter.counts = quad8_counts;
988e357e81fSWilliam Breathitt Gray 	priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
989e357e81fSWilliam Breathitt Gray 	priv->counter.signals = quad8_signals;
990e357e81fSWilliam Breathitt Gray 	priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
991e357e81fSWilliam Breathitt Gray 	priv->counter.priv = priv;
992e357e81fSWilliam Breathitt Gray 	priv->base = base[id];
993f1d8a071SWilliam Breathitt Gray 
994fc069262SSyed Nayyar Waris 	/* Initialize mutex */
995e357e81fSWilliam Breathitt Gray 	mutex_init(&priv->lock);
996fc069262SSyed Nayyar Waris 
997f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
998f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
999f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1000f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1001f1d8a071SWilliam Breathitt Gray 		base_offset = base[id] + 2 * i;
1002f1d8a071SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1003f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1004de65d055SWilliam Breathitt Gray 		/* Reset filter clock factor */
1005de65d055SWilliam Breathitt Gray 		outb(0, base_offset);
1006de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1007de65d055SWilliam Breathitt Gray 		     base_offset + 1);
1008de65d055SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1009de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1010f1d8a071SWilliam Breathitt Gray 		/* Reset Preset Register */
1011f1d8a071SWilliam Breathitt Gray 		for (j = 0; j < 3; j++)
1012f1d8a071SWilliam Breathitt Gray 			outb(0x00, base_offset);
1013f1d8a071SWilliam Breathitt Gray 		/* Reset Borrow, Carry, Compare, and Sign flags */
1014f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1015f1d8a071SWilliam Breathitt Gray 		/* Reset Error flag */
1016f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1017f1d8a071SWilliam Breathitt Gray 		/* Binary encoding; Normal count; non-quadrature mode */
1018f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_CMR, base_offset + 1);
1019f1d8a071SWilliam Breathitt Gray 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1020f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR, base_offset + 1);
1021f1d8a071SWilliam Breathitt Gray 		/* Disable index function; negative index polarity */
1022f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IDR, base_offset + 1);
1023f1d8a071SWilliam Breathitt Gray 	}
1024954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1025954ab5ccSWilliam Breathitt Gray 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1026f1d8a071SWilliam Breathitt Gray 	/* Enable all counters */
1027f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1028f1d8a071SWilliam Breathitt Gray 
1029e357e81fSWilliam Breathitt Gray 	return devm_counter_register(dev, &priv->counter);
1030f1d8a071SWilliam Breathitt Gray }
1031f1d8a071SWilliam Breathitt Gray 
1032f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1033f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1034f1d8a071SWilliam Breathitt Gray 	.driver = {
1035f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1036f1d8a071SWilliam Breathitt Gray 	}
1037f1d8a071SWilliam Breathitt Gray };
1038f1d8a071SWilliam Breathitt Gray 
1039f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1040f1d8a071SWilliam Breathitt Gray 
1041f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1042e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1043f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1044