xref: /openbmc/linux/drivers/counter/104-quad-8.c (revision b11eed15)
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,
100d49e6ee2SWilliam Breathitt Gray 	struct counter_signal *signal, enum counter_signal_value *val)
101f1d8a071SWilliam Breathitt Gray {
102e357e81fSWilliam 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 {
120e357e81fSWilliam 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 {
151e357e81fSWilliam 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 
196fca2534fSWilliam Breathitt Gray static const 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 {
206e357e81fSWilliam 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 {
234e357e81fSWilliam 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;
276*b11eed15SWilliam Breathitt Gray 		default:
277*b11eed15SWilliam Breathitt Gray 			/* should never reach this path */
278*b11eed15SWilliam Breathitt Gray 			mutex_unlock(&priv->lock);
279*b11eed15SWilliam Breathitt Gray 			return -EINVAL;
280f1d8a071SWilliam Breathitt Gray 		}
281f1d8a071SWilliam Breathitt Gray 	}
282f1d8a071SWilliam Breathitt Gray 
283f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
284f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
285f1d8a071SWilliam Breathitt Gray 
286fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
287fc069262SSyed Nayyar Waris 
288f1d8a071SWilliam Breathitt Gray 	return 0;
289f1d8a071SWilliam Breathitt Gray }
290f1d8a071SWilliam Breathitt Gray 
291f1d8a071SWilliam Breathitt Gray static void quad8_direction_get(struct counter_device *counter,
292f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, enum counter_count_direction *direction)
293f1d8a071SWilliam Breathitt Gray {
294e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
295f1d8a071SWilliam Breathitt Gray 	unsigned int ud_flag;
296f1d8a071SWilliam Breathitt Gray 	const unsigned int flag_addr = priv->base + 2 * count->id + 1;
297f1d8a071SWilliam Breathitt Gray 
298f1d8a071SWilliam Breathitt Gray 	/* U/D flag: nonzero = up, zero = down */
299f1d8a071SWilliam Breathitt Gray 	ud_flag = inb(flag_addr) & QUAD8_FLAG_UD;
300f1d8a071SWilliam Breathitt Gray 
301f1d8a071SWilliam Breathitt Gray 	*direction = (ud_flag) ? COUNTER_COUNT_DIRECTION_FORWARD :
302f1d8a071SWilliam Breathitt Gray 		COUNTER_COUNT_DIRECTION_BACKWARD;
303f1d8a071SWilliam Breathitt Gray }
304f1d8a071SWilliam Breathitt Gray 
305f1d8a071SWilliam Breathitt Gray enum quad8_synapse_action {
306f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_NONE = 0,
307f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_RISING_EDGE,
308f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_FALLING_EDGE,
309f1d8a071SWilliam Breathitt Gray 	QUAD8_SYNAPSE_ACTION_BOTH_EDGES
310f1d8a071SWilliam Breathitt Gray };
311f1d8a071SWilliam Breathitt Gray 
3126a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_index_actions_list[] = {
313f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
314f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE
315f1d8a071SWilliam Breathitt Gray };
316f1d8a071SWilliam Breathitt Gray 
3176a9eb0e3SWilliam Breathitt Gray static const enum counter_synapse_action quad8_synapse_actions_list[] = {
318f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_NONE] = COUNTER_SYNAPSE_ACTION_NONE,
319f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_RISING_EDGE] = COUNTER_SYNAPSE_ACTION_RISING_EDGE,
320f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_FALLING_EDGE] = COUNTER_SYNAPSE_ACTION_FALLING_EDGE,
321f1d8a071SWilliam Breathitt Gray 	[QUAD8_SYNAPSE_ACTION_BOTH_EDGES] = COUNTER_SYNAPSE_ACTION_BOTH_EDGES
322f1d8a071SWilliam Breathitt Gray };
323f1d8a071SWilliam Breathitt Gray 
324f1d8a071SWilliam Breathitt Gray static int quad8_action_get(struct counter_device *counter,
325f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, struct counter_synapse *synapse,
326f1d8a071SWilliam Breathitt Gray 	size_t *action)
327f1d8a071SWilliam Breathitt Gray {
328e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
329f1d8a071SWilliam Breathitt Gray 	int err;
330f1d8a071SWilliam Breathitt Gray 	size_t function = 0;
331f1d8a071SWilliam Breathitt Gray 	const size_t signal_a_id = count->synapses[0].signal->id;
332f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction direction;
333f1d8a071SWilliam Breathitt Gray 
334f1d8a071SWilliam Breathitt Gray 	/* Handle Index signals */
335f1d8a071SWilliam Breathitt Gray 	if (synapse->signal->id >= 16) {
336f1d8a071SWilliam Breathitt Gray 		if (priv->preset_enable[count->id])
337f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
338f1d8a071SWilliam Breathitt Gray 		else
339f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_NONE;
340f1d8a071SWilliam Breathitt Gray 
341f1d8a071SWilliam Breathitt Gray 		return 0;
342f1d8a071SWilliam Breathitt Gray 	}
343f1d8a071SWilliam Breathitt Gray 
344f1d8a071SWilliam Breathitt Gray 	err = quad8_function_get(counter, count, &function);
345f1d8a071SWilliam Breathitt Gray 	if (err)
346f1d8a071SWilliam Breathitt Gray 		return err;
347f1d8a071SWilliam Breathitt Gray 
348f1d8a071SWilliam Breathitt Gray 	/* Default action mode */
349f1d8a071SWilliam Breathitt Gray 	*action = QUAD8_SYNAPSE_ACTION_NONE;
350f1d8a071SWilliam Breathitt Gray 
351f1d8a071SWilliam Breathitt Gray 	/* Determine action mode based on current count function mode */
352f1d8a071SWilliam Breathitt Gray 	switch (function) {
353f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_PULSE_DIRECTION:
354f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
355f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
356*b11eed15SWilliam Breathitt Gray 		return 0;
357f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X1:
358f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id) {
359f1d8a071SWilliam Breathitt Gray 			quad8_direction_get(counter, count, &direction);
360f1d8a071SWilliam Breathitt Gray 
361f1d8a071SWilliam Breathitt Gray 			if (direction == COUNTER_COUNT_DIRECTION_FORWARD)
362f1d8a071SWilliam Breathitt Gray 				*action = QUAD8_SYNAPSE_ACTION_RISING_EDGE;
363f1d8a071SWilliam Breathitt Gray 			else
364f1d8a071SWilliam Breathitt Gray 				*action = QUAD8_SYNAPSE_ACTION_FALLING_EDGE;
365f1d8a071SWilliam Breathitt Gray 		}
366*b11eed15SWilliam Breathitt Gray 		return 0;
367f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X2:
368f1d8a071SWilliam Breathitt Gray 		if (synapse->signal->id == signal_a_id)
369f1d8a071SWilliam Breathitt Gray 			*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
370*b11eed15SWilliam Breathitt Gray 		return 0;
371f1d8a071SWilliam Breathitt Gray 	case QUAD8_COUNT_FUNCTION_QUADRATURE_X4:
372f1d8a071SWilliam Breathitt Gray 		*action = QUAD8_SYNAPSE_ACTION_BOTH_EDGES;
373f1d8a071SWilliam Breathitt Gray 		return 0;
374*b11eed15SWilliam Breathitt Gray 	default:
375*b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
376*b11eed15SWilliam Breathitt Gray 		return -EINVAL;
377*b11eed15SWilliam Breathitt Gray 	}
378f1d8a071SWilliam Breathitt Gray }
379f1d8a071SWilliam Breathitt Gray 
38017aa207eSYueHaibing static const struct counter_ops quad8_ops = {
381f1d8a071SWilliam Breathitt Gray 	.signal_read = quad8_signal_read,
382f1d8a071SWilliam Breathitt Gray 	.count_read = quad8_count_read,
383f1d8a071SWilliam Breathitt Gray 	.count_write = quad8_count_write,
384f1d8a071SWilliam Breathitt Gray 	.function_get = quad8_function_get,
385f1d8a071SWilliam Breathitt Gray 	.function_set = quad8_function_set,
386f1d8a071SWilliam Breathitt Gray 	.action_get = quad8_action_get
387f1d8a071SWilliam Breathitt Gray };
388f1d8a071SWilliam Breathitt Gray 
389e357e81fSWilliam Breathitt Gray static const char *const quad8_index_polarity_modes[] = {
390e357e81fSWilliam Breathitt Gray 	"negative",
391e357e81fSWilliam Breathitt Gray 	"positive"
392e357e81fSWilliam Breathitt Gray };
393e357e81fSWilliam Breathitt Gray 
394f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_get(struct counter_device *counter,
395f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t *index_polarity)
396f1d8a071SWilliam Breathitt Gray {
397e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
398f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
399f1d8a071SWilliam Breathitt Gray 
400f1d8a071SWilliam Breathitt Gray 	*index_polarity = priv->index_polarity[channel_id];
401f1d8a071SWilliam Breathitt Gray 
402f1d8a071SWilliam Breathitt Gray 	return 0;
403f1d8a071SWilliam Breathitt Gray }
404f1d8a071SWilliam Breathitt Gray 
405f1d8a071SWilliam Breathitt Gray static int quad8_index_polarity_set(struct counter_device *counter,
406f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t index_polarity)
407f1d8a071SWilliam Breathitt Gray {
408e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
409f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
410f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
411fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = index_polarity << 1;
412fc069262SSyed Nayyar Waris 
413fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
414fc069262SSyed Nayyar Waris 
415fc069262SSyed Nayyar Waris 	idr_cfg |= priv->synchronous_mode[channel_id];
416f1d8a071SWilliam Breathitt Gray 
417f1d8a071SWilliam Breathitt Gray 	priv->index_polarity[channel_id] = index_polarity;
418f1d8a071SWilliam Breathitt Gray 
419f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
420f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
421f1d8a071SWilliam Breathitt Gray 
422fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
423fc069262SSyed Nayyar Waris 
424f1d8a071SWilliam Breathitt Gray 	return 0;
425f1d8a071SWilliam Breathitt Gray }
426f1d8a071SWilliam Breathitt Gray 
427f1d8a071SWilliam Breathitt Gray static struct counter_signal_enum_ext quad8_index_pol_enum = {
428f1d8a071SWilliam Breathitt Gray 	.items = quad8_index_polarity_modes,
429f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
430f1d8a071SWilliam Breathitt Gray 	.get = quad8_index_polarity_get,
431f1d8a071SWilliam Breathitt Gray 	.set = quad8_index_polarity_set
432f1d8a071SWilliam Breathitt Gray };
433f1d8a071SWilliam Breathitt Gray 
434e357e81fSWilliam Breathitt Gray static const char *const quad8_synchronous_modes[] = {
435e357e81fSWilliam Breathitt Gray 	"non-synchronous",
436e357e81fSWilliam Breathitt Gray 	"synchronous"
437e357e81fSWilliam Breathitt Gray };
438e357e81fSWilliam Breathitt Gray 
439f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_get(struct counter_device *counter,
440f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t *synchronous_mode)
441f1d8a071SWilliam Breathitt Gray {
442e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
443f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
444f1d8a071SWilliam Breathitt Gray 
445f1d8a071SWilliam Breathitt Gray 	*synchronous_mode = priv->synchronous_mode[channel_id];
446f1d8a071SWilliam Breathitt Gray 
447f1d8a071SWilliam Breathitt Gray 	return 0;
448f1d8a071SWilliam Breathitt Gray }
449f1d8a071SWilliam Breathitt Gray 
450f1d8a071SWilliam Breathitt Gray static int quad8_synchronous_mode_set(struct counter_device *counter,
451f1d8a071SWilliam Breathitt Gray 	struct counter_signal *signal, size_t synchronous_mode)
452f1d8a071SWilliam Breathitt Gray {
453e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
454f1d8a071SWilliam Breathitt Gray 	const size_t channel_id = signal->id - 16;
455f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id + 1;
456fc069262SSyed Nayyar Waris 	unsigned int idr_cfg = synchronous_mode;
457fc069262SSyed Nayyar Waris 
458fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
459fc069262SSyed Nayyar Waris 
460fc069262SSyed Nayyar Waris 	idr_cfg |= priv->index_polarity[channel_id] << 1;
461f1d8a071SWilliam Breathitt Gray 
462f1d8a071SWilliam Breathitt Gray 	/* Index function must be non-synchronous in non-quadrature mode */
463fc069262SSyed Nayyar Waris 	if (synchronous_mode && !priv->quadrature_mode[channel_id]) {
464fc069262SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
465f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
466fc069262SSyed Nayyar Waris 	}
467f1d8a071SWilliam Breathitt Gray 
468f1d8a071SWilliam Breathitt Gray 	priv->synchronous_mode[channel_id] = synchronous_mode;
469f1d8a071SWilliam Breathitt Gray 
470f1d8a071SWilliam Breathitt Gray 	/* Load Index Control configuration to Index Control Register */
471f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
472f1d8a071SWilliam Breathitt Gray 
473fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
474fc069262SSyed Nayyar Waris 
475f1d8a071SWilliam Breathitt Gray 	return 0;
476f1d8a071SWilliam Breathitt Gray }
477f1d8a071SWilliam Breathitt Gray 
478f1d8a071SWilliam Breathitt Gray static struct counter_signal_enum_ext quad8_syn_mode_enum = {
479f1d8a071SWilliam Breathitt Gray 	.items = quad8_synchronous_modes,
480f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_synchronous_modes),
481f1d8a071SWilliam Breathitt Gray 	.get = quad8_synchronous_mode_get,
482f1d8a071SWilliam Breathitt Gray 	.set = quad8_synchronous_mode_set
483f1d8a071SWilliam Breathitt Gray };
484f1d8a071SWilliam Breathitt Gray 
485f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_floor_read(struct counter_device *counter,
486f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
487f1d8a071SWilliam Breathitt Gray {
488f1d8a071SWilliam Breathitt Gray 	/* Only a floor of 0 is supported */
489f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "0\n");
490f1d8a071SWilliam Breathitt Gray }
491f1d8a071SWilliam Breathitt Gray 
492f1d8a071SWilliam Breathitt Gray static int quad8_count_mode_get(struct counter_device *counter,
493f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t *cnt_mode)
494f1d8a071SWilliam Breathitt Gray {
495e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
496f1d8a071SWilliam Breathitt Gray 
497f1d8a071SWilliam Breathitt Gray 	/* Map 104-QUAD-8 count mode to Generic Counter count mode */
498f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
499f1d8a071SWilliam Breathitt Gray 	case 0:
500f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NORMAL;
501f1d8a071SWilliam Breathitt Gray 		break;
502f1d8a071SWilliam Breathitt Gray 	case 1:
503f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_RANGE_LIMIT;
504f1d8a071SWilliam Breathitt Gray 		break;
505f1d8a071SWilliam Breathitt Gray 	case 2:
506f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_NON_RECYCLE;
507f1d8a071SWilliam Breathitt Gray 		break;
508f1d8a071SWilliam Breathitt Gray 	case 3:
509f1d8a071SWilliam Breathitt Gray 		*cnt_mode = COUNTER_COUNT_MODE_MODULO_N;
510f1d8a071SWilliam Breathitt Gray 		break;
511f1d8a071SWilliam Breathitt Gray 	}
512f1d8a071SWilliam Breathitt Gray 
513f1d8a071SWilliam Breathitt Gray 	return 0;
514f1d8a071SWilliam Breathitt Gray }
515f1d8a071SWilliam Breathitt Gray 
516f1d8a071SWilliam Breathitt Gray static int quad8_count_mode_set(struct counter_device *counter,
517f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t cnt_mode)
518f1d8a071SWilliam Breathitt Gray {
519e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
520f1d8a071SWilliam Breathitt Gray 	unsigned int mode_cfg;
521f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
522f1d8a071SWilliam Breathitt Gray 
523f1d8a071SWilliam Breathitt Gray 	/* Map Generic Counter count mode to 104-QUAD-8 count mode */
524f1d8a071SWilliam Breathitt Gray 	switch (cnt_mode) {
525f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NORMAL:
526f1d8a071SWilliam Breathitt Gray 		cnt_mode = 0;
527f1d8a071SWilliam Breathitt Gray 		break;
528f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_RANGE_LIMIT:
529f1d8a071SWilliam Breathitt Gray 		cnt_mode = 1;
530f1d8a071SWilliam Breathitt Gray 		break;
531f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_NON_RECYCLE:
532f1d8a071SWilliam Breathitt Gray 		cnt_mode = 2;
533f1d8a071SWilliam Breathitt Gray 		break;
534f1d8a071SWilliam Breathitt Gray 	case COUNTER_COUNT_MODE_MODULO_N:
535f1d8a071SWilliam Breathitt Gray 		cnt_mode = 3;
536f1d8a071SWilliam Breathitt Gray 		break;
537*b11eed15SWilliam Breathitt Gray 	default:
538*b11eed15SWilliam Breathitt Gray 		/* should never reach this path */
539*b11eed15SWilliam Breathitt Gray 		return -EINVAL;
540f1d8a071SWilliam Breathitt Gray 	}
541f1d8a071SWilliam Breathitt Gray 
542fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
543fc069262SSyed Nayyar Waris 
544f1d8a071SWilliam Breathitt Gray 	priv->count_mode[count->id] = cnt_mode;
545f1d8a071SWilliam Breathitt Gray 
546f1d8a071SWilliam Breathitt Gray 	/* Set count mode configuration value */
547f1d8a071SWilliam Breathitt Gray 	mode_cfg = cnt_mode << 1;
548f1d8a071SWilliam Breathitt Gray 
549f1d8a071SWilliam Breathitt Gray 	/* Add quadrature mode configuration */
550f1d8a071SWilliam Breathitt Gray 	if (priv->quadrature_mode[count->id])
551f1d8a071SWilliam Breathitt Gray 		mode_cfg |= (priv->quadrature_scale[count->id] + 1) << 3;
552f1d8a071SWilliam Breathitt Gray 
553f1d8a071SWilliam Breathitt Gray 	/* Load mode configuration to Counter Mode Register */
554f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
555f1d8a071SWilliam Breathitt Gray 
556fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
557fc069262SSyed Nayyar Waris 
558f1d8a071SWilliam Breathitt Gray 	return 0;
559f1d8a071SWilliam Breathitt Gray }
560f1d8a071SWilliam Breathitt Gray 
561f1d8a071SWilliam Breathitt Gray static struct counter_count_enum_ext quad8_cnt_mode_enum = {
562f1d8a071SWilliam Breathitt Gray 	.items = counter_count_mode_str,
563f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(counter_count_mode_str),
564f1d8a071SWilliam Breathitt Gray 	.get = quad8_count_mode_get,
565f1d8a071SWilliam Breathitt Gray 	.set = quad8_count_mode_set
566f1d8a071SWilliam Breathitt Gray };
567f1d8a071SWilliam Breathitt Gray 
568f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_direction_read(struct counter_device *counter,
569f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *priv, char *buf)
570f1d8a071SWilliam Breathitt Gray {
571f1d8a071SWilliam Breathitt Gray 	enum counter_count_direction dir;
572f1d8a071SWilliam Breathitt Gray 
573f1d8a071SWilliam Breathitt Gray 	quad8_direction_get(counter, count, &dir);
574f1d8a071SWilliam Breathitt Gray 
575f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%s\n", counter_count_direction_str[dir]);
576f1d8a071SWilliam Breathitt Gray }
577f1d8a071SWilliam Breathitt Gray 
578f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_enable_read(struct counter_device *counter,
579f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
580f1d8a071SWilliam Breathitt Gray {
581e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
582f1d8a071SWilliam Breathitt Gray 
583f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->ab_enable[count->id]);
584f1d8a071SWilliam Breathitt Gray }
585f1d8a071SWilliam Breathitt Gray 
586f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_enable_write(struct counter_device *counter,
587f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
588f1d8a071SWilliam Breathitt Gray {
589e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
590f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id;
591f1d8a071SWilliam Breathitt Gray 	int err;
592f1d8a071SWilliam Breathitt Gray 	bool ab_enable;
593f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
594f1d8a071SWilliam Breathitt Gray 
595f1d8a071SWilliam Breathitt Gray 	err = kstrtobool(buf, &ab_enable);
596f1d8a071SWilliam Breathitt Gray 	if (err)
597f1d8a071SWilliam Breathitt Gray 		return err;
598f1d8a071SWilliam Breathitt Gray 
599fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
600fc069262SSyed Nayyar Waris 
601f1d8a071SWilliam Breathitt Gray 	priv->ab_enable[count->id] = ab_enable;
602f1d8a071SWilliam Breathitt Gray 
603f1d8a071SWilliam Breathitt Gray 	ior_cfg = ab_enable | priv->preset_enable[count->id] << 1;
604f1d8a071SWilliam Breathitt Gray 
605f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration */
606f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
607f1d8a071SWilliam Breathitt Gray 
608fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
609fc069262SSyed Nayyar Waris 
610f1d8a071SWilliam Breathitt Gray 	return len;
611f1d8a071SWilliam Breathitt Gray }
612f1d8a071SWilliam Breathitt Gray 
613e357e81fSWilliam Breathitt Gray static const char *const quad8_noise_error_states[] = {
614e357e81fSWilliam Breathitt Gray 	"No excessive noise is present at the count inputs",
615e357e81fSWilliam Breathitt Gray 	"Excessive noise is present at the count inputs"
616e357e81fSWilliam Breathitt Gray };
617e357e81fSWilliam Breathitt Gray 
618f1d8a071SWilliam Breathitt Gray static int quad8_error_noise_get(struct counter_device *counter,
619f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, size_t *noise_error)
620f1d8a071SWilliam Breathitt Gray {
621e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
622f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
623f1d8a071SWilliam Breathitt Gray 
624f1d8a071SWilliam Breathitt Gray 	*noise_error = !!(inb(base_offset) & QUAD8_FLAG_E);
625f1d8a071SWilliam Breathitt Gray 
626f1d8a071SWilliam Breathitt Gray 	return 0;
627f1d8a071SWilliam Breathitt Gray }
628f1d8a071SWilliam Breathitt Gray 
629f1d8a071SWilliam Breathitt Gray static struct counter_count_enum_ext quad8_error_noise_enum = {
630f1d8a071SWilliam Breathitt Gray 	.items = quad8_noise_error_states,
631f1d8a071SWilliam Breathitt Gray 	.num_items = ARRAY_SIZE(quad8_noise_error_states),
632f1d8a071SWilliam Breathitt Gray 	.get = quad8_error_noise_get
633f1d8a071SWilliam Breathitt Gray };
634f1d8a071SWilliam Breathitt Gray 
635f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_read(struct counter_device *counter,
636f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
637f1d8a071SWilliam Breathitt Gray {
638e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
639f1d8a071SWilliam Breathitt Gray 
640f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->preset[count->id]);
641f1d8a071SWilliam Breathitt Gray }
642f1d8a071SWilliam Breathitt Gray 
643e612b600SWilliam Breathitt Gray static void quad8_preset_register_set(struct quad8 *const priv, const int id,
644e612b600SWilliam Breathitt Gray 				      const unsigned int preset)
645fc069262SSyed Nayyar Waris {
646e357e81fSWilliam Breathitt Gray 	const unsigned int base_offset = priv->base + 2 * id;
647fc069262SSyed Nayyar Waris 	int i;
648fc069262SSyed Nayyar Waris 
649e357e81fSWilliam Breathitt Gray 	priv->preset[id] = preset;
650fc069262SSyed Nayyar Waris 
651fc069262SSyed Nayyar Waris 	/* Reset Byte Pointer */
652fc069262SSyed Nayyar Waris 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
653fc069262SSyed Nayyar Waris 
654fc069262SSyed Nayyar Waris 	/* Set Preset Register */
655fc069262SSyed Nayyar Waris 	for (i = 0; i < 3; i++)
656fc069262SSyed Nayyar Waris 		outb(preset >> (8 * i), base_offset);
657fc069262SSyed Nayyar Waris }
658fc069262SSyed Nayyar Waris 
659f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_write(struct counter_device *counter,
660f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
661f1d8a071SWilliam Breathitt Gray {
662e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
663f1d8a071SWilliam Breathitt Gray 	unsigned int preset;
664f1d8a071SWilliam Breathitt Gray 	int ret;
665f1d8a071SWilliam Breathitt Gray 
666f1d8a071SWilliam Breathitt Gray 	ret = kstrtouint(buf, 0, &preset);
667f1d8a071SWilliam Breathitt Gray 	if (ret)
668f1d8a071SWilliam Breathitt Gray 		return ret;
669f1d8a071SWilliam Breathitt Gray 
670f1d8a071SWilliam Breathitt Gray 	/* Only 24-bit values are supported */
671f1d8a071SWilliam Breathitt Gray 	if (preset > 0xFFFFFF)
672f1d8a071SWilliam Breathitt Gray 		return -EINVAL;
673f1d8a071SWilliam Breathitt Gray 
674fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
675f1d8a071SWilliam Breathitt Gray 
676fc069262SSyed Nayyar Waris 	quad8_preset_register_set(priv, count->id, preset);
677f1d8a071SWilliam Breathitt Gray 
678fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
679f1d8a071SWilliam Breathitt Gray 
680f1d8a071SWilliam Breathitt Gray 	return len;
681f1d8a071SWilliam Breathitt Gray }
682f1d8a071SWilliam Breathitt Gray 
683f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_ceiling_read(struct counter_device *counter,
684f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
685f1d8a071SWilliam Breathitt Gray {
686e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
687fc069262SSyed Nayyar Waris 
688fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
689f1d8a071SWilliam Breathitt Gray 
690f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
691f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
692f1d8a071SWilliam Breathitt Gray 	case 1:
693f1d8a071SWilliam Breathitt Gray 	case 3:
694fc069262SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
695fc069262SSyed Nayyar Waris 		return sprintf(buf, "%u\n", priv->preset[count->id]);
696f1d8a071SWilliam Breathitt Gray 	}
697f1d8a071SWilliam Breathitt Gray 
698fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
699fc069262SSyed Nayyar Waris 
700f1d8a071SWilliam Breathitt Gray 	/* By default 0x1FFFFFF (25 bits unsigned) is maximum count */
701f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "33554431\n");
702f1d8a071SWilliam Breathitt Gray }
703f1d8a071SWilliam Breathitt Gray 
704f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_ceiling_write(struct counter_device *counter,
705f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
706f1d8a071SWilliam Breathitt Gray {
707e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
708fc069262SSyed Nayyar Waris 	unsigned int ceiling;
709fc069262SSyed Nayyar Waris 	int ret;
710fc069262SSyed Nayyar Waris 
711fc069262SSyed Nayyar Waris 	ret = kstrtouint(buf, 0, &ceiling);
712fc069262SSyed Nayyar Waris 	if (ret)
713fc069262SSyed Nayyar Waris 		return ret;
714fc069262SSyed Nayyar Waris 
715fc069262SSyed Nayyar Waris 	/* Only 24-bit values are supported */
716fc069262SSyed Nayyar Waris 	if (ceiling > 0xFFFFFF)
717fc069262SSyed Nayyar Waris 		return -EINVAL;
718fc069262SSyed Nayyar Waris 
719fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
720f1d8a071SWilliam Breathitt Gray 
721f1d8a071SWilliam Breathitt Gray 	/* Range Limit and Modulo-N count modes use preset value as ceiling */
722f1d8a071SWilliam Breathitt Gray 	switch (priv->count_mode[count->id]) {
723f1d8a071SWilliam Breathitt Gray 	case 1:
724f1d8a071SWilliam Breathitt Gray 	case 3:
725fc069262SSyed Nayyar Waris 		quad8_preset_register_set(priv, count->id, ceiling);
726728246e8SWilliam Breathitt Gray 		mutex_unlock(&priv->lock);
727728246e8SWilliam Breathitt Gray 		return len;
728f1d8a071SWilliam Breathitt Gray 	}
729f1d8a071SWilliam Breathitt Gray 
730fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
731fc069262SSyed Nayyar Waris 
732728246e8SWilliam Breathitt Gray 	return -EINVAL;
733f1d8a071SWilliam Breathitt Gray }
734f1d8a071SWilliam Breathitt Gray 
735f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_enable_read(struct counter_device *counter,
736f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, char *buf)
737f1d8a071SWilliam Breathitt Gray {
738e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
739f1d8a071SWilliam Breathitt Gray 
740f1d8a071SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", !priv->preset_enable[count->id]);
741f1d8a071SWilliam Breathitt Gray }
742f1d8a071SWilliam Breathitt Gray 
743f1d8a071SWilliam Breathitt Gray static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
744f1d8a071SWilliam Breathitt Gray 	struct counter_count *count, void *private, const char *buf, size_t len)
745f1d8a071SWilliam Breathitt Gray {
746e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
747f1d8a071SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * count->id + 1;
748f1d8a071SWilliam Breathitt Gray 	bool preset_enable;
749f1d8a071SWilliam Breathitt Gray 	int ret;
750f1d8a071SWilliam Breathitt Gray 	unsigned int ior_cfg;
751f1d8a071SWilliam Breathitt Gray 
752f1d8a071SWilliam Breathitt Gray 	ret = kstrtobool(buf, &preset_enable);
753f1d8a071SWilliam Breathitt Gray 	if (ret)
754f1d8a071SWilliam Breathitt Gray 		return ret;
755f1d8a071SWilliam Breathitt Gray 
756f1d8a071SWilliam Breathitt Gray 	/* Preset enable is active low in Input/Output Control register */
757f1d8a071SWilliam Breathitt Gray 	preset_enable = !preset_enable;
758f1d8a071SWilliam Breathitt Gray 
759fc069262SSyed Nayyar Waris 	mutex_lock(&priv->lock);
760fc069262SSyed Nayyar Waris 
761f1d8a071SWilliam Breathitt Gray 	priv->preset_enable[count->id] = preset_enable;
762f1d8a071SWilliam Breathitt Gray 
763f1d8a071SWilliam Breathitt Gray 	ior_cfg = priv->ab_enable[count->id] | (unsigned int)preset_enable << 1;
764f1d8a071SWilliam Breathitt Gray 
765f1d8a071SWilliam Breathitt Gray 	/* Load I/O control configuration to Input / Output Control Register */
766f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
767f1d8a071SWilliam Breathitt Gray 
768fc069262SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
769fc069262SSyed Nayyar Waris 
770f1d8a071SWilliam Breathitt Gray 	return len;
771f1d8a071SWilliam Breathitt Gray }
772f1d8a071SWilliam Breathitt Gray 
773954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
774954ab5ccSWilliam Breathitt Gray 					     struct counter_signal *signal,
775954ab5ccSWilliam Breathitt Gray 					     void *private, char *buf)
776954ab5ccSWilliam Breathitt Gray {
777e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
778954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
779708d9893SSyed Nayyar Waris 	bool disabled;
780954ab5ccSWilliam Breathitt Gray 	unsigned int status;
781954ab5ccSWilliam Breathitt Gray 	unsigned int fault;
782954ab5ccSWilliam Breathitt Gray 
783708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
784708d9893SSyed Nayyar Waris 
785708d9893SSyed Nayyar Waris 	disabled = !(priv->cable_fault_enable & BIT(channel_id));
786708d9893SSyed Nayyar Waris 
787708d9893SSyed Nayyar Waris 	if (disabled) {
788708d9893SSyed Nayyar Waris 		mutex_unlock(&priv->lock);
789954ab5ccSWilliam Breathitt Gray 		return -EINVAL;
790708d9893SSyed Nayyar Waris 	}
791954ab5ccSWilliam Breathitt Gray 
792954ab5ccSWilliam Breathitt Gray 	/* Logic 0 = cable fault */
793954ab5ccSWilliam Breathitt Gray 	status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
794954ab5ccSWilliam Breathitt Gray 
795708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
796708d9893SSyed Nayyar Waris 
797954ab5ccSWilliam Breathitt Gray 	/* Mask respective channel and invert logic */
798954ab5ccSWilliam Breathitt Gray 	fault = !(status & BIT(channel_id));
799954ab5ccSWilliam Breathitt Gray 
800954ab5ccSWilliam Breathitt Gray 	return sprintf(buf, "%u\n", fault);
801954ab5ccSWilliam Breathitt Gray }
802954ab5ccSWilliam Breathitt Gray 
803954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_enable_read(
804954ab5ccSWilliam Breathitt Gray 	struct counter_device *counter, struct counter_signal *signal,
805954ab5ccSWilliam Breathitt Gray 	void *private, char *buf)
806954ab5ccSWilliam Breathitt Gray {
807e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
808954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
809954ab5ccSWilliam Breathitt Gray 	const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
810954ab5ccSWilliam Breathitt Gray 
811954ab5ccSWilliam Breathitt Gray 	return sprintf(buf, "%u\n", enb);
812954ab5ccSWilliam Breathitt Gray }
813954ab5ccSWilliam Breathitt Gray 
814954ab5ccSWilliam Breathitt Gray static ssize_t quad8_signal_cable_fault_enable_write(
815954ab5ccSWilliam Breathitt Gray 	struct counter_device *counter, struct counter_signal *signal,
816954ab5ccSWilliam Breathitt Gray 	void *private, const char *buf, size_t len)
817954ab5ccSWilliam Breathitt Gray {
818e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
819954ab5ccSWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
820954ab5ccSWilliam Breathitt Gray 	bool enable;
821954ab5ccSWilliam Breathitt Gray 	int ret;
822954ab5ccSWilliam Breathitt Gray 	unsigned int cable_fault_enable;
823954ab5ccSWilliam Breathitt Gray 
824954ab5ccSWilliam Breathitt Gray 	ret = kstrtobool(buf, &enable);
825954ab5ccSWilliam Breathitt Gray 	if (ret)
826954ab5ccSWilliam Breathitt Gray 		return ret;
827954ab5ccSWilliam Breathitt Gray 
828708d9893SSyed Nayyar Waris 	mutex_lock(&priv->lock);
829708d9893SSyed Nayyar Waris 
830954ab5ccSWilliam Breathitt Gray 	if (enable)
831954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable |= BIT(channel_id);
832954ab5ccSWilliam Breathitt Gray 	else
833954ab5ccSWilliam Breathitt Gray 		priv->cable_fault_enable &= ~BIT(channel_id);
834954ab5ccSWilliam Breathitt Gray 
835954ab5ccSWilliam Breathitt Gray 	/* Enable is active low in Differential Encoder Cable Status register */
836954ab5ccSWilliam Breathitt Gray 	cable_fault_enable = ~priv->cable_fault_enable;
837954ab5ccSWilliam Breathitt Gray 
838954ab5ccSWilliam Breathitt Gray 	outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
839954ab5ccSWilliam Breathitt Gray 
840708d9893SSyed Nayyar Waris 	mutex_unlock(&priv->lock);
841708d9893SSyed Nayyar Waris 
842954ab5ccSWilliam Breathitt Gray 	return len;
843954ab5ccSWilliam Breathitt Gray }
844954ab5ccSWilliam Breathitt Gray 
845de65d055SWilliam Breathitt Gray static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
846de65d055SWilliam Breathitt Gray 	struct counter_signal *signal, void *private, char *buf)
847de65d055SWilliam Breathitt Gray {
848e357e81fSWilliam Breathitt Gray 	const struct quad8 *const priv = counter->priv;
849de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
850de65d055SWilliam Breathitt Gray 
851de65d055SWilliam Breathitt Gray 	return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
852de65d055SWilliam Breathitt Gray }
853de65d055SWilliam Breathitt Gray 
854de65d055SWilliam Breathitt Gray static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
855de65d055SWilliam Breathitt Gray 	struct counter_signal *signal, void *private, const char *buf,
856de65d055SWilliam Breathitt Gray 	size_t len)
857de65d055SWilliam Breathitt Gray {
858e357e81fSWilliam Breathitt Gray 	struct quad8 *const priv = counter->priv;
859de65d055SWilliam Breathitt Gray 	const size_t channel_id = signal->id / 2;
860de65d055SWilliam Breathitt Gray 	const int base_offset = priv->base + 2 * channel_id;
861de65d055SWilliam Breathitt Gray 	u8 prescaler;
862de65d055SWilliam Breathitt Gray 	int ret;
863de65d055SWilliam Breathitt Gray 
864de65d055SWilliam Breathitt Gray 	ret = kstrtou8(buf, 0, &prescaler);
865de65d055SWilliam Breathitt Gray 	if (ret)
866de65d055SWilliam Breathitt Gray 		return ret;
867de65d055SWilliam Breathitt Gray 
868d5ed76adSSyed Nayyar Waris 	mutex_lock(&priv->lock);
869d5ed76adSSyed Nayyar Waris 
870de65d055SWilliam Breathitt Gray 	priv->fck_prescaler[channel_id] = prescaler;
871de65d055SWilliam Breathitt Gray 
872de65d055SWilliam Breathitt Gray 	/* Reset Byte Pointer */
873de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
874de65d055SWilliam Breathitt Gray 
875de65d055SWilliam Breathitt Gray 	/* Set filter clock factor */
876de65d055SWilliam Breathitt Gray 	outb(prescaler, base_offset);
877de65d055SWilliam Breathitt Gray 	outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
878de65d055SWilliam Breathitt Gray 	     base_offset + 1);
879de65d055SWilliam Breathitt Gray 
880d5ed76adSSyed Nayyar Waris 	mutex_unlock(&priv->lock);
881d5ed76adSSyed Nayyar Waris 
882de65d055SWilliam Breathitt Gray 	return len;
883de65d055SWilliam Breathitt Gray }
884de65d055SWilliam Breathitt Gray 
885de65d055SWilliam Breathitt Gray static const struct counter_signal_ext quad8_signal_ext[] = {
886de65d055SWilliam Breathitt Gray 	{
887954ab5ccSWilliam Breathitt Gray 		.name = "cable_fault",
888954ab5ccSWilliam Breathitt Gray 		.read = quad8_signal_cable_fault_read
889954ab5ccSWilliam Breathitt Gray 	},
890954ab5ccSWilliam Breathitt Gray 	{
891954ab5ccSWilliam Breathitt Gray 		.name = "cable_fault_enable",
892954ab5ccSWilliam Breathitt Gray 		.read = quad8_signal_cable_fault_enable_read,
893954ab5ccSWilliam Breathitt Gray 		.write = quad8_signal_cable_fault_enable_write
894954ab5ccSWilliam Breathitt Gray 	},
895954ab5ccSWilliam Breathitt Gray 	{
896de65d055SWilliam Breathitt Gray 		.name = "filter_clock_prescaler",
897de65d055SWilliam Breathitt Gray 		.read = quad8_signal_fck_prescaler_read,
898de65d055SWilliam Breathitt Gray 		.write = quad8_signal_fck_prescaler_write
899de65d055SWilliam Breathitt Gray 	}
900de65d055SWilliam Breathitt Gray };
901de65d055SWilliam Breathitt Gray 
902f1d8a071SWilliam Breathitt Gray static const struct counter_signal_ext quad8_index_ext[] = {
903f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
904f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity",	&quad8_index_pol_enum),
905f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM("synchronous_mode", &quad8_syn_mode_enum),
906f1d8a071SWilliam Breathitt Gray 	COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
907f1d8a071SWilliam Breathitt Gray };
908f1d8a071SWilliam Breathitt Gray 
909f1d8a071SWilliam Breathitt Gray #define QUAD8_QUAD_SIGNAL(_id, _name) {		\
910f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
911de65d055SWilliam Breathitt Gray 	.name = (_name),			\
912de65d055SWilliam Breathitt Gray 	.ext = quad8_signal_ext,		\
913de65d055SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_signal_ext)	\
914f1d8a071SWilliam Breathitt Gray }
915f1d8a071SWilliam Breathitt Gray 
916f1d8a071SWilliam Breathitt Gray #define	QUAD8_INDEX_SIGNAL(_id, _name) {	\
917f1d8a071SWilliam Breathitt Gray 	.id = (_id),				\
918f1d8a071SWilliam Breathitt Gray 	.name = (_name),			\
919f1d8a071SWilliam Breathitt Gray 	.ext = quad8_index_ext,			\
920f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_index_ext)	\
921f1d8a071SWilliam Breathitt Gray }
922f1d8a071SWilliam Breathitt Gray 
923f1d8a071SWilliam Breathitt Gray static struct counter_signal quad8_signals[] = {
924f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(0, "Channel 1 Quadrature A"),
925f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(1, "Channel 1 Quadrature B"),
926f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(2, "Channel 2 Quadrature A"),
927f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(3, "Channel 2 Quadrature B"),
928f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(4, "Channel 3 Quadrature A"),
929f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(5, "Channel 3 Quadrature B"),
930f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(6, "Channel 4 Quadrature A"),
931f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(7, "Channel 4 Quadrature B"),
932f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(8, "Channel 5 Quadrature A"),
933f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(9, "Channel 5 Quadrature B"),
934f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(10, "Channel 6 Quadrature A"),
935f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(11, "Channel 6 Quadrature B"),
936f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(12, "Channel 7 Quadrature A"),
937f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(13, "Channel 7 Quadrature B"),
938f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(14, "Channel 8 Quadrature A"),
939f1d8a071SWilliam Breathitt Gray 	QUAD8_QUAD_SIGNAL(15, "Channel 8 Quadrature B"),
940f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(16, "Channel 1 Index"),
941f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(17, "Channel 2 Index"),
942f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(18, "Channel 3 Index"),
943f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(19, "Channel 4 Index"),
944f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(20, "Channel 5 Index"),
945f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(21, "Channel 6 Index"),
946f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(22, "Channel 7 Index"),
947f1d8a071SWilliam Breathitt Gray 	QUAD8_INDEX_SIGNAL(23, "Channel 8 Index")
948f1d8a071SWilliam Breathitt Gray };
949f1d8a071SWilliam Breathitt Gray 
950f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT_SYNAPSES(_id) {					\
951f1d8a071SWilliam Breathitt Gray 	{								\
952f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
953f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
954f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id)			\
955f1d8a071SWilliam Breathitt Gray 	},								\
956f1d8a071SWilliam Breathitt Gray 	{								\
957f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_synapse_actions_list,		\
958f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_synapse_actions_list),	\
959f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 1			\
960f1d8a071SWilliam Breathitt Gray 	},								\
961f1d8a071SWilliam Breathitt Gray 	{								\
962f1d8a071SWilliam Breathitt Gray 		.actions_list = quad8_index_actions_list,		\
963f1d8a071SWilliam Breathitt Gray 		.num_actions = ARRAY_SIZE(quad8_index_actions_list),	\
964f1d8a071SWilliam Breathitt Gray 		.signal = quad8_signals + 2 * (_id) + 16		\
965f1d8a071SWilliam Breathitt Gray 	}								\
966f1d8a071SWilliam Breathitt Gray }
967f1d8a071SWilliam Breathitt Gray 
968f1d8a071SWilliam Breathitt Gray static struct counter_synapse quad8_count_synapses[][3] = {
969f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(0), QUAD8_COUNT_SYNAPSES(1),
970f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(2), QUAD8_COUNT_SYNAPSES(3),
971f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(4), QUAD8_COUNT_SYNAPSES(5),
972f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT_SYNAPSES(6), QUAD8_COUNT_SYNAPSES(7)
973f1d8a071SWilliam Breathitt Gray };
974f1d8a071SWilliam Breathitt Gray 
975f1d8a071SWilliam Breathitt Gray static const struct counter_count_ext quad8_count_ext[] = {
976f1d8a071SWilliam Breathitt Gray 	{
977f1d8a071SWilliam Breathitt Gray 		.name = "ceiling",
978f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_ceiling_read,
979f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_ceiling_write
980f1d8a071SWilliam Breathitt Gray 	},
981f1d8a071SWilliam Breathitt Gray 	{
982f1d8a071SWilliam Breathitt Gray 		.name = "floor",
983f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_floor_read
984f1d8a071SWilliam Breathitt Gray 	},
985f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM("count_mode", &quad8_cnt_mode_enum),
986f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM_AVAILABLE("count_mode", &quad8_cnt_mode_enum),
987f1d8a071SWilliam Breathitt Gray 	{
988f1d8a071SWilliam Breathitt Gray 		.name = "direction",
989f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_direction_read
990f1d8a071SWilliam Breathitt Gray 	},
991f1d8a071SWilliam Breathitt Gray 	{
992f1d8a071SWilliam Breathitt Gray 		.name = "enable",
993f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_enable_read,
994f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_enable_write
995f1d8a071SWilliam Breathitt Gray 	},
996f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM("error_noise", &quad8_error_noise_enum),
997f1d8a071SWilliam Breathitt Gray 	COUNTER_COUNT_ENUM_AVAILABLE("error_noise", &quad8_error_noise_enum),
998f1d8a071SWilliam Breathitt Gray 	{
999f1d8a071SWilliam Breathitt Gray 		.name = "preset",
1000f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_preset_read,
1001f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_preset_write
1002f1d8a071SWilliam Breathitt Gray 	},
1003f1d8a071SWilliam Breathitt Gray 	{
1004f1d8a071SWilliam Breathitt Gray 		.name = "preset_enable",
1005f1d8a071SWilliam Breathitt Gray 		.read = quad8_count_preset_enable_read,
1006f1d8a071SWilliam Breathitt Gray 		.write = quad8_count_preset_enable_write
1007f1d8a071SWilliam Breathitt Gray 	}
1008f1d8a071SWilliam Breathitt Gray };
1009f1d8a071SWilliam Breathitt Gray 
1010f1d8a071SWilliam Breathitt Gray #define QUAD8_COUNT(_id, _cntname) {					\
1011f1d8a071SWilliam Breathitt Gray 	.id = (_id),							\
1012f1d8a071SWilliam Breathitt Gray 	.name = (_cntname),						\
1013f1d8a071SWilliam Breathitt Gray 	.functions_list = quad8_count_functions_list,			\
1014f1d8a071SWilliam Breathitt Gray 	.num_functions = ARRAY_SIZE(quad8_count_functions_list),	\
1015f1d8a071SWilliam Breathitt Gray 	.synapses = quad8_count_synapses[(_id)],			\
1016f1d8a071SWilliam Breathitt Gray 	.num_synapses =	2,						\
1017f1d8a071SWilliam Breathitt Gray 	.ext = quad8_count_ext,						\
1018f1d8a071SWilliam Breathitt Gray 	.num_ext = ARRAY_SIZE(quad8_count_ext)				\
1019f1d8a071SWilliam Breathitt Gray }
1020f1d8a071SWilliam Breathitt Gray 
1021f1d8a071SWilliam Breathitt Gray static struct counter_count quad8_counts[] = {
1022f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(0, "Channel 1 Count"),
1023f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(1, "Channel 2 Count"),
1024f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(2, "Channel 3 Count"),
1025f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(3, "Channel 4 Count"),
1026f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(4, "Channel 5 Count"),
1027f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(5, "Channel 6 Count"),
1028f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(6, "Channel 7 Count"),
1029f1d8a071SWilliam Breathitt Gray 	QUAD8_COUNT(7, "Channel 8 Count")
1030f1d8a071SWilliam Breathitt Gray };
1031f1d8a071SWilliam Breathitt Gray 
1032f1d8a071SWilliam Breathitt Gray static int quad8_probe(struct device *dev, unsigned int id)
1033f1d8a071SWilliam Breathitt Gray {
1034e357e81fSWilliam Breathitt Gray 	struct quad8 *priv;
1035f1d8a071SWilliam Breathitt Gray 	int i, j;
1036f1d8a071SWilliam Breathitt Gray 	unsigned int base_offset;
1037f1d8a071SWilliam Breathitt Gray 
1038f1d8a071SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) {
1039f1d8a071SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
1040f1d8a071SWilliam Breathitt Gray 			base[id], base[id] + QUAD8_EXTENT);
1041f1d8a071SWilliam Breathitt Gray 		return -EBUSY;
1042f1d8a071SWilliam Breathitt Gray 	}
1043f1d8a071SWilliam Breathitt Gray 
1044e357e81fSWilliam Breathitt Gray 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
1045e357e81fSWilliam Breathitt Gray 	if (!priv)
1046f1d8a071SWilliam Breathitt Gray 		return -ENOMEM;
1047f1d8a071SWilliam Breathitt Gray 
1048f1d8a071SWilliam Breathitt Gray 	/* Initialize Counter device and driver data */
1049e357e81fSWilliam Breathitt Gray 	priv->counter.name = dev_name(dev);
1050e357e81fSWilliam Breathitt Gray 	priv->counter.parent = dev;
1051e357e81fSWilliam Breathitt Gray 	priv->counter.ops = &quad8_ops;
1052e357e81fSWilliam Breathitt Gray 	priv->counter.counts = quad8_counts;
1053e357e81fSWilliam Breathitt Gray 	priv->counter.num_counts = ARRAY_SIZE(quad8_counts);
1054e357e81fSWilliam Breathitt Gray 	priv->counter.signals = quad8_signals;
1055e357e81fSWilliam Breathitt Gray 	priv->counter.num_signals = ARRAY_SIZE(quad8_signals);
1056e357e81fSWilliam Breathitt Gray 	priv->counter.priv = priv;
1057e357e81fSWilliam Breathitt Gray 	priv->base = base[id];
1058f1d8a071SWilliam Breathitt Gray 
1059fc069262SSyed Nayyar Waris 	/* Initialize mutex */
1060e357e81fSWilliam Breathitt Gray 	mutex_init(&priv->lock);
1061fc069262SSyed Nayyar Waris 
1062f1d8a071SWilliam Breathitt Gray 	/* Reset all counters and disable interrupt function */
1063f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1064f1d8a071SWilliam Breathitt Gray 	/* Set initial configuration for all counters */
1065f1d8a071SWilliam Breathitt Gray 	for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
1066f1d8a071SWilliam Breathitt Gray 		base_offset = base[id] + 2 * i;
1067f1d8a071SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1068f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1069de65d055SWilliam Breathitt Gray 		/* Reset filter clock factor */
1070de65d055SWilliam Breathitt Gray 		outb(0, base_offset);
1071de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
1072de65d055SWilliam Breathitt Gray 		     base_offset + 1);
1073de65d055SWilliam Breathitt Gray 		/* Reset Byte Pointer */
1074de65d055SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
1075f1d8a071SWilliam Breathitt Gray 		/* Reset Preset Register */
1076f1d8a071SWilliam Breathitt Gray 		for (j = 0; j < 3; j++)
1077f1d8a071SWilliam Breathitt Gray 			outb(0x00, base_offset);
1078f1d8a071SWilliam Breathitt Gray 		/* Reset Borrow, Carry, Compare, and Sign flags */
1079f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
1080f1d8a071SWilliam Breathitt Gray 		/* Reset Error flag */
1081f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
1082f1d8a071SWilliam Breathitt Gray 		/* Binary encoding; Normal count; non-quadrature mode */
1083f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_CMR, base_offset + 1);
1084f1d8a071SWilliam Breathitt Gray 		/* Disable A and B inputs; preset on index; FLG1 as Carry */
1085f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IOR, base_offset + 1);
1086f1d8a071SWilliam Breathitt Gray 		/* Disable index function; negative index polarity */
1087f1d8a071SWilliam Breathitt Gray 		outb(QUAD8_CTR_IDR, base_offset + 1);
1088f1d8a071SWilliam Breathitt Gray 	}
1089954ab5ccSWilliam Breathitt Gray 	/* Disable Differential Encoder Cable Status for all channels */
1090954ab5ccSWilliam Breathitt Gray 	outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
1091f1d8a071SWilliam Breathitt Gray 	/* Enable all counters */
1092f1d8a071SWilliam Breathitt Gray 	outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
1093f1d8a071SWilliam Breathitt Gray 
1094e357e81fSWilliam Breathitt Gray 	return devm_counter_register(dev, &priv->counter);
1095f1d8a071SWilliam Breathitt Gray }
1096f1d8a071SWilliam Breathitt Gray 
1097f1d8a071SWilliam Breathitt Gray static struct isa_driver quad8_driver = {
1098f1d8a071SWilliam Breathitt Gray 	.probe = quad8_probe,
1099f1d8a071SWilliam Breathitt Gray 	.driver = {
1100f1d8a071SWilliam Breathitt Gray 		.name = "104-quad-8"
1101f1d8a071SWilliam Breathitt Gray 	}
1102f1d8a071SWilliam Breathitt Gray };
1103f1d8a071SWilliam Breathitt Gray 
1104f1d8a071SWilliam Breathitt Gray module_isa_driver(quad8_driver, num_quad8);
1105f1d8a071SWilliam Breathitt Gray 
1106f1d8a071SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
1107e357e81fSWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver");
1108f1d8a071SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
1109