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