xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision 493b938a)
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
31f1d8a071SWilliam Breathitt Gray  * @counter:		instance of the counter_device
32954ab5ccSWilliam Breathitt Gray  * @fck_prescaler:	array of filter clock prescaler configurations
33f1d8a071SWilliam Breathitt Gray  * @preset:		array of preset values
34f1d8a071SWilliam Breathitt Gray  * @count_mode:		array of count mode configurations
35f1d8a071SWilliam Breathitt Gray  * @quadrature_mode:	array of quadrature mode configurations
36f1d8a071SWilliam Breathitt Gray  * @quadrature_scale:	array of quadrature mode scale configurations
37f1d8a071SWilliam Breathitt Gray  * @ab_enable:		array of A and B inputs enable configurations
38f1d8a071SWilliam Breathitt Gray  * @preset_enable:	array of set_to_preset_on_index attribute configurations
39f1d8a071SWilliam Breathitt Gray  * @synchronous_mode:	array of index function synchronous mode configurations
40f1d8a071SWilliam Breathitt Gray  * @index_polarity:	array of index function polarity configurations
41954ab5ccSWilliam Breathitt Gray  * @cable_fault_enable:	differential encoder cable status enable configurations
42e357e81fSWilliam Breathitt Gray  * @base:		base port address of the device
43f1d8a071SWilliam Breathitt Gray  */
44e357e81fSWilliam Breathitt Gray struct quad8 {
45fc069262SSyed Nayyar Waris 	struct mutex lock;
46f1d8a071SWilliam Breathitt Gray 	struct counter_device counter;
47de65d055SWilliam Breathitt Gray 	unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
48f1d8a071SWilliam Breathitt Gray 	unsigned int preset[QUAD8_NUM_COUNTERS];
49f1d8a071SWilliam Breathitt Gray 	unsigned int count_mode[QUAD8_NUM_COUNTERS];
50f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
51f1d8a071SWilliam Breathitt Gray 	unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
52f1d8a071SWilliam Breathitt Gray 	unsigned int ab_enable[QUAD8_NUM_COUNTERS];
53f1d8a071SWilliam Breathitt Gray 	unsigned int preset_enable[QUAD8_NUM_COUNTERS];
54f1d8a071SWilliam Breathitt Gray 	unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
55f1d8a071SWilliam Breathitt Gray 	unsigned int index_polarity[QUAD8_NUM_COUNTERS];
56954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
57f1d8a071SWilliam Breathitt Gray 	unsigned int base;
58f1d8a071SWilliam Breathitt Gray };
59f1d8a071SWilliam Breathitt Gray 
60f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_CHAN_OP 0x11
61f1d8a071SWilliam Breathitt Gray #define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
62954ab5ccSWilliam Breathitt Gray #define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
63f1d8a071SWilliam Breathitt Gray /* Borrow Toggle flip-flop */
64f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_BT BIT(0)
65f1d8a071SWilliam Breathitt Gray /* Carry Toggle flip-flop */
66f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_CT BIT(1)
67f1d8a071SWilliam Breathitt Gray /* Error flag */
68f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_E BIT(4)
69f1d8a071SWilliam Breathitt Gray /* Up/Down flag */
70f1d8a071SWilliam Breathitt Gray #define QUAD8_FLAG_UD BIT(5)
71f1d8a071SWilliam Breathitt Gray /* Reset and Load Signal Decoders */
72f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_RLD 0x00
73f1d8a071SWilliam Breathitt Gray /* Counter Mode Register */
74f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_CMR 0x20
75f1d8a071SWilliam Breathitt Gray /* Input / Output Control Register */
76f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IOR 0x40
77f1d8a071SWilliam Breathitt Gray /* Index Control Register */
78f1d8a071SWilliam Breathitt Gray #define QUAD8_CTR_IDR 0x60
79f1d8a071SWilliam Breathitt Gray /* Reset Byte Pointer (three byte data pointer) */
80f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_BP 0x01
81f1d8a071SWilliam Breathitt Gray /* Reset Counter */
82f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_CNTR 0x02
83f1d8a071SWilliam Breathitt Gray /* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
84f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_FLAGS 0x04
85f1d8a071SWilliam Breathitt Gray /* Reset Error flag */
86f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_RESET_E 0x06
87f1d8a071SWilliam Breathitt Gray /* Preset Register to Counter */
88f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_CNTR 0x08
89f1d8a071SWilliam Breathitt Gray /* Transfer Counter to Output Latch */
90f1d8a071SWilliam Breathitt Gray #define QUAD8_RLD_CNTR_OUT 0x10
91de65d055SWilliam Breathitt Gray /* Transfer Preset Register LSB to FCK Prescaler */
92de65d055SWilliam Breathitt Gray #define QUAD8_RLD_PRESET_PSC 0x18
93f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
94f1d8a071SWilliam Breathitt Gray #define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
95f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X1 0x08
96f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X2 0x10
97f1d8a071SWilliam Breathitt Gray #define QUAD8_CMR_QUADRATURE_X4 0x18
98f1d8a071SWilliam Breathitt Gray 
99f1d8a071SWilliam Breathitt Gray static int quad8_signal_read(struct counter_device *counter,
100*493b938aSWilliam Breathitt Gray 			     struct counter_signal *signal,
101*493b938aSWilliam Breathitt Gray 			     enum counter_signal_level *level)
102f1d8a071SWilliam Breathitt Gray {
103e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
104f1d8a071SWilliam Breathitt Gray 	unsigned int state;
105f1d8a071SWilliam Breathitt Gray 
106f1d8a071SWilliam Breathitt Gray 	/* Only Index signal levels can be read */
107f1d8a071SWilliam Breathitt Gray 	if (signal->id < 16)
108f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
109f1d8a071SWilliam Breathitt Gray 
110f1d8a071SWilliam Breathitt Gray 	state = inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
111f1d8a071SWilliam Breathitt Gray 		& BIT(signal->id - 16);
112f1d8a071SWilliam Breathitt Gray 
113*493b938aSWilliam Breathitt Gray 	*level = (state) ? COUNTER_SIGNAL_LEVEL_HIGH : COUNTER_SIGNAL_LEVEL_LOW;
114f1d8a071SWilliam Breathitt Gray 
115f1d8a071SWilliam Breathitt Gray 	return 0;
116f1d8a071SWilliam Breathitt Gray }
117f1d8a071SWilliam Breathitt Gray 
118f1d8a071SWilliam Breathitt Gray static int quad8_count_read(struct counter_device *counter,
119d49e6ee2SWilliam Breathitt Gray 	struct counter_count *count, unsigned long *val)
120f1d8a071SWilliam Breathitt Gray {
121e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
122f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
123f1d8a071SWilliam Breathitt Gray 	unsigned int flags;
124f1d8a071SWilliam Breathitt Gray 	unsigned int borrow;
125f1d8a071SWilliam Breathitt Gray 	unsigned int carry;
126f1d8a071SWilliam Breathitt Gray 	int i;
127f1d8a071SWilliam Breathitt Gray 
128f1d8a071SWilliam Breathitt Gray 	flags = inb(base_offset + 1);
129f1d8a071SWilliam Breathitt Gray 	borrow = flags & QUAD8_FLAG_BT;
130f1d8a071SWilliam Breathitt Gray 	carry = !!(flags & QUAD8_FLAG_CT);
131f1d8a071SWilliam Breathitt Gray 
132f1d8a071SWilliam Breathitt Gray 	/* Borrow XOR Carry effectively doubles count range */
133d49e6ee2SWilliam Breathitt Gray 	*val = (unsigned long)(borrow ^ carry) << 24;
134f1d8a071SWilliam Breathitt Gray 
135fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
136fc069262SSyed Nayyar Waris 
137f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer; transfer Counter to Output Latch */
138f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
139f1d8a071SWilliam Breathitt Gray 	     base_offset + 1);
140f1d8a071SWilliam Breathitt Gray 
141f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
142d49e6ee2SWilliam Breathitt Gray 		*val |= (unsigned long)inb(base_offset) << (8 * i);
143f1d8a071SWilliam Breathitt Gray 
144fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
145fc069262SSyed Nayyar Waris 
146f1d8a071SWilliam Breathitt Gray 	return 0;
147f1d8a071SWilliam Breathitt Gray }
148f1d8a071SWilliam Breathitt Gray 
149f1d8a071SWilliam Breathitt Gray static int quad8_count_write(struct counter_device *counter,
150d49e6ee2SWilliam Breathitt Gray 	struct counter_count *count, unsigned long val)
151f1d8a071SWilliam Breathitt Gray {
152e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
153f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
154f1d8a071SWilliam Breathitt Gray 	int i;
155f1d8a071SWilliam Breathitt Gray 
156f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
157d49e6ee2SWilliam Breathitt Gray 	if (val > 0xFFFFFF)
158e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
159f1d8a071SWilliam Breathitt Gray 
160fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
161fc069262SSyed Nayyar Waris 
162f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
163f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
164f1d8a071SWilliam Breathitt Gray 
165f1d8a071SWilliam Breathitt Gray 	/* Counter can only be set via Preset Register */
166f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
167d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
168f1d8a071SWilliam Breathitt Gray 
169f1d8a071SWilliam Breathitt Gray 	/* Transfer Preset Register to Counter */
170f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
171f1d8a071SWilliam Breathitt Gray 
172f1d8a071SWilliam Breathitt Gray 	/* Reset Byte Pointer */
173f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
174f1d8a071SWilliam Breathitt Gray 
175f1d8a071SWilliam Breathitt Gray 	/* Set Preset Register back to original value */
176d49e6ee2SWilliam Breathitt Gray 	val = priv->preset[count->id];
177f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < 3; i++)
178d49e6ee2SWilliam Breathitt Gray 		outb(val >> (8 * i), base_offset);
179f1d8a071SWilliam Breathitt Gray 
180f1d8a071SWilliam Breathitt Gray 	/* Reset Borrow, Carry, Compare, and Sign flags */
181f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
182f1d8a071SWilliam Breathitt Gray 	/* Reset Error flag */
183f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
184f1d8a071SWilliam Breathitt Gray 
185fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
186fc069262SSyed Nayyar Waris 
187f1d8a071SWilliam Breathitt Gray 	return 0;
188f1d8a071SWilliam Breathitt Gray }
189f1d8a071SWilliam Breathitt Gray 
190f1d8a071SWilliam Breathitt Gray enum quad8_count_function {
191f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_FUNCTION_PULSE_DIRECTION = 0,
192f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_FUNCTION_QUADRATURE_X1,
193f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_FUNCTION_QUADRATURE_X2,
194f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_FUNCTION_QUADRATURE_X4
195f1d8a071SWilliam Breathitt Gray };
196f1d8a071SWilliam Breathitt Gray 
197fca2534fSWilliam Breathitt Gray static const enum counter_count_function quad8_count_functions_list[] = {
198f1d8a071SWilliam Breathitt Gray 	[QUAD8_COUNT_FUNCTION_PULSE_DIRECTION] = COUNTER_COUNT_FUNCTION_PULSE_DIRECTION,
199f1d8a071SWilliam Breathitt Gray 	[QUAD8_COUNT_FUNCTION_QUADRATURE_X1] = COUNTER_COUNT_FUNCTION_QUADRATURE_X1_A,
200f1d8a071SWilliam Breathitt Gray 	[QUAD8_COUNT_FUNCTION_QUADRATURE_X2] = COUNTER_COUNT_FUNCTION_QUADRATURE_X2_A,
201f1d8a071SWilliam Breathitt Gray 	[QUAD8_COUNT_FUNCTION_QUADRATURE_X4] = COUNTER_COUNT_FUNCTION_QUADRATURE_X4
202f1d8a071SWilliam Breathitt Gray };
203f1d8a071SWilliam Breathitt Gray 
204f1d8a071SWilliam Breathitt Gray static int quad8_function_get(struct counter_device *counter,
205f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t *function)
206f1d8a071SWilliam Breathitt Gray {
207e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
208f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
209f1d8a071SWilliam Breathitt Gray 
210fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
211fc069262SSyed Nayyar Waris 
212fc069262SSyed Nayyar Waris 	if (priv->quadrature_mode[id])
213fc069262SSyed Nayyar Waris 		switch (priv->quadrature_scale[id]) {
214f1d8a071SWilliam Breathitt Gray 		case 0:
215f1d8a071SWilliam Breathitt Gray 			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X1;
216f1d8a071SWilliam Breathitt Gray 			break;
217f1d8a071SWilliam Breathitt Gray 		case 1:
218f1d8a071SWilliam Breathitt Gray 			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X2;
219f1d8a071SWilliam Breathitt Gray 			break;
220f1d8a071SWilliam Breathitt Gray 		case 2:
221f1d8a071SWilliam Breathitt Gray 			*function = QUAD8_COUNT_FUNCTION_QUADRATURE_X4;
222f1d8a071SWilliam Breathitt Gray 			break;
223f1d8a071SWilliam Breathitt Gray 		}
224f1d8a071SWilliam Breathitt Gray 	else
225f1d8a071SWilliam Breathitt Gray 		*function = QUAD8_COUNT_FUNCTION_PULSE_DIRECTION;
226f1d8a071SWilliam Breathitt Gray 
227fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
228fc069262SSyed Nayyar Waris 
229f1d8a071SWilliam Breathitt Gray 	return 0;
230f1d8a071SWilliam Breathitt Gray }
231f1d8a071SWilliam Breathitt Gray 
232f1d8a071SWilliam Breathitt Gray static int quad8_function_set(struct counter_device *counter,
233f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t function)
234f1d8a071SWilliam Breathitt Gray {
235e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
236f1d8a071SWilliam Breathitt Gray 	const int id = count->id;
237f1d8a071SWilliam Breathitt Gray 	unsigned int *const quadrature_mode = priv->quadrature_mode + id;
238f1d8a071SWilliam Breathitt Gray 	unsigned int *const scale = priv->quadrature_scale + id;
239f1d8a071SWilliam Breathitt Gray 	unsigned int *const synchronous_mode = priv->synchronous_mode + id;
240f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * id + 1;
241fc069262SSyed Nayyar Waris 	unsigned int mode_cfg;
242fc069262SSyed Nayyar Waris 	unsigned int idr_cfg;
243fc069262SSyed Nayyar Waris 
244fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
245fc069262SSyed Nayyar Waris 
246fc069262SSyed Nayyar Waris 	mode_cfg = priv->count_mode[id] << 1;
247fc069262SSyed Nayyar Waris 	idr_cfg = priv->index_polarity[id] << 1;
248f1d8a071SWilliam Breathitt Gray 
249f1d8a071SWilliam Breathitt Gray 	if (function == QUAD8_COUNT_FUNCTION_PULSE_DIRECTION) {
250f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 0;
251f1d8a071SWilliam Breathitt Gray 
252f1d8a071SWilliam Breathitt Gray 		/* Quadrature scaling only available in quadrature mode */
253f1d8a071SWilliam Breathitt Gray 		*scale = 0;
254f1d8a071SWilliam Breathitt Gray 
255f1d8a071SWilliam Breathitt Gray 		/* Synchronous function not supported in non-quadrature mode */
256f1d8a071SWilliam Breathitt Gray 		if (*synchronous_mode) {
257f1d8a071SWilliam Breathitt Gray 			*synchronous_mode = 0;
258f1d8a071SWilliam Breathitt Gray 			/* Disable synchronous function mode */
259f1d8a071SWilliam Breathitt Gray 			outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
260f1d8a071SWilliam Breathitt Gray 		}
261f1d8a071SWilliam Breathitt Gray 	} else {
262f1d8a071SWilliam Breathitt Gray 		*quadrature_mode = 1;
263f1d8a071SWilliam Breathitt Gray 
264f1d8a071SWilliam Breathitt Gray 		switch (function) {
265f1d8a071SWilliam Breathitt Gray 		case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
266f1d8a071SWilliam Breathitt Gray 			*scale = 0;
267f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X1;
268f1d8a071SWilliam Breathitt Gray 			break;
269f1d8a071SWilliam Breathitt Gray 		case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
270f1d8a071SWilliam Breathitt Gray 			*scale = 1;
271f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X2;
272f1d8a071SWilliam Breathitt Gray 			break;
273f1d8a071SWilliam Breathitt Gray 		case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
274f1d8a071SWilliam Breathitt Gray 			*scale = 2;
275f1d8a071SWilliam Breathitt Gray 			mode_cfg |= QUAD8_CMR_QUADRATURE_X4;
276f1d8a071SWilliam Breathitt Gray 			break;
277b11eed15SWilliam Breathitt Gray 		default:
278b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
279b11eed15SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
280b11eed15SWilliam Breathitt Gray 			return -EINVAL;
281f1d8a071SWilliam Breathitt Gray 		}
282f1d8a071SWilliam Breathitt Gray 	}
283f1d8a071SWilliam Breathitt Gray 
284f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
285f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
286f1d8a071SWilliam Breathitt Gray 
287fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
288fc069262SSyed Nayyar Waris 
289f1d8a071SWilliam Breathitt Gray 	return 0;
290f1d8a071SWilliam Breathitt Gray }
291f1d8a071SWilliam Breathitt Gray 
292f1d8a071SWilliam Breathitt Gray static void quad8_direction_get(struct counter_device *counter,
293f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, enum counter_count_direction *direction)
294f1d8a071SWilliam Breathitt Gray {
295e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
296f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
297f1d8a071SWilliam Breathitt Gray 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
298f1d8a071SWilliam Breathitt Gray 
299f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
300f1d8a071SWilliam Breathitt Gray 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
301f1d8a071SWilliam Breathitt Gray 
302f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
303f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
304f1d8a071SWilliam Breathitt Gray }
305f1d8a071SWilliam Breathitt Gray 
306f1d8a071SWilliam Breathitt Gray enum quad8_synapse_action {
307f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_NONE = 0,
308f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_RISING_EDGE,
309f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
310f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_BOTH_EDGES
311f1d8a071SWilliam Breathitt Gray };
312f1d8a071SWilliam Breathitt Gray 
3136a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
314f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
315f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
316f1d8a071SWilliam Breathitt Gray };
317f1d8a071SWilliam Breathitt Gray 
3186a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
319f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
320f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
321f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
322f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
323f1d8a071SWilliam Breathitt Gray };
324f1d8a071SWilliam Breathitt Gray 
325f1d8a071SWilliam Breathitt Gray static int quad8_action_get(struct counter_device *counter,
326f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, struct counter_synapse *synapse,
327f1d8a071SWilliam Breathitt Gray 	size_t *action)
328f1d8a071SWilliam Breathitt Gray {
329e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
330f1d8a071SWilliam Breathitt Gray 	int err;
331f1d8a071SWilliam Breathitt Gray 	size_t function = 0;
332f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
333f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
334f1d8a071SWilliam Breathitt Gray 
335f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
336f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
337f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
338f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
339f1d8a071SWilliam Breathitt Gray 		else
340f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_NONE;
341f1d8a071SWilliam Breathitt Gray 
342f1d8a071SWilliam Breathitt Gray 		return 0;
343f1d8a071SWilliam Breathitt Gray 	}
344f1d8a071SWilliam Breathitt Gray 
345f1d8a071SWilliam Breathitt Gray 	err = quad8_function_get(counter, count, &function);
346f1d8a071SWilliam Breathitt Gray 	if (err)
347f1d8a071SWilliam Breathitt Gray 		return err;
348f1d8a071SWilliam Breathitt Gray 
349f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
350f1d8a071SWilliam Breathitt Gray 	*action = QUAD8_SYNAPSE_ACTION_NONE;
351f1d8a071SWilliam Breathitt Gray 
352f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
353f1d8a071SWilliam Breathitt Gray 	switch (function) {
354f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
355f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
356f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
357b11eed15SWilliam Breathitt Gray 		return 0;
358f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
359f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
360f1d8a071SWilliam Breathitt Gray 			quad8_direction_get(counter, count, &direction);
361f1d8a071SWilliam Breathitt Gray 
362f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
363f1d8a071SWilliam Breathitt Gray 				*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
364f1d8a071SWilliam Breathitt Gray 			else
365f1d8a071SWilliam Breathitt Gray 				*action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
366f1d8a071SWilliam Breathitt Gray 		}
367b11eed15SWilliam Breathitt Gray 		return 0;
368f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
369f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
370f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
371b11eed15SWilliam Breathitt Gray 		return 0;
372f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
373f1d8a071SWilliam Breathitt Gray 		*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
374f1d8a071SWilliam Breathitt Gray 		return 0;
375b11eed15SWilliam Breathitt Gray 	default:
376b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
377b11eed15SWilliam Breathitt Gray 		return -EINVAL;
378b11eed15SWilliam Breathitt Gray 	}
379f1d8a071SWilliam Breathitt Gray }
380f1d8a071SWilliam Breathitt Gray 
38117aa207eSYueHaibing static const struct counter_ops quad8_ops = {
382f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
383f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
384f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
385f1d8a071SWilliam Breathitt Gray 	.function_get = quad8_function_get,
386f1d8a071SWilliam Breathitt Gray 	.function_set = quad8_function_set,
387f1d8a071SWilliam Breathitt Gray 	.action_get = quad8_action_get
388f1d8a071SWilliam Breathitt Gray };
389f1d8a071SWilliam Breathitt Gray 
390e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
391e357e81fSWilliam Breathitt Gray 	"negative",
392e357e81fSWilliam Breathitt Gray 	"positive"
393e357e81fSWilliam Breathitt Gray };
394e357e81fSWilliam Breathitt Gray 
395f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
396f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t *index_polarity)
397f1d8a071SWilliam Breathitt Gray {
398e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
399f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
400f1d8a071SWilliam Breathitt Gray 
401f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
402f1d8a071SWilliam Breathitt Gray 
403f1d8a071SWilliam Breathitt Gray 	return 0;
404f1d8a071SWilliam Breathitt Gray }
405f1d8a071SWilliam Breathitt Gray 
406f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
407f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t index_polarity)
408f1d8a071SWilliam Breathitt Gray {
409e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
410f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
411f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
412fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
413fc069262SSyed Nayyar Waris 
414fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
415fc069262SSyed Nayyar Waris 
416fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
417f1d8a071SWilliam Breathitt Gray 
418f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
419f1d8a071SWilliam Breathitt Gray 
420f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
421f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
422f1d8a071SWilliam Breathitt Gray 
423fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
424fc069262SSyed Nayyar Waris 
425f1d8a071SWilliam Breathitt Gray 	return 0;
426f1d8a071SWilliam Breathitt Gray }
427f1d8a071SWilliam Breathitt Gray 
428f1d8a071SWilliam Breathitt Gray static struct counter_signal_enum_ext quad8_index_pol_enum = {
429f1d8a071SWilliam Breathitt Gray 	.items = quad8_index_polarity_modes,
430f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
431f1d8a071SWilliam Breathitt Gray 	.get = quad8_index_polarity_get,
432f1d8a071SWilliam Breathitt Gray 	.set = quad8_index_polarity_set
433f1d8a071SWilliam Breathitt Gray };
434f1d8a071SWilliam Breathitt Gray 
435e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
436e357e81fSWilliam Breathitt Gray 	"non-synchronous",
437e357e81fSWilliam Breathitt Gray 	"synchronous"
438e357e81fSWilliam Breathitt Gray };
439e357e81fSWilliam Breathitt Gray 
440f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
441f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t *synchronous_mode)
442f1d8a071SWilliam Breathitt Gray {
443e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
444f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
445f1d8a071SWilliam Breathitt Gray 
446f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
447f1d8a071SWilliam Breathitt Gray 
448f1d8a071SWilliam Breathitt Gray 	return 0;
449f1d8a071SWilliam Breathitt Gray }
450f1d8a071SWilliam Breathitt Gray 
451f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
452f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t synchronous_mode)
453f1d8a071SWilliam Breathitt Gray {
454e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
455f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
456f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
457fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
458fc069262SSyed Nayyar Waris 
459fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
460fc069262SSyed Nayyar Waris 
461fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
462f1d8a071SWilliam Breathitt Gray 
463f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
464fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
465fc069262SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
466f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
467fc069262SSyed Nayyar Waris 	}
468f1d8a071SWilliam Breathitt Gray 
469f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
470f1d8a071SWilliam Breathitt Gray 
471f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
472f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
473f1d8a071SWilliam Breathitt Gray 
474fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
475fc069262SSyed Nayyar Waris 
476f1d8a071SWilliam Breathitt Gray 	return 0;
477f1d8a071SWilliam Breathitt Gray }
478f1d8a071SWilliam Breathitt Gray 
479f1d8a071SWilliam Breathitt Gray static struct counter_signal_enum_ext quad8_syn_mode_enum = {
480f1d8a071SWilliam Breathitt Gray 	.items = quad8_synchronous_modes,
481f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
482f1d8a071SWilliam Breathitt Gray 	.get = quad8_synchronous_mode_get,
483f1d8a071SWilliam Breathitt Gray 	.set = quad8_synchronous_mode_set
484f1d8a071SWilliam Breathitt Gray };
485f1d8a071SWilliam Breathitt Gray 
486f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_floor_read(struct counter_device *counter,
487f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
488f1d8a071SWilliam Breathitt Gray {
489f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
490f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "0\n");
491f1d8a071SWilliam Breathitt Gray }
492f1d8a071SWilliam Breathitt Gray 
493f1d8a071SWilliam Breathitt Gray static int quad8_count_mode_get(struct counter_device *counter,
494f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t *cnt_mode)
495f1d8a071SWilliam Breathitt Gray {
496e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
497f1d8a071SWilliam Breathitt Gray 
498f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
499f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
500f1d8a071SWilliam Breathitt Gray 	case 0:
501f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
502f1d8a071SWilliam Breathitt Gray 		break;
503f1d8a071SWilliam Breathitt Gray 	case 1:
504f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
505f1d8a071SWilliam Breathitt Gray 		break;
506f1d8a071SWilliam Breathitt Gray 	case 2:
507f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
508f1d8a071SWilliam Breathitt Gray 		break;
509f1d8a071SWilliam Breathitt Gray 	case 3:
510f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
511f1d8a071SWilliam Breathitt Gray 		break;
512f1d8a071SWilliam Breathitt Gray 	}
513f1d8a071SWilliam Breathitt Gray 
514f1d8a071SWilliam Breathitt Gray 	return 0;
515f1d8a071SWilliam Breathitt Gray }
516f1d8a071SWilliam Breathitt Gray 
517f1d8a071SWilliam Breathitt Gray static int quad8_count_mode_set(struct counter_device *counter,
518f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t cnt_mode)
519f1d8a071SWilliam Breathitt Gray {
520e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
521f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
522f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
523f1d8a071SWilliam Breathitt Gray 
524f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
525f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
526f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
527f1d8a071SWilliam Breathitt Gray 		cnt_mode = 0;
528f1d8a071SWilliam Breathitt Gray 		break;
529f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
530f1d8a071SWilliam Breathitt Gray 		cnt_mode = 1;
531f1d8a071SWilliam Breathitt Gray 		break;
532f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
533f1d8a071SWilliam Breathitt Gray 		cnt_mode = 2;
534f1d8a071SWilliam Breathitt Gray 		break;
535f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
536f1d8a071SWilliam Breathitt Gray 		cnt_mode = 3;
537f1d8a071SWilliam Breathitt Gray 		break;
538b11eed15SWilliam Breathitt Gray 	default:
539b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
540b11eed15SWilliam Breathitt Gray 		return -EINVAL;
541f1d8a071SWilliam Breathitt Gray 	}
542f1d8a071SWilliam Breathitt Gray 
543fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
544fc069262SSyed Nayyar Waris 
545f1d8a071SWilliam Breathitt Gray 	priv->count_mode[count->id] = cnt_mode;
546f1d8a071SWilliam Breathitt Gray 
547f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
548f1d8a071SWilliam Breathitt Gray 	mode_cfg = cnt_mode << 1;
549f1d8a071SWilliam Breathitt Gray 
550f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
551f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
552f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
553f1d8a071SWilliam Breathitt Gray 
554f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
555f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
556f1d8a071SWilliam Breathitt Gray 
557fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
558fc069262SSyed Nayyar Waris 
559f1d8a071SWilliam Breathitt Gray 	return 0;
560f1d8a071SWilliam Breathitt Gray }
561f1d8a071SWilliam Breathitt Gray 
562f1d8a071SWilliam Breathitt Gray static struct counter_count_enum_ext quad8_cnt_mode_enum = {
563f1d8a071SWilliam Breathitt Gray 	.items = counter_count_mode_str,
564f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(counter_count_mode_str),
565f1d8a071SWilliam Breathitt Gray 	.get = quad8_count_mode_get,
566f1d8a071SWilliam Breathitt Gray 	.set = quad8_count_mode_set
567f1d8a071SWilliam Breathitt Gray };
568f1d8a071SWilliam Breathitt Gray 
569f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_direction_read(struct counter_device *counter,
570f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *priv, char *buf)
571f1d8a071SWilliam Breathitt Gray {
572f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction dir;
573f1d8a071SWilliam Breathitt Gray 
574f1d8a071SWilliam Breathitt Gray 	quad8_direction_get(counter, count, &dir);
575f1d8a071SWilliam Breathitt Gray 
576f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
577f1d8a071SWilliam Breathitt Gray }
578f1d8a071SWilliam Breathitt Gray 
579f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_enable_read(struct counter_device *counter,
580f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
581f1d8a071SWilliam Breathitt Gray {
582e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
583f1d8a071SWilliam Breathitt Gray 
584f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
585f1d8a071SWilliam Breathitt Gray }
586f1d8a071SWilliam Breathitt Gray 
587f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_enable_write(struct counter_device *counter,
588f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
589f1d8a071SWilliam Breathitt Gray {
590e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
591f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
592f1d8a071SWilliam Breathitt Gray 	int err;
593f1d8a071SWilliam Breathitt Gray 	bool ab_enable;
594f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
595f1d8a071SWilliam Breathitt Gray 
596f1d8a071SWilliam Breathitt Gray 	err = kstrtobool(buf, &ab_enable);
597f1d8a071SWilliam Breathitt Gray 	if (err)
598f1d8a071SWilliam Breathitt Gray 		return err;
599f1d8a071SWilliam Breathitt Gray 
600fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
601fc069262SSyed Nayyar Waris 
602f1d8a071SWilliam Breathitt Gray 	priv->ab_enable[count->id] = ab_enable;
603f1d8a071SWilliam Breathitt Gray 
604f1d8a071SWilliam Breathitt Gray 	ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
605f1d8a071SWilliam Breathitt Gray 
606f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
607f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
608f1d8a071SWilliam Breathitt Gray 
609fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
610fc069262SSyed Nayyar Waris 
611f1d8a071SWilliam Breathitt Gray 	return len;
612f1d8a071SWilliam Breathitt Gray }
613f1d8a071SWilliam Breathitt Gray 
614e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
615e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
616e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
617e357e81fSWilliam Breathitt Gray };
618e357e81fSWilliam Breathitt Gray 
619f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
620f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t *noise_error)
621f1d8a071SWilliam Breathitt Gray {
622e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
623f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
624f1d8a071SWilliam Breathitt Gray 
625f1d8a071SWilliam Breathitt Gray 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
626f1d8a071SWilliam Breathitt Gray 
627f1d8a071SWilliam Breathitt Gray 	return 0;
628f1d8a071SWilliam Breathitt Gray }
629f1d8a071SWilliam Breathitt Gray 
630f1d8a071SWilliam Breathitt Gray static struct counter_count_enum_ext quad8_error_noise_enum = {
631f1d8a071SWilliam Breathitt Gray 	.items = quad8_noise_error_states,
632f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_noise_error_states),
633f1d8a071SWilliam Breathitt Gray 	.get = quad8_error_noise_get
634f1d8a071SWilliam Breathitt Gray };
635f1d8a071SWilliam Breathitt Gray 
636f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_read(struct counter_device *counter,
637f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
638f1d8a071SWilliam Breathitt Gray {
639e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
640f1d8a071SWilliam Breathitt Gray 
641f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->preset[count->id]);
642f1d8a071SWilliam Breathitt Gray }
643f1d8a071SWilliam Breathitt Gray 
644e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
645e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
646fc069262SSyed Nayyar Waris {
647e357e81fSWilliam Breathitt Gray 	const unsigned int base_offset = priv->base + 2 * id;
648fc069262SSyed Nayyar Waris 	int i;
649fc069262SSyed Nayyar Waris 
650e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
651fc069262SSyed Nayyar Waris 
652fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
653fc069262SSyed Nayyar Waris 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
654fc069262SSyed Nayyar Waris 
655fc069262SSyed Nayyar Waris 	/* Set Preset Register */
656fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
657fc069262SSyed Nayyar Waris 		outb(preset >> (8 * i), base_offset);
658fc069262SSyed Nayyar Waris }
659fc069262SSyed Nayyar Waris 
660f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_write(struct counter_device *counter,
661f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
662f1d8a071SWilliam Breathitt Gray {
663e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
664f1d8a071SWilliam Breathitt Gray 	unsigned int preset;
665f1d8a071SWilliam Breathitt Gray 	int ret;
666f1d8a071SWilliam Breathitt Gray 
667f1d8a071SWilliam Breathitt Gray 	ret = kstrtouint(buf, 0, &preset);
668f1d8a071SWilliam Breathitt Gray 	if (ret)
669f1d8a071SWilliam Breathitt Gray 		return ret;
670f1d8a071SWilliam Breathitt Gray 
671f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
672f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
673e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
674f1d8a071SWilliam Breathitt Gray 
675fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
676f1d8a071SWilliam Breathitt Gray 
677fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
678f1d8a071SWilliam Breathitt Gray 
679fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
680f1d8a071SWilliam Breathitt Gray 
681f1d8a071SWilliam Breathitt Gray 	return len;
682f1d8a071SWilliam Breathitt Gray }
683f1d8a071SWilliam Breathitt Gray 
684f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
685f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
686f1d8a071SWilliam Breathitt Gray {
687e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
688fc069262SSyed Nayyar Waris 
689fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
690f1d8a071SWilliam Breathitt Gray 
691f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
692f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
693f1d8a071SWilliam Breathitt Gray 	case 1:
694f1d8a071SWilliam Breathitt Gray 	case 3:
695fc069262SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
696fc069262SSyed Nayyar Waris 		return sprintf(buf, "%u\n", priv->preset[count->id]);
697f1d8a071SWilliam Breathitt Gray 	}
698f1d8a071SWilliam Breathitt Gray 
699fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
700fc069262SSyed Nayyar Waris 
701f1d8a071SWilliam Breathitt Gray 	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
702f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "33554431\n");
703f1d8a071SWilliam Breathitt Gray }
704f1d8a071SWilliam Breathitt Gray 
705f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
706f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
707f1d8a071SWilliam Breathitt Gray {
708e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
709fc069262SSyed Nayyar Waris 	unsigned int ceiling;
710fc069262SSyed Nayyar Waris 	int ret;
711fc069262SSyed Nayyar Waris 
712fc069262SSyed Nayyar Waris 	ret = kstrtouint(buf, 0, &ceiling);
713fc069262SSyed Nayyar Waris 	if (ret)
714fc069262SSyed Nayyar Waris 		return ret;
715fc069262SSyed Nayyar Waris 
716fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
717fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
718e2ff3198SWilliam Breathitt Gray 		return -ERANGE;
719fc069262SSyed Nayyar Waris 
720fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
721f1d8a071SWilliam Breathitt Gray 
722f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
723f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
724f1d8a071SWilliam Breathitt Gray 	case 1:
725f1d8a071SWilliam Breathitt Gray 	case 3:
726fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
727728246e8SWilliam Breathitt Gray 		mutex_unlock(&priv->lock);
728728246e8SWilliam Breathitt Gray 		return len;
729f1d8a071SWilliam Breathitt Gray 	}
730f1d8a071SWilliam Breathitt Gray 
731fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
732fc069262SSyed Nayyar Waris 
733728246e8SWilliam Breathitt Gray 	return -EINVAL;
734f1d8a071SWilliam Breathitt Gray }
735f1d8a071SWilliam Breathitt Gray 
736f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
737f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
738f1d8a071SWilliam Breathitt Gray {
739e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
740f1d8a071SWilliam Breathitt Gray 
741f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
742f1d8a071SWilliam Breathitt Gray }
743f1d8a071SWilliam Breathitt Gray 
744f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
745f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
746f1d8a071SWilliam Breathitt Gray {
747e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
748f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
749f1d8a071SWilliam Breathitt Gray 	bool preset_enable;
750f1d8a071SWilliam Breathitt Gray 	int ret;
751f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
752f1d8a071SWilliam Breathitt Gray 
753f1d8a071SWilliam Breathitt Gray 	ret = kstrtobool(buf, &preset_enable);
754f1d8a071SWilliam Breathitt Gray 	if (ret)
755f1d8a071SWilliam Breathitt Gray 		return ret;
756f1d8a071SWilliam Breathitt Gray 
757f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
758f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
759f1d8a071SWilliam Breathitt Gray 
760fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
761fc069262SSyed Nayyar Waris 
762f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
763f1d8a071SWilliam Breathitt Gray 
764f1d8a071SWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
765f1d8a071SWilliam Breathitt Gray 
766f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
767f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
768f1d8a071SWilliam Breathitt Gray 
769fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
770fc069262SSyed Nayyar Waris 
771f1d8a071SWilliam Breathitt Gray 	return len;
772f1d8a071SWilliam Breathitt Gray }
773f1d8a071SWilliam Breathitt Gray 
774954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
775954ab5ccSWilliam Breathitt Gray 					     struct counter_signal *signal,
776954ab5ccSWilliam Breathitt Gray 					     void *private, char *buf)
777954ab5ccSWilliam Breathitt Gray {
778e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
779954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
780708d9893SSyed Nayyar Waris 	bool disabled;
781954ab5ccSWilliam Breathitt Gray 	unsigned int status;
782954ab5ccSWilliam Breathitt Gray 	unsigned int fault;
783954ab5ccSWilliam Breathitt Gray 
784708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
785708d9893SSyed Nayyar Waris 
786708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
787708d9893SSyed Nayyar Waris 
788708d9893SSyed Nayyar Waris 	if (disabled) {
789708d9893SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
790954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
791708d9893SSyed Nayyar Waris 	}
792954ab5ccSWilliam Breathitt Gray 
793954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
794954ab5ccSWilliam Breathitt Gray 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
795954ab5ccSWilliam Breathitt Gray 
796708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
797708d9893SSyed Nayyar Waris 
798954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
799954ab5ccSWilliam Breathitt Gray 	fault = !(status & BIT(channel_id));
800954ab5ccSWilliam Breathitt Gray 
801954ab5ccSWilliam Breathitt Gray 	return sprintf(buf, "%u\n", fault);
802954ab5ccSWilliam Breathitt Gray }
803954ab5ccSWilliam Breathitt Gray 
804954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_enable_read(
805954ab5ccSWilliam Breathitt Gray 	struct counter_device *counter, struct counter_signal *signal,
806954ab5ccSWilliam Breathitt Gray 	void *private, char *buf)
807954ab5ccSWilliam Breathitt Gray {
808e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
809954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
810954ab5ccSWilliam Breathitt Gray 	const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
811954ab5ccSWilliam Breathitt Gray 
812954ab5ccSWilliam Breathitt Gray 	return sprintf(buf, "%u\n", enb);
813954ab5ccSWilliam Breathitt Gray }
814954ab5ccSWilliam Breathitt Gray 
815954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_enable_write(
816954ab5ccSWilliam Breathitt Gray 	struct counter_device *counter, struct counter_signal *signal,
817954ab5ccSWilliam Breathitt Gray 	void *private, const char *buf, size_t len)
818954ab5ccSWilliam Breathitt Gray {
819e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
820954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
821954ab5ccSWilliam Breathitt Gray 	bool enable;
822954ab5ccSWilliam Breathitt Gray 	int ret;
823954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
824954ab5ccSWilliam Breathitt Gray 
825954ab5ccSWilliam Breathitt Gray 	ret = kstrtobool(buf, &enable);
826954ab5ccSWilliam Breathitt Gray 	if (ret)
827954ab5ccSWilliam Breathitt Gray 		return ret;
828954ab5ccSWilliam Breathitt Gray 
829708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
830708d9893SSyed Nayyar Waris 
831954ab5ccSWilliam Breathitt Gray 	if (enable)
832954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
833954ab5ccSWilliam Breathitt Gray 	else
834954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
835954ab5ccSWilliam Breathitt Gray 
836954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
837954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
838954ab5ccSWilliam Breathitt Gray 
839954ab5ccSWilliam Breathitt Gray 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
840954ab5ccSWilliam Breathitt Gray 
841708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
842708d9893SSyed Nayyar Waris 
843954ab5ccSWilliam Breathitt Gray 	return len;
844954ab5ccSWilliam Breathitt Gray }
845954ab5ccSWilliam Breathitt Gray 
846de65d055SWilliam Breathitt Gray static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
847de65d055SWilliam Breathitt Gray 	struct counter_signal *signal, void *private, char *buf)
848de65d055SWilliam Breathitt Gray {
849e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
850de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
851de65d055SWilliam Breathitt Gray 
852de65d055SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
853de65d055SWilliam Breathitt Gray }
854de65d055SWilliam Breathitt Gray 
855de65d055SWilliam Breathitt Gray static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
856de65d055SWilliam Breathitt Gray 	struct counter_signal *signal, void *private, const char *buf,
857de65d055SWilliam Breathitt Gray 	size_t len)
858de65d055SWilliam Breathitt Gray {
859e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
860de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
861de65d055SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id;
862de65d055SWilliam Breathitt Gray 	u8 prescaler;
863de65d055SWilliam Breathitt Gray 	int ret;
864de65d055SWilliam Breathitt Gray 
865de65d055SWilliam Breathitt Gray 	ret = kstrtou8(buf, 0, &prescaler);
866de65d055SWilliam Breathitt Gray 	if (ret)
867de65d055SWilliam Breathitt Gray 		return ret;
868de65d055SWilliam Breathitt Gray 
869d5ed76adSSyed Nayyar Waris 	mutex_lock(&priv->lock);
870d5ed76adSSyed Nayyar Waris 
871de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
872de65d055SWilliam Breathitt Gray 
873de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
874de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
875de65d055SWilliam Breathitt Gray 
876de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
877de65d055SWilliam Breathitt Gray 	outb(prescaler, base_offset);
878de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
879de65d055SWilliam Breathitt Gray 	     base_offset + 1);
880de65d055SWilliam Breathitt Gray 
881d5ed76adSSyed Nayyar Waris 	mutex_unlock(&priv->lock);
882d5ed76adSSyed Nayyar Waris 
883de65d055SWilliam Breathitt Gray 	return len;
884de65d055SWilliam Breathitt Gray }
885de65d055SWilliam Breathitt Gray 
886de65d055SWilliam Breathitt Gray static const struct counter_signal_ext quad8_signal_ext[] = {
887de65d055SWilliam Breathitt Gray 	{
888954ab5ccSWilliam Breathitt Gray 		.name = "cable_fault",
889954ab5ccSWilliam Breathitt Gray 		.read = quad8_signal_cable_fault_read
890954ab5ccSWilliam Breathitt Gray 	},
891954ab5ccSWilliam Breathitt Gray 	{
892954ab5ccSWilliam Breathitt Gray 		.name = "cable_fault_enable",
893954ab5ccSWilliam Breathitt Gray 		.read = quad8_signal_cable_fault_enable_read,
894954ab5ccSWilliam Breathitt Gray 		.write = quad8_signal_cable_fault_enable_write
895954ab5ccSWilliam Breathitt Gray 	},
896954ab5ccSWilliam Breathitt Gray 	{
897de65d055SWilliam Breathitt Gray 		.name = "filter_clock_prescaler",
898de65d055SWilliam Breathitt Gray 		.read = quad8_signal_fck_prescaler_read,
899de65d055SWilliam Breathitt Gray 		.write = quad8_signal_fck_prescaler_write
900de65d055SWilliam Breathitt Gray 	}
901de65d055SWilliam Breathitt Gray };
902de65d055SWilliam Breathitt Gray 
903f1d8a071SWilliam Breathitt Gray static const struct counter_signal_ext quad8_index_ext[] = {
904f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
905f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity",	&quad8_index_pol_enum),
906f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
907f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
908f1d8a071SWilliam Breathitt Gray };
909f1d8a071SWilliam Breathitt Gray 
910f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
911f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
912de65d055SWilliam Breathitt Gray 	.name = (_name),			\
913de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
914de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
915f1d8a071SWilliam Breathitt Gray }
916f1d8a071SWilliam Breathitt Gray 
917f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
918f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
919f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
920f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
921f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
922f1d8a071SWilliam Breathitt Gray }
923f1d8a071SWilliam Breathitt Gray 
924f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
925f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
926f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
927f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
928f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
929f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
930f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
931f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
932f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
933f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
934f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
935f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
936f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
937f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
938f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
939f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
940f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
941f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
942f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
943f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
944f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
945f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
946f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
947f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
948f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
949f1d8a071SWilliam Breathitt Gray };
950f1d8a071SWilliam Breathitt Gray 
951f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
952f1d8a071SWilliam Breathitt Gray 	{								\
953f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
954f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
955f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
956f1d8a071SWilliam Breathitt Gray 	},								\
957f1d8a071SWilliam Breathitt Gray 	{								\
958f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
959f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
960f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
961f1d8a071SWilliam Breathitt Gray 	},								\
962f1d8a071SWilliam Breathitt Gray 	{								\
963f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
964f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
965f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
966f1d8a071SWilliam Breathitt Gray 	}								\
967f1d8a071SWilliam Breathitt Gray }
968f1d8a071SWilliam Breathitt Gray 
969f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
970f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
971f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
972f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
973f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
974f1d8a071SWilliam Breathitt Gray };
975f1d8a071SWilliam Breathitt Gray 
976f1d8a071SWilliam Breathitt Gray static const struct counter_count_ext quad8_count_ext[] = {
977f1d8a071SWilliam Breathitt Gray 	{
978f1d8a071SWilliam Breathitt Gray 		.name = "ceiling",
979f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_ceiling_read,
980f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_ceiling_write
981f1d8a071SWilliam Breathitt Gray 	},
982f1d8a071SWilliam Breathitt Gray 	{
983f1d8a071SWilliam Breathitt Gray 		.name = "floor",
984f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_floor_read
985f1d8a071SWilliam Breathitt Gray 	},
986f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
987f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
988f1d8a071SWilliam Breathitt Gray 	{
989f1d8a071SWilliam Breathitt Gray 		.name = "direction",
990f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_direction_read
991f1d8a071SWilliam Breathitt Gray 	},
992f1d8a071SWilliam Breathitt Gray 	{
993f1d8a071SWilliam Breathitt Gray 		.name = "enable",
994f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_enable_read,
995f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_enable_write
996f1d8a071SWilliam Breathitt Gray 	},
997f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
998f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
999f1d8a071SWilliam Breathitt Gray 	{
1000f1d8a071SWilliam Breathitt Gray 		.name = "preset",
1001f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_preset_read,
1002f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_preset_write
1003f1d8a071SWilliam Breathitt Gray 	},
1004f1d8a071SWilliam Breathitt Gray 	{
1005f1d8a071SWilliam Breathitt Gray 		.name = "preset_enable",
1006f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_preset_enable_read,
1007f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_preset_enable_write
1008f1d8a071SWilliam Breathitt Gray 	}
1009f1d8a071SWilliam Breathitt Gray };
1010f1d8a071SWilliam Breathitt Gray 
1011f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1012f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1013f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1014f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1015f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1016f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1017f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1018f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1019f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1020f1d8a071SWilliam Breathitt Gray }
1021f1d8a071SWilliam Breathitt Gray 
1022f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1023f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1024f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1025f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1026f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1027f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1028f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1029f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1030f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1031f1d8a071SWilliam Breathitt Gray };
1032f1d8a071SWilliam Breathitt Gray 
1033f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1034f1d8a071SWilliam Breathitt Gray {
1035e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1036f1d8a071SWilliam Breathitt Gray 	int i, j;
1037f1d8a071SWilliam Breathitt Gray 	unsigned int base_offset;
1038f1d8a071SWilliam Breathitt Gray 
1039f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1040f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1041f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1042f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1043f1d8a071SWilliam Breathitt Gray 	}
1044f1d8a071SWilliam Breathitt Gray 
1045e357e81fSWilliam Breathitt Gray 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1046e357e81fSWilliam Breathitt Gray 	if (!priv)
1047f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
1048f1d8a071SWilliam Breathitt Gray 
1049f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
1050e357e81fSWilliam Breathitt Gray 	priv->counter.name = dev_name(dev);
1051e357e81fSWilliam Breathitt Gray 	priv->counter.parent = dev;
1052e357e81fSWilliam Breathitt Gray 	priv->counter.ops = &quad8_ops;
1053e357e81fSWilliam Breathitt Gray 	priv->counter.counts = quad8_counts;
1054e357e81fSWilliam Breathitt Gray 	priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
1055e357e81fSWilliam Breathitt Gray 	priv->counter.signals = quad8_signals;
1056e357e81fSWilliam Breathitt Gray 	priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
1057e357e81fSWilliam Breathitt Gray 	priv->counter.priv = priv;
1058e357e81fSWilliam Breathitt Gray 	priv->base = base[id];
1059f1d8a071SWilliam Breathitt Gray 
1060fc069262SSyed Nayyar Waris 	/* Initialize mutex */
1061e357e81fSWilliam Breathitt Gray 	mutex_init(&priv->lock);
1062fc069262SSyed Nayyar Waris 
1063f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1064f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1065f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1066f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1067f1d8a071SWilliam Breathitt Gray 		base_offset = base[id] + 2 * i;
1068f1d8a071SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1069f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1070de65d055SWilliam Breathitt Gray 		/* Reset filter clock factor */
1071de65d055SWilliam Breathitt Gray 		outb(0, base_offset);
1072de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1073de65d055SWilliam Breathitt Gray 		     base_offset + 1);
1074de65d055SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1075de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1076f1d8a071SWilliam Breathitt Gray 		/* Reset Preset Register */
1077f1d8a071SWilliam Breathitt Gray 		for (j = 0; j < 3; j++)
1078f1d8a071SWilliam Breathitt Gray 			outb(0x00, base_offset);
1079f1d8a071SWilliam Breathitt Gray 		/* Reset Borrow, Carry, Compare, and Sign flags */
1080f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1081f1d8a071SWilliam Breathitt Gray 		/* Reset Error flag */
1082f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1083f1d8a071SWilliam Breathitt Gray 		/* Binary encoding; Normal count; non-quadrature mode */
1084f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_CMR, base_offset + 1);
1085f1d8a071SWilliam Breathitt Gray 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1086f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR, base_offset + 1);
1087f1d8a071SWilliam Breathitt Gray 		/* Disable index function; negative index polarity */
1088f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IDR, base_offset + 1);
1089f1d8a071SWilliam Breathitt Gray 	}
1090954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1091954ab5ccSWilliam Breathitt Gray 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1092f1d8a071SWilliam Breathitt Gray 	/* Enable all counters */
1093f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1094f1d8a071SWilliam Breathitt Gray 
1095e357e81fSWilliam Breathitt Gray 	return devm_counter_register(dev, &priv->counter);
1096f1d8a071SWilliam Breathitt Gray }
1097f1d8a071SWilliam Breathitt Gray 
1098f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1099f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1100f1d8a071SWilliam Breathitt Gray 	.driver = {
1101f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1102f1d8a071SWilliam Breathitt Gray 	}
1103f1d8a071SWilliam Breathitt Gray };
1104f1d8a071SWilliam Breathitt Gray 
1105f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1106f1d8a071SWilliam Breathitt Gray 
1107f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1108e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1109f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1110